安卓代码加固
安卓代码加固(5):XOR混淆
so 里的资源路径、实现类名、方法名按 0x5A 异或存放,用完立即清零。
对 libhiddendecrypt.so 执行 strings,不应直接得到 payload/hidden.dex.enc 或 HiddenLandingPage。这些字面量在源码里写成 uint8_t 数组,运行时再异或还原。
hidden-sdk/src/main/cpp/hidden_bridge.cpp
#define XOR_KEY 0x5Au
static void xor_decode(char *dst, const uint8_t *src, size_t n) {
for (size_t i = 0; i < n; ++i) {
dst[i] = static_cast<char>(src[i] ^ XOR_KEY);
}
dst[n] = '\0';
}
这只是提高扫描字符串的成本,不是密码学保护。密钥是单字节
0x5A,知道算法就能还原。它要阻止的是:打开 so 就能搜到类名和路径。隐藏资源路径
read_asset 不写 "payload/hidden.dex.enc"。22 个字节解开后才是这条路径:
hidden-sdk/src/main/cpp/hidden_bridge.cpp
static const uint8_t path_enc[] = {
0x2a, 0x3b, 0x23, 0x36, 0x35, 0x3b, 0x3e, 0x75, 0x32, 0x33, 0x3e,
0x3e, 0x3f, 0x34, 0x74, 0x3e, 0x3f, 0x22, 0x74, 0x3f, 0x34, 0x39
};
char path[32];
xor_decode(path, path_enc, sizeof(path_enc));
AAsset *asset = AAssetManager_open(am, path, AASSET_MODE_BUFFER);
hidden_secure_zero(path, sizeof(path));
打开后立刻清除栈上的明文路径。Java 的 HiddenCrypto.ASSET_PATH 仍是明文字符串,但主流程不再用它读取文件。
隐藏实现类名
加载时的 FQCN 同样编码。解开后是 com.example.minidex.hidden.HiddenLandingPage:
hidden-sdk/src/main/cpp/hidden_bridge.cpp
static const uint8_t cls_enc[] = {
0x39, 0x35, 0x37, 0x74, 0x3f, 0x22, 0x3b, 0x37, 0x2a, 0x36, 0x3f,
0x74, 0x37, 0x33, 0x34, 0x33, 0x3e, 0x3f, 0x22, 0x74, 0x32, 0x33,
0x3e, 0x3e, 0x3f, 0x34, 0x74, 0x12, 0x33, 0x3e, 0x3e, 0x3f, 0x34,
0x16, 0x3b, 0x34, 0x3e, 0x33, 0x34, 0x3d, 0x0a, 0x3b, 0x3d, 0x3f
};
char cls_name[64];
xor_decode(cls_name, cls_enc, sizeof(cls_enc));
jstring jname = env->NewStringUTF(cls_name);
hidden_secure_zero(cls_name, sizeof(cls_name));
改包名时必须同步修改这组字节,并重新编译 so。
按名查找方法
mid_by_xor 把方法名解到栈上 64 字节缓冲,GetMethodID 之后立刻 hidden_secure_zero:
hidden-sdk/src/main/cpp/hidden_bridge.cpp
static jmethodID mid_by_xor(
JNIEnv *env, jclass cls,
const uint8_t *name_enc, size_t name_len,
const char *sig
) {
char name[64];
xor_decode(name, name_enc, name_len);
jmethodID mid = env->GetMethodID(cls, name, sig);
hidden_secure_zero(name, sizeof(name));
return mid;
}
JNI 签名(例如 (Landroid/content/Context;)Z)目前仍是明文字符串。隐藏的是 业务方法名。
| 编码数组 | 解开后 |
|---|---|
path_enc | payload/hidden.dex.enc |
cls_enc | com.example.minidex.hidden.HiddenLandingPage |
abi_enc | getAbiVersion |
完整方法名对照见 指令分发 的表。例如 op 7 的字节解开是 performAnswer,op 13 是 applyScreenProtection。
改名时如何同步
改 Kotlin 方法名
只改 HiddenLandingPage 不够。so 会 GetMethodID 失败,表现为加载成功,但 invoke 没有实际效果。
重算 XOR 字节
每个字符异或 0x5A。例如 's' ^ 0x5A == 0x29,其余字符同理。
成对升级 ABI
方法表变化后,应提高 ABI门禁 的版本号,避免旧 so 调用新 dex。
Kotlin
val abiVersion 在 JNI 里是 getAbiVersion。属性改名后,XOR 数组也要改成新的 getter 名。