基于smali流程混淆技术的Android应用加固方法与流程

文档序号:11156089阅读:523来源:国知局
基于smali流程混淆技术的Android应用加固方法与制造工艺

本发明涉及Android应用加固方法,尤其涉及一种基于smali流程混淆技术的Android应用加固方法。



背景技术:

在互联网时代,随着移动智能终端的日益普及以及网络宽带化的高速发展,我们的生活几乎处处充满了互联网的气息。然而,兼具开发性与灵活性的终端系统,及其应用中潜在的信息安全问题却可能对其用户、承载网络等各个环节造成影响,特别是软件逆向技术。逆向分析技术可以被用来在不知道应用程序源代码的情况下分析应用程序的功能流程、篡改应用程序的数据代码等,逆向分析技术如果被不加限制的恶意使用,利用者可以分析获取应用程序的核心技术,也可以篡改应用程序的签名和作者信息,还可以将恶意代码注入到已有的应用程序中并通过二次打包进行伪装,这些行为都极大的危害了应用程序开发者的利益,严重损害了广大用户的隐私安全,甚至危及国家安全与社会稳定,阻碍其健康发展。

在Android系统中,使用Java语言编写的应用程序,被编译成Java类文件(.class格式);Android不会直接运行这些.class文件,而是将这些.class格式的类文件再次编译成dex格式,交由Android平台运行。所以dex文件是应用程序的核心部分,为安装包中不可或缺的部分。但是dex文件本质上是Java字节码,容易被反编译为Java源代码,如果没有经过混淆处理,通过逆向工程可以轻易获取到可读的Java源码,进而达到窃取安卓应用程序的核心逻辑代码。

代码混淆技术是近几年才发展并兴起的一种新技术。第一次对代码混淆进行系统研究是在上世纪90年代末开始的,是Java语言的迅速发展引起了对混淆技术的研究热潮。这是因为Java目标代码—字节代码(bytecode)很容易被反编译为Java源代码,这就迫切要求有效的保护字节代码的方法。这一领域中的大部分工作是由Collberg、Thomborson、Low和Chenxi Wang做出的。

Collberg第一次对混淆转换进行了详细的总结和分类,也首次提出了混淆转换的有效性与性能的若干评价标准。他将混淆转换分为四类:外形混淆转换、控制混淆转换、数据混淆转换以及预防性混淆转换。

Chenxi Wang在文献中实现了在C语言源代码上的若干种控制混淆转换与数据混淆转换。Wang还给出了混淆转换造成的性能过载以及混淆转换对静态分析工具IBM NPIC tool以及Rutger PAF toolkit的有效性。

Hohhl提出用带有时间限制的黑盒(time-limited black box)方法来保护移动Agent。这里的黑盒就是指混淆转换过的Agent程序。对移动Agent进行逆向工程,做出有意义的发现或修改需要一定的时间,据此,限制移动Agent在目的主机上运行的时间。在派发移动Agent的之前,需要对其进行混淆转换,这增加了逆向工程的代价,从而延长在目的主机上的运行时间。

Cullen Linn从另一个角度研究了目标代码混淆技术,通过对反汇编过程的分析,采用一种混淆方法能够阻挠逆向工程,使得获取程序的汇编指令非常困难或不能正确得到程序的汇编指令。

综上所述,现有工作虽然提出了一些加扰混淆方法,但只是增加了逆向阅读的难度而已,而不能够真正地解决防止逆向的问题。



技术实现要素:

为了克服上述现有技术的不足,本发明提供一种基于smali流程混淆技术的Android应用加固方法,应用加密算法、动态加载技术、JNI(Java Native Interface,Java本地接口)编程技术、完整性校验技术和代码混淆技术等,通过改变Android应用程序的加载流程,使得加固后的应用程序难以逆向,另外,需保证加固之后并不影响程序的正常执行;以达到保护版权,防止他人剽窃软件中的智力成果或对软件进行有目的的篡改的目的。

本发明提供的技术方案是:

一种Android应用加固方法,基于smali流程混淆技术,应用加密、动态加载、JNI编程、完整性校验和代码混淆方法,通过改变Android应用源程序的加载流程,使得加固后的应用程序难以逆向,同时保证加固之后并不影响程序的正常执行;包括如下步骤:

1)在动态加载Android应用真正的dex文件之前,用AES加密算法加密真正的dex文件,得到加密后的dex文件class.dex.jar;

2)编写so函数库,执行如下操作;

21)使用JNI编写解密库decrytApp.so,将解密函数写在动态链接库中,用于解密所述加密后的dex文件;

22)使用JNI编写底层核心逻辑库Function.so,将程序的核心逻辑写在动态链接库中;

3)编写伪smali文件,用于程序的完整性校验和对加密后的dex文件class.dex.jar动态加载,由此达到混淆源程序的加载流程的目的,执行如下操作:

31)进行源程序的完整性检验;

32)动态加载加密后的真正的dex文件class.dex.jar;

由此完成对Android应用的加固,达到保护应用程序的目的。

针对上述Android应用加固方法,进一步地,步骤1)所述AES加密算法通过循环进行加密,包括多轮AES加密循环进行迭代运算;除最后一轮外,每轮AES加密循环都包含以下步骤:

11)轮密钥加,明文区块中的每一个字节与该次回合密钥做异或运算;

12)字节代替,S盒变换,用查找表的方式对矩阵中的字节进行置换;

13)行移位,将矩阵中的行进行循环移位;

14)列混淆,对矩阵中的列进行混合变换。

在本发明一实施例中,所述AES加密算法的密钥长度设定为128位,包括10轮迭代运算。

针对上述Android应用加固方法,进一步地,步骤2)具体使用Android NDK编写decrytApp.so和Function.so,包括如下步骤:

a)编写java文件,声明本地文件中的函数;

b)新建decryptApp.c文件,编写解密函数;

c)编写JNI入口函数;

d)编写Android.mk文件;

e)编译生成so库,包括decrytApp.so和Function.so。

针对上述Android应用加固方法,进一步地,步骤21)用C或C++编写解密函数,将解密函数写在动态链接库中。

针对上述Android应用加固方法,进一步地,步骤31)所述进行源程序的完整性检验,具体包括如下步骤:

311)计算Android应用程序的校验和,在所述应用程序中加入哨兵函数;

312)在所述应用程序正式运行之前,先启动哨兵函数,重新计算应用程序的校验和,并与之前的校验和进行对比;

313)当312)中重新计算得到校验和和311)中的校验和相同时,判断得到软件没有被篡改,继续运行;当二者不同时,判断得到软件被篡改,即所述Android应用被二次打包,终止运行。

与现有技术相比,本发明的有益效果是:

本发明提供一种基于smali流程混淆技术的Android应用加固方法,应用加密算法、动态加载技术、JNI编程技术、完整性校验技术和代码混淆技术等,通过改变源程序的加载流程,使得加固后的应用程序难以逆向,另外,需保证加固之后并不影响程序的正常执行;以达到保护版权,防止他人剽窃软件中的智力成果或对软件进行有目的的篡改的目的。因此,利用本发明提供的技术方案,能够有效地对安卓应用程序进行版权保护,防止其被逆向或篡改。

附图说明

图1是本发明提供的Android应用加固方法的流程框图。

图2是本发明实施例中完整性检验的流程框图。

具体实施方式

下面结合附图,通过实施例进一步描述本发明,但不以任何方式限制本发明的范围。

本发明提供一种基于smali流程混淆技术的Android应用加固方法,应用加密算法、动态加载技术、JNI编程技术、完整性校验技术和代码混淆技术等,通过改变源程序的加载流程,使得加固后的应用程序难以逆向,另外,需保证加固之后并不影响程序的正常执行;以达到保护版权,防止他人剽窃软件中的智力成果或对软件进行有目的的篡改的目的。

如图1所示,class.dex文件是真正的dex文件,用于实现程序的主要功能,加密成class.dex.jar文件。Function.so核心库是动态链接库,为程序提供底层核心逻辑功能;decrytApp.so主要用于实现对class.dex.jar的解密。

本发明方法编写伪smali文件,编译生成伪dex文件,主要负责程序的完整性校验和对class.dex.jar动态加载。若程序没有被篡改,则采用动态加载的方式加载解密后的dex文件,否则不能加载dex文件。

本发明的整体流程如图1所示,包括如下步骤:

1)在动态加载真正的dex文件前,用经典的加密算法AES(Advanced Encryption Standard,高级加密标准)算法加密dex文件,得到加密后的dex文件;

AES加密算法通过循环进行加密,包括多轮AES加密循环。除最后一轮外,每轮AES加密循环都包含以下四个步骤:

11)轮密钥加(AddRoundKey),矩阵中的每一个字节与当前轮密钥做异或运算;

12)字节代替(SubBytes),通过S盒变换的方法,用查找表的方式对矩阵中的字节进行置换;

13)行移位(ShiftRow),将矩阵中的行进行循环移位;

14)列混淆(MixColumns),对矩阵中的列进行混合变换。

因此,在实现AES算法时,可以抽象四个函数,分别对应上面四个步骤:AddRoundKey(state),SubBytes(state),ShiftRow(state)和MixColumns(state),其中State为矩阵。

2)编写so函数库,执行如下操作:

Java程序经过编译会生成Java字节码。而Java字节码很容易被逆向工程,虽然可以利用代码混淆技术增加逆向难度,但是,混潜技术并不能彻底阻止逆向工程。因此我们可以使用C/C++编写程序中更重要的代码,利用其难被逆向的特点来达到保护程序源码的目的。除此之外,使用C/C++编写的代码不需要Dalvik虚拟机的解释运行,具有更高的运行效率,可以加快程序执行复杂任务时的运行速度。因此,本发明采用C/C++编写程序的底层链接库,并且利用JNI技术在上层代码中调用这些底层动态链接库。

21.使用JNI编写解密库decrytApp.so,用于解密加密过后的dex文件;

本发明中对dass.dex.jar进行解密的函数用C/C++编写,最终编译成decrytApp.so。之所以将解密函数写在动态链接库中,是因为如果直接将解密逻辑写在Java文件中,攻击者可以通过反编译得到解密代码,从而获得解密的方法。

22.使用JNI编写底层核心逻辑库Function.so;

Function.so库中是应用程序的底层核心代码,是整个应用程序的关键所在。将程序的核心逻辑写在动态链接库中,可以保护诸如重要算法、核心技术等不被逆向工程所窃取。因此,在本发明加固方案中,将核心的逻辑写在Function.so中,防御反编译。

3)编写伪smali文件,伪smali主要负责程序的完整性校验和对class.dex.jar动态加载,达到混淆源程序的加载流程的目的,执行如下操作:

31.进行源程序的完整性检验,具体步骤如图2;

完整性校验是一种防止软件被篡改的技术。主要机制就是事先计算应用程序的校验和,然后在程序中加入哨兵函数,在程序正式运行之前,先启动哨兵函数,重新计算应用程序的校验和,并与之前的校验和进行对比。如果两者一样,就判断软件没有被篡改,继续运行;如果不一样,就认为软件被篡改,随即终止运行。

完整性校验可以很好地检测Android应用是否被二次打包。任何apk文件,哪怕只是受到一点篡改,计算出来的校验和都会不一样。因此利用完整性校验,可以有效防止应用被插入恶意代码、植入广告等攻击手段。

32.动态加载class.dex.jar;

程序的主体代码都在真正的dex文件中,而这个dex文件又不是程序启动的时候需要运行的文件,所以可以对它进行加密,生成class.dex.jar,然后再动态地加载class.dex.jar。经过加密的文件具有很高的安全性,如果攻击者没有密钥的话,很难破解文件。这样,攻击者无法直接逆向分析经过加密的代码,从而达到保护应用程序的目的。

下面通过实例对本发明做进一步说明。

实施例:

AES算法:

AES算法的密钥长度很灵活,可以是128位、192位和256位中的任意一种。不同的密钥长度对应不同的加密算法的运算轮数,通常128位密钥需运算10轮,192位需运算12轮,256位需运算14轮。一般轮数越多,被破解的难度也就越大,但是运算的时间也越长。考虑到本发明是基于Android平台,其内存和处理能力都十分有限,因此密钥长度不宜选得过长。而在AES算法确立之初,6轮迭代运算便可抵抗当时世界上已知的所有攻击。因此在本发明中,AES算法的密钥长度设定为128位。

AES算法一共需要经过10轮迭代运算,而每轮运算都是由四个步骤组成,因此可以按如下方式把加/解密的过程用C语言代码实现,代码如下。

(1)加密

(2)解密

Android系统支持NDK(Native Development Kit,安卓原生开发工具包)编程,本发明中使用JNI编程技术将解密函数写在动态链接库decryptApp.so中,在程序运行的时候,对dex文件进行动态解密。这样既能提高解密的效率,又能利用C语言的特性,保证解密过程的安全。

so函数库:

我们使用Android NDK开发decrytApp.so和Function.so的大概过程如下所示:

1.编写java文件,声明本地文件中的函数(以decrytApp.so库为例)

System.loadLibrary(“decryApp”);//加载动态链接库decrytApp.so

native boolean jnicheckApp();//声明与decrytApp.c中相对应的原生函数

2.新建decryptApp.c文件,编写解密函数

在文件中声明如下函数,函数的作用是解密class.dex.jar,根据Android系统中JNI调用的规则,函数名需要与Java文件中的函数名相对应:JNIEXPORT jboolean NICALL Java_com_bupt_testjni_decrytAppJnicheckApp(JNIENV*env,jobject this){};

3.编写JNI入口函数

JNI—OnLoad()与JNI_OnUnLoad()函数是动态链接库的入口函数,当Dalvik虚拟机执行到System.loadLibrary()函数时,会首先执行C文件中的入口函数,它会告诉Dalvik虚拟机使用哪一个JNI版本,如果动态链接库中没有入口函数,虚拟机会默认使用最老的JNI1.1版本。而且我们也需要在入口函数中进行一些一些初始化的操作。JNI—OnLoad函数的编写格式如下:

4.编写Android.mk文件

Android.mk文件向NDK编译系统描述工程的本地源,主要是传递如下几个参数:

LOCAL_PATH:=$(call my-dir)/project//工程路径

LOCAL_SRC_FILES:=decrytApp.c//本地C文件的名称

LOCAL_MODULE:=decrytApp//要编译的动态链接库的名称

5.编译生成so库

运行工程目录下的ndk编译脚本,编译本地C/C++代码。编译脚本"ndk-build"文件位于NDK文件的根目录下,运行的时候需指定路径。运行完成后,就会在工程的libs目录下生成decrytApp.so库。

6.编译打包应用

最后像编译普通应用程序一样,用SDK中的工具编译工程。SDK编译工具将会把so库打包进应用程序的apk文件中,然后就可以正常运行应用程序了。

采用NDK编程的方式,可以把重要代码保存在C/C++动态链接库中,然后在Java代码中采用JNI技术调用底层的so库。这样既可以实现应用的功能,又能杜绝使用Java语言编写的代码容易被逆向的隐患,是一种变向的软件保护措施。除此之外,把重要代码写到so库中,还能增加代码的重用性,下次在其他应用中,可以直接引入现成的so库,免去了二次幵发的麻烦。

完整性校验:

Android系统中提供的应用签名机制,本质上就是一种完整性校验。而且,Android系统规定,所有安装到Android设备中的应用都必须经过签名。因此,我们可以利用应用签名机制,来实现完整性校验的功能。

数字签名需要一份数字证书,但这个数字证书并不需要权威的数字证书签名机构认证,它只是用来让应用程序包自我认证的,因此可以使用Android SDK自带的签名工具keytool和signapk为应用程序签名。Android将数字证书用来标识应用程序的作者和在应用程序之间建立信任关系。数字证书的私钢保存在程序幵发者的手中,公钥随应用一起打包进apk中。

鉴于数字证书是开发者自己生成的,因此,软件发行者的身份只能由自己持有,是唯一的。其他人如果没有开发者的密钥,无法制造出与开发者一样的数字证书。因此,开发者可以在程序中加入检测发行者身份的方法,在程序真正运行之前,验证程序的发行者,如果符合,则继续运行;如果不符合,程序退出运行。

利用对比发行者身份的完整性校验技术,判断一个应用是否被篡改的步骤如图2所示:

1)获取应用程序的签名

Android提供了获取应用程序签名信息的方法,通过调用PackageManager的getPackageInfo(packageName,PackageManager.GET_SIGNATURES)方法,就可以得到应用的签名信息。核心代码如下:

Packagelnfo pinfo=this.getPackageManager().getPackageInfo(packageName,PackageManager.GET_SIGNATURES);

Signature[]s=pInfo.signatures;

Signature sign=signs[0];

sign中存储的就是应用程序的签名信息。

2)获得应用的签名证书的发行者的信息;

应用的作者在发布应用时,都会为应用签名。签名所用的证书,是作者在本地利用keytool生成,因此证书的发行者是作者本人。如果程序被二次打包,经过重新编译,那么k次签名所用的证书就是攻击者的证书,证书的发行者就变成了攻击者。因此我们可以通过比较证书的发行者,来判断应用是否被二次打包。

所以我们需要得到证书的发行者,核心代码如下:

CertificateFactory certFactory=CertificateFactory.getInstance("X.509");

X509Certificate cert=(X509Certificate)certFactory.generateCertificate(new

ByteArrayInputStreain(sign.toByteArray());

String Issuer=cert.getIssuerDN().toString();

Issuer就是数字证书的发行者。

3)获得应用的原始证书的发行者的信息,对比两个发行者的信息,判断应用是否被二次打包;

我们可以将原始证书的发行者信息以字符串的形式保存在文件中。但是这样存在安全隐患,可能会被攻击者“找到”,从而被修改成攻击者自己构造的信息。因此,我们从服务端实时获取原始证书的发行者信息,或者把原始证书的发行者信息加密,将密文存储在本地,当需要对比的时候,先进行解密。

对比两个发行者的信息,根据当前应用携带的证书的发行者与官方获得发行者的比较结果,判断应用是否被二次打包。如果两者不一样,则判断当前应用已经被修改,哨兵函数就可以中止程序的运行。

动态加载dex文件,得到其中的类:

Dalvik虚拟机不同于标准的Java虚拟机,标准Java虚拟机运行的是Java字节码,而Dalvik虚拟机运行的经过转换的Dalvik字节码,即.dex格式的文件。因此,不能像标准Java虚拟机那样,直接通过Java中的ClassLoader来实现类的动态加载。Dalvik虚拟机通过DexClassLoader和PathClassLoader两个类实现类的动态加载。

动态加载的实现过程大致如下:

1)拿到经过加密的待载入的dex文件。dex文件可以存放在SD卡中,也可以从服务端下载,还可以以资源文件的形式打包在应用中。

2)运行伪程序,对dex文件进行解密处理。

3)为程序通过DexClassLoader类动态加载解密后的dex文件。

4)通过Java反射机制,获得类的实例。

动态加载类的实例代码如下所示:

(1)动态加载代码

(2)反射调用工具类

通过上面的步骤,就实现了程序在运行时动态加载dex文件,并得到其中的类,进而执行程序的主体代码。

需要注意的是,公布实施例的目的在于帮助进一步理解本发明,但是本领域的技术人员可以理解:在不脱离本发明及所附权利要求的精神和范围内,各种替换和修改都是可能的。因此,本发明不应局限于实施例所公开的内容,本发明要求保护的范围以权利要求书界定的范围为准。

当前第1页1 2 3 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1