首页
论坛
专栏
课程

[原创]简单分析VMProtect V1.10

2018-1-20 17:11 11345

[原创]简单分析VMProtect V1.10

2018-1-20 17:11
11345

前言

小生近来被高版本VMProtect的代码混淆虐到体无完肤,特来找找旧版本的VMProtect的晦气。看看能不能刷一波小怪,涨一波经验,积蓄力量,最后再和大BOSS干仗。

工具

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的调用了。

常量展开保护



这次我们只保护了一条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指令。编译后用OllyDbg打开调试。

什么情况?没有重复Handler的调用?看官莫要急躁,且听小生细细说来。

这里引入了一个BL寄存器解密。BL的初始值就是PCODE起始地址的低8位。

004043DB    46              INC ESI                                  ; test_vmp.00404CA0
在该行代码写下断点,则每次中断的时候都能看到AL里面是经解密后的真正操作码了。
接下来走入一个Handler(0x67号)


Handler里面也需要经过变幻解密操作数。
要写段代码解密我觉得应该也不困难,抠解密算法就行了。或者可以用上反汇编引擎,动态抠出来执行,适用范围较广。
一段简单的演示代码。静态抠Handler的办法也不是不可取,主要是工作量大,写代码在Handler表里面遍历抠出来应该也可以。
小生太菜,只能上一段相当笨的代码。
dwEBX equ <0404C9Ch> ;待解码PCODE起始处
dwPID equ <796> ;待解码的进程ID
dwHandler equ <040458Fh> ;Handler表地址

.data
szNtDll db 'ntdll.dll',0
szFunc db 'RtlAdjustPrivilege',0
dwCEBX dd dwEBX


.data?
dwEnable dd ?
bDCode db 1024 dup(?) ;解码缓冲区

.code

Decode_vPop proc bCode:BYTE
LOCAL b:BYTE ;解密结果
pushad
mov bl,byte ptr dwCEBX
mov al,bCode
ADD AL,BL
NOT AL
ADD AL,053h
ROR AL,03h
INC AL
NOT AL
SUB AL,079h                              ; 解密操作数
ADD BL,AL                                ; 更新密钥
mov byte ptr dwCEBX,bl
mov b,AL
popad
mov al,b
movzx eax,al
ret

Decode_vPop endp

Decode proc bCode:BYTE ;解码机器码过程,需要自己抠出来
LOCAL b:BYTE ;解密结果
pushad
mov al,bCode
mov bl,byte ptr dwCEBX
ADD AL,BL                                ; AL += BL
ROL AL,07h                               ; 右移7位
ADD AL,07Fh                              ; AL += 0x7F
XOR AL,021h                              ; 异或0x21
INC AL                                   ; +1
ADD BL,AL                                ; 更新BL密钥
mov byte ptr dwCEBX,bl
mov b,AL
popad
mov al,b
movzx eax,al
ret

Decode endp

Main proc
LOCAL hProcess:DWORD
LOCAL pHandler:DWORD
LOCAL bCode[255]:BYTE
invoke GetModuleHandle,offset szNtDll
invoke GetProcAddress,eax,offset szFunc
push offset dwEnable
push FALSE
push TRUE
push 014H
call eax
;提权
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,dwPID
mov hProcess,eax
invoke ReadProcessMemory,hProcess,dwEBX,offset bDCode,2,NULL
invoke Decode,bDCode[0]
mov bDCode[0],al
;解码第一条指令的操作码
invoke Decode_vPop,bDCode[1]
mov bDCode[1],al
ret

Main endp


[招聘]欢迎市场人员加入看雪学院团队!

上传的附件:
最新回复 (8)
asuralove 2018-1-21 00:31
2
0
挺好的
王旭峰 2018-1-22 00:09
3
0
好..    建议加精
xiaohang 3 2018-2-4 19:04
4
0
还是比较完整的一篇分析文章,可以作为vmp研究的入门贴
心有猛虎 2018-3-15 15:55
5
0
入门帖子,学习。
最爱季节 2018-3-23 15:50
6
0
就喜欢这样的帖子
destnity 2018-3-25 13:21
7
0
入门门槛高哈,感谢分享。
上海刘一刀 2 2019-1-31 15:50
8
0
真棒真棒 楼主真有耐心
你的香气 2019-4-27 08:23
9
0
细节讲解到位, 感谢分享。
游客
登录 | 注册 方可回帖
返回