> 由于Android 系統支持的應用格式主要以apk 形式存在,應用的主要邏輯實現在apk中的classes.dex 文件中,因此,對classes.dex 文件的保護是保護整個apk 安全的關鍵所在。為此文章設計了一套安全加固系統,采取相應的加密方式隱藏源程序classes.dex 文件中的實現邏輯,并且不影響程序的正常運行,對用戶保證加固的透明性。
#### **Android 安全機制**
Android 是建立在Linux 內核之上,并采用Dalvik 虛擬機(Android5.0之后變更為ART)作為應用程序運行環境的操作系統。開發者在開發了Android 應用程序后,將應用程序打包提交給應用市場審核。應用市場對其審核通過后,將其發布給用戶進行下載安裝。Google 在設計開發 Android 操作系統時就在各層設置了相應的安全防范機制,如下表所示:

**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 文件供用戶下載安裝。打包過程實質上如下圖:
:-: 
Android應用程序打包圖
由上圖可知,如果能夠對 Dex 文件和 AndroidManifest.XML 進行逆向,即可還原出應用程序項目的源代碼,在源代碼的基礎上加入惡意代碼,重新簽名打包,即可生成攜帶惡意代碼的應用程序。這種重新打包的應用程序在表面功能上與原應用程序沒有本質區別,只是在特定情況下會觸發惡意行為。如下表:
:-: 
逆向一個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 虛擬機會從中讀取指令和數據,進而運行該應用的程序邏輯。
:-: 
**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。
:-: 
**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 文件中的實現,所以保證了應用的功能不受影響。具體調用邏輯如下圖:
:-: 
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)
- 前言
- Android系統的體系結構
- Dalvik VM 和 JVM 的比較
- Android 打包應用程序并安裝的過程
- Android ADB工具
- Android應用開發
- Android UI相關知識總結
- Android 中window 、view、 Activity的關系
- Android應用界面
- Android中的drawable和bitmap
- AndroidUI組件adapterView及其子類和Adapter的關系
- Android四大組件
- Android 數據存儲
- SharedPreference
- Android應用的資源
- 數組資源
- 使用Drawable資源
- Material Design
- Android 進程和線程
- 進程
- 線程
- Android Application類的介紹
- 意圖(Intent)
- Intent 和 Intent 過濾器(Google官網介紹)
- Android中關于任務棧的總結
- 任務和返回棧(官網譯文)
- 總結
- Android應用安全現狀與解決方案
- Android 安全開發
- HTTPS
- 安卓 代碼混淆與打包
- 動態注入技術(hook技術)
- 一、什么是hook技術
- 二、常用的Hook 工具
- Xposed源碼剖析——概述
- Xposed源碼剖析——app_process作用詳解
- Xposed源碼剖析——Xposed初始化
- Xposed源碼剖析——hook具體實現
- 無需Root也能Hook?——Depoxsed框架演示
- 三、HookAndroid應用
- 四、Hook原生應用程序
- 五、Hook 檢測/修復
- Android 應用的逆向與加固保護技術
- OpenCV在Android中的開發
- Android高級開發進階
- 高級UI
- UI繪制流程及原理
- Android新布局ConstraintLayout約束布局
- 關鍵幀動畫
- 幀動畫共享元素變換
- Android異步消息處理機制完全解析,帶你從源碼的角度徹底理解
- Android中為什么主線程不會因為Looper.loop()里的死循環卡死?
- 為什么 Android 要采用 Binder 作為 IPC 機制?
- JVM 中一個線程的 Java 棧和寄存器中分別放的是什么?
- Android源碼的Binder權限是如何控制?
- 如何詳解 Activity 的生命周期?
- 為什么Android的Handler采用管道而不使用Binder?
- ThreadLocal,你真的懂了嗎?
- Android屏幕刷新機制