简单分析VMProtectV3.3.1

前言
VMProtect3很早就出来了,据说代码使用C++重构了,而且虚拟机架构也有很大的变化。网上关于VMP3.X的帖子不是很多,我这个弱鸡也来上篇文章分析一下。
文章里面用到了一个解混淆的脚本,附录中我会给出这个破脚本的下载链接及大概原理。
三十二变

2019.2.13

准备工作
先对一段VREY EASY的汇编代码进行加密。如下。


配置VMProtect V3.3.1对@Main过程进行加密。注意将除代码虚拟化以外的保护全部去除勾选。
附注:那个szText变量请无视,我是写完文章才发现这个坑的,已经懒得改了。


编译后,发现文件大小为552KB!意思是说,不包含外壳的代码,只虚拟机部分的代码就膨胀了550KB!不敢想象这是一个怎样的存在,开始我认为是虚拟化的混淆程度又有加强,分析完后才发现原来是虚惊一场......
初探
使用IDA的Trace功能对虚拟机的运行全过程进行记录。发现共执行了1486条语句,这个膨胀率相比与VMP2.X架构可以说是非常小了。加密7条语句,在VMP2.X中光虚拟机指令就可以膨胀到700条左右。
仍然在Trace文件中从头部开始搜索RET指令。如下。

有一个比较令人惊讶的发现,VMP3.X没有采用栈混淆。纵览进入虚拟机的环境备份代码,发现只有简单的针对寄存器的插入死代码。
注意,不完全是以push esi/retn指令对的形式进行转移,还有以jmp esi实现流程转移的情况,事实上前者完全可以看成后者的一个变形。
运行脚本,按要求输入进程ID,起始地址(可以输入Dispatch或Handle的起始地址)。
则会输出解混淆后的代码。如下。


注意,栈中的数据被以DWORD为单位从0开始编号,read/write列表中包含每条指令读取/写入的栈变量序号。

Asm\] *纯文本查看* *复制代码* [?](https://www.52hb.com/# "?") |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | `45E756 push0x6dae122f read = [] write = [0]` `//此处压入一个加密后的常数,经解码后可以取出指令流地址` `4748B1 ``push` `esiread = [] write = [2]` `4748B5 ``push` `ebxread = [] write = [3]` `4748B8 ``push` `ediread = [] write = [4]` `4748BF ``push` `edxread = [] write = [5]` `4748C7pushfd read = [] write = [6]` `4748C8 ``push` `ecxread = [] write = [7]` `4748C9 ``push` `ebpread = [] write = [8]` `4748CA ``push` `eaxread = [] write = [9]` `456DA6 ``mov` `eax``,0 read = [] write = []` `456DB2 ``push` `eaxread = [] write = [10]` `//上面这个代码块是在备份虚拟机执行环境,以及重定位信息` `456DBA ``mov` `ebp``,``dword` `ptr` `[``esp` `+ 0x28] read = [0] write = []` `456DC2 ``neg` `ebpread = [] write = []` `456DC4 ``dec` `ebpread = [] write = []` `456DC9 ``rol` `ebp``,3 read = [] write = []` `456DCC ``xor` `ebp``,0x3684751d read = [] write = []` `456DDC ``lea` `ebp``,[``ebp` `+ 0x5bae11e7] read = [] write = []` `456DEA ``neg` `ebpread = [] write = []` `456DEC ``add` `ebp``,``eax` `read = [] write = []` `//上面这个代码块取出了压入的加密常数,进行一系列解密操作后得到指令流起始地址,并赋值给了``ebp``,故``ebp``作为新的指令流寄存器,``add` `ebp``,``eax``是修正重定位操作` `456DF1 ``mov` `edi``,``esp` `read = [] write = []` `//``edi``是虚拟机堆栈` `456E0B ``mov` `ebx``,``ebp` `read = [] write = []` `//初始化执行密钥,``ebx``仍然是密钥寄存器` `456E26 ``lea` `esi``,[0x456e26] read = [] write = []` `//``esi` `= 0x456e26` `456E33 ``mov` `ecx``,``dword` `ptr` `[``ebp``] read = [] write = []` `456E37 ``add` `ebp``,4 read = [] write = []` `456E40 ``xor` `ecx``,``ebx` `read = [] write = []` `456E42 ``dec` `ecxread = [] write = []` `456E44 ``xor` `ecx``,0x25873dcc read = [] write = []` `4691EB ``inc` `ecxread = [] write = []` `4691EF ``neg` `ecxread = [] write = []` `4691F1 ``xor` `ebx``,``ecx` `read = [] write = []` `4691F3 ``add` `esi``,``ecx` `read = [] write = []` `41F273 ``push` `esiread = [] write = [59]` `41F274 ``ret` `read = [59] write = []` `//上面这个代码块从指令流中取出了一个``DWORD``,赋予``ecx``,并递增了指令流指针。将取出的指令流用密钥进行解密,而后与``esi``相加,得到下一条Handle的地址` `总结:在本样本的虚拟机结构中,``edi``作为虚拟机堆栈,``ebp``作为指令流指针,``ebx``仍然作为执行密钥,``esi``作为中转基址,``esp``作为上下文指针。传统的分发器结构消失了,取而代之的是一种新的链式结构的虚拟机。不同样本可能会有不同的架构,不像VMP2.X架构,3.X中寄存器是随机选用的。新架构又需要新的分析工具,不知道何年何月才会有牛人共享出来......` `再探` `vPopImm32` `使用脚本对其解混淆后的代码如下。注意,如果后文中没有特殊说明,贴出的代码均为解混淆后的代码。` `46C27Fmov ``edx``, ``dword` `ptr` `[``edi``] read = [] write = []` `46C28Cadd ``edi``, 4 read = [] write = []` `//从虚拟机堆栈中弹出操作数` `46C292movzx ``ecx``, ``byte` `ptr` `[``ebp``] read = [] write = []` `46C297lea ``ebp``, [``ebp` `+ 1] read = [] write = []` `46C2A3xor ``cl``, ``bl` `read = [] write = []` `46C2A5neg ``cl` `read = [] write = []` `442B1Ainc ``cl` `read = [] write = []` `442B1Dror ``cl``, 1 read = [] write = []` `412AB8neg ``cl` `read = [] write = []` `412AC0xor ``bl``, ``cl` `read = [] write = []` `//从指令流中取出操作数(在上下文结构中的偏移),并解密,更新密钥,递增指令流` `412AC3mov ``dword` `ptr` `[``esp` `+ ``ecx``], ``edx` `read = [] write = []` `412AC8mov ``ecx``, ``dword` `ptr` `[``ebp``] read = [] write = []` `//写入上下文结构` `412AD0add ``ebp``, 4 read = [] write = []` `412AD7xor ``ecx``, ``ebx` `read = [] write = []` `412ADFsub ``ecx``, 0x2d2f25e5 read = [] write = []` `412AE5rol ``ecx``, 2 read = [] write = []` `412AE8sub ``ecx``, 0x1a4c24fd read = [] write = []` `43CA2Aror ``ecx``, 3 read = [] write = []` `43CA2Dxor ``ebx``, ``ecx` `read = [] write = []` `43CA34add ``esi``, ``ecx` `read = [] write = []` `43CA36jmp ``esi` `read = [] write = []` `该Handle从虚拟机栈中弹出一个``DWORD``,并写入上下文结构中指定字段。` `vPushImm32` `48D637mov ``eax``, ``dword` `ptr` `[``ebp``] read = [] write = []` `48D63Blea ``ebp``, [``ebp` `+ 4] read = [] write = []` `48D646xor ``eax``, ``ebx` `read = [] write = []` `48D64Cadd ``eax``, 0xb4c16be read = [] write = []` `48D651not ``eax` `read = [] write = []` `48D656lea ``eax``, [``eax` `- 0x51cc037d] read = [] write = []` `48D65Cmov ``cl``, 0x42 read = [] write = []` `48D65Eneg ``eax` `read = [] write = []` `48D66Cror ``eax``, 1 read = [] write = []` `48D676lea ``eax``, [``eax` `- 0x61b03f11] read = [] write = []` `//从指令流中取出操作数,并进行解密操作` `48D67Cxor ``ebx``, ``eax` `read = [] write = []` `48D67Flea ``edi``, [``edi` `- 4] read = [] write = []` `48D688mov ``dword` `ptr` `[``edi``], ``eax` `read = [] write = []` `//将解密后的操作数压入虚拟机堆栈` `48D691mov ``ecx``, ``dword` `ptr` `[``ebp``] read = [] write = []` `48D695lea ``ebp``, [``ebp` `+ 4] read = [] write = []` `40E56Dxor ``ecx``, ``ebx` `read = [] write = []` `40E570rol ``ecx``, 3 read = [] write = []` `40E576sub ``ecx``, 0x1865595b read = [] write = []` `40E583bswap ``ecx` `read = [] write = []` `40E586lea ``ecx``, [``ecx` `- 0x7c371840] read = [] write = []` `40E58Dxor ``ebx``, ``ecx` `read = [] write = []` `422848add ``esi``, ``ecx` `read = [] write = []` `4520F4lea ``eax``, [``esp` `+ 0x60] read = [] write = []` `48294Cpush ``esi` `read = [] write = [0]` `48294Dret read = [0] write = []` `每次执行入栈操作后,都会检查边界,判断虚拟机栈指针与上下文指针是否接近,如果是,则会将上下文结构向下移动,如下。` `lea` `eax``, [``esp``+60h]` `cmp` `edi``, ``eax` `但是我写的那个破脚本脚本没考虑到这点,会将这段代码舍去,所以需要特别注意一下。` `该Handle向虚拟机堆栈中压入一个``DWORD``大小的常数。` `vPushRx32` `413B2Amovzx ``edx``, ``byte` `ptr` `[``ebp``] read = [] write = []` `413B2Fsetge ``al` `read = [] write = []` `413B32shl ``ah``, 0x30 read = [] write = []` `413B35add ``ebp``, 1 read = [] write = []` `413B46xor ``dl``, ``bl` `read = [] write = []` `413B51ror ``dl``, 1 read = [] write = []` `413B53sub ``dl``, 0x3e read = [] write = []` `413B5Aneg ``dl` `read = [] write = []` `413B5Frol ``dl``, 1 read = [] write = []` `413B61inc ``dl` `read = [] write = []` `413B6Arol ``dl``, 1 read = [] write = []` `413B78xor ``bl``, ``dl` `read = [] write = []` `//取出操作数(在上下文结构中的偏移,并解密)` `413B7Dmov ``eax``, ``dword` `ptr` `[``esp` `+ ``edx``] read = [] write = []` `//取出指定字段` `413B89sub ``edi``, 4 read = [] write = []` `413B91mov ``dword` `ptr` `[``edi``], ``eax` `read = [] write = []` `//压栈` `413B93mov ``edx``, ``dword` `ptr` `[``ebp``] read = [] write = []` `413B9Dadd ``ebp``, 4 read = [] write = []` `413BAAxor ``edx``, ``ebx` `read = [] write = []` `413BADror ``edx``, 1 read = [] write = []` `413BB3neg ``edx` `read = [] write = []` `413BB9lea ``edx``, [``edx` `- 0x796d16c6] read = [] write = []` `413BC2not ``edx` `read = [] write = []` `413BC9xor ``ebx``, ``edx` `read = [] write = []` `413BCBadd ``esi``, ``edx` `read = [] write = []` `4520F4lea ``eax``, [``esp` `+ 0x60] read = [] write = []` `48294Cpush ``esi` `read = [] write = [0]` `48294Dret read = [0] write = []` `该Handle从上下文结构中取出指定字段,并压入堆栈。` `vRET` `注意,那个破脚本对这条Handle完全不适用了,等我有空再看看BUG。` `mov` `esp``, ``edi` `pop` `eax` `pop` `ebp` `pop` `ecx` `popf` `pop` `edx` `pop` `edi` `pop` `ebx` `pop` `esi` `retn` `//还原堆栈,同时还原环境` | 总结:结合上述介绍的Handle,读者可自行完成对本文附带的例子的分析。该例并不复杂,与原汇编代码基本可以说是一一对应的关系。稍微注意一下,对于函数调用,VMP3.X的处理方式是先退出虚拟机,同时将返回地址设为进入虚拟机的代码地址。 举个例子。如下。 0012FFA8 00401032 \ 0012FFAC 004207C7 1_vmp.004207C7 0012FFB0 00000000 0012FFB4 00403000 ASCII "VMProtect V2.12.3" 0012FFB8 00403012 ASCII "三十二变" 0012FFBC 00000000 这是在执行vRET的最后一条retn指令时的堆栈环境。 调用完成后,返回到0x004207C7,又重新进入虚拟机。 004207C7 68 B31D7ABA push 0xBA7A1DB3 004207CC E8 FD8FFEFF call 1_vmp.004097CE 奇技淫巧之简单爆破 因为业务需要不同,对VM的研究程度也不同,所以对应的也会产生一些奇技淫巧,比如,无脑爆破......不是我BS这种方法,是真的无脑,但很多分析虚拟机的文章都会介绍这个,作为一篇自重自爱的虚拟机介绍文章,本文当然也不会省略这个环节。 举一个例子,如下。 cmp eax,2010 je label1 [我们](http://52hb.com/ "我们")可以修改cmp指令实现爆破,同样可以修改je指令实现爆破。无脑就无脑到底好了,本文介绍修改跳转指令,因为它不需要了解复杂的逻辑门运算知识。 以爆破je指令为例。 以下为未加密前的源文件。 @Main proc mov eax,2018h .if eax == 2019h invoke MessageBox,0,offset szOK,offset szTitle,MB_OK .else invoke MessageBox,0,offset szNO,offset szTitle,MB_OK .endif ret @Main endp 因为VMP有执行密钥,用来动态解密指令流。跳转指令有两个分支,两条分支下去,不可能还能再用同一个密钥继续编码下去。所以遇到流程转移指令一定会重新设置密钥,我们直接搜索mov ebx语句。 第二次对ebx(密钥)直接赋值的地方与第一次(进入虚拟机)相差很远,我们再以此为基准向上搜索00000040(对应的是ZF标志位为1的EFLAGS)。 搜索3次后来到此处。 ![](https://i-blog.csdnimg.cn/img_convert/462431c65e1445ddbc9ea01b199eab45.jpeg) 注意这条shr指令,这表明我们目前处于vShr4 Handle中。 在OD中动态调试,在该处下一个条件断点。可以按当时指令流来设置。 中断时,将EAX由0x40修改为0x0即可。 ![](https://i-blog.csdnimg.cn/img_convert/c92eb03bb953d62939a716901bdc243d.jpeg) ![](https://i-blog.csdnimg.cn/img_convert/f571718332e85e8d353ea3d20ed2c348.jpeg) 如果想要深入了解,请去参阅布尔代数,一般讲离散数学的书都有这个内容。 附录 文章中用到的破脚本可以到我的GITEE上面下载。 https://gitee.com/sanxcr/VMPFuck 大概原理就是消除死代码。比如。 int x; x = 10; ///1 x = 5; //2 x += 20; printf("%d", x); 其中1是死代码,因为1仅对x进行赋值操作而x在引用前就被重新定值了。 因为VMP3.X中没有栈混淆,所以可以直接对寄存器进行消除死代码。 而VMP2.X因为有栈混淆,所以可能复杂一些,思路就是将栈中的数据以DWORD为单位开始编号,仍然将它们视为变量,进行消除死代码。 以栈变量活跃分析为例(寄存器与标志位的活跃分析是一样的),需要遵循以下原则。 1.活跃分析应从底部向顶部分析 2.出口代码处的所有栈变量都是活跃的 3.栈变量的活跃性持续向上传递,但遇到对该栈变量的读写操作时,会变更活跃性 4.当某一栈变量向上传递活跃性遇到写操作时,则活跃性变为死状态,若遇到读操作时,则活跃性变为活状态 5.当出现对同一栈变量进行读写操作时,我们默认读操作先于写操作 具体实现请看脚本...... 使用需基于capstone、pythonwin 安装capstone: 1. pip install capstone 安装pythonwin: 1. 百度搜索pythonwin,找到对应你的python版本即可

相关推荐
BullSmall2 小时前
Doris的部署
学习
小清兔2 小时前
一个unity中URP的环境下旋转天空盒的脚本(RotationSky)
开发语言·数据库·学习·程序人生·unity·c#·游戏引擎
喵了meme3 小时前
Linux学习日记16:守护进程
linux·服务器·学习
武哥聊编程3 小时前
基于Springboot3+Vue3的仓库管理系统,经典项目,免费学习
java·学习·mysql·vue·springboot·课程设计
学编程的闹钟3 小时前
85【CSS选择器简介】
学习
学编程的闹钟3 小时前
编写VMP爆破插件 (上)
学习
小猪佩奇TONY4 小时前
OpenGL-ES 学习(18) ---- 实例化渲染
学习·elasticsearch·信息可视化
一只乔哇噻4 小时前
java后端工程师+AI大模型开发进修ing(研一版‖day61)
java·开发语言·学习·算法·语言模型
车载测试工程师5 小时前
CAPL学习-SOME/IP交互层-回调函数
学习·tcp/ip·交互·以太网·capl·canoe