Dex简单保护
字数 1128 2025-08-22 18:37:22
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. 关键代码分析
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. 关键点说明
- attachBaseContext:这是壳程序最先执行的方法,在这里完成解密和加载
- DexClassLoader:用于动态加载解密后的Dex文件
- 反射操作:通过反射修改Android运行时关键对象,实现Application替换
- 清理原有引用:必须清理掉原有Application的引用,避免冲突
四、加密处理实现
虽然示例中为了简化没有实际加密,但实际应用中应该:
- 选择合适的加密算法(AES等)
- 将加密后的Dex文件存储在特定位置
- 壳程序运行时解密到内存或临时目录
五、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">
修改步骤:
- 使用apktool反编译原始APK
- 修改application的android:name属性值为壳程序的Application类
- 重新打包并签名
六、实现效果
成功加固后:
- 应用能正常打开执行
- 原始Dex代码被保护
- 反编译只能看到壳程序代码
七、进阶优化方向
- 加密算法增强:使用更复杂的加密方式
- 反调试检测:在壳程序中加入反调试代码
- 完整性校验:校验Dex文件完整性防止篡改
- 多级加载:采用多级解密加载增加分析难度
- 虚拟机检测:检测是否运行在模拟器中
八、参考资源
- CSDN加壳原理
- Android源码分析:ActivityThread、LoadedApk等类
- DexClassLoader官方文档
通过以上技术实现,可以有效保护Android应用的Dex文件不被轻易反编译和分析,提高应用的安全性。