最新消息:

如何更好更正确的利用VMProtect保护你的软件

编程 eben 87浏览 0评论

一.前言

这篇文章主要目的是对VMP壳主要特性有初步的了解,掌握VMProtect3.5软件正确有效的使用方法,并以一个具体案例来演示,演示所使用的版本为VMProtect3.5已注册版本,授权已经过期但保护效果依旧没有过时,官网也才是3.51补丁版本,文章演示所用版本将会在文章末尾附上下载链接。

二.VMProtect浅析

1.打包(压缩/加密)

压缩/加密可执行文件的代码部分以防止被静态分析,这是很常见的保护手法。考虑到被压缩/加密代码在运行过程终究会被还原成正常的未加密的代码,所以会在执行期间某个时间点被转储,并导出修复,修复的程序跟源程序基本一样,所以相对的说,打包只是增加了破解的门槛,并没有增加太多时间成本,起不到有效遏制破解的作用。

在未使用VMProtect打包之前可以看到:区段结构清楚,没有异常

如何更好更正确的利用VMProtect保护你的软件-1

使用VMProtect打包之后:对比明显,重要区段内容清空,并多出了两个区段vmp0与vmp1,并清理可能对破解者有帮助的区段信息

如何更好更正确的利用VMProtect保护你的软件-2

而且在这种情况下,VMProtect不会在PE头中存储真正的“RawFile”部分信息,。但是虚拟地址和大小依旧存储在头部数据中,以便让系统可以为可执行文件分配正确大小内存空间,因此我们依旧可以读到区段的大小和可能的地址(不考虑 ASLR,也就是地址空间布局随机化)。

关于源文件的打包,你可以很容易发现区段.vmp1占据了很大的空间,这是因为VMProtect将大部分程序内容加密整合写入了.vmp1区段,其中包含已经被加密的代码,解包的程序以及其他信息(这里你会发现还有一个.vmp0区段,这里我们只是演示打包操作,这个区段在虚拟化的时候会使用到)。

唯一不受“保护”的部分是 .rsrc区段,因为.rsrc区段是一个可执行程序的资源区段,你的程序相关图标作者语言等信息都存储在其中,若是被直接加密,那一定会显得很异常。 所以VMProtect 有一个选项来保护图片作者等相关资源,它将资源分成两部分,一个可用于 Windows操作系统提取使用,还有一个只包含其他信息,并在程序执行过程中被解密,以供程序运行时使用。

需要注意的是:如果选中 VMProtect 的压缩选项,.vmp1区段将包含 VMProtect 的句柄和变异代码。反之,则所有内容都在 .vmp0区段 中。

我在做相关案例时观察到,一般除.data 区段,其他大多数区段都是不可写入的。所以有个疑问,VMProtect是如何进行写入的呢?所以VMProtect得调用 ZwProtectVirtualMemory这个未公布函数来修改部分区段的属性以来修改区段中的内容,而相对应的从破解者的视角来看,这个函数就显得尤为重要,可以通过针对这个函数下断点的方式监控VMProtect解包过程中该函数的使用情况,并进行动态解包,通过转储工具导出,当然这一部分并不是我说的如此简略的而且这不是文章的主要目的,所以不再做具体叙述。

2.代码变异与虚拟

可以说VMProtect最核心最有威慑力的就是它的虚拟引擎了,但是大佬认为它的虚拟引擎并不是最厉害的,但是摸清楚它的虚拟引擎太费时间,让人望而却步,所以在这里并不会特别的去关注它,在这里就简单了解一下虚拟引擎的原理就可以了,在这一段我们主要了解代码变异,来看看VMProtect的代码变异一些特性……

首先VMProtect的虚拟功能是基于虚拟机(VM)的代码虚拟化,成为了实现代码混淆的一种有效方法。基于VM的保护的基本原理是用攻击者不熟悉的虚拟指令替换程序指令。然后这些虚拟指令将在运行时转换为本机代码,在底层硬件平台上执行。使用基于虚拟机的方案,模糊代码的执行路径由虚拟指令调度器控制。一个典型的调度程序由两个组件组成:一个用于确定哪条指令准备好执行的调度程序,以及一组字节码处理程序,它们首先对字节码进行解码,然后将其转换为本机代码。这个过程用定制字节码替换了原始的程序指令,允许开发人员隐藏敏感代码区域的用途或逻辑。

简单介绍完了虚拟,来了解一下本段最主要的内容代码变异,代码变异是以用户标注的函数为组成单位,加密代码部分中的每个变异函数都跳转到包含下一个要执行的代码的 VMProtect生成的部分。因为在不破坏整个代码结构的情况下修改(插入)编译器生成的原始代码段是不可行的。因此 VMProtect将所有变异函数存储在一个新区段中以避免大小问题(请谷歌:在 PE 头中插入代码)。

VMProtect变异是很多小技巧的组合使用,例如代码变形等义替换、垃圾代码(以前叫花指令)插入、控制流跳转和块不对齐。

我还在读高中的时候,研究VMProtect时就很清晰的发现,VMProtect的代码变异并不会对原始指令进行太多的变化,貌似喜欢在标定的变异函数插入垃圾指令,倒是自从上了大学,就纯学Java了,所以到现在为止都没有什么较大发现,但我在写这篇文章的时候已经在重拾以前的兴趣了,现在是VMProtect3.5x版本了,应该会比从前的VMProtect2.x有明显改进。

还有一些补充:VMProtect在代码变异中使用控制流保护。它专注于所有基本块的跳转,而不是条件跳转,因此即使在变异之后原始控制流也可以看到(这也是为什么在IDA中单纯使用变异干扰效果不佳的原因,但与虚拟结合起来效果极佳),但VMProtect在变异代码上花费力气也很大,有很多有趣的细节可以探索,但在这里就不多说了。

3.虚拟引擎(VM)

首先你要确定你使用的不是官网的演示demo版本,演示版的难度比不上付费版。演示版实际是一个阉割的虚拟机,有着一个带有类似偏移跳转到句柄开关的调度程序。可以轻松跟踪 vm 代码,没有跳转到下一个操作码的加密流程链,操作码的参数都没有加密。而且句柄中的改变很少。演示版生成的文件可以说是仅仅作为展示学习用,因为受到的保护没有付费版那么完整,而目前官网放出的3.x版本的demo演示实际上就是曾经的2.x老版本修改得来的。

下面简单检查(对源程序进行重要函数虚拟化处理,这一段会跟具体演示部分相关)

当我们对已经虚拟化的演示函数sub_140018660    .text进行查看,可以发现它跳转到了一个sub_1401FAFB0函数且在.vmp0区段,可以想到VMProtect已经接管了原始函数,将原始函数的代码替换为了实际的VMProtect代码。

如何更好更正确的利用VMProtect保护你的软件-3

如何更好更正确的利用VMProtect保护你的软件-4

对我来说,比较敏感的指令代码就是push 0FFFFFFFF9B92B72Dh和call sub_1402AC1FD,在这里就可以大胆的猜测,这可能就是VMProtect虚拟进程的开始,将VMProtect生成的虚拟代码在运行过程中转换为正常的程序代码,但是按照VMProtect的尿性,可能它只是一个永远都不返回的调用,而且它调用的数据是随机的,力图将调试者带入岔路,也可能它是一个自定义句柄调用一个 VENTER指令(VENTER意思是VMProtect实现的VM虚拟机中的类似ENTER指令用来在被调用过程自动创建堆栈帧,为之后的解密做准备),可能push传入的值可能就是加密操作码的开始,后续调用一个VENTER来解密它………。

这里讲的就显得尤为重要了!首先,想要让VMProtect真正有效的保护你的开发成果,你需要在源代码中或从 VMProtect导入已编译的程序后(从地址或使用 PDB 文件手动标记)标记所有要变异(虚拟)的函数。如果你只是简单的将需要保护的程序导入VMProtect而不标记敏感重要函数,VMProtect是没有体现它应该具备的保护效果的,就像我第一部分所说的那样,实质上是对源程序进行一次打包压缩,却没有有效保护自己的程序。

 三、具体演示部分

 

下面演示使用PDB文件标注重要函数,并对程序有效保护和无效保护操作的对比

  1. 未处理的源程序(载入PDB)IDA7.5静态分析(图中演示函数静态地址140018660):

如何更好更正确的利用VMProtect保护你的软件-5

可以发现不做保护措施的程序,在反编译软件面前近乎都是裸奔,且IDA反编译出来的伪代码已经是可以阅读了,经验丰富的逆向人员都可以如常阅读伪代码了,就这样你程序的业务逻辑也随之暴露,想想这方面的后果吧。

  1. 未有效加密的效果展示(只加入了入口函数,不载入PDB,未压缩):

如何更好更正确的利用VMProtect保护你的软件-6

这张图近乎模拟了破解者的角度,没有PDB文件,经过VMProtect处理可以看到与上方蓝色代码段部分大大减少,棕色数据段明显增多,因为没有PDB文件就没有正常的函数名注释,但破解者依旧可以通过很多办法找到他们想要找到的代码段,例如你在某一段代码调用的API都会让破解者得到线索,只是增加了时间的花销,而且破解者不仅仅只有静态分析的手段,还有动态调试。如图,不完善的保护,依旧让写入代码中的游戏数值地址暴露,代码逻辑暴露无遗。

  1. 有效的加密效果展示(敏感函数标记并变异+虚拟,加入并利用PDB文件标记函数,未压缩):

将编译生成的PDB文件跟被处理程序放置在同一文件目录下。

如何更好更正确的利用VMProtect保护你的软件-7

可以看到截图中标注是CSeemygoPVZCheaterDlg::xxxxxxx系列函数,我们记住地址140018660,这就是演示的OnBnClickedKill函数,这里我们全选这些函数并在选项中选择变异+虚拟直接拉满(变异对IDA来说干扰作用不大,两者结合效果极佳),然后添加进程,点击左上角三角加密即可。下面我们来看看效果……

IDA中搜索140018660,可以看到:

如何更好更正确的利用VMProtect保护你的软件-8

点击伪代码中函数,查看:

如何更好更正确的利用VMProtect保护你的软件-9

再次点击进入sub_140585393:

如何更好更正确的利用VMProtect保护你的软件-10

这是个什么玩意?在IDA给予的分析结果来看,确实看不出来啥玩意,也没有之前那么清晰明了的伪代码了,动态调试也只会浪费大量时间跟虚拟机搏斗,这时候有了完整有效的加密流程,破解者的时间金钱成本都在呈指数增加,这样就很容易让破解者知难而退,但是呢,凡事没有绝对,永远没有不会被破解的程序,只能说我们增加了破解这个加密程序成本罢了,如果你的程序经济效益让人十分眼红,那尽量还是不要节省这些小钱了,直接上大公司优秀商业方案吧,不仅便捷还有满满的技术支持,总会适合你的。

下面演示使用在项目中导入VMProtect提供的SDK,在源码上标记需要保护的重要函数。

将VMProtect安装目录下的include和lib引入到你想保护的项目中:

如何更好更正确的利用VMProtect保护你的软件-11

并在项目源码中引入#include “VMProtectSDK.h”,使用方法如下:

  1. VMProtectBegin(“DoDataExchange”);//默认虚拟化
  2. //VMProtectBeginVirtualization(“DoDataExchange”);//虚拟
  3. //VMProtectBeginMutation(“DoDataExchange”);//变异
  4. //VMProtectBeginUltra(“DoDataExchange”);//超级 (变异 + 虚拟)
  5. VMProtectEnd();

如何更好更正确的利用VMProtect保护你的软件-12

如何更好更正确的利用VMProtect保护你的软件-13

生成完毕时点击运行,会发现缺少dll错误,这个直接到VMProtect目录寻找寻找lib文件下的VMProtectSDK64.dll文件,将其放置到程序所在目录即可运行。

如何更好更正确的利用VMProtect保护你的软件-14

如何更好更正确的利用VMProtect保护你的软件-15

如何更好更正确的利用VMProtect保护你的软件-16

 

下面将程序载入VMProtect,记得移除PDB文件,毕竟我们现在演示的是在源码中标注重要函数。

如何更好更正确的利用VMProtect保护你的软件-17

看,可以看到在源码中标注的函数出现在了VMProtect添加进程界面中,而且经过VMProtect处理后,也不再需要VMProtectSDK64.dll支持了,直接可以运行。

如何更好更正确的利用VMProtect保护你的软件-18

还有最后一点小小的提醒:没有任何一款保护软件是万无一失的,只是提高了破解的门槛,增加了破解的成本。是的,这样的确会让人担心,如果觉得实在不安心,可以在VMProtect将反调试等等选项都选上,处理完毕的程序可以在VMP壳的基础上再加一层兼容性强的压缩/加密壳,尽可能的将安全性拉满,或者加钱上商业方案。。。拜拜咯~  欢迎交流~

 

https://vmpsoft.com/products/vmprotect/

VMProtect 是新一代的软件保护系统,将保护后的代码放到虚拟机中运行,这将使分析反编译后的代码和破解变得极为困难。使用 MAP 文件或内建的反编译引擎,您可以快速选择需要保护的代码。

与传统保护方式的比较:

传统的保护软件都有一个共同的弱点,即他们都不修改源代码。保护方式仅仅是通过“信封”原理将软件主体封装起来,然后通过一个装载器解压缩保护的软件主体,解压后的软件在内存中很容易被转储并被非法修改。破解者拥有一系列反编译工具可以破坏这种保护,网络上也有许多文章阐述如何脱壳常规的保护软件。

VMProtect 是新一代的软件保护系统,不像市场上其它常见的保护软件,VMProtect 可以修改软件产品的源代码,转换部分代码为在虚拟机上运行的字节码(bytecode)。您可以将虚拟机想象成为带有不同于 Intel 8086 处理器系统指令的虚拟处理器。例如,虚拟机没有比较两个操作数的指令,也没有条件跳转和无条件跳转指令等等。这样一来,破解者就需要开发一整套的解析引擎来分析和反编译字节码,以现有的解密理论,破解者想要还原出源代码几乎是不可能的。

VMProtect保护原理

与其它大部分的保护程序不同,VMProtect可修改程序的源代码[2]。VMProtect可将被保护文件中的部分代码转化到在虚拟机(以下称作VM)上运行的程序(以下称作bytecode)中。您同样可把VM想象为具备命令系统的虚拟处理器,该命令系统与Intel 8086处理器所使用的完全不同。例 如,VM没有负责比较2个操作数的命令,也没有有条件与无条件的移转等。就象您现在看到的,黑客必须开发一款特定的工具以分析与反编译bytecode, 而且还相当地耗时。可是,我们知道没有无法破解的保护程序,这也是我们为什么会将保护级别达到破解费用与购买费用相当(或破解费用甚至超过购买费用)的程度。不管怎么样,请记住VMProtect是唯一一款能帮助您“隐藏”主要软件保护机制的工具。

Vmprotect的结构

Vmprotect使用虚拟一个不同于x86的CPU来执行转化后的程序,这个CPU只支持简单运算以及最简单的无条件跳转指令,因此为了实现x86一条指令同样的功能,Vmp的CPU需要执行多条指令。这样令代码的阅读者需要阅读大量的代码才能知道其中的程序逻辑。
转化成Vmp Cpu 指令数据(bytecode)使用了简单的数据加密,阅读者不解密是无法了解bytecode的指令意义
VMP的 CPU 解释模拟程序,有用代码只有大概500条x86指令,但是使用了大量的垃圾指令,将其解释程序塞满了2000多条x86指令,同时将程序的正常逻辑打乱,不借助代码优化工具,人类的眼睛看起来会非常的费力。
Vmp cpu 为Stack based 的RISC CPU, 内部嵌入 64byte 的暂存器

Mutationb

在处理器命令的程度上修改了执行文件[2](修改了现有的命令,添加了所有类型的垃圾命令等)。该编译类型无法充分地保护代码。它只能避免破解、解析以及避免进行中的功能由签名解析器确定(PEiD+KANAL、IDA+FLIRT等)。作为固定规则,我们并不需要保护库功能不受破解以及解析,只需要更改它们的签名就足够保护在您应用程序中应用的库(对破解与解析的保护程度不高,代码的执行率很高)。

虚拟化

可执行代码被转化为由虚拟机执行的bytecode。[3]该编译类型应该应用到所有执行率非常重要的代码的关键部分以防止破解与解析(对破解与解析的保护程度中等,代码的执行率中等)。

mutation+虚拟化

以处理器命令的程度修改可执行代码,然后将它转化为由虚拟机执行的bytecode。该编译类型应该应用到执行率不重要的代码(对破解与解析的保护程度高,代码的执行率低)。

调试模式

用来确认外部地址,寻找外部代码参考的地址。

隐藏衡量

如果启用该选项,就不能在打开的表格中找到变量地址或调用的功能。

动态创建在线命令

VM解释程序不能执行所有的Intel 8086命令,这就是为什么这些命令会以它们在保护部分中展现的形式执行的原因。动态创建在线命令同样让针对bytecode的破解变得艰难。

检查VM对象的完整性

当执行程序的时候,VM解释程序会自动读取当执行命令时被运用的程序、bytecode以及水印中任意部分的检查结果。VM对象的完整性检查可保护解释程序、bytecode以及水印免遭修改。

水印

选择您希望内置到被保护文件中的水印[3]

项目名称

能为即将被写入VM解释程序与bytecode的新项目指定名称

移除fixup元素(只针对EXE文件编译程序(尤其象Delphi)会为EXE文件创建一个fixup元素列表。当加载EXE文件时,操作系统不会使用这些元素。如果您启用该选项,VM将使用被fixup元素列表所包含的部分。

在指定完所有必要的选项后,开始编译工程。在编译完成后,在工程文件(例如,TEST.EXE)旁边会创建一个新文件(例如,TEST.VMP.EXE)。指定的程序将在虚拟机的该文件中运行。

转载请注明:落伍老站长 » 如何更好更正确的利用VMProtect保护你的软件

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址