Android第一代落地DEX加固总结
字数 1354 2025-08-22 12:23:00
Android第一代DEX落地加固技术详解
一、DEX加固基础理论
1.1 DEX文件关键字段
DEX加固主要涉及DEX头部的三个关键字段:
- checksum:文件校验码,使用alder32算法校验文件除去magic、checksum外余下的所有文件区域
- signature:使用SHA-1算法hash除去magic、checksum和signature外余下的所有文件区域
- file_size:DEX文件的大小
1.2 加固基本原理
DEX加固的核心是一个拼接过程:
成品 = 待加固app(加密后) + 解密dex
加固后需要手动修改上述三个字段以保证DEX文件的合法性。
二、加固流程
2.1 加密源APP流程
- 加密源APP
- 将加密后的源APP放在壳(classes.dex)后面,最后4个字节填充源APP大小
- 修改壳的checksum、signature和file_size
2.2 解壳流程
- 主动读取classes.dex末端的加密数据
- 调用本APK的解密方法进行解密
- 动态加载解密之后的APK
三、关键技术实现
3.1 动态加载解密APK
使用DexClassLoader加载解密后的APK,并替换LoadedApk中的mClassLoader字段:
RefInvoke.setFieldOjbect("android.app.LoadedApk","mClassLoader",
weakReference.get(), DexClassLoader);
3.2 加密和组装实现
public static void main(String[] args) {
try {
// 读取源APK和壳DEX
File payloadSrcFile = new File("原apk路径");
File unShellDexFile = new File("壳dex路径");
// 加密源APK(此处使用异或加密示例)
byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile), 0x66);
byte[] unShellDexArray = readFileBytes(unShellDexFile);
int payloadLen = payloadArray.length;
int unShellDexLen = unShellDexArray.length;
int totalLen = payloadLen + unShellDexLen + 4; // 最后4字节存储源APP大小
byte[] newdex = new byte[totalLen];
// 组装新DEX
System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);
System.arraycopy(payloadArray, 0, newdex, unShellDexLen, payloadLen);
System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4);
// 修正DEX头
fixFileSizeHeader(newdex);
fixSHA1Header(newdex);
fixCheckSumHeader(newdex);
// 保存新DEX
File file = new File("输出路径");
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream localFileOutputStream = new FileOutputStream(file);
localFileOutputStream.write(newdex);
localFileOutputStream.flush();
localFileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
3.3 System.arraycopy方法详解
方法原型:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
参数说明:
- src: 源数组
- srcPos: 源数组开始复制的位置
- dest: 目标数组
- destPos: 目标数组开始粘贴的位置
- length: 要复制的元素数量
四、解壳APK实现要点
4.1 AndroidManifest.xml配置
解壳APP不需要启动界面,直接配置主启动Activity为待加固APP的入口:
<activity android:name="待加固APP的主Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
4.2 Application类配置
在解壳APP的AndroidManifest.xml中添加描述信息:
<meta-data
android:name="APPLICATION_CLASS_NAME"
android:value="com.android.sourceapp.XApplication"/>
其中com.android.sourceapp.XApplication是源APP的一个简单Application类:
package com.android.sourceapp;
import android.app.Application;
public class XApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
}
}
五、实践注意事项
- 加固后处理:生成加密后的dex后,直接替换原APP的classes.dex,无需重新签名
- 首次运行:第一次运行会进行解密操作,后续运行直接使用解密后的APK(落地加载特性)
- 测试环境:建议在Android 9环境(如雷电模拟器9)进行测试
六、参考实现
-
DEX头修改函数:
fixFileSizeHeader():修正DEX文件大小fixSHA1Header():重新计算SHA1签名fixCheckSumHeader():重新计算校验和
-
辅助函数:
readFileBytes():读取文件为字节数组intToByte():整型转字节数组encrpt():加密函数(示例中使用简单异或加密)
七、常见问题解决
- Application替换失败:确保meta-data中的APPLICATION_CLASS_NAME指向源APP的正确Application类
- 类加载问题:检查DexClassLoader的路径设置和权限
- 签名验证:某些APP可能有签名校验,需要在源APP中处理或绕过
通过以上步骤和注意事项,可以实现Android第一代DEX落地加固方案,有效保护APK中的代码安全。