VMP之反VM(还原代码)初探
VMP之还原VMP初探作者:小娃崽
这应该是一篇菜鸟一看就懂,高手不用看就懂的帖子。不知道为什么,前段时间老是在想VMP这个东西,可能是因为太闲的原因吧。突然想到 如果虚拟了 MOV EAX,1这个指令最终结果还会是令EAX这个寄存器中的数据为1的。这两天休息,就下了VMP1.08回来虚拟了几个小程序,F8从头到尾把程序跑了一趟。累啊,以下是一点心得,先记录下来,免得以后忘记了。
【1】总的纲领:
汇编中操作数主要分为三类:寄存器操作数,内存操作数,立即操作数。CPU指令执行的最终结果还是改变寄存器或者内存中的数据。
比如 :
MOV EAX,1
mov ,eax
使EAX=1,=1
也可以变换一种形式改变EAX的值,
比如:
push 1
popeax
不管怎样,最终结果还是一样的,只是换了个形式,VMP也一样,只是跑了N条等价的伪指令,晃点我们的眼睛而已。
【2】VMP是堆栈机:
它有自己的"寄存器",有自己的伪指令,它对数据的操作都是通过堆栈进行的,从它的伪指令可以看出,伪指令太多了,我只看了几十条,其他的就看不下去了。
如【1】所述,指令的结果只是改变数据.
pop dx
pop ax
pop cx
div cx
push ax
push dx
JMP vm_execute //字除法,并把商和余数压入堆栈 v_divw
pop ecx
add dword ptr , ecx
jmp vm_execute //可以看做是V_ADD
lods byte ptr
add al, bl
sub al, 0C6
ror al, 1
not al
ror al, 3
add bl, al
cbw
cwde
push eax
jmp vm_execute //可以看做是V_PUSH
pop eax
pop dword ptr
jmp vm_execute //可以看做是和MOV ,reg 等价的伪指令
。。。。。。。。。。。。
没猜想错的话,在虚拟机内部,VMP对数据的操作转换到了VM_COMTEXT,堆栈当中,只是通过伪指令进行而已。
在论坛里还看到前辈们总结的 ,这个或那个再或那个等于异或什么的,还画有图,应该是精华部分吧,没看,因为我觉得我看不懂,以后用的上的话在看吧。
【3】VMP的构造:
猜想进入VMP之前和退出VMP之后,堆栈的情况应该差不多的,改变不了多少。于是对VM入口下了硬件访问断点。
00404313 58 pop eax
00404314 61 popad //F9几下来到这里 和VMP的入口刚好匹配得上 应该就是传说中的VM出口了。
00404315 9D popfd
00404316 C3 retn //还有一条差不多的,只是这里是retnf
F8按了好久,于是干脆就写了几句脚本,专门查看EAX的值,发现大体结构都是差不多的。
VM_START:
00401000 > $68 EB4C4000 push 00404CEB //把PCODE压入堆栈
00401005 .- E9 A63A0000 jmp 00404AB0
VM_EXECUTE:
00404AB0 9C pushfd
00404AB1 60 pushad
00404AB2 68 00000000 push 0
00404AB7 8B7424 28 mov esi, dword ptr
00404ABB FC cld
00404ABC BF 00404000 mov edi, 00404000
00404AC1 89F3 mov ebx, esi
00404AC3 033424 add esi, dword ptr
00404AC6 AC lods byte ptr
00404AC7 00D8 add al, bl
00404AC9 FEC0 inc al
00404ACB F6D0 not al
00404ACD C0C0 07 rol al, 7
00404AD0 34 D3 xor al, 0D3
00404AD2 00C3 add bl, al
00404AD4 0FB6C0 movzx eax, al
00404AD7 FF2485 DD404000 jmp dword ptr //开始执行伪指令
0040404F AC lods byte ptr
00404050 00D8 add al, bl
00404052 34 4A xor al, 4A
00404054 FEC8 dec al
00404056 C0C8 05 ror al, 5
00404059 FEC8 dec al
0040405B 00C3 add bl, al
0040405D 8F0487 pop dword ptr //把之前PUSH的寄存器保存到VM_COMTEXT,10次,V_SaveToVMContext
00404060 E9 610A0000 jmp 00404AC6
。。。。。。。。。。。。。。。。。N条伪指令
00404B32 AC lods byte ptr
00404B33 00D8 add al, bl
00404B35 34 4A xor al, 4A
00404B37 FEC8 dec al
00404B39 C0C8 05 ror al, 5
00404B3C FEC8 dec al
00404B3E 00C3 add bl, al
00404B40 FF3487 push dword ptr //把数据压入堆栈 ,10次,V_VMContextToStack
00404B43^ E9 7EFFFFFF jmp 00404AC6
004045B6 58 pop eax
004045B7 61 popad
004045B8 9D popfd //再把堆栈的数据压入到真实的寄存器
004045B9 C3 retn
N条伪指令下来,最终执行了几条等价的汇编指令而已。以下是我自己总结的VMP大概构造:
Vm_Start
VM_Execute
V_SaveToVMContext
N条伪指令
V_VMContextToStack
V_RETN
【4】验证与猜想:
回到MOV EAX,1这条指令上来,我就虚拟了这个指令而已,然后在VM的出口下断,发现最终的结果就是EAX=1,其他寄存器与没VM之前的变化不大。以下是代码片段,我可是完整的F8几回才看到了点真相。
V_SaveToVMContext10次
。。。。。。。。。。。。。
00404421 AC lods byte ptr
00404422 00D8 add al, bl
00404424 2C C6 sub al, 0C6
00404426 D0C8 ror al, 1
00404428 F6D0 not al
0040442A C0C8 03 ror al, 3
0040442D 00C3 add bl, al
0040442F 66:50 push ax
00404431 E9 57060000 jmp 00404A8D //V_PUSH 5
。。。。。。。。。。。。
00404B17 AC lods byte ptr
00404B18 00D8 add al, bl
00404B1A C0C0 07 rol al, 7
00404B1D FEC0 inc al
00404B1F 34 3E xor al, 3E
00404B21 04 2D add al, 2D
00404B23 00C3 add bl, al
00404B25 8F0487 pop dword ptr //V_SaveToVMContext对应的V_EAX被改变
00404B28^ E9 60FFFFFF jmp 00404A8D
。。。。。。。。。。。。
V_VMContextToStack 10次
V_RETN //退出VM
【5】还原VMP的一点感悟
知道了事情的真相,感觉人肉还原VMP还是可行的,需要的只是时间和毅力,N条伪指令下来,才还原成几条汇编指令,我才不干!我想VMP强大的地方应该数PUSH,POP操作了太多次,有的负责解码,但大多是的时候我都觉得它们跟花指令差不多。而且HANDLE与PCODE可是随机组合的,同一段程序会VM出不同的版本。
如果MOV EAX,1VM后的结果是 :
V_SaveToVMContext
V_PUSH 5
V_SaveToVMContext
V_VMContextToStack
V_RETN
我想还原起来可就不费力了。
可以从VM的外部着手,观察和猜测这段VM的大体功能,像高手们说的,把它看成是一个函数,有时候不用跟进VM就能看出个大概了,比如一个虚拟了的MessageBoxA,接着进入VM内部,注意观察VM_COMTEXT,堆栈的数据变化,把无效的PUSH,POP给剔除,精简出有效的伪指令,再还原。而且对数据操作的指令(ADD,MUL,DIV之类的)在VMP内部都有等效的伪指令,而且不难看出。
【总结】:
终于理解了什么是VM,高兴
页:
[1]