欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

VMProtect加密程序解析

VMProtect加密程序解析

此文章是我学习VMProtect虚拟机加密程序的心得,关于VMProtect宏观理论,参考了N多的网络文章和资料,做个笔记,以后好回顾,O(∩_∩)O哈哈~
软件:正版VMProtect2.04加密的Win98记事本
加密选项:除了 编译--调试模式与水印 以外,全部打钩;虚拟计算机--数量为默认值1;编译类型:超级(变异+虚拟)
说明:即使是同一个程序,VMProtect每次加密出来的代码都是不同的,所以如果你也去加密一个Win98记事本,是不会得到我下面的代码的。
一、VMProtect虚拟机概述
虚拟机加密,就是说像VMP这样的保护程序,它会把源程序的X86指令变成自定义的伪指令,等到执行的时候,VMP内置在保护程序中的VM就会启动,读取伪指令,然后解析执行。
VMP是一个堆栈虚拟机,它的一切操作都是基于堆栈传递的。在VMP中,伪指令就是一个个的handler,VM中有一个核心的Dispatch部分,它通过读取程序的bytecode,然后在DispatchiTable里面定位到不同的handler中执行。绝大多数情况下,在一个handler中执行完成后,程序将回到Dispatch部分,然后到next handler中执行。
 
在上面的框架中,核心的部件就是Dispatch部分,下面并列的部件就是handlers。
经过VMP加密的X86指令,一条简单的指令被分解成数条VMP的伪指令,它按照自己的伪指令排列去实现原指令的功能,在加上其他的花指令混乱等等,你将完全看不到源程序指令。VMP自带的各种机制都不再是以X86指令的形式去实现,而是用自己的伪指令去测试。
二、VMProtect虚拟机具体解析
1)到达VM的Dispatch部分
VMP要保存程序当前的context状况,然后进入虚拟代码执行
以下的代码完整粘贴自OD。有*标记的是程序的流程代码。其他的是junk code
CPU Disasm
Address   Hex dump          Command                                  Comments
00437817   .  68 B59DF9FC   PUSH FCF99DB5                           
0043781C   .^ E9 F320FFFF   JMP 00429914
00429914   > /C70424 0A4142 MOV DWORD PTR SS:[ESP],E342410A          ; *
0042991B   . |56            PUSH ESI
0042991C   . |E8 E3C50000   CALL 00435F04   
;开头放入了1个数字,这是基本数据。当然它现在是加密的。第一个push的数据并没有用,因为第二个push就覆盖掉了它,在动态调试中比较容易看出。
00435F04   $  E8 60160000   CALL 00437569                            ; \NOTEPAD.00437569
00437569  /$  C64424 04 87  MOV BYTE PTR SS:[ARG.1],87               ; NOTEPAD.00437569(guessed Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8,Arg9,Arg10,Arg11,Arg12,Arg13,Arg14,Arg15,Arg16,Arg17,Arg18,Arg19,Arg20)
0043756E  |.  C74424 08 D3A MOV DWORD PTR SS:[ARG.2],B433AFD3        ; *
00437576  |.  60            PUSHAD
00437577  |.  9C            PUSHFD
00437578  |.  8D6424 2C     LEA ESP,[ESP+2C] ;压入再多的数据都没用,这条指令直接修改了ESP指针
0043757C  \.  E9 015C0000   JMP 0043D182
;又放入了一个数据,当然它依然是加密的
;当前堆栈情况:
;0013FFBC   B433AFD3  3
;0013FFC0   E342410A  .AB
;0013FFC4   7C817077  wp|   ; RETURN to kernel32.7C817077
0043D182  \> /E9 31020000   JMP 0043D3B8
0043D3B8  /> \60            PUSHAD
0043D3B9  |.  9C            PUSHFD
0043D3BA  |.  68 82589578   PUSH 78955882
0043D3BF  |.  897424 24     MOV DWORD PTR SS:[ESP+24],ESI            ; *
0043D3C3  |.  68 07421102   PUSH 2114207
0043D3C8  |.  68 8098AB76   PUSH 76AB9880
0043D3CD  |.  57            PUSH EDI
0043D3CE  |.  8D6424 30     LEA ESP,[ESP+30]
0043D3D2  \.  E9 6C040000   JMP 0043D843
;我们现在可以来认识一点VMP的伎俩:
;1、虽然有很多的push指令,但是通过使用lea esp,[esp+xx]这样的指令,前面的所有的push都将作废
;2、为了跳过前面的junk push指令,在真正需要往堆栈放入数据的时候就使用mov dword ptr ss:[esp+xx],esi这样的指令,跳过了堆栈的xx空间来存储
;保存esi寄存器
0043D843  |> >E8 B6F6FFFF   CALL 0043CEFE                            ; \NOTEPAD.0043CEFE
0043CEFE   $  BE 054674C6   MOV ESI,C6744605                         ; NOTEPAD.0043CEFE(guessed Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8,Arg9,Arg10,Arg11,Arg12,Arg13,Arg14,Arg15,Arg16,Arg17,Arg18,Arg19,Arg20)
0043CF03   .  8D6424 04     LEA ESP,[ESP+4]
0043CF07   .  0F80 D5050000 JO 0043D4E2
0043CF0D   .  57            PUSH EDI                                 ; *
0043CF0E   .  68 EF0D9B40   PUSH 409B0DEF
0043CF13   .  0FCF          BSWAP EDI
0043CF15   .  5F            POP EDI
0043CF16   .  53            PUSH EBX
0043CF17   .  891424        MOV DWORD PTR SS:[ESP],EDX               ; *
0043CF1A   .  0FB6F1        MOVZX ESI,CL
0043CF1D   .  50            PUSH EAX                                 ; *
0043CF1E   .  9C            PUSHFD                                
0043CF1F   .  E9 C41A0000   JMP 0043E9E8
;保存edi edx eax
0043E9E8  |> \E8 8AF7FFFF   CALL 0043E177                            ; \NOTEPAD.0043E177
0043E177  /$  66:0FCF       BSWAP DI                                 ; Undocumented instruction or encoding
0043E17A  |.  896C24 04     MOV DWORD PTR SS:[ARG.1],EBP             ; *
0043E17E  \.^ E9 BEF5FFFF   JMP 0043D741
;保存ebp
0043D741  |> /871C24        XCHG DWORD PTR SS:[ESP],EBX              ; *
0043D744  |. |9C            PUSHFD
0043D745  |. |60            PUSHAD
0043D746  |. |894C24 20     MOV DWORD PTR SS:[ESP+20],ECX            ; *
0043D74A  |. |60            PUSHAD
0043D74B  |. |9C            PUSHFD
0043D74C  |. |66:891C24     MOV WORD PTR SS:[ESP],BX                 ; |Arg1
0043D750  |. |875424 40     XCHG DWORD PTR SS:[ESP+40],EDX           ; *
0043D754  |. |E8 D50E0000   CALL 0043E62E                            ; \NOTEPAD.0043E62E
;保存ebx ecx 再次的保存edx
0043E62E  /$  9C            PUSHFD                                   ; *
0043E62F  |.  8F4424 40     POP DWORD PTR SS:[ESP+40]                ; *
0043E633  |.  66:89C6       MOV SI,AX
0043E636  |.  FF35 89D24300 PUSH DWORD PTR DS:[43D289]               ; *
0043E63C  |.  8F4424 3C     POP DWORD PTR SS:[ESP+3C]                ; *
;保存eflags。和读取了一个数字0保存,这个0非常重要
0043E640  |.  0FCD          BSWAP EBP
0043E642  |.  66:FFC7       INC DI
0043E645  |.  47            INC EDI
0043E646  |.  C74424 38 000 MOV DWORD PTR SS:[ESP+38],0              ; *
;再次放入了一个0
;我们必须要在这里停顿一下了。由于是静态的看文章,O__O"…,让我们来看一下堆栈:
0013FF90   00000000  ....  ; 0
0013FF94   00000000  ....  ; 数字0
0013FF98   00000246  F..  ; eflags
0013FF9C   7C92E514  |    ; edx
0013FFA0   0013FFB0  .    ; ecx
0013FFA4   7FFDF000  .0    ; ebx
0013FFA8   0013FFF0  .    ; ebp
0013FFAC   00000000  ....   ;eax
0013FFB0   7C92E514  |    ;edx
0013FFB4   00500065  e.P.   ;edi
0013FFB8   00640061  a.d.   ;esi
0013FFBC   B433AFD3  3     ;最开始的2个基础加密数据
0013FFC0   E342410A  .AB
0013FFC4   7C817077  wp|   ; RETURN to kernel32.7C817077
;除了动态的esp寄存器,其他的寄存器已经保存完毕了,
0043E64E  |.  0FCD          BSWAP EBP
0043E650  |.  66:87D5       XCHG BP,DX
0043E653  |.  66:897C24 08  MOV WORD PTR SS:[ESP+8],DI
0043E658  |.  66:F7D3       NOT BX
0043E65B  |.  8B7424 68     MOV ESI,DWORD PTR SS:[ESP+68]            ; *
;它读取的是第一个基础数据
0013FFC0   E342410A  .AB   ;*
0013FFC4   7C817077  wp|   ; RETURN to kernel32.7C817077
0043E65F  |.  66:0FCD       BSWAP BP                                 ; Undocumented instruction or encoding
0043E662  |.  0F98C6        SETS DH
0043E665  |.  0FCE          BSWAP ESI                                ; *
0043E667  |.  66:F7D2       NOT DX
0043E66A  |.  66:0FBEEB     MOVSX BP,BL
0043E66E  |.  4E            DEC ESI                                  ; *
0043E66F  |.  66:FFC5       INC BP
0043E672  |.  20F6          AND DH,DH
0043E674  |.  66:C1D2 0E    RCL DX,0E
0043E678  |.  66:81FE 3214  CMP SI,1432                              ; *
0043E67D  |.  81F6 63A1000A XOR ESI,0A00A163                         ; *
;到这里esi的计算完成,esi==0041E381,在VMP里,esi都是指向bytecode的,看来马上就要开始读取了,然后跳转到handler了,哈哈。。还有一些准备工作的,就是要划分堆栈空间的,理解好VMP的堆栈空间划分是至关重要的。
0043E683  |.  45            INC EBP
0043E684  |.  80FD EF       CMP CH,0EF
0043E687  |.  66:0FB3D5     BTR BP,DX
0043E68B  |.  5A            POP EDX
0043E68C  |.  8D6C24 34     LEA EBP,[ESP+34]                         ; *
;这里给ebp传递0013FF90,切下第一刀
0043E690  |.  86F8          XCHG AL,BH
0043E692  |.  81EC 8C000000 SUB ESP,8C                               ; *
;分配大段的堆栈空间
0043E698  |.  66:0FBEFA     MOVSX DI,DL
0043E69C  |.  89E7          MOV EDI,ESP                              ; *
;这里给edi传递0013FED0,切下第二刀

0043E69E  |>  C0FE 04       SAR DH,4
0043E6A1  |.  0F9FC3        SETG BL
0043E6A4  |.  FEC8          DEC AL
0043E6A6  |.  C0C0 02       ROL AL,2
0043E6A9  |.  89F3          MOV EBX,ESI                              ; *
;在ebx中备份一下esi的0041e381
0043E6AB  |.  0FBAF2 05     BTR EDX,5
0043E6AF  |.  F7DA          NEG EDX
0043E6B1  |.  0FCA          BSWAP EDX
0043E6B3  |.  66:0FBAEA 0E  BTS DX,0E
0043E6B8  |.  0375 00       ADD ESI,DWORD PTR SS:[EBP]               ; *
;[ebp]中的是堆栈0013FF90里面的0,看来这是一个偏移量,用来修正bytecode地址
0043E6BB  |>  66:0FA5FA     SHLD DX,DI,CL                            ; Dispatch
0043E6BF  |.  8A46 FF       MOV AL,BYTE PTR DS:[ESI-1]               ; *
;开始读取bytecode,看来准备工作完成。它没有读取[esi],而是[esi-1]
0043E6C2  |.  88FA          MOV DL,BH
0043E6C4  |.  30D8          XOR AL,BL                                ; * 在ebx中备份bytecode地址;原来是要地址参与到计算中
0043E6C6  |.  FECA          DEC DL                                  
0043E6C8  |.  8D95 88EB93F7 LEA EDX,[EBP+F793EB88]
0043E6CE  |.  F6D0          NOT AL                                   ; *
0043E6D0  |.  42            INC EDX
0043E6D1  |.  F7D2          NOT EDX
0043E6D3  |.  0F9FC2        SETG DL
0043E6D6  |.  FEC8          DEC AL                                   ; *
0043E6D8  |.  D0CA          ROR DL,1
0043E6DA  |.  C0C8 07       ROR AL,7                                 ; * 一直在计算al值,算好了
0043E6DD  |.  F5            CMC
0043E6DE  |.  C0E6 06       SHL DH,6
0043E6E1  |.  83EE 01       SUB ESI,1                                ; * 指针减1,
0043E6E4  |.  D0D2          RCL DL,1
0043E6E6  |.  66:0FB6D1     MOVZX DX,CL
0043E6EA  |.  66:D1DA       RCR DX,1
0043E6ED  |.  30C3          XOR BL,AL                                ; * bl可是参与到计算的,bl帮al算完了,现在al来帮bl算好下一次的值
0043E6EF  |.  60            PUSHAD
0043E6F0  |.  66:0FBAE1 06  BT CX,6
0043E6F5  |.^ E9 32E9FFFF   JMP 0043D02C
0043D02C  |> /C0DE 07       RCR DH,7
0043D02F  |. |0FB6C0        MOVZX EAX,AL                             ; *
0043D032  |. |F5            CMC
0043D033  |. |E9 E7200000   JMP 0043F11F
0043F11F  |> \38F5          CMP CH,DH
0043F121  |.  66:FFC2       INC DX
0043F124  |.  8B1485 DBE143 MOV EDX,DWORD PTR DS:[EAX*4+43E1DB]      ; *等你等到我心痛呀
;终于开始,DispatchTable原来是在43E1DB,获得的是加密的地址,如:49C4C29F,数据窗口去43E1DB看看,茫茫多的地址呀,有200多个,不过这个不代表着有200多个handler洒,应该有一些是指向相同的handler。
0043F12B  |.  F9            STC
0043F12C  |.  84F4          TEST AH,DH
0043F12E  |.  60            PUSHAD
0043F12F  |.^ E9 CCEFFFFF   JMP 0043E100
0043E100  |> /81C2 6B197FB6 ADD EDX,B67F196B                         ; *原来只用一条ADD指令就解出来了,上面的49C4C29F=》0043DC0A,很简单的解密,自己可以去DispatchTable计算出所有的
0043E106  |. |C60424 46     MOV BYTE PTR SS:[ESP],46
0043E10A  |. |895424 3C     MOV DWORD PTR SS:[ESP+3C],EDX            ; * handler地址存一下,存到哪里呢?看看堆栈吧
;0013FECC   0043DC0A  .C.   ; 存到这里
;0013FED0   E5064000  .@   ;这里是EDI指向的空间,看一下前面的堆栈划分图
0043E10E  |. |9C            PUSHFD
0043E10F  |. |68 A42795AD   PUSH AD9527A4
0043E114  |. |FF7424 0C     PUSH DWORD PTR SS:[ESP+0C]
0043E118  |. |FF3424        PUSH DWORD PTR SS:[ESP]
0043E11B  |. |FF7424 4C     PUSH DWORD PTR SS:[ESP+4C]               ; *
0043E11F  |. |C2 5000       RETN 50                                  ; *
;这两条*指令要一起看,第一条push的就是0013FECC   0043DC0A这个handler地址,第二条就跑去找handler

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功