Dex简单保护
字数 1128 2025-08-22 18:37:22

Dex简单保护技术详解

一、加固原理概述

Dex保护的核心思想是通过加密和动态加载技术来保护原始APK/Dex文件不被轻易反编译和分析。主要原理包括:

  1. 加密保护:使用加密算法将原始APK/Dex文件加密
  2. 壳程序:编写专门的壳程序负责运行时解密和加载
  3. 动态加载:运行时动态解密并加载原始Dex文件
  4. 入口重定向:修改AndroidManifest.xml将启动入口指向壳程序

二、加固流程详解

1. 准备工作

  • 编写解密并加载原始APK的壳程序(Shell App)
  • 生成对应的壳Dex文件

2. 加密处理

  • 加密原始APK/Dex文件
  • 将加密后的数据附加到壳Dex尾部或存储到特定目录

3. 文件修改

  • 修改壳Dex的相应字段
  • 修改被加固App的AndroidManifest.xml文件
    • 添加Application组件
    • 将启动入口指向壳程序
  • 删除原始签名文件
  • 替换dex文件
  • 添加必要的so文件

三、壳程序实现细节

1. 壳程序主要功能

  1. 解密出Dex/APK/JAR文件
  2. 替换dexclassloader对象
  3. 执行原始APK的Application类

2. 关键代码分析

public class shell extends Application {
    private Application mApp = this;
    private File LIBS;
    private static final String DEFAULT_APPLICATION = "android.app.Application";

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        
        // 1. 获取原始Dex文件
        LIBS = mApp.getDir("libs", Context.MODE_PRIVATE);
        StringBuffer dexPathList = new StringBuffer();
        dexPathList.append(LIBS).append("/").append("classes.dex");
        
        // 2. 加载Dex文件
        String classActivityThread = "android.app.ActivityThread";
        String classLoadedApk = "android.app.LoadedApk";
        DexClassLoader loader;
        
        Object activityThread = RefInvoke.invokeStaticMethod(
            classActivityThread, "currentActivityThread", 
            new Class[]{}, new Object[]{});
        
        String packageName = this.getPackageName();
        Map<?,?> mPackage = (Map<?,?>)RefInvoke.getField(
            activityThread, classActivityThread, "mPackages");
        
        WeakReference<?> wr = (WeakReference<?>)mPackage.get(packageName);
        
        loader = new DexClassLoader(
            dexPathList.toString(), 
            mApp.getCacheDir().getAbsolutePath(),
            nativeLib.getAbsolutePath(),
            (ClassLoader)RefInvoke.getField(wr.get(), "android.app.LoadedApk", "mClassLoader"));
        
        RefInvoke.setField(wr.get(), classLoadedApk, "mClassLoader", loader);
        
        // 3. 执行原始Application
        String applicationName = "com.scoreloop.games.gearedub.GearApplication";
        
        // 清理原有Application引用
        Object currentActivityThread = RefInvoke.invokeStaticMethod(
            classActivityThread, "currentActivityThread", new Class[]{}, new Object[]{});
        
        Object mBoundApplication = RefInvoke.getField(
            currentActivityThread, classActivityThread, "mBoundApplication");
        
        Object loadedApkInfo = RefInvoke.getField(
            mBoundApplication, classActivityThread + "$AppBindData", "info");
        
        RefInvoke.setField(loadedApkInfo, classLoadedApk, "mApplication", null);
        
        // 移除初始Application
        Object mInitApplication = RefInvoke.getField(
            currentActivityThread, classActivityThread, "mInitialApplication");
        
        List<Application> mAllApplications = (List<Application>)RefInvoke.getField(
            currentActivityThread, classActivityThread, "mAllApplications");
        
        mAllApplications.remove(mInitApplication);
        
        // 设置新的Application类名
        ((ApplicationInfo)RefInvoke.getField(
            loadedApkInfo, classLoadedApk, "mApplicationInfo")).className = applicationName;
        
        ((ApplicationInfo)RefInvoke.getField(
            mBoundApplication, classActivityThread + "$AppBindData", "appInfo")).className = applicationName;
        
        // 创建并设置新的Application实例
        Application makeApplication = (Application)RefInvoke.invokeMethod(
            loadedApkInfo, classLoadedApk, "makeApplication", 
            new Class[]{boolean.class, Instrumentation.class}, 
            new Object[]{false, null});
        
        RefInvoke.setField(
            currentActivityThread, classActivityThread, "mInitialApplication", makeApplication);
        
        // 更新Provider的Context
        Map<?,?> mProviderMap = (Map<?,?>)RefInvoke.getField(
            currentActivityThread, classActivityThread, "mProviderMap");
        
        for(Map.Entry<?, ?> entry : mProviderMap.entrySet()) {
            Object providerClientRecord = entry.getValue();
            Object mLocalProvider = RefInvoke.getField(
                providerClientRecord, classActivityThread + "$ProviderClientRecord", "mLocalProvider");
            
            RefInvoke.setField(
                mLocalProvider, "android.content.ContentProvider", "mContext", makeApplication);
        }
        
        makeApplication.onCreate();
    }
}

3. 关键点说明

  1. attachBaseContext:这是壳程序最先执行的方法,在这里完成解密和加载
  2. DexClassLoader:用于动态加载解密后的Dex文件
  3. 反射操作:通过反射修改Android运行时关键对象,实现Application替换
  4. 清理原有引用:必须清理掉原有Application的引用,避免冲突

四、加密处理实现

虽然示例中为了简化没有实际加密,但实际应用中应该:

  1. 选择合适的加密算法(AES等)
  2. 将加密后的Dex文件存储在特定位置
  3. 壳程序运行时解密到内存或临时目录

五、AndroidManifest.xml修改

关键修改点:

<application 
    android:description="@string/app_description"
    android:icon="@drawable/icon"
    android:label="@string/app_label"
    android:name="com.stub.shell"  <!-- 关键修改,指向壳程序 -->
    android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">

修改步骤:

  1. 使用apktool反编译原始APK
  2. 修改application的android:name属性值为壳程序的Application类
  3. 重新打包并签名

六、实现效果

成功加固后:

  1. 应用能正常打开执行
  2. 原始Dex代码被保护
  3. 反编译只能看到壳程序代码

七、进阶优化方向

  1. 加密算法增强:使用更复杂的加密方式
  2. 反调试检测:在壳程序中加入反调试代码
  3. 完整性校验:校验Dex文件完整性防止篡改
  4. 多级加载:采用多级解密加载增加分析难度
  5. 虚拟机检测:检测是否运行在模拟器中

八、参考资源

  1. CSDN加壳原理
  2. Android源码分析:ActivityThread、LoadedApk等类
  3. DexClassLoader官方文档

通过以上技术实现,可以有效保护Android应用的Dex文件不被轻易反编译和分析,提高应用的安全性。

Dex简单保护技术详解 一、加固原理概述 Dex保护的核心思想是通过加密和动态加载技术来保护原始APK/Dex文件不被轻易反编译和分析。主要原理包括: 加密保护 :使用加密算法将原始APK/Dex文件加密 壳程序 :编写专门的壳程序负责运行时解密和加载 动态加载 :运行时动态解密并加载原始Dex文件 入口重定向 :修改AndroidManifest.xml将启动入口指向壳程序 二、加固流程详解 1. 准备工作 编写解密并加载原始APK的壳程序(Shell App) 生成对应的壳Dex文件 2. 加密处理 加密原始APK/Dex文件 将加密后的数据附加到壳Dex尾部或存储到特定目录 3. 文件修改 修改壳Dex的相应字段 修改被加固App的AndroidManifest.xml文件 添加Application组件 将启动入口指向壳程序 删除原始签名文件 替换dex文件 添加必要的so文件 三、壳程序实现细节 1. 壳程序主要功能 解密出Dex/APK/JAR文件 替换dexclassloader对象 执行原始APK的Application类 2. 关键代码分析 3. 关键点说明 attachBaseContext :这是壳程序最先执行的方法,在这里完成解密和加载 DexClassLoader :用于动态加载解密后的Dex文件 反射操作 :通过反射修改Android运行时关键对象,实现Application替换 清理原有引用 :必须清理掉原有Application的引用,避免冲突 四、加密处理实现 虽然示例中为了简化没有实际加密,但实际应用中应该: 选择合适的加密算法(AES等) 将加密后的Dex文件存储在特定位置 壳程序运行时解密到内存或临时目录 五、AndroidManifest.xml修改 关键修改点: 修改步骤: 使用apktool反编译原始APK 修改application的android:name属性值为壳程序的Application类 重新打包并签名 六、实现效果 成功加固后: 应用能正常打开执行 原始Dex代码被保护 反编译只能看到壳程序代码 七、进阶优化方向 加密算法增强 :使用更复杂的加密方式 反调试检测 :在壳程序中加入反调试代码 完整性校验 :校验Dex文件完整性防止篡改 多级加载 :采用多级解密加载增加分析难度 虚拟机检测 :检测是否运行在模拟器中 八、参考资源 CSDN加壳原理 Android源码分析:ActivityThread、LoadedApk等类 DexClassLoader官方文档 通过以上技术实现,可以有效保护Android应用的Dex文件不被轻易反编译和分析,提高应用的安全性。