首页 / 专利库 / 人工智能 / 语言代码 / 基于Dex2C与LLVM的Android应用程序保护方法

基于Dex2C与LLVM的Android应用程序保护方法

阅读:809发布:2020-05-18

专利汇可以提供基于Dex2C与LLVM的Android应用程序保护方法专利检索,专利查询,专利分析的服务。并且本 发明 公开了基于Dex2C与LLVM的Android应用程序保护方法,包括:解压APK获取并解析Dex文件,得到每条汇编指令的能够恢复C代码的所有必要信息,根据评估模型选择是否进行Dex2C的转换,若超过 阈值 ,则进行Dex2C的转换:转换预处理操作,包括查找待保护方法、插入汇编指令语句、建立指令的邻接关系等,逐个根据汇编指令类型选择三套转换逻辑中的一种进行转换;基于LLVM实现编译时虚拟化,若未超过阈值,则直接执行LLVM编译虚拟化模 块 ;通过该 框架 生成So文件后,进行重打包、签名,生成功能等效的APK。本发明结合了Dex层和本 地层 的防护方法,一方面能够提高APK的执行效率,另一方面极大的提升了恶意攻击者攻击的难度和成本。,下面是基于Dex2C与LLVM的Android应用程序保护方法专利的具体信息内容。

1.基于Dex2C与LLVM的Android应用程序保护方法,其特征在于,包括以下步骤:
从待保护的应用程序安装包中获取Dex文件,按照文件格式对其进行逐层解析,得到Dex文件中的每一条汇编指令的能够恢复到C语言代码的所有必要信息并存储于数据结构中;确定待保护的方法并将其修改为本地层类型方法然后重写Dex文件,进行转换前预处理工作;建立评估模型,将待保护方法运行时的核心调用时间占比作为评估模型的决策依据,通过设置阈值,来判定是否将待保护的方法进行Dex2C转换,从而尽可能避免频繁的反射调用和冗余的循环操作;
如待保护的方法进行Dex2C转换,则将存储在所述数据结构中对应于待保护方法的必要信息转换为C语言代码,转换过程中针对于不同的汇编指令建立不同的转换逻辑,并恢复汇编指令前驱、后继之间的连接关系,同时保证汇编指令类型正确恢复、数据传递的一致性;将转换后的C代码作为待保护对象;
如待保护的方法不进行Dex2C转换,则将待保护的应用程序安装包中的So文件里的入口函数作为待保护对象;
将待保护对象进行编译的同时虚拟化,生成虚拟化后的二进制So文件,进行重打包、签名,生成保护后的应用程序。
2.如权利要求1所述的基于Dex2C与LLVM的Android应用程序保护方法,其特征在于,所述的汇编指令的能够恢复到C语言代码的所有必要信息,包括类中方法、字段的描述以及每条指令的细节信息;
所述的数据结构用于存放每条汇编指令所涉及的寄存器的个数及内容、所属类的信息、参数信息等。
3.如权利要求1所述的基于Dex2C与LLVM的Android应用程序保护方法,其特征在于,所述的转换前预处理工作,包括在待保护方法的所在类的执行类构造器中插入汇编指令语句以建立指令间的前驱后继关系。
4.如权利要求1所述的基于Dex2C与LLVM的Android应用程序保护方法,其特征在于,所述的评估模型为:
计算待保护方法的函数调用链中函数自身的调用时间除以待保护方法的自身调用、子函数调用以及相关系统API的调用时间总和,将计算值与设置的阈值比较,如果超过阈值,则将待保护方法加入转换白名单进行Dex2C转换操作,否则将待保护方法加入转换黑名单不进行Dex2C转换操作。
5.如权利要求1所述的基于Dex2C与LLVM的Android应用程序保护方法,其特征在于,所述的针对于不同的汇编指令建立不同的转换逻辑,包括:
根据不同的汇编指令建立三种转换逻辑:
第一种常规类型指令,包括数据操作指令、返回指令、数据定义指令以及数据运算指令,这类指令根据汇编指令的语义信息直接翻译;
第二种引用类型指令,包括实例操作指令、方法调用指令、字段操作指令,这类指令通过JNI函数反射调用Java层的方法来实现这些指令所表达的语义;
第三种跳转类型指令,包括跳转指令,则根据指令连接关系进行作用域的划分及指令的转换。
6.如权利要求1所述的基于Dex2C与LLVM的Android应用程序保护方法,其特征在于,所述的将待保护对象进行编译的同时虚拟化,包括:
在LLVM编译器框架下,对待保护对象进行词法分析、语法分析,解析构建其AST树,从而生成中间表示IR,中间表示剔除了源代码与平台相关的特性,但保留了其逻辑与语义信息;
虚拟指令解释器将虚拟指令划分为三种类型进行具体操作,分别为算数运算指令、数据转移指令以及控制流转移类型指令;
程序调度器用于模拟CPU的执行过程,首先获取虚拟指令,对虚拟指令解码后索引到解释器,将控制权交由解释器解释该指令,之后重夺控制权并循环上述过程,直至解释所有指令;
函数体替换器在中间表示的基础上对函数的函数体执行变形,首先将函数体删除并生成函数的签名,函数签名用来定位该函数所执行的虚拟指令地址,并将该函数的参数传递给虚拟指令的解释器以初始化解释器中相关虚拟寄存器。

说明书全文

基于Dex2C与LLVM的Android应用程序保护方法

技术领域

[0001] 本发明属于Android应用程序中Dex文件加密与So文件编译时虚拟化的技术领域,具体涉及到Dex文件到C文件的转换与基于LLVM编译时虚拟的 Android应用程序保护方法。

背景技术

[0002] 近年来,随着移动互联网生态系统的蓬勃发展,移动应用程序的数量呈指数级增长。根据Statista的一项调查,截至2019年3月,Google Play共提供了260 万个Android应用程序。但是,由于逆向工具链的成熟,攻击者使用逆向工具很容易获取到合法应用程序中So(shared object)文件或classes.dex文件的核心逻辑,然后进行篡改,譬如添加恶意代码或替换原始广告等,最终进行重打包签名,通过非法渠道流入市场。这不仅损害了应用开发者的利益,而且也对用户财产与隐私构成了威胁,严重影响了移动应用行业的健康发展。
[0003] 市场上大多数APP是由Java语言和C语言开发的,Java代码在编译过程中生成Dex文件,C代码在编译的过程中生成So文件。目前Dex文件的主要防护方式有以下几种:整体加密、部分类加载加密及Dex文件虚拟化,但相应的, DexExtractor、ZjDroid及PackGrind工具能够对这三种Dex层的保护方案进行有效的攻击。Dex文件目前主流的防护方式还有一个最主要的缺陷是加壳的方法并不会提高Dex文件的执行效率。目前So文件的主要防护方式有:OLLVM与Upx 加壳。但相应的攻击工具或方案有DecLLVM与Upx Shell Tools。由此可见,一方面,目前的保护方案存在着保护能不足和保护后效率降低的问题,另一方面,目前市面上还没有一种能够同时对Dex文件和So文件同时进行防护的系统。

发明内容

[0004] 本发明提出了一种基于Dex2C与LLVM(Low Level Virtual Machine)的 Android应用程序保护方法,能够同时对Dex文件和So文件进行防护,以有效抵御恶意攻击者的静态分析和动态分析。
[0005] 为了实现上述任务,本发明采用以下技术方案:
[0006] 基于Dex2C与LLVM的Android应用程序保护方法,包括以下步骤:
[0007] 从待保护的应用程序安装包中获取Dex文件,按照文件格式对其进行逐层解析,得到Dex文件中的每一条汇编指令的能够恢复到C语言代码的所有必要信息并存储于数据结构中;确定待保护的方法并将其修改为本地层类型方法然后重写Dex文件,进行转换前预处理工作;建立评估模型,将待保护方法运行时的核心调用时间占比作为评估模型的决策依据,通过设置阈值,来判定是否将待保护的方法进行Dex2C转换,从而尽可能避免频繁的反射调用和冗余的循环操作;
[0008] 如待保护的方法进行Dex2C转换,则将存储在所述数据结构中对应于待保护方法的必要信息转换为C语言代码,转换过程中针对于不同的汇编指令建立不同的转换逻辑,并恢复汇编指令前驱、后继之间的连接关系,同时保证汇编指令类型正确恢复、数据传递的一致性;将转换后的C代码作为待保护对象;
[0009] 如待保护的方法不进行Dex2C转换,则将待保护的应用程序安装包中的So 文件里的入口函数作为待保护对象;
[0010] 将待保护对象进行编译的同时虚拟化,生成虚拟化后的二进制So文件,进行重打包、签名,生成保护后的应用程序。
[0011] 进一步地,所述的汇编指令的能够恢复到C语言代码的所有必要信息,包括类中方法、字段的描述以及每条指令的细节信息;
[0012] 所述的数据结构用于存放每条汇编指令所涉及的寄存器的个数及内容、所属类的信息、参数信息等。
[0013] 进一步地,所述的转换前预处理工作,包括在待保护方法的所在类的执行类构造器中插入汇编指令语句以建立指令间的前驱后继关系。
[0014] 进一步地,所述的评估模型为:
[0015] 计算待保护方法的函数调用链中函数自身的调用时间除以待保护方法的自身调用、子函数调用以及相关系统API的调用时间总和,将计算值与设置的阈值比较,如果超过阈值,则将待保护方法加入转换白名单进行Dex2C转换操作,否则将待保护方法加入转换黑名单不进行Dex2C转换操作。
[0016] 进一步地,所述的针对于不同的汇编指令建立不同的转换逻辑,包括:
[0017] 根据不同的汇编指令建立三种转换逻辑:
[0018] 第一种常规类型指令,包括数据操作指令、返回指令、数据定义指令以及数据运算指令,这类指令根据汇编指令的语义信息直接翻译;
[0019] 第二种引用类型指令,包括实例操作指令、方法调用指令、字段操作指令,这类指令通过JNI函数反射调用Java层的方法来实现这些指令所表达的语义;
[0020] 第三种跳转类型指令,包括跳转指令,则根据指令连接关系进行作用域的划分及指令的转换。
[0021] 进一步地,所述的将待保护对象进行编译的同时虚拟化,包括:
[0022] 在LLVM编译器框架下,对待保护对象进行词法分析、语法分析,解析构建其AST树,从而生成中间表示IR,中间表示剔除了源代码与平台相关的特性,但保留了其逻辑与语义信息;
[0023] 虚拟指令解释器将虚拟指令划分为三种类型进行具体操作,分别为算数运算指令、数据转移指令以及控制流转移类型指令;
[0024] 程序调度器用于模拟CPU的执行过程,首先获取虚拟指令,对虚拟指令解码后索引到解释器,将控制权交由解释器解释该指令,之后重夺控制权并循环上述过程,直至解释所有指令;
[0025] 函数体替换器在中间表示的基础上对函数的函数体执行变形,首先将函数体删除并生成函数的签名,函数签名用来定位该函数所执行的虚拟指令地址,并将该函数的参数传递给虚拟指令的解释器以初始化解释器中相关虚拟寄存器。
[0026] 本发明与现有技术相比具有以下技术特点:
[0027] 1.本发明能够有效的防止Dex层被逆向破解,相比于传统的Dex层的加壳加密技术,本发明提出的方法通过将Dex层的代码实现通过自定义的转换器转换为本地层的C代码实现。能够有效保护核心的Java方法。
[0028] 2.本发明能够同时对Dex层的方法和本地层的方法进行防护。对于Dex层的方法,本方案通过Dex2C和基于LLVM的编译时虚拟化方案进行双重加密。对于本地层的方法,本方案通过基于LLVM的编译时虚拟化方案进行防护。恶意攻击者需要同时对两种保护方式进行深入分析和研究,这两种防护方案的组合有效提高了攻击的槛。
[0029] 3.本发明设计的兼容性较好,首先Dex2C将用户提供的待保护方法通过自定义解析器进行解析,将Dex文件转换为C代码,属于代码级转换。后续的基于 LLVM编译时虚拟化属于编译时代码级别的转换,因而不存在现有加固方案中兼容性欠佳的问题。
[0030] 4.本发明设计灵活性强,能够使用评估模型自行调配保护方案,尽可能的提高了APK的执行效率,避免了因冗余循环操作、频繁JNI调用操作所带来的性能开销。
[0031] 5.测试实验表明,经本发明保护后的应用程序相较于保护前的应用程序, APK包的体积平均减少了13.53%,Dex文件的体积平均减少了20.72%,而CPU 的使用率减少了12.51%。这是因为抽取了Dex层的方法实现,转而用本地层的实现取代了Dex层的实现,而且在本地层上的运行要比在DVM虚拟机上的运行开销小;本方法能够有效抵御恶意攻击者的静态分析和动态分析。
附图说明
[0032] 图1是本发明的流程图
[0033] 图2是本发明的系统框架图;
[0034] 图3(a)是Dex文件格式的示意图;图3(b)是Dalvik汇编指令集的所有类型。
[0035] 图4(a)是对invoke-static汇编指令转换前后的示例图;图4(b)是对if...lez汇编指令转换前后的示例图;
[0036] 图5(a)是生成的Smali指令片段;图5(b)是中间代码表示;图5(c)是根据中间表示生成的路径树。
[0037] 图6是转换前Java方法和转换后C方法的对比示例图;
[0038] 图7是LLVM虚拟化模中的虚拟指令及其功能描述;
[0039] 图8是虚拟化前后的代码对比图;
[0040] 图9(a)是F-Droid中五个Android应用程序保护前后APK文件和Dex文件的体积变化示意图;图9(b)是五个Android应用程序保护前后CPU使用率变化示意图。

具体实施方式

[0041] 本发明提出了一种基于Dex2C与LLVM的Android应用程序保护方法,其核心是转换Dex层中的待保护方法为相应的C代码和使用基于LLVM的编译时虚拟化。Dex2C首先解压APK文件并对Dex文件进行解析,得到每条指令的 Node节点信息,随后对函数声明和函数体进行转换。本方案针对256条汇编指令设计了三种转换逻辑,将转换后的C代码经过LLVM编译虚拟化模块,形成特定于CPU架构的虚拟化后的二进制So文件,重打包、签名生成新的APK,本发明保护后的应用程序一方面能够有效减少APK的体积,提高APK的执行效率;另一方面也能够增大恶意攻击者在Dex层和本地层的逆向攻击成本,对于 Dex层的待保护方法实现了双重加密。
[0042] 一种基于Dex2C与LLVM的Android应用程序保护方法,包括以下步骤:
[0043] 步骤1,从待保护的应用程序安装包中获取Dex文件,按照文件格式对其进行逐层解析,得到Dex文件中的每一条汇编指令的能够恢复到C语言代码的所有必要信息并存储于数据结构中。
[0044] 该步骤的基本过程为,解压待保护应用程序的APK安装包文件以获取Dex 文件及AndroidManifest.xml文件,解析xml文件以获取主入口类;将Dex文件按照文件格式进行逐层解析,如图3(a)所示,分别解析Dex文件中的每一个Class、 Class中的每一个Method方法以及Method方法中的每一条汇编指令;最终得到每一个汇编指令的能够恢复到C文件的所有必要信息,具体如下:
[0045] 步骤1.1,解包待保护的安卓应用程序安装包以获取Dex文件及AndroidMani fest.xml文件,解析xml文件,获取所有的Activity入口类。
[0046] 步骤1.2,按照如图3(a)所示的Dex文件格式进行解析,首先解析dex_header 字段信息,获取到dex文件中string_ids、type_ids、method_ids等几个字段的偏移和大小,以便准确定位至各个字段的起始地址和结束地址。
[0047] 步骤1.3,开始进行class_defs和method_ids字段的解析,从class_defs主要获取每个类的类型、父类类型以及相应的静态/实例字段、直接/虚方法的位置偏移、相关注解信息等。随后以类为单位对每个方法进行解析,从method_ids中主要获取该方法的所属类的信息、参数信息以及方法名信息。
[0048] 步骤1.4,以方法为单位对每条汇编指令进行解析,基于BakSmali反编译引擎本方案获取到二进制与Dalvik汇编指令之间的对应关系,最终得到足够恢复C 文件的所有必要信息如图5(a)所示;必要信息包括了类中方法、字段的描述、以及每条指令的细节信息等。本方案将每条指令的细节信息存储于数据结构 InsInfoNode中。
[0049] 具体而言,谷歌的Dalvik虚拟机指令集共有256条汇编指令譬如move、return、 new-instance、goto、if-eq、cmpl-float、invoke-virtual、add-type等,业界根据其指令功能的不同将其划分为14种类型,其类型信息如图3(b)所示,本方案构造了数据结构InsInfoNode,存放了每条汇编指令所涉及的寄存器的个数及内容、所属类的信息、参数信息等等,根据读取指令类型的不同本方案在该数据结构中存放不同的数据信息。假如读取的是const_string之类的数据定义类型的汇编指令,本方案只需要获取其是存入第几号寄存器、操作码以及String类型的值,存储于InsInfoNode数据结构中。而对于invoke_direct之类的方法调用类型的汇编指令,本方案不仅需要获取被调用方法的所属类信息、参数信息以及方法名信息等,而且需要获取寄存器的数目、值及操作码信息等,依次读取,从而得到该 Method中封装的每条指令的细节信息。
[0050] 步骤2,确定待保护的方法并将其修改为本地层类型方法然后重写Dex文件,进行转换前预处理工作。
[0051] 该步骤中,主要是通过用户指定的待保护方法所在的类和方法名确定唯一的待保护方法,构造调用本地层解释器代码的汇编指令语句,将找到的待保护方法修改为本地层类型方法,重写Dex文件,具体如下:
[0052] 步骤2.1,用户指定待保护方法所在的类和方法名,根据这两种信息在 class_defs字段和method_ids字段遍历,从而确定唯一的待保护方法。
[0053] 在本实施例中,本方案是对开源商店F-Droid中的闹钟应用程序talalarmo.apk 进行保护,本方案的保护方法的方法名为onStartCommand,类名为 trikita.talalarmo.alarm.AlarmService。该方法的功能是播放铃声并跳到一个新的 Activity界面。
[0054] 步骤2.2,转换前的预处理
[0055] 在由步骤2.1获取到的待保护方法的所在类的执行类构造器Clinit中插入汇编指令语句以建立指令间的前驱后继关系;本实施例在这里为:
[0056] “const-string v1
[0057] “libDex2C”;
[0058] invoke-static{v1},Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V;”。
[0059] 此时将步骤2.1获取到的待保护方法的属性改为native类型(本地层类型),随后对Dex文件进行重写。这几个操作的目的是便于在类的初始化阶段加载后续编译生成的So文件。
[0060] 步骤3,建立评估模型,将待保护方法运行时的核心调用时间占比作为评估模型的决策依据,通过设置阈值,来判定是否将待保护的方法进行Dex2C转换,从而尽可能避免频繁的反射调用和冗余的循环操作。
[0061] 建立评估模型,JNI是Java层与Native层通信的桥梁,JNI函数通过本地层调用时所提供的参数实现反射调用,这种方式要比直接的Dalvik虚拟机耗时多。考虑到待转换方法在转换后可能会包含过多的JNI调用,势必会引起性能的下降,本系统引入评估模型,尽可能的提高APK的执行效率。
[0062] 本方案中,将待保护方法运行时的核心调用时间(函数调用链中所有函数自身的调用时间)占比作为评估模型的决策依据。
[0063] 首先使用谷歌的CPU Profiler收集保护前Android应用程序的每个函数的调用时间,递归查找当前待保护方法的所有被调用的子函数,直至达到所有子函数底部,即子函数中无其他函数调用情况,根据以上形成的调用关系生成待保护方法的函数调用链。在这里本方案的当前待保护函数(方法)为:onStartCommand。
[0064] 所述的评估模型为:
[0065] 计算待保护方法的函数调用链中函数自身的调用时间除以待保护方法的自身调用、子函数调用以及相关系统API的调用时间总和,将计算值与设置的阈值比较,如果超过阈值,则将待保护方法加入转换白名单进行Dex2C转换操作,执行步骤4;否则将待保护方法加入转换黑名单不进行Dex2C转换操作,执行步骤5。本实施例中,将阈值设置为60%,在这里用户可以根据保护强度和执行效率自行调配,若追求执行效率,可以将阈值调高;若追求保护强度,则将阈值调低。
[0066] 举例来说,假设待保护方法为方法A,方法A的自身调用时间占方法A执行总时间的40%,方法A中调用了方法B,方法B的执行时间占方法A执行总时间的35%,方法A中相关系统API的时间占比为25%;方法B的自身调用时间占方法B执行总时间的50%,方法B调用了方法C,方法C的执行时间占方法B总执行时间的35%,方法B中相关系统API的时间占比为
15%;方法C没有其他函数的调用,其自身调用时间占方法A执行总时间的80%,方法C中相关系统API的时间占比为20%。因此其核心调用时间为 40%+35%*(50%+35%*80%)=
67.3%,超过阈值60%,因此将该待保护方法写入转换白名单,转而执行步骤4。
[0067] 步骤4,Dex2C转换操作
[0068] 如待保护的方法进行Dex2C转换,则将存储在所述数据结构中对应于待保护方法的必要信息转换为C语言代码,转换过程中针对于不同的汇编指令建立不同的转换逻辑,并恢复汇编指令前驱、后继之间的连接关系,同时保证汇编指令类型正确恢复、数据传递的一致性;将转换后的C代码作为待保护对象;具体如下:
[0069] 步骤4.1,将步骤1.4中建立的InsInfoNode数据结构中,对应于待保护方法的汇编指令能够恢复到C语言代码的所有必要信息转换为C语言代码,开始对函数声明和函数体进行转换。
[0070] 本实施例中,待保护函数为onStartCommand。针对不同汇编指令类型本方案选择不同的转换逻辑,在这里本方案建立三套转换逻辑,利用模拟寄存器方法和部分变量递增部分变量随机化方法保证了Dex2C类型的正确恢复和数据传递的一致性。
[0071] 步骤4.1.1,函数声明的转换
[0072] 利用静态注册方法解决了函数重载问题,将方法名和参数进行整合转换。
[0073] 本实施例中,保护前的方法为:
[0074] public int onStartCommand(Intent intent,int flags,int startId).[0075] 转换后的静态注册方法为:
[0076] JNIEXPORT  jint Java_trikita_talalarmo_alarm_AlarmService_onStartCommand (JNIEnv*env,jobject a0,jstring a1,jint a2,jint a3).[0077] 其中env为对于Android虚拟机环境的一个引用,a0为局部变量寄存器,其余为参数寄存器,这有效的解决了函数重载问题。
[0078] 步骤4.1.2,函数体的转换
[0079] 针对不同指令类型建立不同的转换逻辑,本方案在这里主要分为三种转换逻辑:
[0080] 第一种,常规类型指令
[0081] 包括数据操作指令、返回指令、数据定义指令以及数据运算指令等,这一类相对比较简单,根据汇编指令的语义信息直接翻译。以const-string vx,string_id 数据定义指令为例,其语义信息为通过字符串索引构造一个字符串并赋给寄存器 vx,因此本方案直接在string_ids里根据偏移地址获取相应的字符串信息。并将该获取到的字符串中赋给相应的变量。转换后的C代码为:char*dqP= "java/lang/Math";再比如cmp-long v1,v1,v2比较指令等。其语义信息为:比较两个长整数型数,若v1寄存器的值大于v2寄存器的值,则结果为1,相等为0,小于为-1。因此转换后的C代码为:jint a16=(a15>a14?1:(a15
[0082] 第二种,引用类型指令
[0083] 包括实例操作指令、方法调用指令、字段操作指令等,这一类汇编指令主要通过JNI反射调用Java层的方法来实现这些指令所表达的语义。以invoke-static {parameters},methodcall方法调用指令为例。其语义信息为调用实例的静态方法。本方案首先从string_ids中获取到调用方法的方法名、类名和参数类型的字符串信息,通过FindClass方法获取到相应的jclass对象,解析参数列表信息以获取到相应的寄存器内容,通过GetStaticMethodID将刚才得到的字符串信息及jclass 对象构建为jmethodId对象,最后调CallStaticLongMethodA方法实现反射获取,最后将结果返回至相应的寄存器中。转换前后代码示例如图4(a)所示。
[0084] 第三种,跳转类型指令
[0085] 包括跳转指令,则根据指令连接关系进行作用域的划分及指令的转换;这一类指令主要根据步骤4.2所建立的指令连接关系进行转换的。以if-lez vx,target 跳转指令为例进行说明,此语义信息为假如vx寄存器的值小于等于零则跳转到 target处。因此根据if-lez汇编指令与下一条指令、LabelInsNode指令之间的连接关系,转换后的C代码为if(a17<=0)goto L78b66d36;{...}L78b66d36;如图4(b) 所示。
[0086] 步骤4.2,恢复汇编指令前驱、后继之间的连接关系
[0087] 正常顺序遍历无法解决变量作用域问题,本方案在这里采用深度优先遍历对步骤1.4的每条路径上的节点序列进行翻译操作,直到所有节点上的指令转化完成如步骤4.2所示。翻译过程中根据节点之间的父子关系不断传递当前作用域下可访问变量以及变量与寄存器之间的关联关系,这使得上级作用域中的变量可以被低级别的作用域访问,各个节点依据可用变量完成当前节点的翻译,生成一种C代码的中间表示形式如图5(b)所示。根据中间表示建立路径树如图5(c)所示。从而达到划分作用域、有效建立起前驱与后继之间的连接关系。
[0088] 步骤4.3,保证汇编指令类型正确恢复、数据传递的一致性
[0089] 本方案利用模拟寄存器方法和部分变量递增部分变量随机化的方法保证了 Dex2C类型的正确恢复和数据传递的一致性。
[0090] 本方案共建立了十五个模拟的寄存器,利用寄存器的关联完成指令之间的数据传递。譬如对于汇编语句:
[0091] iget-wide v2,p0,Lcom/uberspot/a2048/MainActivity;->mLastBackPress:J;
[0092] sub-long v2,v0,v2,
[0093] 本方案将第一句汇编语句得到的反射结果存放至v2寄存器中,第二句则是首先使用v0寄存器的值减去v2寄存器的值。最终再将寄存器的结果存放至v2 寄存器当中。因此使用寄存器的关联关系完成了数据传递的一致性。
[0094] 那么寄存器值转换C代码后的变量是如何命名的呢?本方案这里使用部分变量名递增和部分变量名随机化的方法进行完成。针对方法调用指令和字段操作指令等,由于转换后的部分变量不涉及到对于寄存器的读写,本方案采用变量随机化的方式进行变量命名,只需要将该条汇编指令处理的最终结果赋给相应的寄存器即可。而针对于其他指令,本方案采用变量名类增的方式进行变量命名,因为寄存器是无类型的,而变量是有类型的。因此可能会出现某个寄存器在上一个存入的变量为int类型,下一个存入的变量为double类型的情况。本方案采用了变量累加的操作。在存储过程中,倘若变量的类型未发生变化,则无需变量名累加。倘若发生变化,则变量累加操作。与此同时,建立起寄存器与最近变量名之间的关联关系。在读取过程中,本方案只需要通过寄存器获取到最近的变量名即可。
[0095] 在本实施例中,假如第一次写操作为v0寄存器中写入int类型的值,第二次写操作为v0寄存器中写入double类型的值。第三次操作为读取v0寄存器的值。此时,本方案需要将第一个变量命名为a1,其类型为jint,第二个变量命名为a2,其类型为jdouble,第三次操作则是读取的是a2的值。
[0096] 这样就完成了对于Dex文件中自定义保护方法的转换。转换后的C代码本方案写入Dex2C.cpp文件中。完整的转换前后方法的代码如图6所示。
[0097] 步骤4.4,代码转换完成后,将将转换后的C代码作为待保护对象。
[0098] 而如待保护的方法不进行Dex2C转换,则将待保护的应用程序安装包中的 So文件里的入口函数作为待保护对象。
[0099] 步骤5,将待保护对象进行编译的同时虚拟化,生成虚拟化后的二进制So 文件。
[0100] 如果待保护方法经过评估模型评估后的结果超过阈值,则将步骤4转换后的 C代码作为待保护对象,进入LLVM虚拟化模块进行编译虚拟化操作;否则直接将安卓应用程序中原始的So文件中的入口函数(JNI_Onload方法)作为输入,进入LLVM虚拟化模块进行编译虚拟化操作,生成虚拟化后的二进制文件。
[0101] JNI_OnLoad函数是So文件的入口函数,该过程主要分为三个阶段,第一阶段代码进入Clang前端,通过词法分析、语法分析、解析AST语法树操作,最后调用LLVM内建api生成后缀为“.ll”的中间表示。第二阶段构建虚拟组件,主要构建虚拟指令、虚拟指令解释器、程序调度器、函数体替换器等。第三阶段整合编译链,生成具体混淆功能的Clang编译器,具体如下:
[0102] 步骤5.1,将待保护对象经过Clang的词法分析、语法分析、AST树构建等过程生成与平台无关的中间表示IR。LLVM虚拟化模块将原始程序代码拆分并转化为自定义指令体系结构,旨在将程序复杂化。
[0103] 虚拟化代码的运行过程是动态解释执行自定义指令的过程,而非将其换源为原始C代码并执行。LLVM虚拟化模块首先构建类似于JVM架构的虚拟指令。虚拟指令生成过程首先根据IR中所用到的临时变量的数量和大小,确定需要的虚拟寄存器空间大小,使虚拟指令完成动态内存的分配动作。虚拟指令在栈上模拟完成原始程序的逻辑流程,其中虚拟寄存器用来辅助中间过程的存储。最后销毁运行完的虚拟指令。图7为部分虚拟指令名称及描述。
[0104] 步骤5.2,虚拟指令仅仅是IR的自定义表示,其无法直接交还给目标执行,虚拟指令解释器用于解释自定义的虚拟指令,解释器将虚拟指令划分为三种类型进行具体操作,分别为算数运算指令、数据转移指令以及控制流转移类型指令。例如store指令定义为数据转移指令,解释器对应的解释为:
[0105] value v1=vmdata[vpc++];
[0106] value v2=stack[stack_index--];
[0107] if(!Reg[v1])
[0108] alloc(Reg[v1]);
[0109] Reg[v1]=cast_i64(v2)
[0110] 此过程首先修改虚拟程序计数器来获取存储空间的索引,之后从栈顶获取被存储的对象,并将该对象放入对应的存储空间中。
[0111] 步骤5.3,程序调度器用于模拟CPU的执行过程,调度器首先获取虚拟指令,对虚拟指令解码后索引到解释器,将控制权交由解释器解释该指令,之后重夺控制权并循环上述过程,直至解释所有指令。
[0112] 步骤5.4,函数体替换器在中间表示的基础上对函数的函数体执行变形,目的在于将原程序执行过程替换为虚拟解释过程。首先将函数体删除并生成函数的签名,函数签名用来定位该函数所执行的虚拟指令地址,并将该函数的参数传递给虚拟指令的解释器以初始化解释器中相关虚拟寄存器。图8为函数体变化前后的源代码等价表示以及最终的二进制结果。
[0113] 步骤5.5,要想实现编译虚拟还需要对编译链进行整合。而NDK已将clang 整合,其中以上步骤均以LLVM分析pass实现,将组织好的分析pass交由 passManager统一管理,最后重新编译更改后的源码生成具有混淆功能的编译器。
[0114] 为了激活编译链,需要在提前编译生成的Application.mk中通过 NDK_TOOLCHAIN_VERSION参数来指定虚拟编译器,在Android.mk中使用 -mllvm-vm来指定虚拟化所需要参数。Clang接收虚拟化参数并调用 PassManagerBuilder以判定是否进行编译虚拟化功能。可以看出以上虚拟化步骤均是基于IR的,因此虚拟化过程天然兼容各个平台,进行虚拟化的代码将调用 LLVM后端生成与平台相关的二进制可执行文件(So文件)。
[0115] 步骤6,将编译后的生成的So文件和步骤2.2重写后的Dex文件,其他资源文件进行重打包、签名,最终生成与保护前APK功能等效的保护后的安卓应用程序。
[0116] 实验部分:
[0117] 发明人做了如下的性能测试和攻击实验:
[0118] 性能测试的平台为:测试机为Google Nexus 5,Android版本为Android4.4.2, 测试APK为开源商店F-Droid中下载量较高的五个应用程序:BMI汇算器BMICalculate.APK、流行的游戏2048.APK、二维码扫描工具QRScanner.APK、闹钟程序talalarmo.apk以及记事本程序JustNote.APK。
[0119] 保护前后APK体积和Dex体积的体积变化如图9(a)所示,CPU使用率变化如图9(b)所示;其中CPU使用率是五十次测算的平均值。由图可知,APK包的体积平均减少了13.53%,Dex文件的体积平均减少了20.72%。而CPU的使用率减少了12.51%。CPU的减少因为在本底层的执行要比在DVM虚拟机上执行快的多,C代码是直接被编译成机器码进行执行的。APK包和Dex文件减少是因为我们抽取了Dex层的实现,在本地层进行了复现,但So文件本身体积较小,故而APK体积在整体上是减小的。
[0120] 攻击试验的工具分别是:交互式反汇编器IDA Pro、Android反编译工具 AndroidKiller、Dex层的脱壳工具PackGrind、OLLVM对抗的脚本DecLLVM。攻击对象为保护后的Android应用程序。
[0121] 攻击试验表明,以上常见的逆向工具对我们保护后的应用程序是无效的,因为我们的工具并不是简单的加壳、加密。本地层和Dex层的双重加密有效的防止了恶意攻击者的静态和动态攻击。
高效检索全球专利

专利汇是专利免费检索,专利查询,专利分析-国家发明专利查询检索分析平台,是提供专利分析,专利查询,专利检索等数据服务功能的知识产权数据服务商。

我们的产品包含105个国家的1.26亿组数据,免费查、免费专利分析。

申请试用

分析报告

专利汇分析报告产品可以对行业情报数据进行梳理分析,涉及维度包括行业专利基本状况分析、地域分析、技术分析、发明人分析、申请人分析、专利权人分析、失效分析、核心专利分析、法律分析、研发重点分析、企业专利处境分析、技术处境分析、专利寿命分析、企业定位分析、引证分析等超过60个分析角度,系统通过AI智能系统对图表进行解读,只需1分钟,一键生成行业专利分析报告。

申请试用

QQ群二维码
意见反馈