首页
论坛
专栏
课程

[调试逆向] VMP 之反 VM (还原代码) 之初初...

2009-7-26 21:23 15945

[调试逆向] VMP 之反 VM (还原代码) 之初初...

2009-7-26 21:23
15945
VMP之还原VMP初探
作者:小娃崽[CUG]

  这应该是一篇菜鸟一看就懂,高手不用看就懂的帖子。不知道为什么,前段时间老是在想VMP这个东西,可能是因为太闲的原因吧。突然想到 如果虚拟了 MOV EAX,1这个指令最终结果还会是令EAX这个寄存器中的数据为1的。这两天休息,就下了VMP1.08回来虚拟了几个小程序,F8从头到尾把程序跑了一趟。累啊,以下是一点心得,先记录下来,免得以后忘记了。
【1】总的纲领:
  汇编中操作数主要分为三类:寄存器操作数,内存操作数,立即操作数。CPU指令执行的最终结果还是改变寄存器或者内存中的数据。
  比如 :
  MOV EAX,1
  mov [10000000],eax
  使EAX=1,[10000000]=1
  也可以变换一种形式改变EAX的值,
  比如:
  push 1
  pop  eax
  不管怎样,最终结果还是一样的,只是换了个形式,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 [esp], ecx
   jmp     vm_execute           //可以看做是V_ADD

   lods    byte ptr [esi]
   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 [eax]   
   jmp     vm_execute         //可以看做是和MOV [XXXXXXXX],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 [esp+28]
00404ABB    FC              cld
00404ABC    BF 00404000     mov     edi, 00404000
00404AC1    89F3            mov     ebx, esi
00404AC3    033424          add     esi, dword ptr [esp]
00404AC6    AC              lods    byte ptr [esi]
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 [eax*4+4040DD]    //开始执行伪指令

0040404F    AC              lods    byte ptr [esi]
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 [edi+eax*4]       //把之前PUSH的寄存器保存到VM_COMTEXT,10次,V_SaveToVMContext
00404060    E9 610A0000     jmp     00404AC6

。。。。。。。。。。。。。。。。。N条伪指令

00404B32    AC              lods    byte ptr [esi]
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 [edi+eax*4]     //把数据压入堆栈 ,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_SaveToVMContext  10次
   。。。。。。。。。。。。。
   00404421    AC              lods    byte ptr [esi]
   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 [esi]
   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 [edi+eax*4]  //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,1  VM后的结果是 :
     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,高兴

[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最新回复 (20)
小娃崽 13 2009-7-26 21:24
2
0
http://bbs.80x86.com/viewthread.php?tid=69&extra=page%3D1发到这里的都没人看,就转到这里来吧
欢迎各位牛人继续来BS
海风月影 17 2009-7-26 21:32
3
0
精华
123456
Bughoho 8 2009-7-26 22:01
4
0
人肉是可行的,但若能搞出自动还原的JJ,就NB了。
comewhere 1 2009-7-26 22:27
5
0
牛B的说............膜拜之.......
sessiondiy 4 2009-7-26 22:58
6
0
标题不错越看越美
小娃崽 13 2009-7-27 10:04
7
0
海风一叫,水帖也能成精华了.........
sessiondiy 标题不错越看越美


我也觉得
沙加 1 2009-7-27 10:17
8
0
海风有还原器的
yuansunxue 6 2009-7-27 10:21
9
0
好文 貌似之前也有一个初初的
sessiondiy 4 2009-7-27 10:35
10
0
针对型 or 通用型的?
海风月影 17 2009-7-27 10:41
11
0
忽悠型  的
sessiondiy 4 2009-7-27 10:44
12
0
那是高镜界了.
用了模糊理论.
creakerzgz 1 2009-7-27 11:24
13
0
不能学习,只能膜拜
wuhanqi 5 2009-7-27 12:38
14
0
...膜拜之...
假面泰山 2009-7-27 15:18
15
0
不管怎么样,看看学习学习 楼主辛苦了。多多努力 共同进步。
playboyjin 2009-7-27 15:24
16
0
精华了...
竹君 5 2009-7-27 18:56
17
0
好文   
aa1ss2 2 2009-7-27 20:33
18
0
VM的难点不是在这里,试想一下一种比较简单的算法。

MOV EAX,1
mov [10000000],eax

变成

mov eax1,1
mov eax2,1
mov eax3,1
mov eax4,1
mov eax5,1
mov eax6,1
mov eax7,1
mov eax8,1
mov eax9,1

然后一大堆的垃圾操作,最后变成

eax1=10
eax2=20
eax3=4
eax4=5
eax5=1   
eax6=9
eax7=7
eax8=11
eax9=1

然后再执行

mov [10000000],eax6
mov [10000000],eax2
mov [10000000],eax3
mov [10000000],eax8
mov [10000000],eax7
mov [10000000],eax9
.........再省略一大串垃圾操作

mov [10000000],eax5    //假设这个是真正的寄存器,不过是随机的

这些指令全部都是在虚拟机里执行的,把你转到头晕也未必可以跟得出来。
xieph 2009-7-28 16:20
19
0
看来老桥进步了不少啊。我还在原地踏步...
小娃崽 13 2009-8-3 11:49
20
0
[QUOTE=aa1ss2;661920]VM的难点不是在这里,试想一下一种比较简单的算法。

MOV EAX,1
mov [10000000],eax

变成

mov eax1,1
mov eax2,1
mov eax3,1
mov eax4,1
mov eax5,1
mov eax6,1
mov eax7,...[/QUOTE]

我这个周末花了两天的休息时间,尝试还原了一个VM过的crackme,可以还原得到一些,但是我知道遗漏了很多条.....
太浪费时间精力了,还是做点正经事要紧,希望aa1ss2以后多多指点下
tigerlhp 2009-8-3 13:41
21
0
怎么判斷是不是使用了虚拟机 加密的啊
如果这个都不能判断没法后面的工作了 。
游客
登录 | 注册 方可回帖
返回