?看了 上大神们写了好多的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进行处理,非常感谢!