代码保护软件VMP逆向分析虚拟机指令详细分析

?看了网上大神们写了好多的vmp 虚拟代码的分析 ,但是在对实在项目时,插件总是提示不对或者未知版本,一直对vm代码的还原有质疑,于是就萌发了具体分析这个vm代码是怎么回事,若有错误,欢迎指出,能力有限只能分析皮毛,只谈vm的代码。

VMProtect正版授权在线订购享受最低价,仅售801元起!还不赶紧加入你的订购清单/span>>>更多详情可点击咨询购买

相关链接:

代码保护软件VMP逆向分析虚拟机指令:初步认识与环境搭建

代码保护软件VMP逆向分析虚拟机指令:VMP代码的提取

三、详细分析

我们先分析一下头和尾,那我们怎么知道哪里才是一个VM指令的开始和结束呢得jmp reg 和 push + ret 没,我们就由这两个去确定一个VM指令的范围

  • A、分析一下VM进入的操作
VM_Entry----------------------------------------------------------------0044A580 Main     PUSH 83819110                             ; Key 你可以看做一个密文经过解密    压栈1  Key0044A585 Main     CALL vmptest_.0042FA52                    ; Key 经过解密后就是bytescode 压找2  压返回地址0042FA52 Main     PUSH EDX                                  ; 压栈3  EDX0042FA53 Main     JMP vmptest_.0042BF920042BF92 Main     PUSHFD                                    ; 压栈4  eflags符号寄存器0042BF93 Main     TEST CX,SI0042BF96 Main     CMC0042BF97 Main     PUSH ECX                                  ; 压栈5  ECX0042BF98 Main     SHLD CX,SI,0BA0042BF9D Main     ADD CL,97                                 ; ECX=000000970042BFA0 Main     PUSH EBX                                 ;  压栈6  EBX0042BFA1 Main     MOV CL,DL                                 ; ECX=000000080042BFA3 Main     SHR EBX,CL                                ; EBX=007FFDF00042BFA5 Main     PUSH EBP                                 ; 压栈7  EBP0042BFA6 Main     PUSH ESI                                  ; 压栈8  ESI0042BFA7 Main     PUSH EAX                                 ; 压栈9  EAX0042BFA8 Main     PUSH EDI                                  ; 压栈10 EDI0042BFA9 Main     ADD ECX,173475C8                          ; ECX=173475D00042BFAF Main     MOV EAX,00042BFB4 Main     XOR ESI,239C226E                          ; ESI=239C226E0042BFBA Main     PUSH EAX                                  ; 压栈11 0x000000000042BFBB Main     SETLE BH                                  ; EBX=007F00F00042BFBE Main     MOV ESI,DWORD PTR SS:[ESP+28]             ; ESI=83819110   取ESP+0x28地址的值 0x28/4= 10 往上数第10个0042BFC2 Main     LEA ESI,DWORD PTR DS:[ESI+D093E2F1]       ; ESI=54157401   从0开始数 你看看是不是取到了Key的值 注意0042BFC8 Main     NEG ESI                                   ; ESI=ABEA8BFF   注意这个 [ESP + 0x28]这个特征 现在赋值给ESI0042BFCA Main     BTS BP,BX                                 ; EBP=0012FF950042BFCE Main     JMP vmptest_.00430A0300430A03 Main     ROR ESI,1                                 ; ESI=D5F545FF00430A05 Main     INC ESI                                   ; ESI=D5F5460000430A06 Main     ADD EBP,64941F8C                          ; EBP=64A71F2100430A0C Main     ADC CX,SP                                 ; ECX=1734753000430A0F Main     BTR EDI,7300430A13 Main     BSWAP ESI                                 ; ESI=0046F5D500430A15 Main     CMOVPE EBX,ESI                            ; EBX=0046F5D500430A18 Main     BTC BP,8C                                 ; EBP=64A70F2100430A1D Main     ADD ESI,EAX00430A1F Main     MOV EBP,ESP                               ; EBP=0012FF60  ;注意这里EBP的赋值 记住这里现在ebp是上面压的栈顶00430A21 Main     SUB ESP,0C0                              ; 和这里esp的开栈 其实这里是给vm_context开空间00430A27 Main     MOV EBX,ESI                                               ;和给vm_ss开空间00430A29 Main     MOV ECX,0                                 ; ECX=00000000  ;注意这里有个mov ebx,esi 解密因子 我接下来就不00430A2E Main     OR EDI,EBP                                ; EDI=0012FF60  ;会去多提他 如果你要运行程序你就要注意他 这个解密因子 往上看是不是Key解密而来的00430A30 Main     SUB EBX,ECX                             ;分析好几个vm 分析ebx一直指向这个vm_decryptfactor00430A32 Main     ROL CX,CL                                 ;我不知道这样叫他对不对 不过这不是重点00430A35 Main     LEA EDI,DWORD PTR DS:[430A35]             ; EDI=00430A35  ;知道他是干什么的就可以了00430A3B Main     SUB CX,SI                                 ; ECX=00000A2B ;看到这里的LEA 本身这个指令上一个地址 没错他就是vm_JumpBase 相对谁去跳到下一个地址 肯定是当前00430A3E Main     OR CL,0D5                                 ; ECX=00000AFF00430A41 Main     ROL CX,23                                 ; ECX=000057F800430A45 Main     MOV ECX,DWORD PTR DS:[ESI]                ; ECX=B0D37F60 ;到这里 经过解密之后ESI才是真正的vm_bytescode00430A47 Main     ADD ESI,4                                 ; ESI=0046F5D9 ;你或者叫他(ESI)为跳转间隔 好像也没什么错00430A4D Main     TEST EDI,EBX                              ; 取值后 ESI +=4 往后移00430A4F Main     XOR ECX,EBX                               ; ECX=B0958AB500430A51 Main     JMP vmptest_.00439FEC00439FEC Main     LEA ECX,DWORD PTR DS:[ECX+DEFE95D9]       ; ECX=8F94208E00439FF2 Main     CMP AX,25AD00439FF6 Main     TEST BH,9000439FF9 Main     NOT ECX                                   ; ECX=706BDF7100439FFB Main     TEST AL,DL00439FFD Main     CMC00439FFE Main     CMP SP,7CB80043A003 Main     NEG ECX                                   ; ECX=8F94208F0043A005 Main     LEA ECX,DWORD PTR DS:[ECX+A504E271]       ; ECX=349903000043A00B Main     JMP vmptest_.0041656200416562 Main     BSWAP ECX                                 ; ECX=0003993400416564 Main     XOR EBX,ECX                               ; EBX=00456CE100416566 Main     CMP SI,130B0041656B Main     ADD EDI,ECX                               ; EDI=0046A369 ;上面说到了取bytescode链上的值0041656D Main     JMP vmptest_.0045F2B5                     ; 经过解密后 与JumpBase相加得到下一个指令的地址0045F2B5 Main     PUSH EDI                                 ; 然后把这个地址压栈 RET反弹给物理机EIP0045F2B6 Main     RETN                                     ;或者这里你可能也会遇到jmp edi 他们是一样的功能

上面这个我们没有去混淆,我们直接分析了。那么肯定是有技巧的是吧,从上往下看,push 和pushfd肯定要保留,这个是重要的,然后从下往上分析,EDI是重要的,然后看到谁影响了EDI一直这样往上找去,有关的提取出来。另外关于ESP寄存器的要保留下来,主要是ESP减0xC0这个开VM栈空间和VM_REG空间,还有ESP在减0xC0之前把值给了谁,谁就是VM_ESP即vm栈顶(栈底),当然给的这个寄存器不在传递给其他寄存器的话才行,但目前我并没有遇到还在传递的情况。我们是看了开始,我们在看看紧跟着的VM指令:

VM_PopReg32  vm_context->0x10---------------------------------------------------------------------------------------------0046A369 Main     MOV EAX,DWORD PTR SS:[EBP]                ;返回上面看看我们说EBP是上面压的物理环境的栈顶0046A36D Main     STC                                                       ;0046A36E Main     ROR DL,CL                                 ; EDX=004010800046A370 Main     LEA EBP,DWORD PTR SS:[EBP+4]              ; EBP=0012FF64 ;这里EBP = EBP + 4 栈回缩是不 那不就是出栈的意思0046A376 Main     MOVZX EDX,BYTE PTR DS:[ESI]               ; EDX=0000008D ;注意这种取1byte的指令类似这个0046A379 Main     CMP CX,BP0046A37C Main     ADD ESI,1                                 ; ESI=0046F5DA ;ESI+1  刚刚说了esi是bytescode 他也保存了寄存器0046A382 Main     CMP CX,4F65                                              ;编号  我们不是之前叫他跳转间隔吗 噢见鬼了0046A387 Main     CMP ECX,EBX                                              ;反正我们知道他有这样的能力就可以  这里ESI+=10046A389 Main     XOR DL,BL                                 ; EDX=0000006C ;有可能他不是正向走 可能是-1 那上面的也对应-40046A38B Main     TEST DI,5D07                                             ;这没什么好争论的不是吗  具体看汇编就出来了0046A390 Main     STC                                                      ;为什么会这么说 是以为我之前有遇到是反向增长0046A391 Main     NOT DL                                    ; EDX=00000093 ;看到上面的 xor dl,bl  记得我说的EBX是解密因子了没0046A393 Main     CMP DI,27680046A398 Main     JMP vmptest_.0043EDAF0043EDAF Main     SUB DL,8B                                 ; EDX=000000080043EDB2 Main     CMC0043EDB3 Main     ROR DL,1                                  ; EDX=000000040043EDB5 Main     DEC DL                                    ; EDX=000000030043EDB7 Main     JMP vmptest_.00413DC100413DC1 Main     NOT DL                                    ; EDX=000000FC00413DC3 Main     CMP SP,6B2900413DC8 Main     JMP vmptest_.00416D4800416D48 Main     ADD DL,14                                 ; EDX=0000001000416D4B Main     STC00416D4C Main     XOR BL,DL                                 ; EBX=00456CF1;而这里我们主要关心EDX的值不是吗 这里是0x1000416D4E Main     MOV DWORD PTR SS:[ESP+EDX],EAX            ; 发现EDX是从上面去esi地址1byte值经过变化而来的00416D51 Main     AND DH,0F7                                ; 而我们之前说的什么ESP指向的是vm_context是吧00416D54 Main     NEG DX                                    ; EDX=0000FFF0;在结合我们上面的分析 这应该是一个出栈的操作00416D57 Main     MOV EDX,DWORD PTR DS:[ESI]                ; EDX=E45184BF;在次取ESI (bytescode)的值 关键 这个是跳转间隔密文00416D59 Main     CMP ESI,EDI00416D5B Main     CMC00416D5C Main     JMP vmptest_.00419F0800419F08 Main     ADD ESI,4                                 ; ESI=0046F5DE;ESI += 400419F0E Main     STC00419F0F Main     XOR EDX,EBX                               ; EDX=E414E84E00419F11 Main     TEST DH,3600419F14 Main     XOR EDX,29474E33                          ; EDX=CD53A67D00419F1A Main     TEST ESP,EDX00419F1C Main     ADD EDX,64682765                          ; EDX=31BBCDE200419F22 Main     JMP vmptest_.0047289000472890 Main     NEG EDX                                   ; EDX=CE44321E00472892 Main     TEST SI,BX00472895 Main     STC00472896 Main     ADD EDX,44017C67                          ; EDX=1245AE850047289C Main     JMP vmptest_.0046306800463068 Main     ROR EDX,1                                 ; EDX=8922D7420046306A Main     CMP CX,DI0046306D Main     STC0046306E Main     CMP DI,358A00463073 Main     NEG EDX                                   ; EDX=76DD28BE00463075 Main     JMP vmptest_.0047F9030047F903 Main     BSWAP EDX                                 ; EDX=BE28DD760047F905 Main     INC EDX                                   ; EDX=BE28DD770047F906 Main     STC0047F907 Main     XOR EDX,41D507D0                          ; EDX=FFFDDAA70047F90D Main     XOR EBX,EDX                               ; EBX=FFB8B656;注意这里解密因子 ebx 改变 每一个vm指令都有这个0047F90F Main     CMP SP,BP0047F912 Main     TEST EDX,29E10AB5                         ;解密因子的参与  接下来我就不多提他了 记得每个指令都有0047F918 Main     STC                                     ;而且每运行一个vm指令 他都会变0047F919 Main     ADD EDI,EDX                             ; EDI=00447E10;跳转间隔经过解密后 与JumpBase相加0047F91B Main     JMP vmptest_.00420B9F00420B9F Main     PUSH EDI00420BA0 Main     RETN                                      ;跳转到下一条指令地址-------------------------------------------------------------------------------------------------

然后针对VM指令非入口和出口的分析,也有方法,当然可能这些方法这是适用我们当前遇到的指令(即目前这个demo)。看到第一条指令是取VM_ESP位置的uint32_t值 ,然后VM_ESP回缩4byte,第一反应就是这个应该是pop操作,在往下看看,能不能看到MOV [ESP + REG1],REG2 这个样子的汇编,OK能找到,那么明显就是栈中弹数据到VM_REG中了。在往下看是取VM_Bytescode中的值然后解密与VM_JumpBase相加。无疑是VM_PopReg32了,但是那个寄存器呢到

00416D4E Main     MOV DWORD PTR SS:[ESP+EDX],EAX            ;往上看到EDX的值 看右边的追踪结果 EDX=0x10

所以我们这里应该翻译为VM_PopReg32 vm_context->0x10 或者 VM_PopReg32 DwReg4 (0x10/4=4) 可能名字和别人不一样,差不多就是这个意思。注意看看这个EDX的值也是从VM_Bytescode中得到解密出来的。然后紧跟着的几条重要的VM指令我就不一一分析了,和上面是同理的,我直接贴分析出的VM指令

[---------VM_Init--------]  这里我们可以又把这几条vm指令解释为VM_Init 主要是把物理环境反弹到虚拟机里面/----------------------------------------------------------------------VM_PopReg32  vm_context->0x10   0x00000000VM_PopReg32  vm_context->0x2C VM_PopReg32  vm_context->0x18VM_PopReg32  vm_context->0x28VM_PopReg32  vm_context->0x20   0x0012FF94VM_PopReg32  vm_context->0x08   0x7FFDF000VM_PopReg32  vm_context->0x24   0x00000000VM_PopReg32  vm_context->0x3C   0x00000246VM_PopReg32  vm_context->0x30   0x00401008VM_PopReg32  vm_context->0x14   0x0044A58A           那我们很早之前说的上面压栈是保存物理机环境就不太对了VM_PopReg32  vm_context->0x0C   0x83819110           应该说是 物理机环境映射到虚拟机的一个过渡-------------------------------------------------------------------------

把物理环境弹入VM寄存器环境后,重新调整vm_bytescode的指向和之前的VM_REG乱序,就比如上面这个vm_context->0x14是push + call 中call的下一个地址 ,经过调整后vm_context->0x14也就不是这个地址了(这个地址并不是退出VM后仅跟着的执行真实汇编的地址),再比如EBP是vm_bytescode,变换后EBP可能就不是了,可能是EBX或者EAX等。我就不贴代码了,之后会有整理。

  • B、分析一下VM退出的操作
VM_Exit/------------------------------------------------------------------------------0047A839 Main     MOV ESP,EDI                               ;虚拟机栈给物理机esp栈 没错了这里应该是要0047A83B Main     OR AX,CX                                  ; EAX=0000EFEA;返回物理机了0047A83E Main     POP ECX                                   ; ECX=00000000;弹栈  ECX0047A83F Main     ROR DX,CL                                 ;看到这么多pop 看来是真的要还原到物理机了0047A842 Main     CMP DH,DL                                 ;在看看我们的进度条 也到底了 终于分析完了0047A844 Main     POP EBP                                   ; EBP=0012FF94;弹栈  EBP0047A845 Main     POP EAX                                   ; EAX=00004545;弹栈  EAX0047A846 Main     POP EBX                                   ; EBX=7FFDF000;弹栈  EBX0047A847 Main     OR DI,73E7                                ; EDI=0012FFEF0047A84C Main     ADC DI,6287                               ; EDI=001262760047A851 Main     CMOVO SI,DI0047A855 Main     POP EDI                                   ; EDI=00000000;弹栈  EDI0047A856 Main     CWD                                       ; EDX=001200000047A858 Main     POPFD                                                   ;弹栈  EFLAGS  符号寄存器0047A859 Main     POP ESI                                   ; ESI=00000000;弹栈  ESI0047A85A Main     POP EDX                                   ; EDX=00401008;弹栈  EDX0047A85B Main     RETN                                                    ;返回物理机    Breakpoint at vmptest_.0040104000401040 Main     PUSH EAX                                  ; <%04X> = 4545 返回物理机第一条汇编    Run trace closed

我们可以看到ESP寄存器的值被EDI赋值了,然后从栈中弹出值到物理机(真实环境)的寄存器中。那么这个EDI应该是被安排了真实环境的值,很可能他就是VM_ESP是吧。我们在往上分析一下看看,我直接贴关键的VM指令

[---------VM_Destroy----------] 下面的这几句你可以各类为新的vm指令 他其实就是准备退出虚拟机时把 处理后的物理环境压栈/----------------------------------------------------------------------------------------VM_PushImm32 0x00401040        retAddrVM_PushReg32 vm_context->0x14  0x00401008VM_PushReg32 vm_context->0x30  0x00000000VM_PushReg32 vm_context->0x18  0x00000202VM_PushReg32 vm_context->0x24  0x00000000VM_PushReg32 vm_context->0x04  0x7FFDF000VM_PushReg32 vm_context->0x10  0x00004545  我们要的最终值VM_PushReg32 vm_context->0x3C  0x0012FF94VM_PushReg32 vm_context->0x28  0x00000000-------------------------------------------------------------------------------------------

这里我就不展示EDI是VM_ESP了,我可以告诉你他是的。看看这个返回地址他是由VM_Bytescode中得到的,还记得我们VM_Entry进来时压的那个push + call 中 call指令后面的地址吗,不是返回仅跟着CALL后面地址的位置,所以我们往往在那个地方下断点就没作用。

如果您对该加密/解密软件感兴趣,欢迎加入加密/解密QQ交流群:

标签:

来源:慧都

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年5月19日
下一篇 2021年5月19日

相关推荐

发表回复

登录后才能评论