转帖|使用教程|编辑:杨鹏连|2021-06-30 09:25:48.143|阅读 669 次
概述:看了网上大神们写了好多的vmp 虚拟代码的分析 ,但是在对实在项目时,插件总是提示不对或者未知版本,一直对vm代码的还原有质疑,于是就萌发了具体分析这个vm代码是怎么回事,若有错误,欢迎指出,能力有限只能分析皮毛,只谈vm的代码。
# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>
VMProtect是一种很可靠的工具,可以保护应用程序代码免受分析和破解,但只有在应用程序内保护机制正确构建且没有可能破坏整个保护的严重错误的情况下,才能实现最好的效果。
VMProtect通过在具有非标准体系结构的虚拟机上执行代码来保护代码,这将使分析和破解软件变得十分困难。除此之外,VMProtect还可以生成和验证序列号,限制免费升级等等。
VMProtect正版授权在线订购享受最低价,仅售801元起!还不赶紧加入你的订购清单?>>更多详情可点击咨询购买
相关链接:
代码保护软件VMP逆向分析虚拟机指令:初步认识与环境搭建(一)
代码保护软件VMP逆向分析虚拟机指令:VMP代码的提取(二)
代码保护软件VMP逆向分析虚拟机指令:分析那4条汇编被VM的VM指令
五、分析VM的一些细节
我们差不多讲完基础分析是吧,基本上可以说是可以动手分析了,但是在那之前我们有些东西好像还没讲清楚,一些细节。
1、VM的栈问题
有个地方我们还没讲 ,就是如果压栈时 , 如果超出了栈大小 (就目前我们看到的还没有超栈的情况),占用到了vm_context怎么办下面是我截取的汇编, 所以你看到地址有时候很奇怪 。我们来分析一下如果压栈超出了栈空间怎么办
00433391 8D4424 60 lea eax,dword ptr ss:[esp+0x60] 00433395 F6C5 65 test ch,0x65 00433398 3BC9 cmp ecx,ecx 0043339A 66:81FD EA79 cmp bp,0x79EA 0043339F 3BE8 cmp ebp,eax 004333A1 E9 0F3A0000 jmp vmptest_.00436DB5 00436DB5 /0F87 F2C80200 ja vmptest_.004636AD ;看到这里我们之前都是跳了 我们追踪代码 00436DBB |8BC4 mov eax,esp ;的时候下面的代码就被跳过了,直接到了 00436DBD |8ACB mov cl,bl ;vmtest_.004636AD 接下来我们重点讲没跳 00436DBF |B9 40000000 mov ecx,0x40 ;过的这部分代码 00436DC4 |0FC0F6 xadd dh,dh 00436DC7 |8D5425 80 lea edx,dword ptr ss:[ebp-0x80] 00436DCB |3BFF cmp edi,edi 00436DCD ^|E9 101CFEFF jmp vmptest_.004189E2 004189E2 81E2 FCFFFFFF and edx,0xFFFFFFFC 004189E8 F9 stc 004189E9 66:F7C7 8465 test di,0x6584 004189EE 2BD1 sub edx,ecx 004189F0 ^ E9 38D1FFFF jmp vmptest_.00415B2D 00415B2D 8BE2 mov esp,edx 00415B2F 57 push edi 00415B30 66:BF CD59 mov di,0x59CD 00415B34 0FBFF8 movsx edi,ax 00415B37 E9 FA590400 jmp vmptest_.0045B536 0045B536 56 push esi 0045B537 ^ E9 058AFEFF jmp vmptest_.00443F41 00443F41 9C pushfd 00443F42 66:0FCE bswap si 00443F45 8BF0 mov esi,eax 00443F47 66:0F46FB cmovbe di,bx 00443F4B 8BFA mov edi,edx 00443F4D E9 490E0400 jmp vmptest_.00484D9B 00484D9B FC cld 00484D9C ^ E9 9513F9FF jmp vmptest_.00416136 00416136 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] 00416138 C1E6 B0 shl esi,0xB0 0041613B C1C7 FA rol edi,0xFA 0041613E 66:0FBAFF 16 btc di,0x16 00416143 9D popfd 00416144 5E pop esi 00416145 66:0FCF bswap di 00416148 8BFB mov edi,ebx 0041614A 5F pop edi 0041614B E9 5DD50400 jmp vmptest_.004636AD 004636AD FFE7 jmp edi我们先去混淆一下看看,按之前的方法
00433391 8D4424 60 lea eax,dword ptr ss:[esp+0x60] 0043339F 3BE8 cmp ebp,eax ;比较栈顶 与vm_context末尾位置 00436DB5 /0F87 F2C80200 ja vmptest_.004636AD ;如果栈顶 大于 vm_context最末尾位置 证明栈还没有顶到vm_context 00436DBB |8BC4 mov eax,esp ;eax记录一下vm_context位置 00436DBF |B9 40000000 mov ecx,0x40 00436DC7 |8D5425 80 lea edx,dword ptr ss:[ebp-0x80] 004189E2 81E2 FCFFFFFF and edx,0xFFFFFFFC ;让后2位为0 地址对齐 004189EE 2BD1 sub edx,ecx ;其实就是栈顶 -0xC0 00415B2D 8BE2 mov esp,edx ;把vm_context 与栈底 拉高0xC0个字节 主要是栈底不动 vm_context动 00415B2F 57 push edi ;这一方面也是看出来为什么每次都要和esp+0x60比较 0045B536 56 push esi ------------------------------------- 00443F41 9C pushfd ;备份 edi 、esi、和eflags值 00443F45 8BF0 mov esi,eax 00443F4B 8BFA mov edi,edx 00416136 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] ;然后把旧vm_context线上值 拷贝到新vm_context上 00416143 9D popfd --------------------------------------- 00416144 5E pop esi ;还原 edi、esi、和eflags值 0041614A 5F pop edi 004636AD FFE7 jmp edi然后你发现了什么问题, 在最后的rep movs中, 我们是拷贝byte为单位, 拷贝的长度是由ecx决定的,但是ecx=0x40, 而我们之前很早之前说 esp+0x60我们推出寄存器有 0x60/4=0x18 个, 芜湖!!!!!!!那么我们现在怎么解释呢? 这里我们应该可以看出来 vm_context应该只有16个寄存器。 即0x10个寄存器而多出来的 0x20个byte(8 int32) 应该是给压栈时缓冲用的, 比如像这里他是先压栈, 在判断栈是否报栈了 ,那么如果爆栈了, 假如vm_context是0x18个寄存器, 他就会覆盖到第0x17编号寄存器,那么他就没办法回缩 还原vm_context上被感染的寄存器值(如果要能还原 带价太大) 所以这里多出的8 int32 应该是缓冲用的。
所以说分析问题 往往都是 ,从假设开始 ,你之前看到的可能 会被后面的又重新定义 ,然后你会发现
你之前的好像不太严谨 ,往后看这个就严谨了 ,好像一切 就 豁然开朗了。
2、寄存器轮转机制
VM_PushImm32 0x00002222 ;执行下面的VM_Add后 | dwResult 0x00005555 VM_PushReg32 vm_context->0x00 ;0x00003333 | eflags 0x00000206 VM_Add VM_PopReg32 vm_context->0x1C ;0x00000206 EFLAGS VM_PopReg32 vm_context->0x1C ;0x00005555 VM_PushImm32 0x00001010 VM_PushReg32 vm_context->0x1C ;0x00005555 dwResult 上一次Add的结果 VM_PushReg32 vm_context->0x1C ;0x00005555 VM_Nand ;执行后 [ESP] = EFLAGS = 0x286 [ESP+4]=0xFFFFAAAA VM_PopReg32 vm_context->0x00 ;0x286 弹出这个后 现在栈上应该是[ESP]=0xFFFFAAAA [ESP+4]=0x0x00001010 VM_Add ;执行这个这里后 [ESP] = EFLAGS = 0x282 [ESP+4]=0xFFFFBABA VM_PopReg32 vm_context->0x0C ;0x282 eflags VM_PushReg32 vm_esp ;现在[ESP] = ESP+4 即保存这个地址 这个地址对应的值是 0xFFFFBABA 没问题吧 VM_SSReadMemSS ;把栈顶的值当mem读 值返回到自身 [ESP]=[[ESP]] = [ESP + 4] = 0xFFFFBABA VM_Nand ;还没运行这个指令时[ESP]=0xFFFFBABA [ESP+4]=0xFFFFBABA VM_PopReg32 vm_context->0x20 ;0x202 eflags VM_PopReg32 vm_context->0x10 ;0x00004545 到这里 可以说这4句汇编已经运行完毕了我们可以看到 0x2222 + 0x3333 = 0x5555 如果按我们的那4句汇编来的话, 这个0x5555应该是在eax寄存器上,如果说vm_context 的某个偏移值与我们物理机的寄存器是绝对对应关系的话, 那么往下走得到的最终结果应该也是在 0x5555所在的这个寄存器上即 vm_context->0x1C, 但是我们发现不是了, 而是vm_context + 0x10上。这就是VMP所谓的寄存器轮转机制, 这个轮转是在程序编译期间有一张表, 所以现在你是无法看到这个对应关系的转变。
3、同一条指令的不同比较
------------------------------------------------------------------------------------------- VM_PushReg32 vm_context->0x1C VM_PushReg32 vm_context->0x1C /------------------------------------------------------------------------------------------ 0042E1A7 LEA ESI,DWORD PTR DS:[ESI-1] 0043B877 SUB ESI,1 0042E1AD MOVZX EAX,BYTE PTR DS:[ESI] 0043B87D ROL EAX,CL 0042E1B0 SHR DX,CL 0043B87F MOVSX ECX,DX 0042E1B3 MOVZX EDX,BX 0043B882 MOVZX EAX,BYTE PTR DS:[ESI] 0042E1B6 CDQ 0043B885 BTC CX,BX 0042E1B7 XOR AL,BL 0043B889 BTS ECX,EBX 0042E1B9 ADD ECX,605116AB 0043B88C OR DH,64 0042E1BF CMP DH,87 0043B88F XOR AL,BL 0042E1C2 ADD AL,19 0043B891 ADD AL,19 0042E1C4 ROR AL,1 0043B893 MOVZX CX,DH 0042E1C6 CMOVNB EDX,ESI 0043B897 ROR AL,1 0042E1C9 XCHG CX,DX 0043B899 JMP vmptest_.0045256F 0042E1CC MOV EDX,EBP 0045256F INC AL 0042E1CE INC AL 00452571 XOR AL,49 0042E1D0 AND DX,53E2 00452573 ROR DH,CL 0042E1D5 XOR AL,49 00452575 XOR BL,AL 0042E1D7 BSF CX,BX 00452577 MOVSX EDX,SP 0042E1DB SBB ECX,EBP 0045257A BTR ECX,ESP 0042E1DD XOR BL,AL 0045257D SUB EDX,1D2A7287 0042E1DF SAR DX,CL 00452583 MOV EDX,DWORD PTR SS:[ESP+EAX] 0042E1E2 MOV EDX,DWORD PTR SS:[ESP+EA 00452586 NOT ECX 0042E1E5 MOV CL,1A 00452588 LEA EDI,DWORD PTR DS:[EDI-4] 0042E1E7 RCR CH,CL 0045258E MOV DWORD PTR DS:[EDI],EDX 0042E1E9 MOVSX ECX,CX 00452590 SUB ESI,4 0042E1EC LEA EDI,DWORD PTR DS:[EDI-4] 00452596 DEC CX 0042E1F2 MOV DWORD PTR DS:[EDI],EDX 00452599 JMP vmptest_.00410D3E 0042E1F4 XOR CH,0A4 00410D3E MOV ECX,DWORD PTR DS:[ESI] 0042E1F7 BTS ECX,EBX 00410D40 CMC 0042E1FA SUB ESI,4 00410D41 STC 0042E200 MOV ECX,DWORD PTR DS:[ESI] 00410D42 CLC 0042E202 STC 00410D43 XOR ECX,EBX 0042E203 TEST DH,DH 00410D45 CMP EDI,ESP 0042E205 XOR ECX,EBX 00410D47 NOT ECX 0042E207 CLC 00410D49 CMP DL,0F7 0042E208 CMC 00410D4C STC 0042E209 NOT ECX 00410D4D ADD ECX,1B2352AE 0042E20B CLC 00410D53 BSWAP ECX 0042E20C ADD ECX,1B2352AE 00410D55 TEST ESP,4D941F4C 0042E212 CMC 00410D5B XOR ECX,26A7DD4 0042E213 CLC 00410D61 STC 0042E214 BSWAP ECX 00410D62 XOR EBX,ECX 0042E216 TEST EDI,EDI 00410D64 CLC 0042E218 STC 00410D65 CMP BP,11E1 0042E219 XOR ECX,26A7DD4 00410D6A CMC 0042E21F CMP CX,BX 00410D6B ADD EBP,ECX 0042E222 XOR EBX,ECX 00410D6D JMP vmptest_.0047F0F3 0042E224 STC 0047F0F3 JMP vmptest_.00472E41 0042E225 TEST ECX,ESI 00472E41 LEA EDX,DWORD PTR SS:[ESP+60] 0042E227 ADD EBP,ECX 00472E45 TEST DH,AL 0042E229 JMP vmptest_.00409E73 00472E47 CLC 00409E73 JMP vmptest_.00472E41 00472E48 CMP EDI,EDX 00472E41 LEA EDX,DWORD PTR SS:[ESP+60] 00472E4A JMP vmptest_.0046EE86 00472E45 TEST DH,AL 0046EE86 JA vmptest_.00480A05 00472E47 CLC 00480A05 JMP EBP 00472E48 CMP EDI,EDX 00472E4A JMP vmptest_.0046EE86 0046EE86 JA vmptest_.00480A05 00480A05 JMP EBP \---------------------------------------------------------------------------------指令与压哪个寄存器无关,我这里只是恰巧找了两个压了相同VM寄存器的。 你看他们的主要代码是不是一样的, 去混淆一下, 就发现是一模一样的, 解密都一样,不过可能存在乱序(在不影响经结果的情况下), 而每次vmp加壳同一个程序这些同vm指令解密都不一样。去混淆我就不去了, 很简单 1-2min的事情 从vm的环境去提取关键汇编就可以了。不过我相信现在你用肉眼就已经看出来了。那么我们上面分析的东西材料我都放在demo文件夹下了。
六、简单总结一下过程
总结就是 :
1.VM_Entry 进入虚拟机
2.VM_Init 物理环境映射虚拟机
3.VM_Init_bytescode vm的代码解析环境初始化
4.执行VM的代码
5.VM_Destroy 虚拟机环境压栈
6.VM_Exit 物理环境从栈上弹出
在整个过程中我们并没有看到有一个大循环, 心脏去驱动 去取指令, 然后解析指令是吧。 这个就是vmp3与vmp1和vmp2的最大区别,解析bytescode不在由VMDispatcher 分发下一个指令执行什么了(每个指令记为一个handle) 而是有vm_bytescode掌管,执行上一个指令才能得到下一个指令地址 这样一来代码的膨胀可想而知。在VM_Instruct内部应该是没有CALL指令的。
如果您对该加密/解密软件感兴趣,欢迎加入vmpQQ交流群:740060302
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn
文章转载自: