目前对抗逆向分析最有效的手段为代码虚拟化技术,但是仍旧有许多攻击者通过知识累积、符号执行等手段对代码虚拟化技术进行逆向破解。设计并实现了Muti-Thread-Vmp原型系统。该系统利用多线程实现不同的虚拟化指令,从而让攻击者难以跟踪与分析,提高了代码虚拟化的保护效果。理论分析与实验结果表明,Muti-Thread-Vmp原型系统能够有效地提高攻击者分析的难度。
内容目录:
0 引 言
1 代码虚拟化技术的基本原理
1.1 原理框架
1.2 保护后的执行流程
2 Muti-Thread-Vmp系统
2.1 Muti-Thread-Vmp基本原理
2.2 Muti-Thread-Vmp关键技术
3 实验分析
3.1 功能验证
3.2 性能验证
4 结 语
随着计算机技术的不断累积与进步,逆向破解一款软件的成本变得越来越低。许多时候,逆向分析人员只需通过诸如IDA、OllyDbg此类软件,便可以在极短的时间内,使用极小成本的情况下,完成对一款商业软件的破解。而遭受软件破解的商业公司可能就将面临开发成本无法回本,甚至更严重的经济损失。为了保护这些商业公司的软件著作权,技术人员们从技术角度提出了许多保护措施,其中最为广泛使用的是软件加壳保护技术。通过软件加壳,可以对原有的程序代码进行压缩、加密甚至虚拟化,从而达到抗反汇编、抗逆向的效果。本文所提出的代码虚拟化技术就属于这种保护技术中的一种。在逆向技术与软件保护技术的不断对抗中,代码虚拟化保护被证明是现今为止十分有效的一种抗逆向技术。
代码虚拟化技术采用虚拟机思想,将受保护程序中的程序逻辑转化为属于自己虚拟机的虚拟机指令,这样的代码虚拟化转化使得程序脱离了常规的寄存器和堆栈,从而使得逆向人员无法直接通过原先的知识积累进行逆向分析,若想分析此类受代码虚拟化保护的软件,需要对独特设计的虚拟机指令集、虚拟机堆栈进行分析,这样的保护有效地增加了逆向分析人员所需要付出的时间和经济成本。
近几年,研究人员提出了许多对于代码虚拟化保护方法的改进和加固方案。Wang等提出了NISLVMP方案,通过将虚拟机上下文复杂化的思想,实现了多组虚拟寄存器值的转换算法,获得最终虚拟机寄存器值来提高抗分析能力。Fang等提出了一种多阶段代码虚拟化保护方法,初始阶段对原生指令进行虚拟化保护,后续通过对前一阶段产生的处理器中的虚拟指令进一步虚拟化,通过增加语义复杂度来提高逆向分析的难度。Averbuch等通过对虚拟指令分发器进行隐藏,防止逆向分析人员找到虚拟机部分,从而阻止对于虚拟机的逆向分析。房鼎益等提出了基于时间执行不同路径而产生的多样性来对抗时间积累的逆向分析。侯留洋提出了结合混淆思想、使用多套虚拟机环境随机选择来执行构造的混淆基本块和关键代码的代码虚拟化保护方法。由于私有虚拟环境使得混淆基本块难以去除并且是随机映射关系,无法累积字节码知识进行后续分析,因此显著增加了逆向分析的难度。
上述加固方法固然有效,但是对于动态跟踪的调试方式,这些方法还是容易被跟踪到虚拟机的分发器与虚拟机的虚拟机指令。
1、代码虚拟化技术的基本原理
1.1 原理框架
代码虚拟化技术的核心原理在于设计一套拥有私有的虚拟机指令集和虚拟上下文,然后将待保护的关键代码设计成在虚拟机上下文中执行,从而使得逆向分析人员不得不对这套私有的虚拟机指令集和虚拟机上下文进行分析,而由于是独特设计的虚拟机指令集和虚拟机上下文,逆向人员无法轻松定位关键代码,也无法利用原有的知识积累来完成逆向,这种代码保护技术显著提升了逆向分析所需的成本。代码虚拟化保护方法的基本流程如图1所示。
图1 代码虚拟化保护方案基本流程图
通常的虚拟机软件保护加固方法:
(1)函数识别:对目标程序中的函数进行识别,并且记录所需要保护的关键部分的位置。
(2)代码提取:对选中的关键部分进行提取抽离。
(3)代码生成:用虚拟机指令集对待保护的关键部分进行翻译编码,生成符合虚拟机指令集的字节码。
(4)目标重写:将步骤3中生成的字节码和虚拟机运行所需的组件添加到目标程序的新节中,并将原来关键部分的起始处改为跳转指令。关键代码其余部分使用指定字符进行填充,最终生成一个符合规范的PE文件。
代码虚拟化保护方法通过在特别设计的虚拟环境上模拟x86指令的执行,通常来说能够在任意指令处恢复原有的CPU上下文,然后交由原CPU继续执行原汇编指令,所以私有的虚拟指令集和原CPU指令集之间的关系应该为图灵等价。
1.2 保护后的执行流程
通常来说,软件在受到代码虚拟化保护后,再运行时流程如图2所示。
图2 受保护程序执行流程
当软件受到代码虚拟化保护后,再运行时首先会对未被保护的代码部分正常执行。由于在代码虚拟化保护过程中已经将关键代码处的代码替换为虚拟机代码,而真正的关键代码部分已经转换为特定虚拟指令集对应的虚拟字节码放于数据段,所以运行到受保护的关键代码部分时,会直接执行代码虚拟化中的虚拟机初始化代码,将原始CPU上下文环境保存,并且创建一套虚拟化的CPU上下文环境,然后由该虚拟机中的VMdisaptcher部分读取数据段中的虚拟字节码,通过虚拟机字节码确定虚拟机运行的指令和数据,从而改变虚拟化的CPU上下文环境。由于虚拟字节码与原始关键代码部分图灵等价,所以读取全部虚拟字节码后,虚拟机VMexit部分直接将虚拟化CPU上下文环境恢复至原始CPU上下文即可等价完成对关键代码部分的执行。
代码虚拟化保护的关键部分在于将原有CPU的逻辑转移到私有的虚拟环境中执行。所以如何保护这个虚拟环境不被攻击者轻易分析,增加攻击者分析虚拟环境所带来的成本是一个最基本的想法。为此,本文在此提出一种结合多线程技术的代码虚拟化方法,设计并实现了Muti-Thread-Vmp系统,这样攻击者在分析私有虚拟环境时,不得不频繁切换调试器所需跟踪的线程,显著增加了攻击者分析所需要的时间和工作量,同时由于程序在被保护时,虚拟字节码与虚拟指令之间做了随机化映射,所以对于不同的受到Muti-Thread-Vmp保护的程序,需要单独分析,无法形成知识累积型攻击。
2、Muti-Thread-Vmp系统
2.1 Muti-Thread-Vmp基本原理
Muti-Thread-Vmp的保护对象为Windows平台上的PE文件(.exe和.dll等),使用Muti-Thread-Vmp时需要提供需要保护的关键代码的函数地址,Muti-Thread-Vmp将自动识别该函数的结束位置,并将该关键代码进行代码虚拟化保护。运行Muti-Thread-Vmp系统时,一共经过如下几个步骤:
(1)通过使用者提供的关键代码的函数地址,自动识别并提取关键代码的结束位置。
(2)将原生字节反汇编转换为汇编代码,常用的反汇编引擎有capstone、ODDisassm、BeaEngine和udis86等,本文采用的反汇编引擎为capstone,反汇编引擎将字节码逐条翻译成汇编指令,如8B C1会被翻译成mov eax, ecx。
(3)生成随机操作码表,虚拟函数映射表,按照该操作码表对原反汇编处的汇编代码进行翻译,生成虚拟机字节码。
(4)在受保护的程序创建两个新的节区,一个节区命名为VMP-0,用于置入虚拟机代码,另一个节区命名为VMP-1,用于存放生成的虚拟机字节码,使用垃圾代码填充原始关键代码,在原始关键代码入口写入跳转指令,跳转至VMP-0中虚拟机VMinti函数入口。
(5)将改动过的受保护程序另存为新的PE文件,完成保护。
2.2 Muti-Thread-Vmp关键技术
在Muti-Thread-Vmp运用到的代码虚拟化保护技术中,较以往代码虚拟化保护技术的不同点在于以下几个关键技术,也是因为这些关键技术加强了保护强度。
(1)多线程化的虚拟机设计。本文设计的虚拟机将不同指令的模拟实现置于不同的线程中,虚拟CPU上下文通过线程间的全局变量进行操作,通过VMdispatcher读取虚拟字节码选择线程进行运行,如图3所示。
图3 多线程化虚拟机运行示意图
目前实现的线程指令集如表1所示。
表1 线程指令集统计表
MOVI | MOVR | LODI | LODR | STRI | STRR |
ADDI | ADDR | SUBI | SUBR | ANDB | ANDW |
ANDR | YORB | YORW | YORR | XORB | XORW |
XORR | NOTR | MULI | MULR | DIVI | DIVR |
SHLI | SHLR | SHRI | SHRR | PUSH | POOP |
CMPB | CMPW | CMPR | JMPI | JMPR | JPAI |
JPAR | JPBI | JPBR | JPEI | JPER | JPNI |
JPNR | CALL | RETN | SHIT | NOPE | GRMN |
DEBG | NUM_OPS |
(2)随机化的线程选择。在使用多线程将指令集模拟后,攻击者可以通过跟踪分析每一个线程,从而了解整个虚拟机运行环境以及虚拟操作码和线程之间的关系。通过一定时间的累积便可完成对本系统的逆向分析。为了解决这个问题,在每一次进行虚拟化保护的时候,将线程与虚拟操作码之间的关键进行随机对应。如保护A软件时0x01操作码原先对应的可能是MOVI线程,在保护B软件时,0x01对应的便可能为LODI。这样的随机化操作可以有效避免攻击者对本系统的保护找到统一的分析方法。
(3)线程函数加密。仅从动态分析的角度进行防御显然是不全面的,通过静态分析,攻击者可以轻松分析出每一个线程的含义,从而定位每个线程的指令,恢复原始代码。为了对抗静态分析,本系统将所有的线程函数在VMinit前进行了XOR加密,XOR加密的密钥为每个线程对应的虚拟操作码,在VMinit运行后,再进行解密操作。这样可以有效对抗静态分析,进一步加强本系统的保护强度。
3、实验分析
实验环境为WIN10-1909,3.6GHz CPU,32G内存。由于只实现了部分对32位CPU指令集的模拟,故仅对32位可执行程序进行测试。
3.1 功能验证
选用样例程序进行保护,样例程序源码如图3所示,保护前可以轻松使用IDA逆向工具得到其程序逻辑。
图3 未受保护的calc.exe
而在经过Muti-Thread-Vmp保护后,反编译时,IDA开始报错,并且出现大量无法识别的字节码,如图4所示。
图4 受保护的calc.exe
通过测试,本文设计并实现的代码虚拟化技术可以有效地对抗逆向分析,增大攻击者的分析成本,从而对软件进行保护。
3.2 性能验证
对本系统进行实例测试,主要测试文件大小和性能损耗。对比实验结果如表2、表3所示。
表2 空间损耗测试
测试程序 | 原始大小 | Muti-Thread-Vmp保护后大小 |
calc.exe | 27.00KB | 3.5MB |
test.exe | 192.00KB | 3.8MB |
maze.exe | 8.00KB | 3.43MB |
表3 时间损耗测试
测试程序 | 原始运行启动速度 | Muti-Thread-Vmp保护后速度 |
test.exe | 3ms | 14ms |
maze.exe | 65ms | 140ms |
通过实验可以看出,本文提出的系统在保护程序时,会将虚拟机加入原始程序,造成额外的空间损耗;在执行效率上也会造成额外的时间损耗。但是对于程序保护来说,这些额外的损耗是不可避免的。
4、结语
由于目前代码虚拟化保护技术仍存在着不断被破解的情况,本文提出一种基于多线程模拟虚拟指令集的方法来对抗逆向分析,从而加强代码虚拟化保护的强度。本文从设计、理论分析、功能验证以及与商用软件对比的角度上,介绍并证明了本系统的有效性。
在未来的研究中,本文提出的方法还可以进行进一步优化与加强。
(1)将可以并行的汇编指令通过多线程并行运行,从而提升程序效率,理想的极端情况下,效率甚至会比原始程序还要高。
(2)可以设置真假线程,如满足一定条件的假线程启动后,真线程才启动,这样可以进一步增大分析的难度。
引用本文:丁宁,王轶骏,薛质. 面向二进制代码的多线程虚拟化保护技术[J].通信技术,2021,54(1):169-174.
作者简介
丁宁,硕士,主要研究方向为网络空间安全;
王轶骏,,硕士,主要研究方向为网络攻防及系统安全;
薛质,博士,教授,主要研究方向为计算机通信网及信息安全。
基金项目:国家重点研发计划项目“网络空间安全”重点专项(No.2017YFB0803203)
选自《通信技术》2021年第1期(为便于排版,已省去原文参考文献)
声明:本文来自信息安全与通信保密杂志社,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。