http://hi.baidu.com/ximo2006/blog/item/1ae9d6044406c976030881e1.html
小菜鸟初玩VM,简单记录下,当做学习笔记。
只是简单的爆破分析而已,不涉及还原,当然知道了原理,还原也只是个体力活。能力有限,写的东西可能有很多错误,请大家多多指点。
首先,先写几句简单的代码,当做试炼程序。
#include <stdio.h>
int main()
{
int input;
while(1)
{
scanf("%d",&input);
if (input==2010)
{
printf("yes\n");
}
else
{
printf("no\n");
}
}
return 0;
}
几句很挫的代码,也没有对输入的对象进行判断,比如是否是整形还是字符等等,因为仅仅作为个试炼品,当然越简单越好。
写完后,用网上可下到的最新版的VMP2.05,加密下关键的算法段。
选项如下:
只是去掉了使用VMP外壳的选线,其他的选项都是VMP默认的,所加保护的强度为U保护,也就是(V+M)。
这个程序很简单,输入的数字为2010,则显示"yes",否则则显示"no",要爆破的话,也仅仅是1个跳转的事。但是,现在由于加了VM保护,看上去复杂了很多。其实分析后,也并没有想象中的那么难。
下面就分析下爆破过程:
网上分析VM架构的文章已经很多了,科普的帖子也有很多了,人云亦云的内容,我就不写了。
爆破前,需要知道,VM对跳转指令的处理过程
1。用e_flag来进行相应的计算,其中关键的一条指令就是XX门。
2。根据计算后的结果,来决定程序的流程。
而XX门,就是爆破的关键,先来看下这条handler的样子:
0040D125 10CC adc ah,cl ; Handler_Nor32
0040D127 66:0D DC76 or ax,76DC
0040D12B 8B45 00 mov eax,dword ptr ss:[ebp]
0040D12E 66:0FABF2 bts dx,si
0040D132 8B55 04 mov edx,dword ptr ss:[ebp+4]
0040D135 66:0FBAE4 08 bt sp,8
0040D13A ^ E9 EAFDFFFF jmp fkvmp_vm.0040CF29
0040CF29 F8 clc
0040CF2A F7D0 not eax
0040CF2C 38E4 cmp ah,ah
0040CF2E 60 pushad
0040CF2F F7D2 not edx
0040CF31 66:0FBAE1 0E bt cx,0E
0040CF36 21D0 and eax,edx
0040CF38 66:C74424 04 DB2A mov word ptr ss:[esp+4],2ADB
0040CF3F E8 7A080000 call fkvmp_vm.0040D7BE
0040CB15 8945 04 mov dword ptr ss:[ebp+4],eax
0040CB18 9C pushfd
0040CB19 ^ E9 85F7FFFF jmp fkvmp_vm.0040C2A3
对于这条指令的介绍,网上相应的文章已经有非常多了,不再赘述。
其次,VM中有唯一一条跳转指令,VM_JMP,这条语句来确定程序的下一步动作,而决定跳转地址的,需要依靠上面的那条XX门handler。
也来看一下VM_JMP这条handler样子:
0040CE19 F5 cmc
0040CE1A 66:81D6 2058 adc si,5820
0040CE1F 8B75 00 mov esi,dword ptr ss:[ebp]
0040CE22 66:0FA3E9 bt cx,bp
0040CE26 66:39D0 cmp ax,dx
0040CE29 F8 clc
0040CE2A F9 stc
0040CE2B 83C5 04 add ebp,4
0040CE2E 55 push ebp
0040CE2F 68 E2F55BAA push AA5BF5E2
0040CE34 FF3424 push dword ptr ss:[esp]
0040CE37 FF7424 08 push dword ptr ss:[esp+8]
0040CE3B 8D6424 10 lea esp,dword ptr ss:[esp+10]
0040CE3F E9 620D0000 jmp fkvmp_vm.0040DBA6
下面是VM_JMP的后半段:
0040DBA6 FEC9 dec cl
0040DBA8 F8 clc
0040DBA9 66:81D9 A7DB sbb cx,0DBA7
0040DBAE 66:0FBAE4 03 bt sp,3
0040DBB3 89F3 mov ebx,esi
0040DBB5 66:0FA5C9 shld cx,cx,cl
0040CE76 0375 00 add esi,dword ptr ss:[ebp]
0040CE79 8D6424 08 lea esp,dword ptr ss:[esp+8]
0040CE7D D2D0 rcl al,cl
0040CE7F 18F8 sbb al,bh
0040CE81 F5 cmc
0040CE82 F6D0 not al
0040CE84 8A06 mov al,byte ptr ds:[esi]
0040CE86 66:0FB3C1 btr cx,ax
0040CE8A 80ED 92 sub ch,92
0040CE8D D2E9 shr cl,cl
0040CE8F 30D8 xor al,bl
0040CE91 66:0FBCCD bsf cx,bp
0040CE95 19C1 sbb ecx,eax
....
省略部分
下面就切换到下面的部分进行处理了。
分析到这,爆破的方法已经很明确了:
1)修改HOOK e_flag,到关键的时候,修改e_flag值
2)直接修改VM_JMP的走向,当然关键也是看前面e_flag的计算结果。
实践下:
1)HOOK E_FLAG,在
0040D132 8B55 04 mov edx,dword ptr ss:[ebp+4]
这里下断,观察[ebp+4]的值,找关键。并同时记录Esi的值。
可以发现,当esi==0041064f的时候,[ebp+4]==[0012FF78]==0x00000282
此时就是爆破的关键,修改[ebp+4]的值为0x242,即可完成爆破。
2) 在VM_JMP中,
0040CE1F 8B75 00 mov esi,dword ptr ss:[ebp] ; fkvmp_vm.0040FD14
断下后,esi==41601a,(注意下,此时的esi,已经不是取opcode的那个地址,因为这条语句前,已经有条语句adc si,5820,对esi进行处理了)
根据E_FALG==0x282和E_FALG==0x242,
[ebp]的值会不同,也就是跳向不同的分支进行处理
当e_falg==0x282 [ebp]=0x40fd14 //显示no的分支
当e_falg==0x242 [ebp]=0x40ddcc //显示yes的分支
此时,修改[ebp]的值为yes分支的值,即可完成爆破
最后就是patch代码的编写了,很简单,要记录下esi的值,因为VM中,VM.EIP是靠ESI来进行的。
只要判断esi是否是关键的esi,然后赋对应的值即可,大家可以自由发挥。
示例下,比如是修改e_flag的值来进行爆破的,可以这么写:
原来的:
0040D132 8B55 04 mov edx,dword ptr ss:[ebp+4]
0040D135 66:0FBAE4 08 bt sp,8
0040D13A ^ E9 EAFDFFFF jmp fkvmp_vm.0040CF29
patch后:
0040D132 /E9 89470000 jmp fkvmp_vm.004118C0
0040D137 |90 nop
0040D138 |90 nop
0040D139 |90 nop
004118C0 60 pushad
004118C1 81FE 4F064100 cmp esi,fkvmp_vm.0041064F
004118C7 75 07 jnz short fkvmp_vm.004118D0
004118C9 C745 04 42020000 mov dword ptr ss:[ebp+4],242
004118D0 60 pushad
004118D1 8B55 04 mov edx,dword ptr ss:[ebp+4]
004118D4 66:0FBAE4 08 bt sp,8
004118D9 ^ E9 5CB8FFFF jmp fkvmp_vm.0040D13A
保存后,即可完成爆破。
其余的,大家自由发挥吧。
附上试练品以及爆破后的文件:
http://u.115.com/file/t0fdba1584