VMP 脱壳心得

时间:2019-04-27
花了不少时间,把一个程序脱壳,确定是 VMP 的壳,下面简单 说说,我自己脱壳的心得。
该程序不是一个标准的VMP 壳 ,是 作者花了很大功夫用VMP 加密的。鉴于 敏感性,因此不把程序放上来。
基本信息:原始程序是Delphi 编写的,然后用 VMP 加壳之后,再用Aspack压缩。
我简单标记下 下面用到的简写:ASPACK 代表原始EXE 文件。
VMP1代表 VMP保护程序以及 被保护的程序的整体。
VMP0 代表 原始程序以及被VMP 脚本化的程序的整体。

开工了:
下图 是我用到一些 OD 插件

为了不让 程序检测 到 OD,需要设置StrongOD ,把 自己隐藏起来。
如图:

为了 避免 一些其他的检测,用到 Olly Advanced
如下图设置:


调试选项:事件  设置为 暂停于系统 断点。所有的异常 都传递给程序。如下图:

第一步: 手工把 ASPACK 脱掉,ASPACK 没什么特别的。
由于 ASPACK 会自动将VMP1 的导入表 通过自己的代码 查出 函数地址并填充好,因此,我在脱壳 ASPACK 的时候,将其 导入表的 处理函数跳过,因此 在解压缩完成后,内存得到了 完整的内存镜像,把这个 镜像  保存为 中间文件。VMP1.exe 此程序主要用于分析。因为 没有导入表,就不容易做 静态分析,静态分析使用IDA。

简单说下  ASPACK 干了些啥:
首先其 保存原始的 TLS 信息,设置 新的TLS 信息,通过这样的办法达到,于VMP1层的程序共享 TLS 定义的目的。 其次  将 EXE 镜像中的每一个段的数据,通过自己的解压缩算法,按照其 程序空间中的数据,将 所有数据解压缩到 目标节空间中。对于代码段 进行特殊的处理:对于 E8,E9 跳转指令修正重定位。根据其内存保存的 导入表地址,对 VMP1 恢复导入表。根据其保存的 PE 头节信息表,对于所有的节重新修正 节内存属性。(通过调用VirtualProtect),最后 恢复原始的TLS 信息。跳转到 VMP1 的入口。

这里必须将TLS CALLBack 设置清空,否则 新的VMP1.exe 直接运行就会报错。
其检测的原理是:TLS 有设置回调函数地址,如果 VMP1.exe 直接运行,那么 就会在运行程序主入口之前,先调用 TLS CallBack,该CallBack 程序是用 VMP 保护的,会 设置 一个内存标志,该标志,在 VMP 的保护程序中会检测,如果有设置,直接退出。处理的办法很简单,就是 清空 TLS CallBack 字段。

在此,可以直接用 zeus 插件,用这个插件的时候,ASPACK 的导入表修复,要让其生效,否则VMP1 执行不会正常。用其菜单上的 UnPacking 功能 将 VMP1 层脱壳。
主要是利用 Zenus 插件 把 VMP1 的内存用到的一些数据保存下来,因为 VMP 会自己构造一些数据,Zeus 插件 能够 自动的为 EXE 构造一个新的节,NewAntid在这个节中存放了一些 数据, 而这些数据,在 VMP0 层的程序会 去调用。

VMP1 能干了些啥呢?
其实主要就做了两件事情:第一 防止 DEbug,各种机制检查,所有这些都在 VMP 的保护下来。具体我就不列举了。
另外一件事情 就是恢复 原始程序的运行环境,也就是 恢复 原始程序的导入表,其 实际上是把 原始程序的导入表 ,按照DLL为单位,一个 DLL 一段程序,将 VMP 空间的 导入表,复制到 VMP0 程序的导入表中。

所以 对于 Zeus 导出的程序,直接通过恢复 PE 中的 相关指针,就能够完整的恢复 导入表。
因此 对于 Zeus 导出的程序  NEWIAT节 就用不上了。

详细检查  导出的程序的相关设置,这个时候,要补丁  CPUID ,让其 可以跨机器运行。
如何 补丁 CPUID 清参考  ximo 的视频。再此 特别感谢 ximo 大牛的无私奉献。
我的目标程序没有 使用任何 RDTSC 指令,因此我就不需要补丁 该指令了。

对于一般的 VMP  壳 ,到这里就结束了。算脱好了。

对于我这个程序,其代码还有大量的代码,用VMP 保护起来,这些保护的代码 还有各种检测,因此需要 读懂他这些代码,才能 把这些暗桩拔出。 因此我们要用 到另外两个插件:

我们可以用 VMSweeper 插件来对于 我们的代码段搜索 有哪些 代码是被 VMP 保护的。

用 VmpFenxi 插件来,仔细阅读 其 VMP 代码。再此 特别 感谢 zdhysd大牛的 VMP分析插件1.4 。程序已经相当完善。

VMP 加壳 真正的厉害之处在于 其能够将 你程序中的代码用 虚拟化的指令系统 替换掉,并且由于其构造的虚拟机 的目标码是有前后相关性的,也就是说 后一条指令的指令码 和前一条的指令码 是有相关的,因此 你修改 虚拟程序的指令码的中间任何一个字节,那么后续所有的指令的含义全变了。 VMP分析插件1.4  有补丁功能,我没有去测试,也不知道是否有效。

所以真正 进行VMP 脱壳,基本上你都需要去 读懂  VMP 化的虚拟代码的程序的含义,才能真正的将目标程序的保护 脱去。因此 VMP 的脱壳难度其实 和 被保护的程序的作者 在这上面花多少工作有关。
我的这个实例,作者保护的代码 达到12处左右。基本上都在系统初始化,以及主循环中。因此强化了程序的保护。

VMP 的代码 基本上没办法去修改 虚拟化指令流中间的 指令,但是 我们还是可以以指令块为单位 修改 虚拟化程序的 执行流程的。虚拟化程序 在调用 虚拟机外部程序的时候,会设置一个返回地址,因此 我们在这个 返回地址 可以去做补丁处理。要做补丁处理,就必须理解 虚拟机的  运行机制,才能修改其 存放在堆栈中的 数据,通过修改这些数据,可以改变 程序执行的结果。

下面提供个 简单的工具,针对我这个VMP 实例的。不保证对其他人的有效。
截图如下:

我的实例中,有两种虚拟机,一种是 程序代码是倒着走的。也就是 每执行一条指令 是 IP 地址 减一。另一个是正着走的。
工具的 解密 就是将  虚拟机 加密 后的vEIP 地址 翻译成 实际的物理地址。

比如:0x9D609200 解码后 为 0x6E9F63。
另外一个 是 0x63711383 解码后为 0x6EDC19

虚拟机有一张 指令跳转表,都是经过处理的。
因此 我做了两个按钮 负责将 用UE 保存的指令跳转表 转换 格式。

一个负责将  指令指针格式 进行 bswap 转换。
一个负责将  指令指针格式 进行求 负 转换。

参考附件 程序。
 Project1.rar
以上是一点点心得,也许讲的不是很清楚,我想有真正实际去做的人,应该能够有些感悟。
文笔 比较烂,请各位见谅。