【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 反射获取系统的 Element[] dexElements )
文章目錄
- 一、dex 文件準備
- 二、加載 dex 文件流程
- 三、Element[] dexElements 分析
- 四、反射獲取系統的 Element[] dexElements
參考博客 :
- 【Android 安全】DEX 加密 ( 常用 Android 反編譯工具 | apktool | dex2jar | enjarify | jd-gui | jadx )
- 【Android 安全】DEX 加密 ( Proguard 簡介 | Proguard 相關網址 | Proguard 混淆配置 )
- 【Android 安全】DEX 加密 ( Proguard 簡介 | 默認 ProGuard 分析 )
- 【Android 安全】DEX 加密 ( Proguard keep 用法 | Proguard 默認混淆結果 | 保留類及成員混淆結果 | 保留注解以及被注解修飾的類/成員/方法 )
- 【Android 安全】DEX 加密 ( Proguard 混淆 | 混淆后的報錯信息 | Proguard 混淆映射文件 mapping.txt )
- 【Android 安全】DEX 加密 ( Proguard 混淆 | 將混淆后的報錯信息轉為原始報錯信息 | retrace.bat 命令執行目錄 | 暴露更少信息 )
- 【Android 安全】DEX 加密 ( DEX 加密原理 | DEX 加密簡介 | APK 文件分析 | DEX 分割 )
- 【Android 安全】DEX 加密 ( 多 DEX 加載 | 65535 方法數限制和 MultiDex 配置 | PathClassLoader 類加載源碼分析 | DexPathList )
- 【Android 安全】DEX 加密 ( 不同 Android 版本的 DEX 加載 | Android 8.0 版本 DEX 加載分析 | Android 5.0 版本 DEX 加載分析 )
- 【Android 安全】DEX 加密 ( DEX 加密使用到的相關工具 | dx 工具 | zipalign 對齊工具 | apksigner 簽名工具 )
- 【Android 安全】DEX 加密 ( 支持多 DEX 的 Android 工程結構 )
- 【Android 安全】DEX 加密 ( 代理 Application 開發 | multiple-dex-core 依賴庫開發 | 配置元數據 | 獲取 apk 文件并準備相關目錄 )
- 【Android 安全】DEX 加密 ( 代理 Application 開發 | 解壓 apk 文件 | 判定是否是第一次啟動 | 遞歸刪除文件操作 | 解壓 Zip 文件操作 )
在 【Android 安全】DEX 加密 ( 支持多 DEX 的 Android 工程結構 ) 博客中介紹了 DEX 加密工程的基本結構 ,
app 是主應用 , 其 Module 類型是 “Phone & Tablet Module” ,
multiple-dex-core 是 Android 依賴庫 , 其作用是解密并加載多 DEX 文件 , 其 Module 類型是 “Android Library” ,
multiple-dex-tools 是 Java 依賴庫 , 其類型是 “Java or Kotlin Library” , 其作用是用于生成主 DEX ( 主 DEX 的作用就是用于解密與加載多 DEX ) , 并且還要為修改后的 APK 進行簽名 ;
在 【Android 安全】DEX 加密 ( 代理 Application 開發 | multiple-dex-core 依賴庫開發 | 配置元數據 | 獲取 apk 文件并準備相關目錄 ) 博客中講解了 multiple-dex-core 依賴庫開發 , 每次啟動都要解密與加載 dex 文件 , 在該博客中講解到了 獲取 apk 文件 , 并準備解壓目錄 ;
在 【Android 安全】DEX 加密 ( 代理 Application 開發 | 解壓 apk 文件 | 判定是否是第一次啟動 | 遞歸刪除文件操作 | 解壓 Zip 文件操作 ) 博客中講解了 apk 文件解壓操作 ;
本博客中主要講解 dex 文件加載操作 ;
一、dex 文件準備
上一篇博客講解的是 apk 文件解壓 , 繼續后面的步驟 ;
如果本次是第一次啟動 , 則需要 解壓 apk 文件 ,
解壓后 , 將所有的 dex 文件放到 dexDir 中 , 解密該 dex 文件 ,
解密完成后 , 將文件路徑存放在 var dexFiles : ArrayList<File> 集合中 ;
如果本次不是第一次啟動 , 則直接從 dexDir 中獲取 dex 文件 ,
將所有的 dex 文件路徑放在 var dexFiles : ArrayList<File> 集合中 ;
// 遍歷解壓后的 apk 文件 , 將需要加載的 dex 放入如下集合中var dexFiles : ArrayList<File> = ArrayList<File>()// 如果該 dexDir 存在 , 并且該目錄不為空 , 并進行 MD5 文件校驗if( !dexDir.exists() || dexDir.list().size == 0){// 將 apk 中的文件解壓到了 appDir 目錄unZipApk(apkFile, appDir)// 獲取 appDir 目錄下的所有文件var files = appDir.listFiles()// 遍歷文件名稱集合for(i in files.indices){var file = files[i]// 如果文件后綴是 .dex , 并且不是 主 dex 文件 classes.dex// 符合上述兩個條件的 dex 文件放入到 dexDir 中if(file.name.endsWith(".dex") &&TextUtils.equals(file.name, "classes.dex")){// 篩選出來的 dex 文件都是需要解密的// 解密需要使用 OpenSSL 進行解密// 獲取該文件的二進制 Byte 數據// 這些 Byte 數組就是加密后的 dex 數據var bytes = Utils.getBytes(file)// 解密該二進制數據, 并替換原來的加密 dex, 直接覆蓋原來的文件即可Utils.decrypt(bytes, file.absolutePath)// 將解密完畢的 dex 文件放在需要加載的 dex 集合中dexFiles.add(file)}// 判定是否是需要解密的 dex 文件}// 遍歷 apk 解壓后的文件}else{// 已經解密完成, 此時不需要解密, 直接獲取 dexDir 中的文件即可for (file in dexDir.listFiles()) {dexFiles.add(file)}}二、加載 dex 文件流程
加載上述 dex 文件集合 , 這些 dex 文件已經解密 ;
加載 dex 文件流程 :
1 . 步驟一 : 獲得系統 DexPathList 中的 Element[] dexElements 數組 ,
( libcore/dalvik/src/main/java/dalvik/system/DexPathList.java ) ;
2 . 步驟二 : 在本應用中創建 Element[] dexElements 數組 , 用于存放解密后的 dex 文件 ;
3 . 步驟三 : 將 系統加載的 Element[] dexElements 數組 , 與我們自己創建的 Element[] dexElements 數組進行 合并操作
4 . 步驟四 : 替換 ClassLoader 加載過程中的 Element[] dexElements 數組 ( 封裝在 DexPathList 中 ) ;
三、Element[] dexElements 分析
系統的 Element[] dexElements 數組 封裝在 libcore/dalvik/src/main/java/dalvik/system/DexPathList.java 中
/*package*/ final class DexPathList {/*** dex/resource (class path) 元素集合.* 應該調用 pathElements , 但是 Facebook 應用通過反射修改 dexElements .*/private final Element[] dexElements;public DexPathList(ClassLoader definingContext, String dexPath,String libraryPath, File optimizedDirectory) {// save dexPath for BaseDexClassLoaderthis.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory,suppressedExceptions);} }參考源碼地址 : 6.0.1_r16/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
DexPathList 對象被封裝在 libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java 中 ;
public class BaseDexClassLoader extends ClassLoader {private final DexPathList pathList; }參考源碼地址 : libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
PathClassLoader 是我們可以拿到的類加載器 , 該類是 BaseDexClassLoader 的子類 ,
源碼位置是 libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java ;
public class PathClassLoader extends BaseDexClassLoader { }參考源碼地址 : libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
在 Context 中調用 getClassLoader() 方法 , 可以拿到 PathClassLoader 后 , 需要從其父類 BaseDexClassLoader 中找到 DexPathList , 進而獲取封裝在 DexPathList 類中的 Element[] dexElements 數組 ;
四、反射獲取系統的 Element[] dexElements
上述的 DexPathList 對象 是 BaseDexClassLoader 的 私有成員 , Element[] dexElements 數組 也是 DexPathList 的 私有成員 , 因此只能使用 反射 獲取 Element[] dexElements 數組 ;
反射獲取系統的 Element[] dexElements , 需要分三個階段完成 ;
第一階段 : 在 Context 中調用 getClassLoader() 方法 , 可以 拿到 PathClassLoader ;
classLoader第二階段 : 從 PathClassLoader 父類 BaseDexClassLoader 中 找到 DexPathList ;
// 階段一二 : 調用 getClassLoader() 方法可以獲取 PathClassLoader 對象// 從 PathClassLoader 對象中獲取 private final DexPathList pathList 成員var pathListField = reflexField(classLoader, "DexPathList");// 獲取 classLoader 對象對應的 DexPathList pathList 成員var pathList = pathListField.get(classLoader)第三階段 : 獲取封裝在 DexPathList 類中的 Element[] dexElements 數組 ;
/*1 . 獲得系統 DexPathList 中的 Element[] dexElements 數組第一階段 : 在 Context 中調用 getClassLoader() 方法 , 可以拿到 PathClassLoader ;第二階段 : 從 PathClassLoader 父類 BaseDexClassLoader 中找到 DexPathList ;第三階段 : 獲取封裝在 DexPathList 類中的 Element[] dexElements 數組 ;上述的 DexPathList 對象 是 BaseDexClassLoader 的私有成員Element[] dexElements 數組 也是 DexPathList 的私有成員因此只能使用反射獲取 Element[] dexElements 數組*/// 階段一二 : 調用 getClassLoader() 方法可以獲取 PathClassLoader 對象// 從 PathClassLoader 對象中獲取 private final DexPathList pathList 成員var pathListField = reflexField(classLoader, "DexPathList");// 獲取 classLoader 對象對應的 DexPathList pathList 成員var pathList = pathListField.get(classLoader)//階段三 : 獲取封裝在 DexPathList 類中的 Element[] dexElements 數組var dexElementsField = reflexField(pathList, "dexElements")// 獲取 pathList 對象對應的 Element[] dexElements 數組成員var dexElements : Array<Any> = dexElementsField.get(pathList) as Array<Any>總結
以上是生活随笔為你收集整理的【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 反射获取系统的 Element[] dexElements )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 安全】DEX 加密 (
- 下一篇: 【Android 安全】DEX 加密 (