深入理解Apk加固之Dex保护
字数 2390 2025-08-22 18:37:22

深入理解Apk加固之Dex保护

1. 预知识:Android应用启动过程

Android应用的启动过程是一个复杂的流程,理解这个过程对于掌握Dex保护技术至关重要:

  1. Launcher启动阶段

    • 用户点击图标,Launcher应用程序通知ActivityManagerService开启Activity
    • ActivityManagerService通过IBinder向Launcher组件发送SCHEDULE_PAUSE_ACTIVITY_TRANSACTION
    • Launcher的ActivityThread处理消息,暂停Launcher组件,发送ACTIVITY_PAUSED_TRANSACTION消息给ActivityManagerService
  2. 进程创建阶段

    • ActivityManagerService处理Launcher消息,创建应用程序进程
    • 通过zygote创建新的应用进程和新的ActivityThread
  3. 应用初始化阶段

    • 应用进程向ActivityManagerService发送ATTACH_APPLICATION_TRANSACTION
    • ActivityManagerService初始化ProcessRecord对象
    • 执行bindApplication,创建和初始化Application类
  4. Activity启动阶段

    • 在启动Activity前检查Application是否创建
    • 实例化Application初始化全局变量
    • 通过全局变量启动Activity组件

2. Java类加载机制

2.1 类加载器类型

  1. 引导类加载器(Bootstrap ClassLoader)

    • 加载Java核心库
    • 原生代码实现,不继承自java.lang.ClassLoader
  2. 扩展类加载器(Extensions ClassLoader)

    • 加载Java扩展库
    • 在扩展库目录中查找并加载类
  3. 系统类加载器(System ClassLoader)

    • 根据CLASSPATH加载Java类
    • 通过ClassLoader.getSystemClassLoader()获取

2.2 类加载器代理模式

类加载器采用双亲委派模型

  • 子类加载器加载类时,先委托给父类加载器
  • 父类加载器无法加载时,才由子类加载器加载
  • defineClass:真正完成类的加载工作(定义加载器)
  • loadClass:启动类的加载过程(初始加载器)

2.3 重要特性

  1. 类标识

    • Java虚拟机判断类是否相同,需要类名和定义加载器都相同
    • 不同类加载器加载的类是不兼容的,形成相互隔离的类空间
  2. 线程上下文类加载器

    • 默认使用系统类加载器
    • 解决引导类加载器无法加载系统类加载器需要加载的类的问题

3. Android中的DexClassLoader

3.1 DexClassLoader工作原理

  1. 设置父类加载器
  2. 优化并加载dex文件到内存中(通过本地方法实现)

3.2 默认类加载器

Android默认使用PathClassLoader加载应用类

4. Dex保护技术原理

4.1 基本思路

  1. 动态加载Dex文件:防止直接反编译原始Dex
  2. 替换关键组件:在运行时替换系统加载的Application和类加载器

4.2 具体实现步骤

  1. 创建壳Application

    • 自定义一个壳Application并生成壳Dex文件
    • 系统启动时先加载壳Application
  2. 加载原始Dex

    • 在壳Application中动态加载原始Dex文件
    • 使用DexClassLoader加载原始Dex
  3. 替换关键对象

    • 替换ActivityThread中LoadedApk对象的mClassLoader
    • 创建原始Dex文件的Application对象
    • 替换ActivityThread中的关键Application引用
  4. 启动原始应用

    • 调用原始Application的onCreate方法
    • 完成原始应用组件的启动

4.3 关键替换点

  1. 替换ActivityThread中mBoundApplication的mApplication属性
  2. 替换ActivityThread中的mInitialApplication属性
  3. 替换LoadedApk的className为原始Application的name
  4. 替换mBoundApplication里appInfo的className
  5. 替换mProviderMap中每个provider的mContext
  6. 调用原始Application的onCreate方法

5. 具体实现代码分析

5.1 类加载器替换

// 获取当前ActivityThread对象
String classActivityThread = "android.app.ActivityThread";
Object activityThread = RefInvoke.invokeStaticMethod(classActivityThread, 
    "currentActivityThread", new Class[]{}, new Object[]{});

// 获取当前包名和LoadedApk对象
String packageName = this.getPackageName();
Map<?,?> mPackage = (Map<?,?>)RefInvoke.getField(activityThread, 
    classActivityThread, "mPackages");
WeakReference<?> wr = (WeakReference<?>)mPackage.get(packageName);

// 创建DexClassLoader加载原始Dex
DexClassLoader loader = new DexClassLoader(
    dexPathList.toString(), 
    mApp.getCacheDir().getAbsolutePath(),
    nativeLib.getAbsolutePath(),
    (ClassLoader)RefInvoke.getField(wr.get(), "android.app.LoadedApk", "mClassLoader"));

// 替换LoadedApk的类加载器
RefInvoke.setField(wr.get(), "android.app.LoadedApk", "mClassLoader", loader);

5.2 Application替换

// 原始Application类名
String applicationName = "com.example.RealApplication";

// 获取当前ActivityThread和LoadedApk对象
Object currentActivityThread = RefInvoke.invokeStaticMethod(
    "android.app.ActivityThread", "currentActivityThread", new Class[]{}, new Object[]{});
Object mBoundApplication = RefInvoke.getField(currentActivityThread, 
    "android.app.ActivityThread", "mBoundApplication");
Object loadedApkInfo = RefInvoke.getField(mBoundApplication, 
    "android.app.ActivityThread$AppBindData", "info");

// 重置Application相关属性
RefInvoke.setField(loadedApkInfo, "android.app.LoadedApk", "mApplication", null);

// 移除旧的Application
Object mInitApplication = RefInvoke.getField(currentActivityThread, 
    "android.app.ActivityThread", "mInitialApplication");
List<Application> mAllApplications = (List<Application>)RefInvoke.getField(
    currentActivityThread, "android.app.ActivityThread", "mAllApplications");
mAllApplications.remove(mInitApplication);

// 设置新的Application类名
((ApplicationInfo)RefInvoke.getField(loadedApkInfo, 
    "android.app.LoadedApk", "mApplicationInfo")).className = applicationName;
((ApplicationInfo)RefInvoke.getField(mBoundApplication, 
    "android.app.ActivityThread$AppBindData", "appInfo")).className = applicationName;

// 创建并设置新的Application对象
Application makeApplication = (Application)RefInvoke.invokeMethod(
    loadedApkInfo, "android.app.LoadedApk", "makeApplication", 
    new Class[]{boolean.class, Instrumentation.class}, 
    new Object[]{false, null});
RefInvoke.setField(currentActivityThread, "android.app.ActivityThread", 
    "mInitialApplication", makeApplication);

// 更新Provider的Context
Map<?,?> mProviderMap = (Map<?,?>)RefInvoke.getField(
    currentActivityThread, "android.app.ActivityThread", "mProviderMap");
for(Map.Entry<?, ?> entry : mProviderMap.entrySet()) {
    Object providerClientRecord = entry.getValue();
    Object mLocalProvider = RefInvoke.getField(providerClientRecord, 
        "android.app.ActivityThread$ProviderClientRecord", "mLocalProvider");
    RefInvoke.setField(mLocalProvider, "android.content.ContentProvider", 
        "mContext", makeApplication);
}

// 启动原始Application
makeApplication.onCreate();

6. 关键技术点总结

  1. 动态加载机制

    • 利用DexClassLoader动态加载加密的原始Dex
    • 保持原始Dex不在APK中直接暴露
  2. 运行时替换

    • 通过反射替换系统关键对象
    • 无缝切换壳Application和原始Application
  3. 组件生命周期管理

    • 正确处理Application生命周期
    • 确保ContentProvider等组件使用正确的Context
  4. 类加载隔离

    • 维护正确的类加载器关系
    • 避免类加载冲突和ClassNotFoundException

7. 参考资源

  1. 深入探讨Java类加载器
  2. 示例代码仓库

8. 实际应用注意事项

  1. 兼容性问题

    • 不同Android版本实现可能有差异
    • 需要测试主要API版本
  2. 性能影响

    • 动态加载会增加启动时间
    • 需要优化加载过程
  3. 加固强度

    • 结合其他保护措施(如Native保护、代码混淆)
    • 防止内存Dump等攻击
  4. 反调试措施

    • 检测调试器
    • 防止动态分析

通过这种Dex保护技术,可以有效防止直接反编译APK获取原始代码,提高Android应用的安全性。

深入理解Apk加固之Dex保护 1. 预知识:Android应用启动过程 Android应用的启动过程是一个复杂的流程,理解这个过程对于掌握Dex保护技术至关重要: Launcher启动阶段 : 用户点击图标,Launcher应用程序通知ActivityManagerService开启Activity ActivityManagerService通过IBinder向Launcher组件发送SCHEDULE_ PAUSE_ ACTIVITY_ TRANSACTION Launcher的ActivityThread处理消息,暂停Launcher组件,发送ACTIVITY_ PAUSED_ TRANSACTION消息给ActivityManagerService 进程创建阶段 : ActivityManagerService处理Launcher消息,创建应用程序进程 通过zygote创建新的应用进程和新的ActivityThread 应用初始化阶段 : 应用进程向ActivityManagerService发送ATTACH_ APPLICATION_ TRANSACTION ActivityManagerService初始化ProcessRecord对象 执行bindApplication,创建和初始化Application类 Activity启动阶段 : 在启动Activity前检查Application是否创建 实例化Application初始化全局变量 通过全局变量启动Activity组件 2. Java类加载机制 2.1 类加载器类型 引导类加载器(Bootstrap ClassLoader) : 加载Java核心库 原生代码实现,不继承自java.lang.ClassLoader 扩展类加载器(Extensions ClassLoader) : 加载Java扩展库 在扩展库目录中查找并加载类 系统类加载器(System ClassLoader) : 根据CLASSPATH加载Java类 通过ClassLoader.getSystemClassLoader()获取 2.2 类加载器代理模式 类加载器采用 双亲委派模型 : 子类加载器加载类时,先委托给父类加载器 父类加载器无法加载时,才由子类加载器加载 defineClass :真正完成类的加载工作(定义加载器) loadClass :启动类的加载过程(初始加载器) 2.3 重要特性 类标识 : Java虚拟机判断类是否相同,需要类名和定义加载器都相同 不同类加载器加载的类是不兼容的,形成相互隔离的类空间 线程上下文类加载器 : 默认使用系统类加载器 解决引导类加载器无法加载系统类加载器需要加载的类的问题 3. Android中的DexClassLoader 3.1 DexClassLoader工作原理 设置父类加载器 优化并加载dex文件到内存中(通过本地方法实现) 3.2 默认类加载器 Android默认使用 PathClassLoader 加载应用类 4. Dex保护技术原理 4.1 基本思路 动态加载Dex文件 :防止直接反编译原始Dex 替换关键组件 :在运行时替换系统加载的Application和类加载器 4.2 具体实现步骤 创建壳Application : 自定义一个壳Application并生成壳Dex文件 系统启动时先加载壳Application 加载原始Dex : 在壳Application中动态加载原始Dex文件 使用DexClassLoader加载原始Dex 替换关键对象 : 替换ActivityThread中LoadedApk对象的mClassLoader 创建原始Dex文件的Application对象 替换ActivityThread中的关键Application引用 启动原始应用 : 调用原始Application的onCreate方法 完成原始应用组件的启动 4.3 关键替换点 替换ActivityThread中mBoundApplication的mApplication属性 替换ActivityThread中的mInitialApplication属性 替换LoadedApk的className为原始Application的name 替换mBoundApplication里appInfo的className 替换mProviderMap中每个provider的mContext 调用原始Application的onCreate方法 5. 具体实现代码分析 5.1 类加载器替换 5.2 Application替换 6. 关键技术点总结 动态加载机制 : 利用DexClassLoader动态加载加密的原始Dex 保持原始Dex不在APK中直接暴露 运行时替换 : 通过反射替换系统关键对象 无缝切换壳Application和原始Application 组件生命周期管理 : 正确处理Application生命周期 确保ContentProvider等组件使用正确的Context 类加载隔离 : 维护正确的类加载器关系 避免类加载冲突和ClassNotFoundException 7. 参考资源 深入探讨Java类加载器 示例代码仓库 8. 实际应用注意事项 兼容性问题 : 不同Android版本实现可能有差异 需要测试主要API版本 性能影响 : 动态加载会增加启动时间 需要优化加载过程 加固强度 : 结合其他保护措施(如Native保护、代码混淆) 防止内存Dump等攻击 反调试措施 : 检测调试器 防止动态分析 通过这种Dex保护技术,可以有效防止直接反编译APK获取原始代码,提高Android应用的安全性。