程序加入VMProtect以后,变化比较明显的通常是受保护函数的执行方式。原来的机器指令会经过转换,由VMProtect内置的虚拟机解释执行,这种处理适合放在授权判断、算法逻辑或其他不希望直接暴露的代码区域。不过虚拟化范围并不是越大越合适,一些调用频繁、耗时明显或者控制流比较复杂的函数全部套上保护后,程序启动速度、运行性能和兼容性都可能发生变化。配置时把保护范围收在关键代码上,后面出现异常也更容易判断是哪一段引起的。
一、VMProtect怎么设置虚拟化保护
VMProtect可以直接从可执行文件中选择函数,也能结合PDB、MAP文件或者源码中的标记确定保护范围。工程刚开始配置时,可以先挑少量函数测试,确认运行正常以后再逐步增加。
1、把需要保护的函数加入工程
先挑业务逻辑比较明确的函数,不要一开始就把整个程序的大量代码全部加入虚拟化。
①、打开【VMProtect】并载入需要保护的【EXE】或【DLL】文件。
②、进入【Functions for Protection】。
③、点击【Add Function】。
④、在函数列表中搜索准备处理的函数名称。
⑤、选中目标函数并加入当前工程。
⑥、确认函数地址和名称与当前编译版本一致。
2、给目标函数选择Virtualization
①、在【Functions for Protection】中选中目标函数。
②、找到右侧的【Compilation type】。
③、选择【Virtualization】。
④、暂时不要同时启用【Lock to Serial Number】等与当前测试无关的选项。
⑤、保存当前【VMProtect Project】。
虚拟化会把选中代码转换成虚拟机字节码,执行速度和原始代码相比可能会发生变化,因此频繁进入的大循环、实时处理函数可以先单独评估。
3、使用源码标记限定保护区域
①、在源码中引入对应的【VMProtect SDK】。
②、在保护区域开始位置加入【VMProtectBeginVirtualization】。
③、在目标逻辑结束位置加入【VMProtectEnd】。
④、重新编译原始程序。
⑤、重新载入生成的【EXE】或【DLL】。
⑥、确认【Functions for Protection】中已经识别对应标记。
⑦、执行【Compile】生成保护后的程序。
二、VMProtect虚拟化保护后程序运行异常如何排查
程序保护前能够正常运行,保护后才出现退出、卡顿或者功能异常时,可以先保留原始文件,再缩小虚拟化范围。这样比较容易区分是代码本身的问题,还是某一处保护配置带来的变化。
1、先确认异常是不是由某个保护函数引起
如果一次加入了多个函数,可以先减少保护对象,再逐个恢复。
①、复制当前【VMProtect Project】作为测试版本。
②、进入【Functions for Protection】。
③、暂时取消部分函数的保护。
④、只保留一个准备检查的【Virtualization】函数。
⑤、点击【Compile】重新生成文件。
⑥、运行程序并测试对应功能。
⑦、逐步恢复其他函数,观察异常从哪一次修改后重新出现。
2、检查标记有没有跨过复杂跳转区域
①、打开加入【VMProtectBeginVirtualization】的位置。
②、检查保护区域内是否包含循环中间位置。
③、确认外部代码不会直接跳入标记内部。
④、把【VMProtectBeginVirtualization】移动到完整逻辑块开始位置。
⑤、把【VMProtectEnd】放到完整逻辑块结束位置。
⑥、重新编译原始程序。
⑦、再次执行【Compile】测试保护后的文件。
3、保护后程序明显变慢
①、在【Functions for Protection】中找到调用频率较高的函数。
②、查看当前【Compilation type】。
③、将非关键函数从【Virtualization】改为【Mutation】或取消保护。
④、保留少量关键逻辑继续使用【Virtualization】。
⑤、重新执行【Compile】。
⑥、按照相同操作路径测试运行耗时。
如果性能问题只在某个热点函数启用虚拟化以后出现,可以把保护范围缩到函数内部较短的关键代码,而不用整段逻辑都交给虚拟机执行。
4、打开Debug mode检查标记边界问题
使用源码标记以后,如果程序在特定分支、循环或回调位置出现异常,可以利用VMProtect提供的调试模式查保护区边界。
①、打开当前【VMProtect Project】。
②、进入工程的保护设置页面。
③、启用【Debug mode】。
④、重新生成受保护程序。
⑤、在开发调试环境中运行测试版本。
⑥、观察异常是否落在未保护区域跳入保护区域的位置。
⑦、根据结果重新调整【VMProtectBeginVirtualization】和【VMProtectEnd】的位置。
三、VMProtect虚拟化保护调整后怎么验证
配置修改以后,不能只确认程序能不能启动。关键功能、性能变化以及工程使用的函数地址都应该再过一遍,特别是重新编译过原程序以后。
1、拿原程序和保护版本做功能对照
①、保留同一次编译生成的【原始程序】。
②、运行保护后的【输出文件】。
③、分别测试启动、登录、文件读写和关键业务功能。
④、重复执行被虚拟化的功能入口。
⑤、记录两份程序出现差异的位置。
⑥、发现异常时回到对应的【Functions for Protection】继续缩小范围。
这种前后对照能把程序原有问题和保护后新增的问题分开,尤其适合工程里已经加入多个保护函数的情况。
2、重新编译后核对保护对象
①、重新生成当前项目的【PDB】或【MAP】文件。
②、重新载入新编译的【EXE】或【DLL】。
③、检查【Functions for Protection】中的函数名称和地址。
④、确认源码标记仍然能正常识别。
⑤、保存新的【VMProtect Project】。
⑥、重新执行【Compile】并完成一次功能测试。
总结
VMProtect虚拟化保护比较适合放在少量关键代码上,用得太散,后面调试和性能判断都会变得麻烦。程序保护后出现运行异常时,可以先把保护函数减到很少,再按函数逐个恢复,这样比一次修改很多设置更容易找到影响点。使用源码标记的项目还要留意代码跳转边界,避免把循环或分支切在不合适的位置。平时保存一份能正常运行的原始程序和对应工程配置,后面继续增加保护范围时,也能随时拿来比较。
