[原创]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
以上是一点点心得,也许讲的不是很清楚,我想有真正实际去做的人,应该能够有些感悟。
文笔 比较烂,请各位见谅。

上传的附件:
最新回复 ()
回 11楼,zdhysd大牛的 VMP分析插件1.4  只是帮你把 虚拟机的代码流帮你整理出来,并进行一些化简,还是要你自己去分析的。并不是 其 做了之后,你就不用做什么了,其实工作量最大的就是理解 虚拟代码。
贴一段  该工具分析出来的虚拟代码程序吧。
/$  00           vPopReg4 vR10        DWORD _t0 = 0
|.  D8 680876AA  vPushImm4 760DD915   DWORD _t1 = 760DD915
|.  5A           vAdd4                DWORD _t2 = 760DD915; DWORD _t3 = AddFlag(760DD915, check00000000)
|.  49           vPopReg4 vR15        DWORD _t4 = AddFlag(760DD915, check00000000)
|.  D5           vPopReg4 vR2         DWORD _t5 = 760DD915
|.  39           vPopReg4 vR9         DWORD _t6 = EDX
|.  04           vPopReg4 vR14        DWORD _t7 = EBP
|.  80           vPopReg4 vR0         DWORD _t8 = EAX
|.  E4           vPopReg4 vR5         DWORD _t9 = EDI
|.  C8           vPopReg4 vR1         DWORD _t10 = ECX
|.  24           vPopReg4 vR12        DWORD _t11 = EFL
|.  60           vPopReg4 vR4         DWORD _t12 = EBX
|.  14           vPopReg4 vR3         DWORD _t13 = EDI
|.  77           vPopReg4 vR13        DWORD _t14 = ESI
|.  B3           vPopReg4 vR8         DWORD _t15 = 11A45053
|.  8F           vPopReg4 vR8         DWORD _t16 = 0BE82F160
|.  49           vPushReg4 vR12       DWORD _v0 = EFL
|.  5E A0        vPushImm1 0          WORD _t18 = 0
|.  1A B45FEE56  vPushImm4 0A14F2B    DWORD _t19 = 0A14F2B
|.  43           vPushReg4 vR10       DWORD _t20 = 0
|.  F0           vAdd4                DWORD _t21 = 0A14F2B; DWORD _t22 = AddFlag(0, 0A14F2B)
|.  27           vPopReg4 vR15        DWORD _t23 = AddFlag(0, 0A14F2B)
|.  49           vReadMemDs1          WORD _t24 = 0
|.  CB           vPushVEsp            DWORD _t25 = 2C
|.  56           vReadMemSs1          WORD _t26 = 0
|.  C7           vNand1               WORD _t27 = 0FF; DWORD _t28 = AndFlag(0FF, 0FF)
|.  7F           vPopReg4 vR7         DWORD _t29 = AndFlag(0FF, 0FF)
|.  BD           vAdd1                BYTE _t30 = 0FF; DWORD _t31 = AddFlag(0FF, 0)
|.  CD           vPopReg4 vR7         DWORD _t32 = AddFlag(0FF, 0)
|.  B8           vPushVEsp            DWORD _t33 = 2E
|.  2A           vReadMemSs1          WORD _t34 = 0FF
|.  97           vNand1               WORD _t35 = 0; DWORD _t36 = AndFlag(0, 0)
|.  2F           vPopReg4 vR11        DWORD _t37 = AndFlag(0, 0)
|.  D3 4F        vPopReg1 vR8BL       BYTE _t38 = 0
|.  65           vPushReg4 vR7        DWORD _t39 = AddFlag(0FF, 0)
|.  87           vPushVEsp            DWORD _t40 = 2C
|.  C3           vReadMemSs4          DWORD _t41 = AddFlag(0FF, 0)
|.  93           vNand4               DWORD _t42 = ~AddFlag(0FF, 0); DWORD _t43 = NotFlag(AddFlag(0FF, 0))
|.  8E           vPopReg4 vR15        DWORD _t44 = NotFlag(AddFlag(0FF, 0))
|.  C5 30DE      vPushImmSx2 0F7EA    DWORD _t45 = 0FFFFF7EA
|.  CB           vNand4               DWORD _t46 = 815 & AddFlag(0FF, 0); DWORD _t47 = AndFlag(815, AddFlag(0FF, 0))
|.  EF           vPopReg4 vR12        DWORD _t48 = AndFlag(815, AddFlag(0FF, 0))
|.  E1           vPushReg4 vR11       DWORD _t49 = AndFlag(0, 0)
|.  6D           vPushVEsp            DWORD _t50 = 28
|.  55           vReadMemSs4          DWORD _t51 = AndFlag(0, 0)
|.  25           vNand4               DWORD _t52 = ~AndFlag(0, 0); DWORD _t53 = NotFlag(AndFlag(0, 0))
|.  81           vPopReg4 vR3         DWORD _t54 = NotFlag(AndFlag(0, 0))
|.  E8 8410      vPushImmSx2 815      DWORD _t55 = 815
|.  E3           vNand4               DWORD _t56 = 0FFFFF7EA & AndFlag(0, 0); DWORD _t57 = AndFlag(0FFFFF7EA, AndFlag(0, 0))
|.  3F           vPopReg4 vR3         DWORD _t58 = AndFlag(0FFFFF7EA, AndFlag(0, 0))
|.  88           vAdd4                DWORD _t59 = SubFlag(0, 0); DWORD _t60 = AddFlag(0FFFFF7EA & AndFlag(0, 0), 815 & AddFlag(0FF, 0))
|.  18           vPopReg4 vR6         DWORD _t61 = AddFlag(0FFFFF7EA & AndFlag(0, 0), 815 & AddFlag(0FF, 0))
|.  E4           vPopReg4 vR6         DWORD _t62 = SubFlag(0, 0)
|.  7C 6F5B3F77  vPushImm4 1FB11690   DWORD _t63 = 1FB11690
|.  EC 63478E68  vPushImm4 1FB10FF4   DWORD _t64 = 1FB10FF4
|.  53           vPushVEsp            DWORD _t65 = 28
|.  1A 60        vPushImm1 4          WORD _t66 = 4
|.  E8           vPushReg4 vR6        DWORD _t67 = SubFlag(0, 0)
|.  B6           vPushReg4 vR6        DWORD _t68 = SubFlag(0, 0)
|.  42           vNand4               DWORD _t69 = ~SubFlag(0, 0); DWORD _t70 = NotFlag(SubFlag(0, 0))
|.  3D           vPopReg4 vR15        DWORD _t71 = NotFlag(SubFlag(0, 0))
|.  17 63        vPushImmSx1 0BF      DWORD _t72 = 0FFFFFFBF
|.  74           vNand4               DWORD _t73 = 40 & SubFlag(0, 0); DWORD _t74 = AndFlag(40, SubFlag(0, 0))
|.  D0           vPopReg4 vR3         DWORD _t75 = AndFlag(40, SubFlag(0, 0))
|.  D0           vShr4                DWORD _t76 = Je(SubFlag(0, 0)); DWORD _t77 = ShrFlag(40 & SubFlag(0, 0), 4)
|.  CC           vPopReg4 vR7         DWORD _t78 = ShrFlag(40 & SubFlag(0, 0), 4)
|.  F9           vAdd4                DWORD _t79 = Je(SubFlag(0, 0)) + 28; DWORD _t80 = AddFlag(Je(SubFlag(0, 0)), 28)
|.  49           vPopReg4 vR3         DWORD _t81 = AddFlag(Je(SubFlag(0, 0)), 28)
|.  C9           vReadMemSs4          DWORD m0 = DWORD SS:[Je(SubFlag(0, 0)) + 28]
|.  AD           vPopReg4 vR12        DWORD _t83 = m0
|.  C9           vPopReg4 vR8         DWORD _t84 = 1FB10FF4
|.  5C           vPopReg4 vR15        DWORD _t85 = 1FB11690
|.  96           vPushReg4 vR12       DWORD _t86 = m0
|.  AE           vPopReg4 vR7         DWORD _t87 = m0
|.  80           vPushReg4 vR7        DWORD _t88 = m0
|.  56           vPushReg4 vR7        DWORD _t89 = m0
|.  02           vNand4               DWORD _t90 = ~m0; DWORD _t91 = NotFlag(_m0)
|.  26           vPopReg4 vR12        DWORD _t92 = NotFlag(_m0)
|.  1E 75EFF949  vPushImm4 0E0EE71D8  DWORD _t93 = 0E0EE71D8
|.  C6           vNand4               DWORD _t94 = 1F118E27 & m0; DWORD _t95 = AndFlag(1F118E27, _m0)
|.  EA           vPopReg4 vR12        DWORD _t96 = AndFlag(1F118E27, _m0)
|.  FC           vPushReg4 vR7        DWORD _t97 = m0
|.  B8 1ABB3097  vPushImm4 1F118E27   DWORD _t98 = 1F118E27
|.  87           vNand4               DWORD _t99 = 1F118E27 ~& m0; DWORD _t100 = NandFlag(1F118E27, _m0)
|.  CB           vPopReg4 vR8         DWORD _t101 = NandFlag(1F118E27, _m0)
|.  73           vNand4               DWORD _t102 = 1F118E27 ^ m0; DWORD _t103 = XorFlag(1F118E27, _m0)
|.  97           vPopReg4 vR12        DWORD _t104 = XorFlag(1F118E27, _m0)
|.  B3           vPopReg4 vR8         DWORD _t105 = 1F118E27 ^ m0
|.  B5           vPushReg4 vR1        DWORD _t106 = ECX
|.  02           vPushReg4 vR14       DWORD _t107 = EBP
|.  50           vPushReg4 vR6        DWORD _t108 = SubFlag(0, 0)
|.  36           vPushReg4 vR1        DWORD _v1 = ECX
|.  8C           vPushReg4 vR11       DWORD _t110 = AndFlag(0, 0)
|.  79           vPushReg4 vR14       DWORD _v2 = EBP
|.  9F           vPushReg4 vR9        DWORD _v3 = EDX
|.  BD           vPushReg4 vR4        DWORD _v4 = EBX
|.  63           vPushReg4 vR5        DWORD _v5 = EDI
|.  E8           vPushReg4 vR13       DWORD _v6 = ESI
|.  66           vPushReg4 vR0        DWORD _v7 = EAX
|.  F4           vPushReg4 vR2        DWORD _t117 = 760DD915
|.  50 3EAD011F  vPushImm4 89F226EB   DWORD _t118 = 89F226EB
|.  8C           vAdd4                DWORD _t119 = 0; DWORD _t120 = AddFlag(89F226EB, 760DD915)
|.  E4           vPopReg4 vR11        DWORD _t121 = AddFlag(89F226EB, 760DD915)
|.  EE           vPushReg4 vR10       DWORD _v8 = 0
|.  EC           vPushReg4 vR8        DWORD _t123 = 1F118E27 ^ m0
|.  22           vJmp_00A1D3C3        if (0 != 0) goto exe.00xxxxxxx

把上面这段程序读懂,是我们的任务,你觉得没有挑战?
返回