通知短信+运营短信,5秒速达,支持群发助手一键发送🚀高效触达和通知客户 广告
> 由于Android 系统支持的应用格式主要以apk 形式存在,应用的主要逻辑实现在apk中的classes.dex 文件中,因此,对classes.dex 文件的保护是保护整个apk 安全的关键所在。为此文章设计了一套安全加固系统,采取相应的加密方式隐藏源程序classes.dex 文件中的实现逻辑,并且不影响程序的正常运行,对用户保证加固的透明性。 #### **Android 安全机制** Android 是建立在Linux 内核之上,并采用Dalvik 虚拟机(Android5.0之后变更为ART)作为应用程序运行环境的操作系统。开发者在开发了Android 应用程序后,将应用程序打包提交给应用市场审核。应用市场对其审核通过后,将其发布给用户进行下载安装。Google 在设计开发 Android 操作系统时就在各层设置了相应的安全防范机制,如下表所示: ![](https://box.kancloud.cn/e2069090e1e715d60a52b1b4f9f4792a_1045x377.jpg) **1.1 内核级安全机制** **由于Android是基于Linux内核的,所以也继承了Linux内核的安全机制**。在Android内核级主要的安全机制包括可一直操作系统接口用户(POSIX USER)和文件访问控制。这些机制的基本元素是用户及其拥有的对象,用户再进一步分配到用户组。另外,内存控制机制也一定程度上保证了Android的运行安全。 **1.2 运行环境级安全机制** Android系统中所有的应用程序都运行在Dalvik虚拟机(Android5.0之后变更为ART)上。并且每一个应用程序都运行在一个独立的虚拟机上。但是不同于其他虚拟机,如JVM和.NET Runtime作为安全边界有着隔离代码的作用,**由于底层Android内核已经实现了沙箱机制,所以Dalvik虚拟机并不作为隔离代码的安全边界。所有应用程序都能运行本地代码,并且均使用相同的安全等级运行在沙盒中**。所以**在系统运行库Android主要采取强制安全类型来加固Android的系统安全**。 **强制类型安全机制通过赋值变量时强制检查声明类型与值是否符合,从而保证变量不被错误地使用**。Android系统采用强类型的Java语言作为其上的编程语言。具体来说是通过编译器类型检查,自动的存储管理和数组边界检查来保证类型安全,阻止类似类型转化错误或者缺少边界检查而造成的缓冲区溢出攻击。 **1.3 应用程序框架级安全机制** 在这一层中,**Android系统主要通过应用程序的权限控制和数字签名来保证Android系统安全**。 Android**应用程序安全的核心在于权限控制**。它**用于限制应用程序的访问系统的API和资源。应用程序必须在权限内运行,而不能访问权限外的任何资源。** Android应用程序都被打包成一个.apk格式文件,其中包含了所有的代码文件,相关资源文件以及代表开发者的数字签名文件。在程序安装之前,Android系统会对应用程序包中的相关文件进行签名检查,检查这些文件是否被篡改。 Android签名文件包含以下几个要点: 1. Android系统只会安装带有有效数字签名的应用程序,禁止安装任何未签名的应用程序。 2. Android应用程序是可以自签名的,不需要权威机构的认证。 3. 数字签名是有时限的,但只有在安装应用时Android系统才会检测数字签名是否过期,一旦安装成功,系统将不再关注数字签名是否过期。 #### **基于逆向的Android恶意程序原理** 逆向工程的Android恶意程序的基本思路:**选取一个Android程序下载其apk应用程序包,通过反编译得到smali 中间代码,并编写需要用到的代码插入到原有的smali 代码中重新编译并重新签名。** **2.1Android 应用程序包分析** 反编译之前首先对Android 应用程序包进行分析。Android 系统的应用程序文件为.apk(Android Package)格式的。Apk 文件实际上是一个压缩文件,解压之后可以得到如下的文 件内容: 1. **classess.dex**:Java 源码被编译后生成的Dalvik 虚拟机字节码文件。 2. **lib**:lib 下的子目录 armeabi 存放的是 so 文件,应用若使用 JNI调用 C/C++动态库,则需要将被调用的so 文件放到该目录下。 3. **AndroidManifest.xml**:程序的全局配置文件。此文件在每个应用中都必须被定义和包含,它描述了应用的名称、开放权限、引用的库文件、版本号等重要信息。 4. **resources.arsc**:经过编译的二进制资源文件。 5. **res**:res 目录是存放资源文件的,包括程序使用的布局文件、图片和字符串常量等。 6. **META-INF**:META-INF 目录下保存的则是应用中的签名信息,签名信息可以一定程度上验证原始apk 的完整性。 7. **assets**:存放的原生资源文件(比如so库,HTML资源等原生资源),android不为/assets下的文件生成ID。如果使用/assets下的文件,需要指定文件的路径和文件名。官方介绍如下 > **访问原始文件** > 尽管并不常见,但您的确有可能需要访问原始文件和目录。如果确有需要,则将您的文件保存在 res/ 中不起作用,因为从 res/读取资源的唯一方法是使用资源 ID。您可以改为将资源保存在 assets/ 目录中。 > > 保存在 assets/ 目录中的文件没有资源 ID,因此您无法通过 R 类或在 XML 资源中引用它们。您可以改为采用类似普通文件系统的方式查询 assets/ 目录中的文件,并利用 AssetManager 读取原始数据。 > > 不过,如果只需要读取原始数据(例如视频文件或音频文件)的能力,则可将文件保存在 res/raw/ 目录中,并利用 openRawResource() 读取字节流。 **2.2 Android 逆向工程原理** Android 应用程序开发的最后阶段是打包签名,生成 Apk 文件供用户下载安装。打包过程实质上如下图: :-: ![](https://box.kancloud.cn/fcf2d96b09dc072a5e2d4622b0ec46a5_808x502.jpg) Android应用程序打包图 由上图可知,如果能够对 Dex 文件和 AndroidManifest.XML 进行逆向,即可还原出应用程序项目的源代码,在源代码的基础上加入恶意代码,重新签名打包,即可生成携带恶意代码的应用程序。这种重新打包的应用程序在表面功能上与原应用程序没有本质区别,只是在特定情况下会触发恶意行为。如下表: :-: ![](https://box.kancloud.cn/86c354dc79b4215a8b33b7ea808d37bd_1170x234.jpg) 逆向一个Android 应用程序需要以下几个步骤: 1. 对Android 应用程序的安装包Apk 文件进行解压缩,得到内部文件如图; 2. 使用dex2jar 工具将解压后得到的classes.dex 文件逆向为.jar 包; 3. 使用xmprinter 工具将AndroidManifest.XML 配置文件解密,使其可读; 4. 使用 jd_gui这种 Java 反编译工具将②中得到的 jar包逆向为.java 代码文件。 通过以上步骤获得原Android 程序的代码与资源文件后,再将恶意程序模块添加到项目中,重新打包签名后上传到应用商店,供用户下载。由于用户无法用肉眼在功能上识别出该应用程序的真伪,用户极有可能下载安装该应用而蒙受危害。 #### **基于保护Classes.dex 不被逆向的安全加固技术** 针对 Android 应用安装包容易被解包逆向注入恶意程序模块这一点,本文对 Android 应用程序主要代码文件进行加密保护,提出了一个既不影响程序正常运行,又让程序安装包得到保护的安全加固技术。 **3.1 Classes.dex 工作原理** 在Android 源码中(不同版本的源码,可能目录不一样),Dalvik 虚拟机的实现位于dalvik/目录下,其中dalvik/vm 是虚拟机的实现部分,将会编译成 libdvm.so;而 dalvik/libdex 将会编译成 libdex.a静态库作为 dex 工具;dalvik/dexdump 是.dex 文件的反编译工具;虚拟机的可执行程序位于dalvik/dalvikvm 中,将会编译成dalvikvm 可执行文件。 Android 平台应用程序在运行时,首先由Dalvik 虚拟机加载解包后的Classes.dex 文件,然后dalvik 虚拟机会从中读取指令和数据,进而运行该应用的程序逻辑。 :-: ![](https://box.kancloud.cn/3a03bcc1c5b61a2e37b6d6f1b40bbb80_884x224.jpg) **3.2 加固方案** Android 应用程序在加固前的文件结构如下图左所示, 为了保护主要程序文件Classes.dex,加固后的Android 应用程序如下图右所示。它们的意义如下: 1. Classes.dex壳:它是用于 Android 程序运行时被 Dalvik 虚拟机加载的 dex 文件,里面不再包含Android 程序的功能代码; 2. AndroidManifest.XML’:它将原AndroidManifest.XML 中的入口替换为现入口; 3. Encrypt_Classes.dex:它是原Classes.dex 经过白盒加密后的文件,包含了Android程序的主要功能代码,也是重点保护对象; 4. Encrypt_decryption.so:decryption.so 是用于解密 Encrypt_Classes.dex 的 so 文件,为了确保解密方法不被泄露,将decryption.so 文件再加密一层为Encrypt_decryption.so。 5. Entry.so:程序启动后的入口so 文件,用于调用Encrypt_decryption.so 来解密Encrypt_Classes.dex。 :-: ![](https://box.kancloud.cn/15533be95a0aab01d874d297b009ef12_741x692.jpg) **3.3 实现流程** 本方案为实现保护源程序Classes.dex 文件的安全性,用白盒算法对Classes.dex 文件进行加密,并自定义一个新的Classes.dex 文件。Dalvik 虚拟机在将应用加载到内存中时,首先加载 Classes.dex,Classes.dex将对原 Classes.dex 文件进行解密和完整性检查,再将原Classes.dex 文件加载至内存并运行,最后将解密后的Classes.dex 文件从本地物理存储空间中删除,从而保证了Classes.dex 文件的安全加载,并且防止Classes.dex 文件被篡改。由于程序的运行逻辑仍然为原Classes.dex 文件中的实现,所以保证了应用的功能不受影响。具体调用逻辑如下图: :-: ![](https://box.kancloud.cn/3965de74b3c2f2312f7d1ca8641e41db_1017x810.jpg) Dalvik 虚拟机加载自定义的classes.dex后, 1. Classes.dex 调用入口文件 Entry.so 中的native 方法对Encrypt_descryption.so 进行解密。 2. 解密得到的decryption.so用白盒算法对Encypt_Classes.dex解密,得到Classes.dex文件。 3. 配置classes.dex的动态加载环境。attachBaseContext方法是应用在启动activity之前必须运行的方法,在应用的生命周期中位于onCreate方法之前。为了可以正常加载源程序的Classes.dex,在attachBaseContext方法中,新建一个带有源程序Classes.dex的DexClassLoader对象,通过反射的方式替换到系统默认的加载类中的属性mClassLoader。 4. 找到源程序的入口,动态加载至内存。在自定义的onCreate方法中,通过AndroidManifest中application中的meta-data属性,找到源程序入口,从而替换成源程序的Application。将现有的“Application壳”从系统调用集合中移除,160 并新建一个Application对象app,将源程序的属性绑定至app,再把app加入到系统调用集合中。最后,由于组件Provider在onCreate方法前就被系统注册,因此把app绑定到源程序中Provider的context上,然后启动系统级onCreate方法。 5. 从本地的物理内存上删除解密后的Classes.dex文件,防止被攻击者窃取。 #### **结论** 本文针对Android应用安装包容易被篡改这点,采取相应的加密方式隐藏源程序Classes.dex文件中的实现逻辑,提出了一个既不影响程序的正常运行,又让程序安装包得到保护的加固技术。 #### **参考文章** [android应用的逆向与加固保护技术](www.java1234.com/a/javabook/andriod/2017/1222/10063.html)