wuhanqi 发表于 2010-3-15 19:27

虚拟机保护逆向分析实战

标 题: 【原创】虚拟机保护逆向分析实战
作 者: 天易love
时 间: 2009-12-05,14:34:03
链 接: http://bbs.pediy.com/showthread.php?t=102528

前言:
    很久以前写了篇文章是穷举vm保护的CM算法的,当时对vm保护一无所知,只好穷举解决,很长时间“耿耿于怀”。现在对vm保护有了点初步的了解,就拿这个简单的CM开刀吧,权当抛砖引玉,高手莫笑。(此Cm在论坛中搜jackozoo_2009.rar即可),非常感谢jackozoo贡献了这个例子。

一、解决的思路和方法
   由于字节码没有加密等特殊处理,并且只vm了一段关键的算法代码,vm_context也没有变化。人肉应该不成问题。首先要知道有多少个字节码指令。

找到虚拟引擎的入口而后单步进去。
004010FB|.68 68F14000   PUSH ZooCMa.0040F168 //该处开始存放字节码
00401100|.E8 7B1A0000   CALL ZooCMa.00402B80 //虚拟引擎的入口
00401105|.83F8 01       CMP EAX,1//判断返回值

进入之后立即来到下面的字节码指令循环处理的地方。
00402BD6 > > /8B9D ECFEFFFF MOV EBX,DWORD PTR SS:
00402BDC   . |0FB613      MOVZX EDX,BYTE PTR DS:
00402BDF   . |FF85 ECFEFFFF INC DWORD PTR SS:
00402BE5   . |BB 00C04000   MOV EBX,ZooCMa.0040C000//ebx就是handler表的首地址
00402BEA   . |FF2493      JMP DWORD PTR DS:   ;dispatch

到handler表去看看,简单算一下一共59个,即十六进制0-3a


那么这0-3a到底是虚拟了哪3b条x86汇编指令呢?这里只有一个一个分析。

当然等局部变量到底是表示vm_context中的哪个虚拟寄存器或虚拟临时变量需要参考虚拟引擎初始化或结束部分以及在调试过程中加以确定。例如:
00402B89   .8985 CCFEFFFF MOV DWORD PTR SS:,EAX//存放__eax
00402B8F   .898D D0FEFFFF MOV DWORD PTR SS:,ECX//存放__ecx
00402B95   .8995 D4FEFFFF MOV DWORD PTR SS:,EDX//存放__edx
00402B9B   .899D D8FEFFFF MOV DWORD PTR SS:,EBX//存放__ebx
00402BA1   .8D85 00FFFFFF LEA EAX,DWORD PTR SS:
00402BA7   .05 00010000   ADD EAX,100
00402BAC   .8985 DCFEFFFF MOV DWORD PTR SS:,EAX //存放__esp
00402BB2   .8B45 00       MOV EAX,DWORD PTR SS:
00402BB5   .8985 E0FEFFFF MOV DWORD PTR SS:,EAX
00402BBB   .89B5 E4FEFFFF MOV DWORD PTR SS:,ESI
00402BC1   .89BD E8FEFFFF MOV DWORD PTR SS:,EDI
00402BC7   .8B45 08       MOV EAX,DWORD PTR SS:   //eax=入口参数 字节码首地址
00402BCA   .8985 C8FEFFFF MOV DWORD PTR SS:,EAX    // 字节码首地址
00402BD0   .8985 ECFEFFFF MOV DWORD PTR SS:,EAX //__eip

简单分析两条指令:VCmp 、VJl
分析一下VCmp
004031DA   .8B9D DCFEFFFF MOV EBX,DWORD PTR SS:
004031E0   .8B13          MOV EDX,DWORD PTR DS:    //虚拟栈顶弹出一个DWORD
004031E2   .83C3 04       ADD EBX,4
004031E5   .8B0B          MOV ECX,DWORD PTR DS:    //虚拟栈顶再弹出一个DWORD
004031E7   .8385 DCFEFFFF>ADD DWORD PTR SS:,8//调整__esp
004031EE   .3BD1          CMP EDX,ECX               //进行比较
004031F0   .9C            PUSHFD
004031F1   .58            POP EAX            //save EFL to eax
004031F2   .8BC8          MOV ECX,EAX
004031F4   .83E0 01       AND EAX,1            
004031F7   .8985 F8FEFFFF MOV DWORD PTR SS:,EAX         //save CFto __CF
004031FD   .8BC1          MOV EAX,ECX
004031FF   .83E0 40       AND EAX,40
00403202   .8985 F0FEFFFF MOV DWORD PTR SS:,EAX      //save ZFto __ZF
00403208   .8BC1          MOV EAX,ECX
0040320A   .25 80000000   AND EAX,80
0040320F   .8985 F4FEFFFF MOV DWORD PTR SS:,EAX      //saveSF to __SF
00403215   .8BC1          MOV EAX,ECX
00403217   .25 00080000   AND EAX,800
0040321C   .8985 FCFEFFFF MOV DWORD PTR SS:,EAX      //saveOF to __OF
00403222   .^ E9 AFF9FFFF   JMP <ZooCMa.handler>                  //VCmp

分析一下VJl
004032FF   .8B9D ECFEFFFF MOV EBX,DWORD PTR SS://__eip
00403305   .0FB713      MOVZX EDX,WORD PTR DS:    //edx=跳转偏移
00403308   .8B85 F4FEFFFF MOV EAX,DWORD PTR SS: // __SF to eax
0040330E   .8B9D FCFEFFFF MOV EBX,DWORD PTR SS: // __OF to ebx
00403314   .3BC3          CMP EAX,EBX                //__SF==__OF
00403316   .74 10         JE SHORT ZooCMa.00403328    //not jmp
00403318   .8B8D C8FEFFFF MOV ECX,DWORD PTR SS://ecx=字节码首地址
0040331E   .03CA          ADD ECX,EDX                      //ecx=字节码中的目的地址
00403320   .898D ECFEFFFF MOV DWORD PTR SS:,ECX//__eip=跳转目的地址
00403326   .EB 07         JMP SHORT ZooCMa.0040332F      
00403328   >8385 ECFEFFFF>ADD DWORD PTR SS:,2
0040332F   >^ E9 A2F8FFFF   JMP <ZooCMa.handler>            //VJl
如此这般你就可以得到opcode 0-3a实现了哪些x86汇编指令的功能。如下表所示:
VNop         0       VPushImm8    1   VPushImm16    2    VPushImm32   3
VPushReg8      4       VPushReg16    5   VPushReg32    6    VPopReg8      7
VPopReg16      8       VPopReg32   9   VPushMem8    a    VPushMem16   b
VPushMem32    c       VpopMem8   d    VPopMem16    e    VPopMem32   f
VAdd8         10       VSub8         11   VMul8         12   VDiv8         13
VAnd8         14       VOr8          15   VXor8         16    VAdd16       17
VSub16      18       VMul16      19   VDiv16      1a    VAnd16       1b
VOr16         1c      VXor16       1d    VAdd32       1e    VSub32       1f
VMul32       20      VDiv32       21   VAnd32       22    VOr32         23
VXor32         24      VTest8       25   VTest32       26    VCmp         27
VJmp          28         VJz         29    VJnz         2a   VJs         2b
VJns         2c         VJl          2d    VJle         2e   VJge          2f
VJg            30         VRetN       31   VExit         32   VPushMem8Reg 33   
VPushMem32Reg 34   VPopMem8Reg35   VPopMem32Reg 36    VsaveReg       37
VloadReg       38      VPopMovsx    39    VsaveEsp3a

二、逆向VM保护的算法
为了了解算法的大致流程,我们可以重点关注这段代码,因为算法循环总以jl语句判断是否要退出循环。

vjl.JPG

004032FF   .8B9D ECFEFFFFMOV EBX,DWORD PTR SS: //关注ebx当前地址 下断
00403305   .0FB713         MOVZX EDX,WORD PTR DS:
00403308   .8B85 F4FEFFFFMOV EAX,DWORD PTR SS:
0040330E   .8B9D FCFEFFFFMOV EBX,DWORD PTR SS:
00403314   .3BC3         CMP EAX,EBX
00403316   .74 10          JE SHORT ZooCMa.00403328
00403318   .8B8D C8FEFFFFMOV ECX,DWORD PTR SS:
0040331E   .03CA         ADD ECX,EDX
00403320   .898D ECFEFFFFMOV DWORD PTR SS:,ECX//关注ecx 目的地址下断
00403326   .EB 07          JMP SHORT ZooCMa.0040332F
00403328   >8385 ECFEFFFF >ADD DWORD PTR SS:,2
0040332F   >^ E9 A2F8FFFF    JMP <ZooCMa.handler>               ;VJl

通过测试可以发现关键算法循环开始于40fa26处。大致可以看出算法是:判断当前密码字符+当前用户名字符是否等于000000db,在调试时发现进入算法循环后用户名字符的第二、三位总是为固定值,是由于进入循环前的如下两段代码所致。
在40f991处开始的一段字节码,修改用户名第二个字符为’q’
37 03         mov temp,_ebx 初始值0
03 01 00 00 00   push 01
06 08         push 00
20            mul
03 01 00 00 00   push 01
1E             00+01=01
06 05          push _ebp 用户名首地址入虚拟栈
1E             用户名首地址+1
09 03          pop _ebx   这样_ebx=用户名首地址+1
01 71          push 71   即’q’
35 03          mov ,71 修改用户名第二个字符为q
38 03          mov _ebx,temp即mov _ebx,0
3A

在40f9d7处开始的一段字节码,修改用户名第三个字符为’p’
37 03 03 01 00 00 00 06 08 20
03 02 00 00 00push 02
1E             00+02=02
06 02
1E
09 03      pop _ebx   这样_ebx=用户名首地址+2
01 70      push 70   即’p’
35 03      mov ,70 修改用户名第三个字符为’p’
38 03
3A

vadd.JPG

在40fa26处开始的一段字节码 关键算法call
3A ;         jl 指令跳转到此 算法call开始处
37 03         mov temp,当前字符索引保存计数变量i
03 01 00 00 00push 1
06 02          push _edx ;用户名首地址
20             mul;= = push _edx
03 64 00 00 00push 00000064;+100
1E             add;= = push _edx+100
06 00          pus _eax ; + 字符索引 初始值为0
1E             add
09 03          mov _ebx,当前密码字符地址
33 03          push 当前密码字符
39 06          mov _esi, 当前密码字符
38 03          mov _ebx, 当前字符索引
3A
37 03          mov temp,当前字符索引
03 01 00 00 00push 00000001
06 02          push _edx ; 用户名首地址
20             mul; = = push _edx
03 00 00 00 00push 0
1E             add   ; _edx + 0
06 00          push _eax ; 即为push I   字符索引
1E             add; _edx + 0+i
09 03          mov _ebx,当前用户名字符地址
33 03          push 当前用户名字符
39 07          mov _edi, 当前用户名字符
38 03          mov _ebx, 当前字符索引
3A
06 07      push 当前用户名字符
06 06      push 当前密码字符
1E          add;当前密码字符+当前用户名字符
09 06      mov _esi,sum
3a
03 DB 00 00 00push 000000db
06 06          push sum
27             cmp          //当前密码字符+当前用户名字符=000000db
3A
2A 43 09      jnz   ;错误注册码结束
3A
03 01 00 00 00   push 00000001
06 00         push I ;计数变量
1E             ; i++
09 00         mov _eax, 计数变量值;更新当前比较的字符数
3A
06 01          push _ecx;用户名长度
06 00          push计数变量值
27             cmp
3A
2D BE 08       Jl   ;继续循环   40f168 +8be = 40fa26
3A
09 07            pop _edi
3A
09 06            pop _esi
3A
09 05            pop _ebp
3A
03 01 00 00 00   push00000001
09 00         pop _eax ;mov _eax,1
3A
09 03         pop _ebx
3A
03 70 00 00 00   push00000070
06 04         push _esp
1E             _esp+00000070
09 04         pop _esp 调整虚拟堆栈指针
3A            
31             add _esp,4
               Mov eax,_eax;return 1
               Ret

算法大致如下:
         name ='q';
name ='p';
for (int i=0;i<namelen;i++)
{   
                            Regcode=name+100;
             If((name + regcode)<>0xdb) return 0;//出错
}
return 1;//成功

总结:VM保护非常有效,它可以浪费逆向者大量的时间和精力。简单的VM况且如此,遇到商业的,你的破解成本就太高了,得不偿失。
                                                                                    by 天易love 09-12-5

fyc132 发表于 2010-3-15 19:40

good``````

745327354 发表于 2010-3-15 20:56

看不懂!!!!!

millerxie 发表于 2010-3-15 21:29

对我来说有点深了

a328439960 发表于 2010-3-15 22:25

:Dweeqw:Dweeqw:Dweeqw:Dweeqw:Dweeqw

fenqingfj 发表于 2010-3-15 22:35

页: [1]
查看完整版本: 虚拟机保护逆向分析实战