VMProtect V1.10(帖子附件)
OllyICE(看雪工具板块)
无任何保护之小试身手
且看这段汇编代码。
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的调用了。
常量展开保护
且看这段汇编代码。
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起始地址。
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的调用了。
这次我们只保护了一条push 0x2018指令。入口点的代码变成了如下:
00401000 > $- E9 023C0000 JMP test_vmp.00404C07
00404C07 68 BB4B4000 PUSH test_vmp.00404BBB ; 入栈PCODE起始地址
00404C0C ^ E9 33F6FFFF JMP test_vmp.00404244 ; 进入 VM Dispatch
VM Dispatch有一点小小变化,不过换汤不换药。
以下是0xAF号Handler和0xD9号Handler(同一个Handler)
0xAF和0xD9是同一条指令,可以按前文一样整理出一份VMCONTEXT结构。此处不赘述。
执行完VMCONTEXT结构的填充之后,发现0x2018常量消失了。
0xFB号Handler,压入一个DWORD操作数。
执行完后,0xECBD5026被压入了堆栈。
0x5E号Handler,压入ESP。
0x3B号Handler,从堆栈里面弹出了原ESP,并压入了0xFB号Handler压入的DWORD常数的低16位。
0x74号Handler,取出0xFB号Handler压入常数的低16位送入BX
接下来又是0x5E,0x3B号调用。具体就是又入栈了待解密常数的低16位。
然后调用0x9Bh号Handler。
VMCONTEXT,0x24偏移处似乎在前面没有哪个Handler备份了环境进去。
感情这些全部都是垃圾指令,浪费青春,谋我钱财,可恶可恨!/手动滑稽笑脸,请看官自行脑部。
前面低16位送入BX也没用到。接着往下面看吧。
接着又调用了0xFB号Handler,压入了0x1342CFF2常数。
然后是0xF2号Handler调用。
这个时候,0x2018出现在堆栈。请懂俄文的看官给VMProtect作者寄一份电子邮件,“心机BOY!!!”
感情这0x2018就是两常数相加即可得到。绕了一大圈。
加密PCODE保护
这次我们只保护了一条push 0x2018指令。入口点的代码变成了如下:
00401000 > $- E9 023C0000 JMP test_vmp.00404C07
00401000 > $- E9 023C0000 JMP test_vmp.00404C07
00404C07 68 BB4B4000 PUSH test_vmp.00404BBB ; 入栈PCODE起始地址
00404C0C ^ E9 33F6FFFF JMP test_vmp.00404244 ; 进入 VM Dispatch
00404C07 68 BB4B4000 PUSH test_vmp.00404BBB ; 入栈PCODE起始地址
00404C0C ^ E9 33F6FFFF JMP test_vmp.00404244 ; 进入 VM Dispatch
VM Dispatch有一点小小变化,不过换汤不换药。
以下是0xAF号Handler和0xD9号Handler(同一个Handler)
0xAF和0xD9是同一条指令,可以按前文一样整理出一份VMCONTEXT结构。此处不赘述。
执行完VMCONTEXT结构的填充之后,发现0x2018常量消失了。
0xFB号Handler,压入一个DWORD操作数。
执行完后,0xECBD5026被压入了堆栈。
0x5E号Handler,压入ESP。
0x3B号Handler,从堆栈里面弹出了原ESP,并压入了0xFB号Handler压入的DWORD常数的低16位。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)