先找到disp2handler,跑个Trace统计影响EIP指令的地址数即可,发现disp2handler。
0064F6D8 <safe | E8 ECA4FEFF | call safengine授权对抗ai.639BC9
{
HITS KIND TARGETS ADDR INSN
----------------------------------------------------------------------
310 ret 21 0x82BA1E ret 0x64 disp2handler
1 ret 1 0x82AA51 ret 0x30
1 jmp indirect 1 0x639763 jmp [ds: + 0x63B044]
}
来个workflow分析Trace根据disp2handler做污点分析回溯handler table,拿到特征
ret 型(Safengine 2.x):`ret imm16`,handler 由更早的
`mov reg,[disp32 + idx*N]` 取出、变换后写回栈顶再弹出
拿到handler表

Handler有混淆,来个workflow,分析Trace当中的混淆特征,基于活跃变量分析通过Nop、简单指令替换的方式削弱死代码去混淆,有些pushfd、popfd基于栈的混淆要求lowering到简单指令,还原回正常不依赖栈的汇编。

接下来个workflow分析Safengine的虚拟机架构,根据去混淆的汇编CFG分析Safengine虚拟机的架构,是基于堆的虚拟机还是栈的虚拟机,是CISC还是RISC。
结果
Safengine 2.x 是栈式虚拟机(stack machine),指令集是 CISC 语义、RISC 实现。
Handler表有256个handler,去重后60个,其中有3种类型的vRet走自己的handler尾部退出虚拟机。其他handler尾部都会回到虚拟机循环里重新调度。相比于VMProtect3.5,Safengine 2.x的opcode是指令变长的。
随便下个断点触发File corrupted,文件被修改,说明有内存校验vChecker。

vChecker在0x82AE17,将其抽象为函数,参数1为校验地址,参数2为校验长度。

来个workflow,编写内存注入dll做bypassSafengine2.xVChecker,忽略掉我们的断点,将hash值hook为预期的即可。预期的hash值通过unicorn模拟和我们实际的hookVChecker修改结果做对比,如不一致调试。最终达到bypass内存校验的效果。

接下去就是跟踪VM了,通过不断地侧信道分析vRet、vCpuid,最终定位到004EFA7C这个地址。同时发现vmp特征,看样子作者在safengine的基础上套了一层vmprotect的保护壳,很强大。

继续跟踪VM,到了00598561,这是个判断CALL,判断用户机器码与预期的机器码是否一致。如果判断失败会启动一个退出线程,参数地址为004F3485,也是一个被VMP保护的函数。


通过不断的跟踪,发现Safengine自行重载了dll进行api的函数调用,总之跟了一堆的VM函数调用。最终定位到计算出base64解码后机器码machinecode的函数地址,这块没有混淆,类似一个shellcode。在SystemBiosVersion调用后进行patch机器码即可,要patch俩次,因为程序有两次获取机器码进行比较的操作。这可能也是作者魔改后的效果,总之,最终patch结果如下。
程序默认弹出机器码“1+LKtQAIEABTBgoAeqbPxVAJBgeiZysuFa1lCg==”,注意到Fail,这说明用户无法通过搜索该机器码的base64解码内存进行回溯机器码的计算,因为仅在弹窗时显示该内容。
2026年11月10日

传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于 1小时前
被zZhouQing编辑
,原因: