在编译过程中对字面字符串进行加密






4.87/5 (22投票s)
CXR 允许您在编译时创建加密的字符串。
引言
您是否曾希望有一种方法可以使用 string
s 来表示应用程序中的消息、密码或其他文本,而无需使用字面 string
,因为您担心有人会在您的 EXE 中找到这些 string
s?别再烦恼了,CXR 来了。
它做什么?
CXR 是文本解析器、流密码和 C 源文件生成器的组合。您可以在正常的编码过程中,在 ".CXR" 文件中定义字面文本字符串,使用标准的 C/C++ 结构。然后,当您构建项目时,CXR 程序将加密这些字符串,并生成一个新.CPP 文件,该文件将自动编译到您的项目中。这个过程有点像您在处理 ATL 项目时处理.IDL 文件的方式。
什么?
这是一个例子
您创建一个包含您的 string
定义和一个唯一密码的 .CXR 文件。
////////////////////
// my .CRX file
//
// here is my password definition:
// CXRP = "SexyBeast"
//
// here are some strings:
// my first string
const char* pString1 = _CXR("AbcdEfg1234 blah\tblah");
// string #2
const char* pString2
= _CXR("This is a long one, not that it should matter...");
正如您所见,与标准 C/C++ 唯一的区别是 _CXR
说明符。带密码的注释行是必需的,任何您想要加密的文本都必须包含在 _CXR(...)
说明符内。文件中的任何其他内容都将原封不动地复制到输出 .CPP 中。所以,这就是设置 .CXR 文件的方法。当您构建项目时,CXR 解析器将读取此文件并生成一个 .CPP 文件,看起来像这样:
///////////////////////////
#ifdef _USING_CXR
// my first string
const char* pString1 = "ab63103ff470cb642b7c319cb56e2dbd591b63a93cf88a";
#else
const char* pString1 = _CXR("AbcdEfg1234 blah\tblah"); // my first string
#endif
///////////////////////////
#ifdef _USING_CXR
// string #2
const char* pString2 =
"baff195a3b712e15ee7af636065910969bb24997c49c6d0cc6a40d3ec1...";
#else
// string #2
const char* pString2 =
_CXR("This is a long one, not that it should matter...");
#endif
...more stuff below...
搞定。CXR 解析器已经加密了您的 string
s。
好的,我如何取回我的字符串?
“...下面的更多内容...”实际上是解密器代码。该解密器代码将与您提供的 CXR 密码一起编译到您的项目中。所以,您需要做的就是这个:
CString csString1 = _CRX(pString1);
// pString1 = "ab63103ff470cb642b7c319cb56e2dbd591b63a93cf88a"
// and now csString1 = "AbcdEfg1234 blah\tblah";
请注意 #ifdef _USING_CXR
标签。由于这些标签的存在,您可以通过简单地更改一个 #define 来禁用 CXR string
s——以便于测试。如果未定义 _USING_CXR
,则所有 string
s 都将恢复为未加密形式,"_CXR
" 将变成一个不起作用的宏。如果定义了 _USING_CXR
,您的 string
s 将采用加密形式,并且 _CXR
宏将调用解密代码。这(几乎)是完全无缝的。
不可能这么容易吧?
嗯,确实是几乎那么容易。您需要遵循一些步骤将您的 .CRX 文件添加到项目中并启用 CXR 解析器。但是,这些都是一次性的操作。一旦您第一次设置好,您就再也不用做第二次了。
设置
- 构建 CXR.EXE 并将其放在您的路径中的某个位置(可能在您的 WinNT 或 Windows 文件夹中)。
- 创建您的 .CRX 文件。创建一个包含您的密码定义和字面文本字符串的文件。请遵循以下规则:
- .CXR 文件必须包含一个形式为的密码行:
// CXRP = "MyPasswordString"
其中密码string
是您想要的任何string
。"// CXRP =
" 部分是必需的。 - 所有要加密的文本
strings
必须包含在_CXR(...)
标签中。 _CXR(...)
标签不能跨越多行。- CXR 目前不支持 Unicode。您的所有
string
s 都将被编码器视为 ANSIstring
s。在 Unicode 构建中使用它可能会给您带来巨大的麻烦。Unicode 支持计划在未来实现,但目前还不支持。
- .CXR 文件必须包含一个形式为的密码行:
- 为您的
string
s 创建一个 .H 文件。实际上,这只是一个基本的 C/C++ 问题。CXR 将生成一个包含您编码的string
s 的 .CPP 文件。但是,与任何外部string
s 一样,如果您想在代码中使用它们,您必须在某处定义它们。在上面的示例中,我会创建一个名为 "strings.h" 的文件并添加以下内容:extern const char* pString1; extern const char* pString2;
- 将 .CXR 文件添加到您的项目中。您必须手动完成(右键单击文件视图中的项目,选择 .CXR 文件等)。
- 将一个新的 .CPP 文件添加到您的项目中。CXR 解析器将为您生成一个 .CPP 文件。但是,您必须告诉编译器编译它。所以,将另一个文件添加到您的项目中。如果您的 .CXR 文件名为 "Strings.CXR",请将 "Strings.CPP" 添加到您的项目中 - 只需在文件对话框弹出时键入名称即可。由于此文件尚不存在,Visual Studio 将询问您是否要添加对它的引用。选择 **是**。第一次构建时,将创建 .CPP 文件。
- 设置自定义构建选项。在 **项目 / 设置** 菜单中找到您的 .CXR 文件。选中 "始终使用自定义构建步骤"。在 **自定义构建** 选项卡上,在 "命令" 部分,输入以下内容:
cxr.exe -i $(InputPath) -o $(ProjDir)\$(InputName).cpp
这将导致 Visual Studio 调用 CXR.EXE,以您的 .CXR 文件名作为输入,并以相同的文件名(但扩展名为 .CPP)作为输出。在 "输出" 部分,输入:
$(ProjDir)\$(InputName).cpp
这会告诉编译器,当 .CXR 文件更改时,此文件需要重新编译。 - 将 cxr_inc.h 添加到您的项目中,并在您想要使用加密
string
s 的每个文件中添加#include "cxr_inc.h"
。在示例 "Test" 项目中有一个该文件的副本。但它看起来就像这样:#ifndef CRXHeaderH #define CRXHeaderH #define _USING_CXR #ifndef _USING_CXR #define _CXR(x) x #else #define _CXR(x) __CXRDecrypt(x) extern CString __CXRDecrypt(const char *pIn); #endif #endif
此文件定义了您需要使用的宏来获取您的string
s。如果出于任何原因您想使用未加密的string
s,这也是一个关闭_USING_CXR
宏的好地方。 - 就这样!我知道看起来很多。但是,再说一遍,之前的步骤只需要做一次。设置完成后,您与 CXR 的唯一交互将是编辑和访问您的
string
s。
详细说明
CXR 非常简单。解析器会扫描 .CXR 输入文件,查找两样东西:1. 密码行和 2. _CXR(...)
标签内的带引号的 string
s。其他任何东西都会被原样复制到输出中。当它找到一个 _CXR
标签时,它会使用您的密码加密 string
,将加密数据转换为可打印字符,并将加密版本与原始版本一起输出在 #ifdef...#endif
块中。解析器有点笨;它不理解很多 C/C++;它只知道查找密码和 _CXR("...")
,这就是它不支持多行文本和 Unicode 的原因。不过,它确实理解 C/C++ 字面 string
s 的语法(包括 K&R v2 中记录的所有转义符)。
加密代码基于 Crypto++ 中的快速小巧的 Sapphire II 流密码。XOR 同样有效,因为我们只关心 混淆 string
s,而不是防止它们免受解密攻击。
你一定是傻瓜。这拦不住破解者
不,我不是傻瓜。我知道这本身并不能阻止破解者。这只是完整防御的一部分。当然,如果他们找到并观察 CXR 解密代码,他们就能看到解密后的 string
s;但他们必须知道去寻找它并找到它,才能看到。CXR 真正做的是阻止他们通过简单地扫描 .EXE 的 string
来找到您的 "注册完成!" 和 "您的试用期已过期!" 消息。防范破解者(就像破解本身一样)是一门黑魔法,而不是科学。在我看来,您能给破解者制造的每一个曲折都有可能让他们放弃并转向下一个目标。您能设置的每一个障碍都是好的。
更新
- 2002年8月9日
- 将字符编码从十六进制数字(1,2,3..f)更改为基于偏移量的。这能更好地混淆
string
s。 - 修复了演示包
- 将字符编码从十六进制数字(1,2,3..f)更改为基于偏移量的。这能更好地混淆
- 2005年3月17日
- 更新了源代码