给Windows桌面程序做发布测试时,英文提示、中文标题和接口返回的文本,经常出现在同一份代码里。想用Themida保护其中几条固定提示,可以用字符串加密宏标记引用这些文字的代码。处理中文时,容易出错的地方是把“显示的是中文”和“编译后是宽字符串”当成一回事。同样是“保存完成”,传给窗口接口的宽字符串和传给日志库的UTF-8字符串,接入时要分开确认。
一、Themida字符串加密宏怎么用
字符串加密宏在源代码里划出一段范围,Themida保护编译产物时,会识别这段代码中对字符串的引用。可以先用一条固定提示验证接入过程,再逐步处理其他位置。
1、在工程里加入宏标记
①、打开原生【C/C++】工程,把随安装包提供的SDK头文件目录加入编译器的包含路径。
②、在使用字符串宏的源文件中包含【ThemidaSDK.h】,按随包示例配置当前架构所需的链接依赖。切换【Win32】和【x64】构建配置时,分别核对两套设置。
③、选一处直接使用普通窄字符串的代码,在引用语句前加入【STR_ENCRYPT_START】,在语句后加入【STR_ENCRYPT_END】。例如,可以把现有的【puts("Saved")】调用放在两个标记之间。
④、把【return】放在结束标记之后,编译并运行未保护的程序,确认原有提示能正常显示。
源码里写下宏标记后,还要用新编译出的文件执行保护。宏的范围围绕字符串的使用语句来划。如果文字在别处定义,当前函数只接收一个指针,能否识别到原始字符串,要看编译产物中的实际引用。
2、保护后核对识别结果
同一条提示可以从识别列表和运行结果两处核对。列表用来确认Themida找到了字符串,实际运行则用来确认原来的显示流程还能走通。
①、用Themida打开刚编译出的【EXE】或【DLL】,进入【虚拟机】面板。
②、选中对应的【STR_ENCRYPT】条目,在下方列表核对识别到的字符串。
③、执行【保护】,运行生成的文件,触发刚才标记的提示,检查文字是否完整。
二、Themida字符串加密宏怎么处理Unicode
先看字面量的前缀和接收参数的类型。Windows原生界面中,以L开头的宽字符串与W后缀接口可以配套使用。项目里的UTF-8文本则要按窄字节数据单独验证。
1、用宽字符串宏处理Windows宽字符文本
①、找到传给【MessageBoxW】或【SetWindowTextW】的固定提示,确认参数接收的是【wchar_t】宽字符串。
②、把直接引用【L"保存完成"】的调用放入【STR_ENCRYPTW_START】和【STR_ENCRYPTW_END】之间。例如,标记【SetWindowTextW(hwnd,L"保存完成")】所在的语句。
③、重新编译程序,在Themida的【虚拟机】面板中选择【STR_ENCRYPTW】,检查下方是否列出了这条文字。
④、执行【保护】并运行输出文件,打开对应窗口,核对中文、标点和混排的英文是否正常显示。
带L前缀的字面量在这里提供宽字符数据,`W`后缀接口按Windows宽字符格式读取。只把普通宏换成宽字符串宏,原有字面量和接口的类型不会随之改变。编译器如果在调用处报告类型不匹配,应回到参数和字面量检查,避免用强制转换掩盖编码问题。
2、把UTF-8字面量单独验证
UTF-8属于Unicode编码,但它以字节存储。一个字面量含有中文,并不能据此决定使用宽字符串宏。使用u8前缀时,还要考虑当前的C++语言版本。
①、检查源文件使用的是【"保存完成"】、【u8"保存完成"】还是【L"保存完成"】,并核对接收接口的参数类型。
②、对使用【u8"保存完成"】的代码,检查【C++语言标准】。在标准C++20中,它的元素类型为【char8_t】,接入只接受【const char*】的接口时要按接口要求处理。
③、如果现有接口接收UTF-8字节,保留原有接口约定。对要纳入保护的固定文本,用【STR_ENCRYPT】做小范围验证,并以识别列表和实际输出为准。
3、分别检查普通字面量和宽字面量
①、在同一窗口选取一条直接引用的窄字符串和一条宽字符串,分别用【STR_ENCRYPT】和【STR_ENCRYPTW】标记。
②、按发布配置生成文件,在【虚拟机】面板逐条核对两类宏识别到的内容。
③、运行保护后的文件,触发对应的【日志输出】和【窗口标题】更新,分别记录结果。
三、Themida字符串加密宏识别不到文本怎么排查
保护界面显示skipped,和程序运行后出现乱码,是两种不同的现象。前者可以从宏标记、字符串类型和编译后的引用入手,后者适合先比较保护前后的显示结果。
1、检查宏区域里是否找到字符串
①、在【保护】面板看到【skipped】时,回到【虚拟机】面板选中对应宏,查看下方的字符串列表。
②、核对字面量前缀。如果引用的是【L"…"】,检查这处代码是否用了成对的【STR_ENCRYPTW_START】和【STR_ENCRYPTW_END】。
③、如果提示缺少【START】或【END】标记,检查标记之间有无提前【return】,再用发布配置重新编译。
④、如果工具识别到了字符串,但提示原位置未清除,在源代码中搜索同一条文字,检查宏范围之外是否还有引用。
编译器可能把重复的字面量合并为一份。只要范围之外的代码还在使用它,原位置就可能保留。这时可以回到具体引用逐处核对,区分“宏内引用已处理”和“原位置仍被其他代码使用”这两个结果。
2、对照保护前后的中文显示
中文提示在未保护版本里就显示异常时,可以把问题定位到原来的文本链路。只在保护后异常时,再把检查范围缩小到对应宏和受影响的调用。
①、用同一次构建得到的【未保护文件】和【受保护文件】触发同一处提示,记录两边的显示差异。
②、如果两边都异常,检查【源文件编码】、编译器的字符集设置以及显示接口的参数类型。使用MSVC的普通窄字面量承载UTF-8时,核对当前配置的【/utf-8】选项。
③、如果只有保护后的版本异常,在测试构建中暂时移除该处【字符串加密宏】,重新编译和保护,再比对同一条提示。
总结
Themida的字符串加密宏适合处理原生程序里明确引用的固定文本。中文能否正常显示,取决于字面量、编译设置和接收接口是否匹配。发布时可以把识别结果和显示结果分开验收,尤其是同时使用Windows宽字符接口和UTF-8日志的工程。这样即使换了编译配置,也能沿着出问题的那条文本查回具体的引用位置。
