基于虚拟机代码隔离的软件保护技术研究

2018-04-09 01:08余祥周元璞李强
指挥与控制学报 2018年1期
关键词:指令集代码指令

余祥 周元璞 李强

随着信息化的不断发展和深入,软件给我们的生产生活方式带来了巨大的变化,深刻地改变着社会生产、科技探索和战争形态等.软件安全问题也显得愈发重要.尤其是指挥信息系统软件等核心软件一旦被非法攻击、破解,进而通过对软件的逆向分析,掌握关键数据和重要情报,甚至修改软件运行结果,从而给国家带来巨大损失.

软件逆向分析是软件安全威胁的重要原因.软件逆向分析是指对二进制文件反汇编的基础上进行理解和再现的过程.一方面,软件逆向分析技术可以促进软件产业的发展;另一方面,这直接导致了软件面临严重的逆向分析威胁.因此,软件抗逆向分析方面的研究也越来越受到人们的重视,用于抗逆向分析的软件保护技术主要有软件加壳[1]、代码混淆[2]、软件加密[3]、代码隔离[4]和虚拟机技术[5]等.

随着软件攻击手段的丰富和发展,单一的软件保护技术已不足以保证软件安全.将多种软件保护技术结合使用是提高软件安全的有效方法.本文提出一种基于虚拟机代码隔离的软件保护方法,将虚拟机技术和代码隔离方法相结合,提高软件抗逆向分析能力.

1 相关工作

基于虚拟机代码隔离的软件保护方法的基本思想是将软件的一部分从软件整体中分隔开,软件运行时再组合成一个整体,从而提高软件抗逆向分析能力.为保证软件被分隔后运行效率,分隔出的部分不宜过大,因此,只分隔软件关键数据和关键代码,将其从软件中提取后,转换为虚拟机代码添加到重构后的软件文件或直接移植到加密锁中.

本文主要研究关键数据和代码的提取、转换和移植方法.关键数据和代码的提取主要包括用户直接指定[6]、动态调试定位[7]和静态分析定位[8]3种.其中,用户直接指定需要用户对软件源程序和汇编较为熟悉;动态调试定位通过动态执行软件获取软件的控制流和数据流,获取软件的关键数据和代码;静态分析定位不需要调试定位,通过关键字匹配、复杂度计算等方法定位关键代码和数据.

文献[9−10]提出通过执行轨迹差异定位关键代码和数据,即通过两次运行程序,给予程序不同输入,使其一次执行特定功能,另一次不执行该功能,分别记录程序运行指令的轨迹,对比轨迹差异获取关键代码.

文献[11]设计了WBRPE(White Box Remote Program Execution)白盒远程程序执行架构,使程序执行过程依赖于本地机器和服务器,文献[12]对其进行了改进,利用混淆器对传至服务器的代码或数据进行了混淆.但这种基于服务器的保护方法将关键代码或数据在服务器中执行,对软件性能影响较大,应用环境有一定限制.

文献[13−14]提出的基于虚拟执行环境的保护方法都是基于指令动态映射完成的,在这个过程中需要运行时修改内存,容易被定位分析,且由于构造虚拟执行环境时间开销较大,仅适用于保护分散的少量指令片段,效率和强度较低.

本文提出的关键代码和数据提取方法基于动态调试和控制流、数据流分析,相较于用户直接指定的方法,对用户要求较低;相较于静态分析的方法以及通过关键字查找的方法,定位关键代码更全面和准确,但实现相对复杂.关键代码和数据的转换方法基于X86指令集系统进行映射,成本开销相对较小,实现复杂度较低.关键数据和代码移植基于硬件加密锁的虚拟执行环境,对软件的性能影响较小,应用环境也更自由.

2 关键代码提取和转换

关键数据和代码是软件最核心的部分,关键数据是软件的核心参数,关键代码一般为软件的核心算法.确定软件关键数据或代码的准则有4条:

准则1.去除数据或代码后软件的核心功能无法正常使用;

准则2.去除数据或代码后软件的运行结果不正确;

准则3.数据或代码被修改后软件的核心功能无法正常使用;

准则4.数据或代码被修改后软件的运行结果不正确.

软件关键代码提取就是要获取软件关键代码的地址信息,即待保护指令序列的起始地址信息;关键数据提取是指获取软件关键数据的值信息.提取软件关键数据和关键代码通过分析软件运行时的指令流和数据流,分析软件代码的行为特征,将指令流信息划分为若干代码块,采用程序切片和特征匹配的方法获取.

找到软件关键代码保护指令序列的起始位置,是进行关键代码提取和转换的首要步骤.将软件中的关键代码转换为虚拟机字节码,也就是将源汇编代码指令系统的可执行代码,转换为自定义的虚拟机字节码,并在其中通过代码混淆等技术保护软件.其工作过程如图1所示.从源汇编代码得到原始指令流,经过随机映射得到虚拟机编码,与待保护的软件关键代码一起通过虚拟机编译器编译成虚拟机字节码,即保护后的字节码.指令处理函数通过变形引擎进行指令转换,转换后的代码通过解释器进行解释执行.

2.1 虚拟机指令集

指令集是存储在CPU内部,对CPU运算进行指导和优化的硬程序,一般包括基本指令和扩展指令两部分.基本指令是所有处理器、虚拟机或运行环境都会提供的指令集,而扩展指令一般只在真实处理器中.虚拟机指令集是虚拟机的基础,规定了虚拟机的运行方式.

图1 基于虚拟机的软件保护过程示意图

构造用于保护软件的虚拟机并不需要实现指令集所有指令的功能,部分指令的功能可以通过其他简单指令进行运算实现.因此,定义一个简化的基本指令集作为虚拟机指令集可以有效避免复杂的指令系统带来的性能损失.据此定义的虚拟机指令集如表1所示.

表1 简化的虚拟机指令集

其中,JMPF指令包括JMP、JLE、JL、JE等.虚拟机指令代码由源汇编代码指令转换而来,因此,每条虚拟机指令代码应包含指令码、源操作数、目的操作数.每条源汇编指令的长度和操作数并不完全一致,为便于实现,将虚拟机指令代码设为定长,为源汇编代码指令最长字节,通过随机生成的方法进行代码转换.所谓随机生成是指对于某一条具体的虚拟机代码指令存在不同的形式,如MOV AX,BX,可以出现在源汇编代码指令字节的不同位置.通过处理增加分析和破解的难度.

2.2 虚拟机编译器

编译器是将一种语言翻译成另一种语言的程序,通常是将高级语言翻译成低级语言.虚拟机编译器主要是完成可执行程序的目标代码到虚拟机自定义指令的转换过程,主要完成的功能有:

1)代码提取:定位程序待保护关键代码段,通过反汇编将关键代码段二进制数据翻译成汇编级别的目标平台机器指令,为指令转换做准备.

2)指令转换:将汇编级别的机器指令按照一定的映射关系转换成虚拟机自定义指令,在每一次保护过程中随机构造指令映射关系.

3)指令编码:对虚拟机自定义指令进行编码,按编码规则将转换后的自定义指令序列翻译成二进制数据,在每一次保护过程中随机生成指令编码规则.

4)虚拟机生成:根据保护过程中的随机映射关系和指令集得到源汇编代码的指令解析代码,并添加到虚拟机解释器中.

5)文件重构:将虚拟机编译器、解释器和指令编码后的代码添加到程序源文件中,重新生成新的程序文件.

虚拟机编译器的输入是软件反汇编得到的源汇编指令,输出是虚拟机指令代码.为简化代码转换规则和虚拟机解释器的设计,提高虚拟机执行效率,基于表1所示指令集设计虚拟机指令集,使用基于寄存器的体系结构并设置包含特殊寄存器的多寄存器,同时引入堆栈,提高虚拟机执行效率.

2.3 虚拟机解释器

虚拟机执行虚拟机指令代码主要有解释执行、即时编译和二者结合3种方法.考虑到转换的关键代码内容较少,为提高执行效率,针对每一个指令码设计对应的解释函数,虚拟机解释执行指令时先根据条件找到对应的解释函数再解释执行.虚拟机解释执行示意图如图2所示.

图2 虚拟机解释执行示意图

简单和直接实现的指令分配方式是每一个指令码对应一个函数,然后通过switch进行分配,示例代码是:

为降低函数调用的开销,将所有函数代码放在一起,形成一个大的switch语句块,可以避免频繁的函数调用,提高虚拟机解释执行效率,示例如下:

但这样执行switch语句需要对每一条执行的指令都进行一次线性搜索,通过函数指针将每个指令码对应的操作写在单独的函数中,然后将每个指令码替换为对应的函数指针,从而省去线性搜索提高效率.替换后的switch语句描述如下:

2.4 提取准则

关键数据和关键代码是软件核心算法所对应的代码和重要参数对应的数据.关键数据和代码的提取是通过分析软件运行时的指令流和数据流进行定位和提取的.

软件运行时的指令流和数据流都是未经解析的二进制信息.为便于理解和分析,首先将其转化为字符串信息.然后根据指令类型分别采用相应的方法进行分析,获取关键代码的地址信息.

2.4.1 静态特征分析

静态特征分析就是通过字符串匹配的方法,在指令流和数据流转换后的字符串信息中查找已知的静态特征.字符串匹配可以是多个条件的逻辑运算.例如,以调用外部函数关键字“Shell”和线程函数“Thread”作为静态特征分析软件的功能,构造逻辑表达式计算模糊查询“Shell”和“Thread”的逻辑与运算,根据匹配结果定位到代码处,然后进一步通过其他特征的分析,得到分析结果.

2.4.2 转移指令分析

转移指令是不按程序的语句流程执行的指令.通过分析转移指令可以分析出软件多个代码块之间的相互调用关系和软件代码的执行路径,有助于理解代码上下文之间的关系,从而分析软件行为特征.

转移指令分析可以通过构建指令流的流程图实现.首先找出指令流信息中所有转移指令,根据转移指令位置将指令流划分为多个代码块,并合并多次出现的相同代码块为一个模块,根据转移指令的跳转关系,将代码块按顺序组合构建软件运行时的指令流的流程图.

2.4.3 程序切片分析

一个程序切片是软件程序的部分语句和判定表达式所组成的集合[15].程序切分分析基于分治算法的思想,主要用于分解程序,将需要分析的问题从复杂的程序中分隔开单独分析,有助于降低问题复杂度,便于分析和解决.程序切片分析通过分析数据的依赖关系,计算出影响输入的数据i的语句和判定表达式,将相关的指令和数据集合作为切片,进行分析.程序切片输入的数据i可以是指令、内存地址、数据值或是寄存器信息等.对运行时的指令流和数据流进行程序切片分析,能在复杂的软件程序中较快地提取关键信息并分析特征.由上,设计软件关键代码提取算法如下所示.

算法.关键代码提取算法;

输入.指令流和数据流信息起始语句Sentence,指令集合CommandSet,数据集合DataSet,指令集合与数据集合的部分映射关系MapRelation;

输出.相关指令和数据集合ResultSet.

算法描述:

2.5 代码转换

软件的关键代码转换是将待保护软件中的关键代码转换为虚拟机字节码,也就是将x86指令系统的可执行代码转换为用户自定义的虚拟机字节码代码.转换主要包括指令映射和指令编码.指令映射是将汇编级别的机器指令按照一定的映射关系转换为自定义的虚拟机指令,并保存在相应的结构和存储单元中,每一次转换过程中随机构造指令映射关系;指令编码是对自定义的虚拟机指令进行编码,按照编码规则将转换后的自定义指令序列翻译成二进制数据,每一次转换过程中随机生成指令编码规则.其中,指令映射是核心.

软件反汇编得到的一条汇编指令记为inm,所有汇编指令组成目标指令结合T={in1,in2,···,inn},ini∈T,≤i≤n.将需要转换的关键代码指令记为insi,关键代码指令集合P={ins1,ins2,···,insk},insi∈P,1≤i≤k,P⊂T.将用于解释被保护指令的虚拟指令记为vi,其集合V={v1,v2,···,vw},vi∈V.一条指令可以被一条或多条虚拟指令解释,解释一条指令insi的虚拟指令序列记为其中,1≤j≤w.如VI(ins2)={v2,v4,v5}表示通过v2,v4,v53条虚拟指令解释指令ins2.转换函数表示虚拟指令和关键代码指令的转换关系,记为TFi.转换函数序列TF(ins)表示解释指令insi的处理函数序列,其集合记为TFSet(insx)={TF(ins1),TF(ins2),···,TF(insc)}由此,设计关键代码转换算法如下所示.

算法.关键代码转换算法

输入.关键代码指令集合P={ins1,ins2,···,insk};

输出.虚拟指令序列VI.

算法描述:

1)定义虚拟指令序列为空集合,即VI=∅;

2)for eachinsi∈P

3)计算转换函数序列TF(insi)={hd1···hdn}

4)for eachhdm∈TF(insi)

5)计算虚拟指令vi

6)将虚拟指令vi加入虚拟指令序列VI;

7)end each

8)end each

3 关键数据和代码隔离

关键数据和代码隔离是将软件关键数据存储在加密锁中,或将关键代码转换为加密锁内可执行的编译语言代码,并将编译生成的文件写入加密锁中.

关键数据和代码在写入加密锁前,需要进行加密处理,常用的加密算法包括3DES、SM4等,软件调用关键数据或关键代码时,通过与加密锁的通信接口获取加密锁获取存储在加密锁中的关键数据或关键代码的运行结果并调用解密算法将数据解密后使用.从而将软件和加密锁捆绑为一个整体,通过基于硬件的方式隐藏关键数据和代码,实现代码隔离.软件关键数据和代码隔离原理示意图如图3所示.应用软件小圆圈A即为要隔离的关键数据或代码,软件“芯”加密锁小圆圈B即为隔离处理后隐藏在加密锁中个关键数据或代码.

图3 关键数据和代码隔离原理示意图

软件关键数据和代码隔离是从软件程序中“切分”出关键代码片段或关键数据,通过基于硬件的方式隐藏所切分的关键数据或代码,将关键数据或代码隐藏在加密锁中,从而使软件主程序和关键代码或数据相分离,难以通过逆向分析获取软件关键数据或核心算法.

加密锁内移植多个关键代码块后,通过计算时间间隔可以判断是否处在动态调试环境,从而有效提高软件抗逆向分析的能力.若加密锁中包含关键代码块S1,S2···Sn,软件P运行时需要按顺序调用S1和S2,即:S1→S2,则执行S1和S2之间的时间间隔在一个固定时间范围T内(范围值T可以通过软件测试计算获得),在软件P调用S1时,获取加密锁时间T1,在P调用S2时,获取加密锁时间T2,计算T0=T2−T1,若T0>T,则可认为是软件正在被调试,可以通过销毁加密锁内关键数据等方法使软件P无法正常使用,防止软件被逆向分析.由此,通过加密锁调用关键代码判断动态调试的算法设计如下所示.

算法.加密锁关键代码调用判断动态调试算法

输入.无

输出.判断结果

算法描述:

4 结论

将多种软件保护手段相结合共同保护软件能有效提高软件破解和攻击的难度.本文提出的基于虚拟机代码隔离的软件保护方法,通过分析和设计虚拟机指令集、编译器和解释器,确定了关键数据和代码的提取准则和转换方法,通过代码隔离的方法将软件关键数据和关键代码分别隔离在构建的虚拟机和硬件加密锁中,从而使软件不再是一个完整的整体,难以通过逆向分析获取软件关键数据和核心算法,有效提高了软件攻击的复杂度.

在多个项目软件中对本文提出的方法进行了使用,目前软件运行稳定,未发现软件被攻击的情况.下一步考虑进一步完善虚拟机技术的相关理论,扩充虚拟机指令集,提高虚拟机编译和解释执行的效率.

1王建民,王朝坤,余志伟.软件保护技术[M].北京:清华大学出版社,2013.

2 COLLBERG C,THOMBORSON C.A taxonomy of obfuscationg transformations[R].Auckland:Department of Computer Science,University of Auckland,1997.

3 段刚.加密与解密[M].北京:电子工业出版社,2003:16−23.

4 MAUDE T,MAUDE D.Hardware protection against software piracy[J].Communications of the ACM,1984,27(9):950−959.

5 AVERBUCHA,KIPERBERGM,ZAIDENBERGN J.Truly-Protect:An efficient vm-based software protection[J].IEEE Systems Journal,2013,3(7):455−466.

6许广莲,房鼎益,王怀军,等.一种白盒环境中抗动态攻击的软件保护方法[J].小型微型计算机系统,2015,36(9):2062−2066.

7王妮.基于攻击建模的软件保护有效性评估方法研究[D].西安:西北大学,2012.

8崔宝江,梁晓冰,王禹,等.基于回溯与引导的关键代码区域覆盖的二进制程序测试技术研究[J].电子与信息学报,2012,34(1):108−113.

9康绯,王乾,肖亚南,等.基于软件执行轨迹差异比对的关键函数定位技术研究[J].通信学报,2013,9(34):177−184.

10 LIU D,ARCUS A,POSHYVANYYK D.Feature location via information retrieval based filterin of a single scenario execution trace[R].New York:IEEE,2007.

11 HERZBERG A,SHULMAN H,SAXENA A,et al.Towards a theory of white-box security[M].Berlin:Springer Berlin Heidelberg,2009:342−352.

12 HERZBERG A,SHULMAN H.Robust combiners for software hardening[M].Berlin:Springer Berlin Heidelberg,2010:282−289.

13 SHARIF M,LANZI A,GIFFLN J,et al.Automatic reverse engineering of malware emulators[C]//Oakland,California:Washington:IEEE Computer Society,2009.

14谢鑫,刘粉林,芦斌,等.一种基于代码并行化和虚拟机多样化的软件保护方法[J].小型微型计算机系统,2015,11(36):2588−2593.

15 HORWITZ S,REPS T,BINKLEY D.Interprocedural slicing using dependence graphs[J].Acm Transactions on Programming Languages and Systems,1990,12(1):26−28.

猜你喜欢
指令集代码指令
基于Kubernetes的RISC-V异构集群云任务调度系统①
基于 Verilog HDL 的多周期 CPU 设计与实现
3DNow指令集被Linux淘汰
《单一形状固定循环指令G90车外圆仿真》教案设计
创世代码
创世代码
创世代码
创世代码
基于Dais—CMX模型机的斐波那契数列指令集设计
中断与跳转操作对指令串的影响