且看这段汇编代码。
VMProtect如下配置。
编译后用OllyDbg打开进行调试。
可以看到入口处的代码被替换为如下:
VM Dispatch:
开头备份环境,应该是为了填充VMCONTEXT。而往下面走没有发现填充VMCONTEXT的代码,可见填充VMCONTEXT应该是由PCODE完成的。执行完3条入栈指令后。我的堆栈布局如下图所示:
此时,待解释执行的PCODE指令如下图所示:
尾部出现了一个亲切的常数,0x2018。前面调用了好几次0x45号Handler,应该就是填充VMCONTEXT的。且看看这0x45号Handler,代码如下:
前面一共有10个0x45号Handler调用。总结一下VMCONTEXT结构。
偏移 | 注释 |
0x24 | 入栈0 |
0x28 | EDI |
0x18 | ESI |
0x0C | EBP |
0x38 | EDX |
0x04 | EBX |
0x08 | ECX |
0x14 | EAX |
0x20 | EFLAGS |
注意,VMCONTEXT结构里面没有ESP,虽然从堆栈里面弹出了ESP,但马上被EDX覆盖掉了(没有必要备份ESP)。
这个VMCONTEXT结构是随机变动的,最好写个脚本什么的统计。
填充完VMCONTEXT结构后,就该执行0xDE号Handler了,Handler代码如下:
00404088 59 POP ECX
00404089 E9 B8080000 JMP test_vmp.00404946
0xDE号Handler,没有操作数。只是从栈顶弹出一个DWORD送入ECX。此时弹出的是PCODE起始地址。
此时应该执行0x71号Handler。
0x71号Handler代码如下:
0x2018被压缩成了WORD,应该是VMProtect出于节省空间的考虑吧。
执行后,堆栈里面出现了0x2018。
接下来是对MessageBoxA的调用,会入栈4个参数。
0xAB号Handler(
把BYTE操作数扩展为DWORD入栈
):
0x5B号Handler(入栈DWORD操作数):
0xF8号Handler(和0x5B号Handler一样):
再次执行一次0xAB号Handler压入0后,开始执行0x53号Handler了(重头戏快到了,调用API)。
这里从VMCONTEXT压入了0x24偏移处的字段(入栈的0,非寄存器),
0x53号Handler:
0xF8号Handler,压入一个DWORD操作数(前文介绍过),0x404C26,看官有无觉得这个地址有点意思呢?在反汇编窗口里面转入这个地址看看。
0xF8号Handler执行完后,此时堆栈布局如图所示:
接下来执行0xCF号Handler。
该Handler代码如下图所示:
0xCF号Handler,堆栈里面第二个数加等于堆栈里第一个数,同时ESP指向栈顶第二个数。有点绕。
没关系,看看执行完0xCF号Handler后的堆栈布局:
文章写到这里,我突然有个猜测,压入的VMCONTEXT结构0x24处偏移的字段,是不是基址(DLL)呢?(俺也不确定哦,只是突发奇想)
执行完0xCF号Handler后,又压入了一个0x401020。
紧跟其后,又压入了VMCONTEXT结构0x24处偏移的字段(0)。
接着执行0xBB号Handler,该Handler于上文介绍的0xCF号Handler一样,在此不赘述了。
接着又是入栈VMCONTEXT各字段。
此处依次压入了VMCONTEXT结构中的EFLAGS,EAX,ECX,EDX,EBX,EAX,EBP,ESI,EDI,入栈的0等字段。
执行完后,堆栈中的布局为:
接着执行0x5号Handler,退出虚拟机。
可以看到,返回地址是VM区段里的地址,到时候还要转到这个地址重新进入虚拟机。
步过RET,即转入对MessageBoxA的调用。
MessageBoxA执行完后,又转入了此处。
可以看到,又开始填充VMCONTEXT结构了。
这里就不统计新的VMCONTEXT结构了。只是接着往下面大概跟踪看看。
又压入了0,紧跟着就是对ExitProcess的调用了。