日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

【Android 安全】DEX 加密 ( Application 替换 | 兼容 ContentProvider 操作 | 源码资源 )

發布時間:2025/6/17 Android 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Android 安全】DEX 加密 ( Application 替换 | 兼容 ContentProvider 操作 | 源码资源 ) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 一、 命中 ActivityThread 中 installProvider 方法的分支三
    • 1、 原理分析
    • 2、 代碼實現
  • 二、 在 ContextImpl 的 createPackageContext 方法執行前進行 Application 替換
    • 1、 原理分析
    • 2、 代碼實現
  • 三、 完整代碼示例
  • 四、日志分析
  • 五、源碼資源



前兩篇分析 ContentProvider 中的 Application 的博客 :

  • 【Android 安全】DEX 加密 ( Application 替換 | 分析 ContentProvider 組件中調用 getApplication() 獲取的 Application )
  • 【Android 安全】DEX 加密 ( Application 替換 | 分析 ContentProvider 組件中調用 getApplication() 獲取的 Application 二 )

ContentProvider 中替換 Application 的總結 :

  • ① 分支選擇 : 首先要命中 ActivityThread 中 installProvider 方法的分支三 ;
  • ② Application 替換 : 然后要在 ContextImpl 的 createPackageContext 方法執行前進行一次 Application 替換 ;




一、 命中 ActivityThread 中 installProvider 方法的分支三





1、 原理分析


ActivityThread 中的 installProvider 方法中的三個分支如下 , 在上面的分析中 , 如果要使得分支一 context.getPackageName().equals(ai.packageName) 與分支二 mInitialApplication.getPackageName().equals(ai.packageName) , 都無法命中 , 就需要 Application 的 getPackageName 方法獲取的包名不等于在 AndroidManifest.xml 中的包名 ai.packageName , 這里重寫 ProxyApplication 的 getPackageName 方法 , 使該方法返回值為 “” 字符串 , 這樣就無法命中前兩個分支 , 只能進入 else 分支 ;


public final class ActivityThread {private ContentProviderHolder installProvider(Context context,ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {// 該上下文對象很重要 Context c = null;ApplicationInfo ai = info.applicationInfo;// 該 context 是 ProxyApplication , 代理 Application if (context.getPackageName().equals(ai.packageName)) {// 在應用中配置的代理 Application 包名與真實 Application 包名都是相等的// 該分支是命中的 c = context;} else if (mInitialApplication != null &&mInitialApplication.getPackageName().equals(ai.packageName)) {// 該分支中 mInitialApplication 就是 Context context 參數 , 肯定不為空 // 該分支無法命中 c = mInitialApplication;} else {// 上述兩個分支都無法命中 , 才進入該分支 // 需要將代理 Application 的包名 與 真實應用的包名設置成不同的// 此時上面兩個分支都無法命中 try {c = context.createPackageContext(ai.packageName,Context.CONTEXT_INCLUDE_CODE);} catch (PackageManager.NameNotFoundException e) {// Ignore}}return retHolder;}}

參考路徑 : frameworks/base/core/java/android/app/ActivityThread.java



2、 代碼實現


代碼示例 : 暫時省略其余代碼 ;

public class ProxyApplication extends Application {@Overridepublic String getPackageName() {if(TextUtils.isEmpty(app_name)){// 如果 AndroidManifest.xml 中配置的 Application 全類名為空// 那么 不做任何操作}else{// 如果 AndroidManifest.xml 中配置的 Application 全類名不為空// 為了使 ActivityThread 的 installProvider 方法// 無法命中如下兩個分支// 分支一 : context.getPackageName().equals(ai.packageName)// 分支二 : mInitialApplication.getPackageName().equals(ai.packageName)// 設置該方法返回值為空 , 上述兩個分支就無法命中return "";}return super.getPackageName();}}



二、 在 ContextImpl 的 createPackageContext 方法執行前進行 Application 替換





1、 原理分析


分支三中調用了 , context 的 createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE) 方法 , 該方法在 ContextImpl 中定義 ;

在 ContextImpl 中的 createPackageContext 方法 , 調用了 createPackageContextAsUser 方法 , 調用了如下代碼 , 創建 Context 上下文 ,

ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,flags, null);

上述代碼中創建 ContextImpl 時 , 使用的 mMainThread , pi , 都沒有替換過 Application , 因此分支三創建的 ContentProvider 對應的 Application 也是代理 Application , 替換前的 Application 對象 ;


class ContextImpl extends Context {// 在該方法中調用了 createPackageContextAsUser 方法創建上下文@Overridepublic Context createPackageContext(String packageName, int flags)throws NameNotFoundException {return createPackageContextAsUser(packageName, flags,mUser != null ? mUser : Process.myUserHandle());}@Overridepublic Context createPackageContextAsUser(String packageName, int flags, UserHandle user)throws NameNotFoundException {if (packageName.equals("system") || packageName.equals("android")) {// The system resources are loaded in every application, so we can safely copy// the context without reloading Resources.return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,flags, null);}// 注意該 LoadedApk 對象LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());if (pi != null) {// 創建新的 ContextImpl // 此時還沒有替換 Application ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,flags, null);final int displayId = mDisplay != null? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;c.setResources(createResources(mActivityToken, pi, null, displayId, null,getDisplayAdjustments(displayId).getCompatibilityInfo()));if (c.mResources != null) {return c;}}// Should be a better exception.throw new PackageManager.NameNotFoundException("Application package " + packageName + " not found");}}

源碼路徑 : frameworks/base/core/java/android/app/ContextImpl.java

ContextImpl 中的 public Context createPackageContext(String packageName, int flags) 方法是公開方法 , 重寫該方法 , 在重寫的 createPackageContext 方法中 , 先進行一次 Application 替換 , 然后繼續執行 super.createPackageContext 方法的后續操作 , 這樣創建的 ContentProvider 中的上下文就是用戶自定義的 MyApplication , 不再是 ProxyApplication ;

只有在創建 ContentProvider 時才調用到該 createPackageContext 方法 , 如果沒有調用到該方法 , 說明該應用中沒有配置 ContentProvider ;



2、 代碼實現


代碼實現 : 在 代理 Application 的中重寫 public Context createPackageContext(String packageName, int flags) 方法 , 先替換 Application , 然后再繼續向后執行 ;

這里建議 Application 替換操作 , 只執行一次 , 使用 Application delegate 是否為空 , 作為替換操作是否執行的標志 ;

public class ProxyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// 如果之前沒有替換過 , 執行 Application 替換操作// 說明沒有調用到 createPackageContext 方法// 該 createPackageContext 方法只有在創建 ContentProvider 時才調用到// 如果沒有調用到 , 說明 AndroidManifest.xml 中沒有配置 ContentProvider// 此時需要在此處進行 Application 替換if (delegate == null){applicationExchange();}}@Overridepublic Context createPackageContext(String packageName, int flags)throws PackageManager.NameNotFoundException {if(TextUtils.isEmpty(app_name)){// 如果 AndroidManifest.xml 中配置的 Application 全類名為空// 說明沒有進行 dex 加密操作 , 返回父類方法執行即可return super.createPackageContext(packageName, flags);}else{// 只有在創建 ContentProvider 時才調用到該 createPackageContext 方法 ,// 如果沒有調用到該方法 , 說明該應用中沒有配置 ContentProvider ;// 該方法不一定會調用到// 先進行 Application 替換applicationExchange();// Application 替換完成之后 , 再繼續向下執行創建 ContentProviderreturn delegate;}}/*** 調用 applicationExchange 替換 Application* 該成員就是替換后的 Application*/private Application delegate;/*** Application 替換主方法*/private void applicationExchange(){} }



三、 完整代碼示例



完整代碼示例 :

package kim.hsl.multipledex;import android.app.Application; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.text.TextUtils; import android.util.Log;import java.io.File; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List;public class ProxyApplication extends Application {public static final String TAG = "ProxyApplication";/*** 應用真實的 Application 全類名*/String app_name;/*** DEX 解密之后的目錄名稱*/String app_version;/*** 在 Application 在 ActivityThread 中被創建之后,* 第一個調用的方法是 attachBaseContext 函數.* 該函數是 Application 中最先執行的函數.*/@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);try {Log.i(TAG, "attachBaseContext");/*在該 Application 中主要進行兩個操作 :1 . 解密并加載多個 DEX 文件2 . 將真實的 Application 替換成應用的主 Application*//*I . 解密與加載多 DEX 文件先進行解密, 然后再加載解密之后的 DEX 文件1. 先獲取當前的 APK 文件2. 然后解壓該 APK 文件*/// 獲取當前的 APK 文件, 下面的 getApplicationInfo().sourceDir 就是本應用 APK 安裝文件的全路徑File apkFile = new File(getApplicationInfo().sourceDir);// 獲取在 app Module 下的 AndroidManifest.xml 中配置的元數據,// 應用真實的 Application 全類名// 解密后的 dex 文件存放目錄ApplicationInfo applicationInfo = null;applicationInfo = getPackageManager().getApplicationInfo(getPackageName(),PackageManager.GET_META_DATA);Bundle metaData = applicationInfo.metaData;if (metaData != null) {// 檢查是否存在 app_name 元數據if (metaData.containsKey("app_name")) {app_name = metaData.getString("app_name").toString();}// 檢查是否存在 app_version 元數據if (metaData.containsKey("app_version")) {app_version = metaData.getString("app_version").toString();}}// 創建用戶的私有目錄 , 將 apk 文件解壓到該目錄中File privateDir = getDir(app_name + "_" + app_version, MODE_PRIVATE);Log.i(TAG, "attachBaseContext 創建用戶的私有目錄 : " + privateDir.getAbsolutePath());// 在上述目錄下創建 app 目錄// 創建該目錄的目的是存放解壓后的 apk 文件的File appDir = new File(privateDir, "app");// app 中存放的是解壓后的所有的 apk 文件// app 下創建 dexDir 目錄 , 將所有的 dex 目錄移動到該 dexDir 目錄中// dexDir 目錄存放應用的所有 dex 文件// 這些 dex 文件都需要進行解密File dexDir = new File(appDir, "dexDir");// 遍歷解壓后的 apk 文件 , 將需要加載的 dex 放入如下集合中ArrayList<File> dexFiles = new ArrayList<File>();// 如果該 dexDir 不存在 , 或者該目錄為空 , 并進行 MD5 文件校驗if (!dexDir.exists() || dexDir.list().length == 0) {// 將 apk 中的文件解壓到了 appDir 目錄ZipUtils.unZipApk(apkFile, appDir);// 獲取 appDir 目錄下的所有文件File[] files = appDir.listFiles();Log.i(TAG, "attachBaseContext appDir 目錄路徑 : " + appDir.getAbsolutePath());Log.i(TAG, "attachBaseContext appDir 目錄內容 : " + files);// 遍歷文件名稱集合for (int i = 0; i < files.length; i++) {File file = files[i];Log.i(TAG, "attachBaseContext 遍歷 " + i + " . " + file);// 如果文件后綴是 .dex , 并且不是 主 dex 文件 classes.dex// 符合上述兩個條件的 dex 文件放入到 dexDir 中if (file.getName().endsWith(".dex") &&!TextUtils.equals(file.getName(), "classes.dex")) {// 篩選出來的 dex 文件都是需要解密的// 解密需要使用 OpenSSL 進行解密// 獲取該文件的二進制 Byte 數據// 這些 Byte 數組就是加密后的 dex 數據byte[] bytes = OpenSSL.getBytes(file);// 解密該二進制數據, 并替換原來的加密 dex, 直接覆蓋原來的文件即可OpenSSL.decrypt(bytes, file.getAbsolutePath());// 將解密完畢的 dex 文件放在需要加載的 dex 集合中dexFiles.add(file);// 拷貝到 dexDir 中Log.i(TAG, "attachBaseContext 解密完成 被解密文件是 : " + file);}// 判定是否是需要解密的 dex 文件}// 遍歷 apk 解壓后的文件} else {// 已經解密完成, 此時不需要解密, 直接獲取 dexDir 中的文件即可for (File file : dexDir.listFiles()) {dexFiles.add(file);}}Log.i(TAG, "attachBaseContext 解密完成 dexFiles : " + dexFiles);for (int i = 0; i < dexFiles.size(); i++) {Log.i(TAG, i + " . " + dexFiles.get(i).getAbsolutePath());}// 截止到此處 , 已經拿到了解密完畢 , 需要加載的 dex 文件// 加載自己解密的 dex 文件loadDex(dexFiles, privateDir);Log.i(TAG, "attachBaseContext 完成");} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}/*** 加載 dex 文件集合* 這些 dex 文件已經解密* 參考博客 : https://hanshuliang.blog.csdn.net/article/details/109608605* <p>* 創建自己的 Element[] dexElements 數組* ( libcore/dalvik/src/main/java/dalvik/system/DexPathList.java )* 然后將 系統加載的 Element[] dexElements 數組 與 我們自己的 Element[] dexElements 數組進行合并操作*/void loadDex(ArrayList<File> dexFiles, File optimizedDirectory)throwsIllegalAccessException,InvocationTargetException,NoSuchFieldException,NoSuchMethodException {Log.i(TAG, "loadDex");/*需要執行的步驟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 中 )*//*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 成員Field pathListField = ReflexUtils.reflexField(getClassLoader(), "pathList");// 獲取 classLoader 對象對應的 DexPathList pathList 成員Object pathList = pathListField.get(getClassLoader());//階段三 : 獲取封裝在 DexPathList 類中的 Element[] dexElements 數組Field dexElementsField = ReflexUtils.reflexField(pathList, "dexElements");// 獲取 pathList 對象對應的 Element[] dexElements 數組成員Object[] dexElements = (Object[]) dexElementsField.get(pathList);/*2 . 在本應用中創建 Element[] dexElements 數組 , 用于存放解密后的 dex 文件不同的 Android 版本中 , 創建 Element[] dexElements 數組的方法不同 , 這里需要做兼容*/Method makeDexElements;Object[] addElements = null;if (Build.VERSION.SDK_INT <=Build.VERSION_CODES.M) { // 5.0, 5.1 makeDexElements// 反射 5.0, 5.1, 6.0 版本的 DexPathList 中的 makeDexElements 方法makeDexElements = ReflexUtils.reflexMethod(pathList, "makeDexElements",ArrayList.class, File.class, ArrayList.class);ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();addElements = (Object[]) makeDexElements.invoke(pathList, dexFiles,optimizedDirectory,suppressedExceptions);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 7.0 以上版本 makePathElements// 反射 7.0 以上版本的 DexPathList 中的 makeDexElements 方法makeDexElements = ReflexUtils.reflexMethod(pathList, "makePathElements",List.class, File.class, List.class);ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();addElements = (Object[]) makeDexElements.invoke(pathList, dexFiles,optimizedDirectory,suppressedExceptions);}/*3 . 將 系統加載的 Element[] dexElements 數組與 我們自己的 Element[] dexElements 數組進行合并操作首先創建數組 , 數組類型與 dexElements 數組類型相同將 dexElements 數組中的元素拷貝到 newElements 前半部分, 拷貝元素個數是 dexElements.size將 addElements 數組中的元素拷貝到 newElements 后半部分, 拷貝元素個數是 dexElements.size*/Object[] newElements = (Object[]) Array.newInstance(dexElements.getClass().getComponentType(),dexElements.length + addElements.length);// 將 dexElements 數組中的元素拷貝到 newElements 前半部分, 拷貝元素個數是 dexElements.sizeSystem.arraycopy(dexElements, 0, newElements, 0, dexElements.length);// 將 addElements 數組中的元素拷貝到 newElements 后半部分, 拷貝元素個數是 dexElements.sizeSystem.arraycopy(addElements, 0, newElements, dexElements.length, addElements.length);/*4 . 替換 ClassLoader 加載過程中的 Element[] dexElements 數組 ( 封裝在 DexPathList 中 )*/dexElementsField.set(pathList, newElements);Log.i(TAG, "loadDex 完成");}@Overridepublic void onCreate() {super.onCreate();// 如果之前沒有替換過 , 執行 Application 替換操作// 說明沒有調用到 createPackageContext 方法// 該 createPackageContext 方法只有在創建 ContentProvider 時才調用到// 如果沒有調用到 , 說明 AndroidManifest.xml 中沒有配置 ContentProvider// 此時需要在此處進行 Application 替換if (delegate == null){applicationExchange();}}@Overridepublic String getPackageName() {if(TextUtils.isEmpty(app_name)){// 如果 AndroidManifest.xml 中配置的 Application 全類名為空// 那么 不做任何操作}else{// 如果 AndroidManifest.xml 中配置的 Application 全類名不為空// 為了使 ActivityThread 的 installProvider 方法// 無法命中如下兩個分支// 分支一 : context.getPackageName().equals(ai.packageName)// 分支二 : mInitialApplication.getPackageName().equals(ai.packageName)// 設置該方法返回值為空 , 上述兩個分支就無法命中return "";}return super.getPackageName();}@Overridepublic Context createPackageContext(String packageName, int flags)throws PackageManager.NameNotFoundException {if(TextUtils.isEmpty(app_name)){// 如果 AndroidManifest.xml 中配置的 Application 全類名為空// 說明沒有進行 dex 加密操作 , 返回父類方法執行即可return super.createPackageContext(packageName, flags);}else{// 只有在創建 ContentProvider 時才調用到該 createPackageContext 方法 ,// 如果沒有調用到該方法 , 說明該應用中沒有配置 ContentProvider ;// 該方法不一定會調用到// 先進行 Application 替換applicationExchange();// Application 替換完成之后 , 再繼續向下執行創建 ContentProviderreturn delegate;}}/*** 調用 applicationExchange 替換 Application* 該成員就是替換后的 Application*/private Application delegate;/*** Application 替換主方法*/private void applicationExchange(){try {/*在此處進行 Application 替換*/// 先判斷是否有配置 Application ,// 那么在 Manifest.xml 中的 meta-data 元數據 app_name 不為空// 如果開發者沒有自定義 Application , 沒有配置元數據 , 直接退出if (TextUtils.isEmpty(app_name)) {return;}// 獲取上下文對象 , 保存下來 , 之后要使用Context baseContext = getBaseContext();// 通過反射獲取 Application , 系統也是進行的反射操作Class<?> delegateClass = Class.forName(app_name);// 創建用戶真實配置的 Applicationdelegate = (Application) delegateClass.newInstance();// 調用 Application 的 attach 函數// 該函數無法直接調用 , 也需要通過反射調用// 這里先通過反射獲取 Application 的 attach 函數Method attach = Application.class.getDeclaredMethod("attach", Context.class);// attach 方法是私有的 , 設置 attach 方法允許訪問attach.setAccessible(true);// 獲取上下文對象 ,// 該 Context 是通過調用 Application 的 attachBaseContext 方法傳入的 ContextImpl// 將該上下文對象傳入 Application 的 attach 方法中attach.invoke(delegate, baseContext);/*參考 : https://hanshuliang.blog.csdn.net/article/details/111569017 博客查詢應該替換哪些對象中的哪些成員截止到此處, Application 創建完畢 , 下面開始逐個替換下面的 Application① ContextImpl 的 private Context mOuterContext成員是 kim.hsl.multipledex.ProxyApplication 對象 ;② ActivityThread 中的 ArrayList<Application> mAllApplications集合中添加了 kim.hsl.multipledex.ProxyApplication 對象 ;③ LoadedApk 中的 mApplication 成員是 kim.hsl.multipledex.ProxyApplication 對象 ;④ ActivityThread 中的 Application mInitialApplication成員是 kim.hsl.multipledex.ProxyApplication 對象 ;*/// I . 替換 ① ContextImpl 的 private Context mOuterContext// 成員是 kim.hsl.multipledex.ProxyApplication 對象Class<?> contextImplClass = Class.forName("android.app.ContextImpl");// 獲取 ContextImpl 中的 mOuterContext 成員Field mOuterContextField = contextImplClass.getDeclaredField("mOuterContext");// mOuterContext 成員是私有的 , 設置可訪問性mOuterContextField.setAccessible(true);// ContextImpl 就是應用的 Context , 直接通過 getBaseContext() 獲取即可mOuterContextField.set(baseContext, delegate);// II . 替換 ④ ActivityThread 中的 Application mInitialApplication// 成員是 kim.hsl.multipledex.ProxyApplication 對象 ;Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");// 獲取 ActivityThread 中的 mInitialApplication 成員Field mInitialApplicationField =activityThreadClass.getDeclaredField("mInitialApplication");// mInitialApplication 成員是私有的 , 設置可訪問性mInitialApplicationField.setAccessible(true);// 從 ContextImpl 對象中獲取其 ActivityThread mMainThread 成員變量Field mMainThreadField = contextImplClass.getDeclaredField("mMainThread");mMainThreadField.setAccessible(true);// ContextImpl 就是本應用的上下文對象 , 調用 getBaseContext 方法獲得Object mMainThread = mMainThreadField.get(baseContext);// ContextImpl 就是應用的 Context , 直接通過 getBaseContext() 獲取即可mInitialApplicationField.set(mMainThread, delegate);// III . 替換 ② ActivityThread 中的 ArrayList<Application> mAllApplications// 集合中添加了 kim.hsl.multipledex.ProxyApplication 對象 ;// 獲取 ActivityThread 中的 mAllApplications 成員Field mAllApplicationsField =activityThreadClass.getDeclaredField("mAllApplications");// mAllApplications 成員是私有的 , 設置可訪問性mAllApplicationsField.setAccessible(true);// 獲取 ActivityThread 中的 ArrayList<Application> mAllApplications 隊列ArrayList<Application> mAllApplications =(ArrayList<Application>) mAllApplicationsField.get(mMainThread);// 將真實的 Application 添加到上述隊列中mAllApplications.add(delegate);// IV . 替換 ③ LoadedApk 中的 mApplication// 成員是 kim.hsl.multipledex.ProxyApplication 對象// 1. 先獲取 LoadedApk 對象// LoadedApk 是 ContextImpl 中的 LoadedApk mPackageInfo 成員變量// 從 ContextImpl 對象中獲取其 LoadedApk mPackageInfo 成員變量Field mPackageInfoField = contextImplClass.getDeclaredField("mPackageInfo");mPackageInfoField.setAccessible(true);// ContextImpl 就是本應用的上下文對象 , 調用 getBaseContext 方法獲得Object mPackageInfo = mPackageInfoField.get(baseContext);// 2. 獲取 LoadedApk 對象中的 mApplication 成員Class<?> loadedApkClass = Class.forName("android.app.LoadedApk");// 獲取 ActivityThread 中的 mInitialApplication 成員Field mApplicationField =loadedApkClass.getDeclaredField("mApplication");// LoadedApk 中的 mApplication 成員是私有的 , 設置可訪問性mApplicationField.setAccessible(true);// 3. 將 Application 設置給 LoadedApk 中的 mApplication 成員mApplicationField.set(mPackageInfo, delegate);// V . 下一步操作替換替換 ApplicationInfo 中的 className , 該操作不是必須的 , 不替換也不會報錯// 在應用中可能需要操作獲取應用的相關信息 , 如果希望獲取準確的信息 , 需要替換 ApplicationInfo// ApplicationInfo 在 LoadedApk 中Field mApplicationInfoField = loadedApkClass.getDeclaredField("mApplicationInfo");// 設置該字段可訪問mApplicationInfoField.setAccessible(true);// mPackageInfo 就是 LoadedApk 對象// mApplicationInfo 就是從 LoadedApk 對象中獲得的 mApplicationInfo 字段ApplicationInfo mApplicationInfo = (ApplicationInfo) mApplicationInfoField.get(mPackageInfo);// 設置 ApplicationInfo 中的 className 字段值mApplicationInfo.className = app_name;// 再次調用 onCreate 方法delegate.onCreate();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchFieldException exception) {exception.printStackTrace();}} }



四、日志分析



修改后再次分析 ContentProvider 中的 Application 是替換后的 MyApplication ;

2021-04-05 19:31:27.928 15336-15336/kim.hsl.dex I/octopus.MyApplication: Application : kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:27.929 15336-15336/kim.hsl.dex I/octopus.MyApplication: ApplicationContext : kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:27.929 15336-15336/kim.hsl.dex I/octopus.MyApplication: ApplicationInfo.className : kim.hsl.dex.MyApplication 2021-04-05 19:31:27.929 15336-15336/kim.hsl.dex I/octopus.MyProvider: Application : kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:27.929 15336-15336/kim.hsl.dex I/octopus.MyProvider: ApplicationContext : kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:27.929 15336-15336/kim.hsl.dex I/octopus.MyProvider: ApplicationInfo.className : kim.hsl.dex.MyApplication 2021-04-05 19:31:28.030 15336-15336/kim.hsl.dex I/octopus.MainActivity: Application : kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:28.030 15336-15336/kim.hsl.dex I/octopus.MainActivity: ApplicationContext : kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:28.030 15336-15336/kim.hsl.dex I/octopus.MainActivity: ApplicationInfo.className : kim.hsl.dex.MyApplication 2021-04-05 19:31:28.033 15336-15336/kim.hsl.dex E/octopus.MyProvider: MyProvider delete : kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:28.051 15336-15336/kim.hsl.dex I/octopus.MyService: Application : kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:28.051 15336-15336/kim.hsl.dex I/octopus.MyService: ApplicationContext : kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:28.051 15336-15336/kim.hsl.dex I/octopus.MyService: ApplicationInfo.className : kim.hsl.dex.MyApplication 2021-04-05 19:31:28.052 15336-15336/kim.hsl.dex I/octopus.MyBroadCastReciver: reciver:android.app.ReceiverRestrictedContext@e207566 2021-04-05 19:31:28.052 15336-15336/kim.hsl.dex I/octopus.MyBroadCastReciver: reciver:kim.hsl.dex.MyApplication@5439ba9 2021-04-05 19:31:28.052 15336-15336/kim.hsl.dex I/octopus.MyBroadCastReciver: reciver:kim.hsl.dex.MyApplication



五、源碼資源



DEX 加密源碼資源 :

  • GitHub 地址 : https://github.com/han1202012/DexEncryption
  • CSDN 源碼快照 : https://download.csdn.net/download/han1202012/16465693
  • 注意事項 : DexEncryption\multiple-dex-tools\src\main\java\kim\hsl\multiple_dex_tools 中的 Main.kt 中 , sdkDirectory 修改成你自己電腦上的 SDK 配置 , 需要使用其中的 build-tools 下的 簽名工具 , 對齊工具 等 ;
val sdkDirectory = "Y:/001_DevelopTools/002_Android_SDK/"
  • 執行流程 : 先按照 【Android 安全】DEX 加密 ( Java 工具開發 | 加密解密算法 API | 編譯代理 Application 依賴庫 | 解壓依賴庫 aar 文件 ) 生成依賴庫的 aar 文件 , 然后選擇 菜單欄 -> Build -> Build Bundle(s) / APK (s) 選項 , 最后執行 DexEncryption\multiple-dex-tools\src\main\java\kim\hsl\multiple_dex_tools 中的 Main.kt 文件 ;

總結

以上是生活随笔為你收集整理的【Android 安全】DEX 加密 ( Application 替换 | 兼容 ContentProvider 操作 | 源码资源 )的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 老熟妇仑乱一区二区视频 | xxxxxhd亚洲人hd | 草草浮力影院 | 亚洲乱码国产乱码精品精软件 | 男人天堂2014| 精品在线免费观看视频 | 18禁网站免费无遮挡无码中文 | 国产乱子伦精品无码码专区 | 91精品欧美| 一级伦理片 | 国产精品jizz| 国产男女网站 | 女人下面流白浆的视频 | 五月天中文字幕av | 国产精品aⅴ | 无码精品人妻一区二区 | 国产美女久久 | 午夜精产品一区二区在线观看的 | 色视频在线看 | 五月天综合婷婷 | 91亚洲国产成人久久精品网站 | 欧美日韩少妇精品 | 黄色中文 | 农民工hdxxxx性中国 | 美美女高清毛片视频免费观看 | 思思99精品视频在线观看 | 男生女生插插插 | 欧美亚洲色综久久精品国产 | 久草视频精品 | 亚洲精选免费 | 久久天天操 | av资源网在线 | 国产麻豆一精品一av一免费 | 一本亚洲 | 欧美xxxxx视频 | 超碰v | 青春草在线视频观看 | 51精品国产人成在线观看 | 精品久久久中文字幕人妻 | 国产福利一区二区三区 | 久久综合操 | 观看av| 狠狠躁夜夜躁人 | 武林美妇肉伦娇喘呻吟 | 森泽佳奈中文字幕 | 亚洲在线天堂 | 就去色av| 啪啪啪一区二区 | 国产精品成人69xxx免费视频 | 日韩色吧| 国产成人精品免费看视频 | 丝袜人妻一区二区三区 | 少女忠诚电影高清免费 | 亚洲av无码乱码在线观看性色 | 午夜精品国产精品大乳美女 | 久久精品视频播放 | 天天操夜夜操 | 亚洲综合欧美综合 | 91国偷自产中文字幕久久 | 日本xxxxxxxxx| 污视频免费在线观看网站 | 香蕉网在线观看 | 波多野结衣视频在线看 | www.成人精品 | 久久久精品中文字幕麻豆发布 | 夜夜骑夜夜 | jizz在线免费观看 | 最好看的日本字幕mv视频大全 | www婷婷av久久久影片 | 献给魔王伊伏洛基亚吧动漫在线观看 | 亚洲最新在线 | 欧美放荡办公室videos4k | 芒果视频在线观看免费 | 中文字幕在线观看视频一区 | 在线永久看片免费的视频 | 国产激情视频在线观看 | 久久888| 性欧美视频 | 玉女心经是什么意思 | 美女福利视频导航 | 亚洲乱码国产乱码精品精大量 | 黄网站色| 男女黄色网 | 成人精品视频一区二区 | 亚洲视频综合网 | 老外一级黄色片 | 欧美国产中文字幕 | 午夜家庭影院 | 欧美一区二区三区四区五区六区 | 国产成人亚洲综合 | 男人天堂网在线视频 | 国产xxxx做受视频 | 五月天丁香激情 | 韩国福利一区 | 麻豆国产一区二区 | 强伦轩人妻一区二区电影 | 日本一级理论片在线大全 | 亚洲精品一区三区三区在线观看 | 玖玖在线资源 |