前言
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 \