Java程序混淆技术综述王建民.docx
- 文档编号:11679381
- 上传时间:2023-06-02
- 格式:DOCX
- 页数:39
- 大小:36.67KB
Java程序混淆技术综述王建民.docx
《Java程序混淆技术综述王建民.docx》由会员分享,可在线阅读,更多相关《Java程序混淆技术综述王建民.docx(39页珍藏版)》请在冰点文库上搜索。
Java程序混淆技术综述王建民
Java程序混淆技术综述
王建民1),2),3) 余志伟1),2),3),4) 王朝坤1),2),3) 付军宁1),2),3)
1)(清华大学软件学院 北京 100084)
2)(清华信息科学与技术重点实验室 北京 100084)
3)(信息系统安全教育部重点实验室 北京 100084)
4)(清华大学计算机科学与技术系 北京 100084)
摘 要 软件混淆技术已经广泛应用于抵制逆向工程和重组工程.文中从混淆技术的历史发展角度对现有的混淆
技术理论、算法、攻击模式和评估进行了综述,将Java程序混淆算法分为类内混淆和类间混淆两个类别,并对其中
的各类算法进行详尽的阐释.最后在现有工作的基础上,展望了软件混淆技术未来的发展与研究方向.
关键词 程序混淆;软件水印;防篡改;软件版权保护
中图法分类号TP309 DOI号:
10.3724/SP.J.1016.2011.01578
ASurveyonJavaProgramObfuscationTechniques
WANGJian-Min1),2),3) YUZhi-Wei1),2),3),4) WANGChao-Kun1),2),3) FUJun-Ning1),2),3)
1)(SchoolofSoftware,TsinghuaUniversity,Beijing 100084)
2)(TsinghuaLaboratoryforInformationScienceandTechnology,Beijing 100084)
3)(KeyLaboratoryforInformationSystemSecurityofMinistryofEducation,Beijing 100084)
4)(DepartmentofComputerScienceandTechnology,TsinghuaUniversity,Beijing 100084)
Abstract Obfuscationtechniqueshavebeenwidelyappliedinthedefensesofreverseengineering
andre-engineeringattacks.Fromtheviewofthedevelopmentofobfuscation,webrieflydis-
cussedtheprinciples,algorithms,differentkindsofattackapproachesandevaluatingstandard.
Javaprogramobfuscationalgorithmscanbedividedintotwotypes:
Oneisobfuscationwithina
class;theotherisobfuscationbetweenclasses.Finally,basedonthesurveyoftheseobfuscation
techniques,thefutureresearchofJavaprogramobfuscationisalsostated.
Keywords obfuscation;softwarewatermark;temp-proofing;softwarecopyrightprotection
1 引 言
随着计算机软件的广泛使用,软件的安全问题
严重威胁着软件产业的发展,主要表现为:
软件攻击
者获得试图攻击的软件备份之后,成功地破解软件.
现有的调试和编辑工具,使直接检查或修改二进制
程序代码变得更加容易.因此,通常情况下攻击者会
重组或破解软件,导致软件的使用控制机制失效.破
解后的软件可以非法向大众分发,更加助长了软件
盗版问题.
Java程序由于平台无关性得以在Internet上迅
速传播和使用.但同时,Java语言的这一特性也带
来了保护知识产权方面的新问题.Java语言为了支
持平台无关性,采用了Class这种字节码文件格
式①.经过预编译之后,Java源代码指令转化为字节
码,而后在虚拟机上解释执行.由于Java字节码的
设计是为了使语言更简洁、更具有平台独立性和网
络灵活性,它的指令集相对简单通用,每一个类编译
成一个单独的文件,Class文件保留方法变量名称等
大量语义信息.这些特点都导致Java字节码更容易
被反编译为Java源代码.攻击者通过反编译和反汇
编技术,获得软件的全部或部分源代码,从而获取关
键信息如核心算法、秘密信息等为自己所用.目前已
有许多Java的反编译工具,如Jad②、Mocha[1]等,都
是常用的针对Java字节码文件的反编译器.因此,
防止针对Java字节码文件的静态分析和反编译攻
击的要求,显得更加迫切.
混淆技术可以被理解成为是一种特殊的编码技
术,目前被广泛地应用于软件知识产权保护领域.通
常所说的混淆是指对拟发布的应用程序进行保持语
义的混淆变换,使得变换后的程序和原来的程序在
功能上相同或相近,但是更难以被静态分析和逆向
工程所攻击,如图1所示.
图1 软件混淆技术应用
本文从混淆技术的发展过程角度,对现有的混
淆技术进行综述.首先,在第2节介绍Collberg和
Thomborson等人[2]最早提出的混淆技术的形式化
定义;第3节介绍混淆算法的分类、各类算法的原理
及其实现方法;第4节介绍针对混淆算法的理论研
究以及攻击模式;第5节阐述针对混淆算法的评估
度量;最后总结并展望混淆技术的前景.
2 软件混淆技术基本原理
软件③混淆技术是在1997年由新西兰Auck-
land大学的Collberg和Thomborson等人[2]首先提
出的.混淆技术属于一种重要的软件保护技术,其本
质是利用程序等价变换的方法,将原始程序转变为
语义等价、难以被理解或反编译的程序.同时,他们
还阐述了对于混淆算法的分类及其评估方法,并分
别就每一类混淆技术提出了若干算法.这些理论及
算法对混淆技术的发展起到了提纲挈领的作用.
在Collberg和Thomborson提出的理论中,混
淆技术可以视为一种高效的程序变换技术,它将原
始程序P转换成新的程序P′.P′与P相比具有相
同的外部行为,并且代码安全性能更强.其形式化定
义如下:
设T是从原始程序P到目标程序P′的一个变
换,P′=T(P).如果该变换满足下列条件,就称
P′=T(P)为一个软件混淆变换:
①若P无法结束或者以错误的状态结束,则P′
可结束也可不结束;
②若P结束,则P′也必须结束并且产生和P
相同的输出结果;
③对于完成某特定的计算任务,P′比P多消耗
的运行时间在一个有限的范围内;
④攻击者将P′恢复为P所耗费的时间大于将
P转换至P′的时间.
其中,P为未经混淆的程序,即原始程序,而P′为混
淆后的程序.对于任意的合法输入,P′与P输出相
同的处理结果,即满足程序语义等价性.
3 软件混淆技术的分类
Java文件经过编译生成类文件.类文件分为不
同的字段,包括魔数(magic)、版本(version)、常量
池(constantpool)、访问标识(accessflags)、(this)
类、(super)类、接口(interfaces)、域(fields)、方法
(methods)和属性(attributes).
根据混淆算法针对的对象不同,可以将混淆分
为类内混淆和类间混淆.
类内混淆指混淆的作用范围在某个类文件内
部,基于字节码的类内混淆作用对象主要是上述类
15799期王建民等:
Java程序混淆技术综述
①
②
③
TheJavaVirtualMachineSpecification.http:
//java.sun.
com/docs/books/jvms/
Jad.
为描述方便,本文对“程序”和“软件”不作区分.
积的作用.
3.1.1.5 移除注释和调试信息混淆
程序的注释和调试信息在一定程度上为攻击者
理解程序语义提供了便利,去除此类信息可以有效
阻碍攻击者对程序的理解.移除注释和调试信息混
淆的主要实现方法是去除注释和调试信息等源代码
格式化信息,以及将原有标识符替换为无意义的标
识符.这是一种单向的变换,源文件在经过混淆之后
无法恢复[3].该混淆方法操作简单,但是强度较差,
因为移除的调试信息量较小.这种移除信息的混淆
方法对程序运行的时间和空间复杂度几乎没有影
响[2].混淆器Crema①是一个典型的应用实例.
3.1.2 控制流混淆
程序的控制转换过程的信息是追踪定位程序状
态的重要线索,如何保护这部分信息也是软件保护
中很重要的一个环节.控制流图(ControlFlow
Graph,CFG)是程序可能执行流程的图形化表示,
它可以用来描述程序的控制转换.一个程序可以被
分成由一系列无分支的代码组成的基本代码块,这
些基本块作为控制流图的结点,而图的边即为各个
基本块之间可能的跳转关系.控制流混淆的目的就
是改变或复杂化程序的控制流,使程序更难以破译.
控制混淆可采用的手段很多,比如应用不透明谓词
增加伪造分支、加入可导致反编译错误的指令(例如
在Java字节码中添加goto语句等)、将一段代码转
换为内联函数调用等[2].
3.1.2.1 控制计算混淆
(1)通过不透明谓词加入不会被执行的分支语句
对于一串语句S,利用不透明谓词可以构造出
两种变换[6].第1种是增加恒真或恒假的不透明谓
词,构造出一条不会被执行的分支,该分支上可以没
有语句,也可以有和S不同的语句;第2种是利用时
为真时为假的不透明谓词,令它的两个分支上的语
句都与原语句S相同.其中第2种混淆变换能够较
好地抵抗动态分析.
(2)循环条件插入变换
通过不透明谓词使一个循环的终止条件变得更
为复杂,可以达到对循环进行混淆的目的[2].两种可
能的变换原理如图2所示.其中f和g是判断谓词
k,j的表达式.
(3)将可化简的控制流转换为不可化简的控制流
通过将一个结构化的循环变换为含有多个入口
的循环,可以将可化简的控制流转换为不可化简的
控制流[2].其原理如图3所示,主要是利用不透明谓
词加入的假分支,生成循环交叉,从而使控制流图难
以被反编译.
(4)并行化代码
并行化代码是编译器优化程序性能的重要方法
之一[2].这一技术可以用于混淆程序控制流,从而增
大攻击者理解程序的难度.具体地,有两种实现方
式:
①在程序中添加伪造的进程代码(dummy
process);②将一组串行程序并行化.
使用并行化代码算法,需要注意代码中的数据
依赖关系,否则可能破坏程序的语义等价性.
3.1.2.2 控制聚合混淆
通过方法内联变换和将某些代码片段转换成为
子程序,可以改变程序自身的控制流图.这种混淆方
15819期王建民等:
Java程序混淆技术综述
①VanVH.Crema-TheJavaobfuscator.http:
//web.inter.
法是单向的,具有较好的鲁棒性[2].
(1)方法交叉调用和方法克隆
其主要思想如图4所示,合并两个调用方法的参
数列表和主体,并增加一个参数或全局变量(图4中
的intV),用以声明执行时具体应当调用哪一个方
法.方法克隆的目的是混淆程序的方法调用关系,克
隆的方法应与原方法看似区别但又具有相同的
语义[2].
(2)循环变换
循环变换可以采用循环模块化(将循环区间划
分为多个模块)、循环展开(拓宽循环的步频)和循环
切割(将一个循环转换为多个循环)等方法实现.
3.1.2.3 控制顺序混淆
控制顺序混淆的实现原理为:
随机打乱表达式、
基本块、方法和类的顺序,使攻击者难以理解程序的
正确意图.使用该方法要注意一些有依赖关系的数
据在重新排序之后是否合法.
classC{
methodM1(T1a){
SM11;…;SM1k;
}
methodM1(T1b;T2c){
SM21;…;SM2m;
}
}
{Cx=newC;
x.M1(a);x.M2(b,c);}
classC′{
methodM(T1a;T2c;intV){
if(V==p){SM11;…;SM1k;}
else{SM21;…;SM2m;}
}
}
{C′x=newC′;
x.M(a,c,V=p);
x.M(b,c,V=q);}
图4 方法交叉调用
对于控制混淆如何抵抗自动化分析工具的攻
击,也有不少研究.Wang[7]提出了通过更改跳转指
令实现控制混淆的一种方法,使得编译时候每一个
跳转指令的目标都是未知的.Chow等人[8]提出了
一种相似的方法,通过一个有限状态自动机来控制
程序的跳转语句.
根据以上的控制混淆算法理论,Low[9]在1998
年提出了针对Java代码的控制混淆算法实现.对于
不透明谓词的设计,Majumdar等人[6]在2006年为
分布式系统的混淆补充了一种分布式不透明谓词.
应用该不透明谓词可以有效地抵抗多种自动静态分
析的攻击.
3.1.3 切片混淆
切片通常是用来帮助理解程序的,而混淆的目
的是使程序更难以被理解.Drape等人[10-11]提出了
切片混淆算法,使得混淆过的程序能够更好地对抗
切片分析攻击.
定义Slice(P,S,V)用来标记程序P在状态S
时,对应的变量集V的一个后向切片(backwards
slice),指令out表明V中变量的输出.
图5中显示了一个包含有变量i,x,y的程序,
当输出为y时的程序切片:
黑体部分为切片时可观
测到的变量值.此时变量x不在观测范围内.
切片混淆(slicingobfuscation)算法的主要思想
就是尽可能多地将多个变量的值放入到切片的观察
范围之内,增加使用切片分析程序的攻击者的困难
intsumprod(intn){
inti=0;
intx=0;
inty=1;
while(i i++; x=x+i; y=y*i;} out(x); out(y);} 图5 原始程序out(y)时的切片 程度[11].切片混淆的主要方法有: 增加恒假谓词、 变量编码和增加循环变量.增加恒假谓词是在恒假 谓词的假分支上增加令x与y相关的函数;变量编 码是在不改变语义的情况下将y的表达式重新编码 为与x相关的表达式;增加循环变量是在循环变量 中添加与x,y相关的变量.图6至图8分别描述了 这几种方法的实现. intbogus(intn){ inti=0; intx=0; inty=1; while(i i++; if(i<5||x x=x+i; elsey=x*i; y=y*i;} out(x); out(y);} 图6 增加伪造分支 intvarEnc(intn){ inti=0; intx=0; inty=1; while(i i++; x=x+i; y=(y+i-x)*i+x;} out(x); out(y-x);} 图7 变量编码 1582计 算 机 学 报2011年 intaddVar(intn){ inti=0; intx=0; inty=1; intj=2; while(i i++; x=x+i; y=y×i; j=j+y-x;} out(x); out(y);} 图8 增加循环变量 Majumdar等人[12]从单词计量、结果求和、定位 时间等角度验证了切片混淆的方法能够增强程序抵 抗切片分析攻击的能力. 3.1.4 针对特定工具的混淆 针对特定工具的混淆是针对自动化的反编译和 反混淆工具提出的混淆方法,旨在阻止这类自动化 工具的使用,或者增加其使用难度和代价[2].该类混 淆方法与前面提到的3种类别的混淆,最大的不同 之处在于,前3种混淆技术主要是针对程序的阅读 者,即针对的是人;而后者针对的是自动化的反混淆 以及反编译工具. 针对特定工具的混淆可以通过与其他的混淆技 术综合使用从而达到更好的鲁棒性[2].在实际操作 中,可以在循环体内部加上伪造的无用变量,形成数 据依赖,增加反混淆的自动工具分析难度.例如在程 序的某处使用了反向循环的混淆,为了防止这一变 换被反编译器分析破解,可以采用图9所示方法: 在 这个例子中,增加了B数组,for循环的前后向顺序 会对B数组的各个元素产生影响,从而形成数据依 赖,阻止反编译器变换循环的方向.这一混淆方法的 弹性,取决于添加伪造数据的复杂度. For(i=0;i<=10;i++) { A[i]=i; } intB[50]; For(i=10;i>=10;i--) { A[i]=i; B[i]+=i×i/5; } 图9 增加伪造的数据依赖 例如,针对Mocha[1]这一反编译工具,我们可 以采用这样的措施造成它反编译失败: 在每个方法 的每一条返回语句之后增加额外的指令.在不影响 程序行为的前提下,造成Mocha的崩溃.Batchelder 等人[13]在2007年较为详细地阐述了如何利用Java 字节码与Java源代码之间指令规范的不同,在字节 码的级别进行可以导致反编译失败的控制流混淆. 例如利用jsr-ret指令取代普通的if-goto;针对模式 匹配的反编译器,将load指令提到if指令之前;在 子类构造函数中对父类的调用进行外包等等. 3.2 类间混淆 Sosonkin等人[3]在2003年提出了3种分别基 于类的合并、类的拆分以及类型隐藏的类间混淆 算法. 3.2.1 类合并 类合并算法的主要思想是合并两个或多个类各 自包含的变量和函数,根据需要重命名变量或函数 标识符[3].若待合并的类中有标识符相同的变量或 非构造函数,则将其改为不重复的新标识符;若待合 并的类中有标识符及参数都相同的构造函数,无法 随意更改标识符,则合并后为其中一个增加伪造的 参数;若待合并的两个类之间有继承关系,则合并后 增加一个布尔型的私有变量用于区分标识符相同的 函数,图10是类合并的一个例子. Originalclasses: classA{ privateinti; publicA(){ i=5; } publicbooleanm(){ returni<0; } } classBextendsA{ publicB(){ super(); i=10; } publicbooleanm(){ returni<10; } } classC{ voidn(){ Aa; if(…){ a=newA(); }else{ a=newB(); } a.m(); } } Obfuscation Obfuscatedclasses: classAB{ privateinti; privatebooleanisA; publicAB(){ i=5; isA=true; } publicAB(intj){ this(); i=10; isA=false; } publicbooleanm(){ if(isA){ returni<0; }else{ returni<10; } } } classC{ voidn(){ ABa; if(…){ a=newAB(); }else{ a=newAB(38); } a.m(); } } 图10 一个类合并算法的实现 3.2.2 类拆分 类的拆分算法首先分析判断待拆分的类内部的 继承和方法调用关系[14],观察它们是否能够进行拆 15839期王建民等: Java程序混淆技术综述 文件结构方法(methods)中的code字段. 类间混淆指混淆作用于两个或者两个以上的类 文件,实现的方式主要包括类合并以及类拆分等. 3.1 类内混淆 类内混淆实现的方式主要包括数据混淆、控制 流混淆、切片混淆和针对特定工具的混淆. 3.1.1 数据混淆 数据混淆的原理是通过对常量、变量和数据结 构这些程序的基本组成元素进行修改的方式,增大 攻击者进行逆向工程的难度.数据混淆包括: 变量存 储和编码混淆、变量聚合混淆、顺序调整混淆、词法 混淆以及移除注释和调试信息混淆. 3.1.1.1 变量存储和编码混淆 (1)分裂变量混淆 分裂变量混淆[2-3]将较为简单的数据结构或数 据类型(例如int、boolean等)分解为一些变量的组 合,从而达到隐藏原始数据的效果. (2)将静态数据转换为与程序相关的数据 以一个静态的字符串为例,可以通过混淆变换 将其转化为一个函数或一段子程序,从而在程序执 行的过程中通过调用生成相应的字符串[2].这种变 换的实现并不困难,但应用时需要考虑以下这对矛 盾的制约因素: ①在常见的应用程序中都包含大量 的静态数据,如果对所有的静态数据都进行混淆,将 导致代码的执行代价和传输代价显著增加;②如果 仅对关键的静态数据进行混淆,则为反混淆者寻找 关键数据提供明显的提示.所以,如何把握①和②的 平衡点,是需要在实践中解决的问题. (3)改变编码方式混淆和改变变量生命周期混淆 改变编码方式混淆隐藏了真实的数据,使原程 序更加复杂[2].下面是一个改变编码方式混淆的例 子: 用表达式i′=c×i+u替代整型变量i,其中c和 u为整型常数. 原程序: inti=1;while(i<1000){…A[i]… i++}. 混淆后的程序: inti=11;while(i<8003){… A[(i-3)/8]…i=i+8}. 此外,也可以把局部变量改变为全局变量,从而 改变变量的生命周期. (4)变量替换 改变变量的类型,例如将一个整型变量更改为 一个整型对象.这种操作的目的在于,利用Java的 自动回收垃圾机制及时清理一些变量的使用信息, 从而增大逆向工程的难度[2]. 3.1.1.2 变量聚合混淆 (1)合并变量混淆 合并变量混淆算法的原理是: 在保持程序语义 等价的前提下,将两个或两个以上的数值变量V1, V2,…,Vn合并为一个变量Vm[2].例如,两个32位 整型变量可被合并为一个64位整型变量.为了增加 混淆变换的弹性,可以考虑在程序中添加不影响原 始变量值的伪操作. (2)数组重构混淆 数组重构混淆有多种实现方式,主要包括数组 分裂变换(将一个数组拆分为两个子数组)、数组合 并变换、数组折叠变换和数组辗平变换[2].为了获得 更好的效果,可以将某些上述变换组合使用. 3.1.1.3 顺序
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 程序 混淆 技术 综述 王建民
![提示](https://static.bingdoc.com/images/bang_tan.gif)