应用安全 --- 逆向技巧 之 ida反编译yahfa的so和源码的差异化比对
逐函数代码对比分析 1. setupTrampoline C // ============================================================ // 源码 (trampoline.c) // ============================================================ void setupTrampoline(uint8_t offset) { // 仅 aarch64 分支 trampoline[9] |= offset << 4; // 修改trampoline数组第9字节 trampoline[10] |= offset >> 4; // 修改trampoline数组第10字节 // 无返回值 (void) } // ============================================================ // 反编译 (setupTrampoline_0000204C.txt) // ============================================================ __int64 __fastcall setupTrampoline(__int64 result) { byte_4C11 |= 16 * (_BYTE)result; // ← trampoline[9], 16* == <<4 ✓ byte_4C12 |= (int)(unsigned __int8)result >> 4; // ← trampoline[10] ✓ return result; // ← 源码是void, 反编译多了return, 实际无影响 } // ============================================================ // 差异汇总 // ============================================================ // [1] trampoline[9]/[10] 的符号名丢失 → 变为全局地址 byte_4C11/byte_4C12 // [2] 函数返回类型 void → __int64 (反编译误判) // [3] offset<<4 等价表达为 16*result (语义相同) 2. genTrampoline C // ============================================================ // 源码 (trampoline.c) —— aarch64 分支 // ============================================================ void *genTrampoline(void *toMethod, void *entrypoint) { // --- 尺寸计算 --- size_t trampolineSize = entrypoint != NULL ? sizeof(trampolineForBackup) // 28 : sizeof(trampoline); // 24 // --- 空间检查 & 分配 --- if (currentTrampolineOff + trampolineSize > trampolineSpaceEnd) { currentTrampolineOff = allocTrampolineSpace(); // mmap 4096字节 if (currentTrampolineOff == NULL) return NULL; trampolineSpaceEnd = currentTrampolineOff + TRAMPOLINE_SPACE_SIZE; // +4096 } unsigned char *targetAddr = currentTrampolineOff; // --- 拷贝模板 --- if (entrypoint != NULL) memcpy(targetAddr, trampolineForBackup, sizeof(trampolineForBackup)); // 28字节 else memcpy(targetAddr, trampoline, sizeof(trampoline)); // 24字节 // --- 填入实际地址 (aarch64) --- if (entrypoint) { memcpy(targetAddr + 20, &entrypoint, pointer_size); // 写entrypoint到offset 20 memcpy(targetAddr + 12, &toMethod, pointer_size); // 写toMethod 到offset 12 } else { memcpy(targetAddr + 16, &toMethod, pointer_size); // 写toMethod 到offset 16 } // --- 跳过 code_size_ 头部4字节 (仅非backup) --- if (entrypoint == NULL) targetAddr += 4; // --- 对齐推进指针 --- currentTrampolineOff += roundUpToPtrSize(trampolineSize); // 对齐到8字节 return targetAddr; } // ============================================================ // 反编译 (genTrampoline_00001DE0.txt) // ============================================================ __int64 __fastcall genTrampoline(__int64 a1, __int64 a2) { // ↑toMethod ↑entrypoint __int64 v2; // 尺寸 __int64 v4; // targetAddr (currentTrampolineOff快照) __int64 v5; // trampolineSize __int64 v6; // 返回值 __int64 v7; // entrypoint局部拷贝 _QWORD v8[2]; // v8[0]=toMethod, v8[1]=stack canary ← 源码无此结构! v8[1] = *(_QWORD *)(_ReadStatusReg(TPIDR_EL0) + 40); // stack canary v8[0] = a1; // toMethod v7 = a2; // entrypoint // --- 尺寸计算 --- v2 = 28; if (!a2) v2 = 24; // ✓ 与源码等价 v5 = v2; // --- 空间检查 --- if (qword_4C60 + v2 > (unsigned __int64)qword_4C68) { // ↑currentTrampolineOff ↑trampolineSpaceEnd qword_4C60 = sub_2084(); // allocTrampolineSpace() ✓ if (!qword_4C60) { v6 = 0; goto LABEL_16; } qword_4C68 = qword_4C60 + 4096; // ✓ } v4 = qword_4C60; // targetAddr 快照 // --- 拷贝模板 --- if (v7) __memcpy_chk(qword_4C60, &trampolineForBackup, 28, -1); // ✓ else __memcpy_chk(qword_4C60, &trampoline, 24, -1); // ✓ // --- 填入地址 --- if (v7) { __memcpy_chk(v4 + 20, &v7, 8, -1); // entrypoint → offset 20 ✓ __memcpy_chk(v4 + 12, v8, 8, -1); // toMethod → offset 12 ✓ // ↑v8即&toMethod,但反编译将其包装进v8[2]数组 } else { __memcpy_chk(v4 + 16, v8, 8, -1); // toMethod → offset 16 ✓ } // --- 跳过code_size_ --- if (!v7) v4 += 4; // ✓ // --- 对齐推进 --- qword_4C60 += (v5 + 7) & 0xFFFFFFFFFFFFFFF8LL; // ↑ 等价于 roundUpToPtrSize(trampolineSize) ✓ v6 = v4; LABEL_16: _ReadStatusReg(TPIDR_EL0); // ← 源码无此调用! 反编译artifact return v6; } // ============================================================ // 差异汇总 // ============================================================ // [1] toMethod参数: 源码直接用指针, 反编译包装进 v8[2] 数组 // 源码: memcpy(targetAddr+12, &toMethod, 8) // 反编: __memcpy_chk(v4+12, v8, 8, -1) ← v8[0]==toMethod, 等价但不直观 // // [2] memcpy → __memcpy_chk 带额外参数-1 (编译器安全检查插桩) // // [3] 多出 _ReadStatusReg(TPIDR_EL0) 调用 ← 栈canary读取的反编译artifact // // [4] currentTrampolineOff/trampolineSpaceEnd 符号名丢失 // → qword_4C60 / qword_4C68 // // [5] roundUpToPtrSize宏展开为位运算: (v5+7) & 0xFFFFFFFFFFFFFF8 3. allocTrampolineSpace (sub_2084) C // ============================================================ // 源码 (trampoline.c) // ============================================================ static void *allocTrampolineSpace() { unsigned char *buf = mmap( NULL, // addr=0 TRAMPOLINE_SPACE_SIZE, // len=4096 (0x1000) PROT_READ|PROT_WRITE|PROT_EXEC, // prot=7 MAP_ANONYMOUS|MAP_PRIVATE, // flags=34 (0x22) -1, // fd=-1 0 // offset=0 ); if (buf == MAP_FAILED) { // MAP_FAILED == (void*)-1 LOGE("mmap failed, errno = %s", strerror(errno)); return NULL; } else { LOGI("allocating space for trampoline code at %p", buf); return buf; } } // ============================================================ // 反编译 (sub_2084_00002084.txt) // ============================================================ void *sub_2084() { int *v0; // errno指针 char *v1; // strerror结果 void *v3; // mmap返回值 v3 = mmap(0, 0x1000u, 7, 34, -1, 0); // ✓ 参数完全一致 if (v3 == (void *)-1LL) { // ✓ MAP_FAILED检查 v0 = (int *)__errno(); // ← 源码直接用errno宏 v1 = strerror(*v0); // ✓ __android_log_print(6, "xjhook-Native", "mmap failed, errno = %s", v1); // ✓ return 0; } else { __android_log_print(4, "xjhook-Native", "allocating space for trampoline code at %p", v3); // ✓ return v3; } } // ============================================================ // 差异汇总 // ============================================================ // [1] errno访问方式不同: // 源码: strerror(errno) ← 直接用宏 // 反编: v0=__errno(); strerror(*v0) ← 宏展开为函数调用 // // [2] 函数名丢失: allocTrampolineSpace → sub_2084 // [3] LOG_TAG: "YAHFA-Native" → "xjhook-Native" (包名差异) // [4] 字面量: TRAMPOLINE_SPACE_SIZE→0x1000, PROT_*→7, MAP_*→34 (语义等价) 4. getFlags / setFlags (sub_1D90 / sub_1DB8) C // ============================================================ // 源码 (HookMain.c) // ============================================================ static uint32_t getFlags(char *method) { // read32(method + OFFSET_access_flags_in_ArtMethod) // 展开为: *((uint32_t*)(method + OFFSET_access_flags_in_ArtMethod)) uint32_t access_flags = read32(method + OFFSET_access_flags_in_ArtMethod); return access_flags; } static void setFlags(char *method, uint32_t access_flags) { // write32(method + OFFSET_access_flags_in_ArtMethod, access_flags) // 展开为: *((uint32_t*)(method + OFFSET_access_flags_in_ArtMethod)) = access_flags write32(method + OFFSET_access_flags_in_ArtMethod, access_flags); } // ============================================================ // 反编译 (sub_1D90 / sub_1DB8) // ============================================================ __int64 __fastcall sub_1D90(__int64 a1) { return *(unsigned int *)(a1 + (unsigned int)dword_4C54); // ↑ OFFSET_access_flags_in_ArtMethod } __int64 __fastcall sub_1DB8(__int64 result, int a2) { *(_DWORD *)(result + (unsigned int)dword_4C54) = a2; // ↑ OFFSET_access_flags_in_ArtMethod return result; // ← 源码setFlags是void, 反编译多了return } // ============================================================ // 差异汇总 // ============================================================ // [1] 宏展开: read32/write32 → 直接指针解引用 (语义相同) // [2] OFFSET_access_flags_in_ArtMethod 符号名丢失 → dword_4C54 // [3] setFlags返回类型: void → __int64 (反编译误判, 无实际影响) 5. setNonCompilable (sub_1B04) C // ============================================================ // 源码 (HookMain.c) // ============================================================ static uint32_t kAccCompileDontBother = 0x01000000; // 初始值,运行时被init修改 static uint32_t kAccPreCompiled = 0x00200000; // 初始值,运行时被init修改 static void setNonCompilable(void *method) { if (SDKVersion < __ANDROID_API_N__) { // < 24 return; // 直接返回 } uint32_t access_flags = getFlags(method); uint32_t old_flags = access_flags; access_flags |= kAccCompileDontBother; // 设置不编译标志 if (SDKVersion >= __ANDROID_API_R__) { // >= 30 access_flags &= ~kAccPreCompiled; // 清除预编译标志 } setFlags(method, access_flags); LOGI("setNonCompilable: change access flags from 0x%x to 0x%x", old_flags, access_flags); // 无返回值 } // ============================================================ // 反编译 (sub_1B04_00001B04.txt) // ============================================================ __int64 __fastcall sub_1B04(__int64 result) { int v1; // old_flags unsigned int v2; // access_flags __int64 v3; // method指针备份 v3 = result; if (SDKVersion >= 24) { // ✓ __ANDROID_API_N__==24 v1 = sub_1D90(result); // getFlags() ✓ v2 = v1 | dword_4BFC; // |= kAccCompileDontBother ✓ // ↑ 运行时值=0x2000000 (SDK27后被init设置) if (SDKVersion >= 30) // ✓ __ANDROID_API_R__==30 v2 &= ~dword_4BF8; // &= ~kAccPreCompiled ✓ // ↑ 运行时值=0x800000 (SDK31后被init设置) sub_1DB8(v3, v2); // setFlags() ✓ return __android_log_print(4, "xjhook-Native", "setNonCompilable: change access flags from 0x%x to 0x%x", v1, v2); // ← 源码LOGI后无return,反编译将log返回值当函数返回值 } return result; // ← 源码SDKVersion<24时直接return(void),反编译返回method指针 } // ============================================================ // 差异汇总 // ============================================================ // [1] 常量符号名丢失: // kAccCompileDontBother → dword_4BFC (运行时值0x2000000) // kAccPreCompiled → dword_4BF8 (运行时值0x800000) // 注意: 源码初始值与运行时值不同(init函数会修改) // // 源码初始值: 反编译观察到的运行时值: // kAccCompileDontBother=0x01000000 → dword_4BFC=0x2000000 (SDK27+) // kAccPreCompiled=0x00200000 → dword_4BF8=0x800000 (SDK31+) // // [2] 返回类型: void → __int64 // - SDK<24时源码直接return, 反编译return result(method指针) // - 正常路径源码无return, 反编译return __android_log_print()的返回值 // // [3] 函数名丢失: setNonCompilable → sub_1B04 6. replaceMethod (sub_1BB4) C // ============================================================ // 源码 (HookMain.c) // ============================================================ static uint32_t kAccNative = 0x0100; static uint32_t kAccFastInterpreterToInterpreterInvoke = 0x40000000; static int replaceMethod(void *fromMethod, void *toMethod, int isBackup) { LOGI("replace method from %p to %p", fromMethod, toMethod); void *newEntrypoint = NULL; if (isBackup) { // 读取toMethod当前入口点(硬编码) void *originEntrypoint = readAddr( (char*)toMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod ); newEntrypoint = genTrampoline(toMethod, originEntrypoint); } else { newEntrypoint = genTrampoline(toMethod, NULL); } LOGI("replace entry point from %p to %p", readAddr((char*)fromMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod), newEntrypoint); if (newEntrypoint) { // 替换fromMethod的入口点 writeAddr((char*)fromMethod + OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod, newEntrypoint); // 同步interpreter入口点(仅低版本) if (OFFSET_entry_point_from_interpreter_in_ArtMethod != 0) { void *interpEntrypoint = readAddr( (char*)toMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod ); writeAddr((char*)fromMethod + OFFSET_entry_point_from_interpreter_in_ArtMethod, interpEntrypoint); } // Android O+ 修改access flags if (SDKVersion >= __ANDROID_API_O__) { // >= 26 uint32_t access_flags = getFlags(fromMethod); uint32_t old_flags = access_flags; if (SDKVersion >= __ANDROID_API_Q__) // >= 29 access_flags &= ~kAccFastInterpreterToInterpreterInvoke; if (SDKVersion <= __ANDROID_API_Q__) // <= 29 access_flags |= kAccNative; setFlags(fromMethod, access_flags); LOGI("change access flags from 0x%x to 0x%x", old_flags, access_flags); } return 0; } else { LOGE("failed to allocate space for trampoline of target method"); return 1; } } // ============================================================ // 反编译 (sub_1BB4_00001BB4.txt) // ============================================================ __int64 __fastcall sub_1BB4(char *a1, char *a2, int a3) { // ↑fromMethod ↑toMethod ↑isBackup int v4; // old_flags unsigned int v5; // access_flags const void *v6; // newEntrypoint __android_log_print(4, "xjhook-Native", "replace method from %p to %p", a1, a2); // ✓ // --- 生成trampoline --- if (a3) // isBackup v6 = (const void*)genTrampoline( (__int64)a2, *(_QWORD*)&a2[dword_4C58] // readAddr(toMethod+OFFSET_quick) ); // ✓ else v6 = (const void*)genTrampoline((__int64)a2, 0); // ✓ __android_log_print(4, "xjhook-Native", "replace entry point from %p to %p", *(const void**)&a1[dword_4C58], v6); // ✓ if (v6) { *(_QWORD*)&a1[dword_4C58] = v6; // writeAddr(fromMethod+OFFSET_quick) ✓ // interpreter入口点同步 if (dword_4C5C) // OFFSET_entry_point_from_interpreter != 0 ✓ *(_QWORD*)&a1[dword_4C5C] = *(_QWORD*)&a2[dword_4C5C]; // ✓ // access flags修改 if (SDKVersion >= 26) { // __ANDROID_API_O__ ✓ v5 = sub_1D90(a1); // getFlags ✓ v4 = v5; if (SDKVersion >= 29) // __ANDROID_API_Q__ ✓ v5 &= ~dword_4C00; // &= ~kAccFastInterpreterToInterpreterInvoke ✓ if (SDKVersion <= 29) // ✓ v5 |= dword_4C04; // |= kAccNative ✓ sub_1DB8(a1, v5); // setFlags ✓ __android_log_print(4, "xjhook-Native", "change access flags from 0x%x to 0x%x", v4, v5); // ✓ } return 0; // ✓ } else { __android_log_print(6, "xjhook-Native", "failed to allocate space for trampoline of target method"); // ✓ return 1; // ✓ } } // ============================================================ // 差异汇总 // ============================================================ // [1] 常量符号名丢失: // OFFSET_entry_point_from_quick_compiled_code → dword_4C58 // OFFSET_entry_point_from_interpreter → dword_4C5C // kAccFastInterpreterToInterpreterInvoke → dword_4C00 (值0x40000000) // kAccNative → dword_4C04 (值0x0100) // // [2] 宏展开: // readAddr(toMethod+OFFSET) → *(_QWORD*)&a2[dword_4C58] // writeAddr(from+OFFSET, v) → *(_QWORD*)&a1[dword_4C58] = v // // [3] interpreter同步逻辑微差: // 源码: if (OFFSET_entry_point_from_interpreter != 0) { readAddr; writeAddr } // 反编: if (dword_4C5C) { 直接赋值 } ← 省略了中间变量,逻辑等价 7. doBackupAndHook (sub_1978) C // ============================================================ // 源码 (HookMain.c) // ============================================================ static int doBackupAndHook(void *targetMethod, void *hookMethod, void *backupMethod) { LOGI("target method is at %p, hook method is at %p, backup method is at %p", targetMethod, hookMethod, backupMethod); int res = 0; if (SDKVersion >= __ANDROID_API_N__) { // >= 24 setNonCompilable(targetMethod); // setNonCompilable(hookMethod); ← 注释掉了,不处理hookMethod! if (backupMethod) setNonCompilable(backupMethod); } if (backupMethod) { res += replaceMethod(backupMethod, targetMethod, 1); // backup←target } res += replaceMethod(targetMethod, hookMethod, 0); // target←hook LOGI("hook and backup done"); return res; } // ============================================================ // 反编译 (sub_1978_00001978.txt) // ============================================================ __int64 __fastcall sub_1978(const void *a1, const void *a2, const void *a3) { // ↑targetMethod ↑hookMethod ↑backupMethod int v4; // res (backup部分) unsigned int v5; // res (total) __android_log_print(4, "xjhook-Native", "target method is at %p, hook method is at %p, backup method is at %p", a1, a2, a3); // ✓ v4 = 0; // res=0 ✓ if (SDKVersion >= 24) { // ✓ sub_1B04(a1); // setNonCompilable(targetMethod) ✓ // hookMethod未处理 // ✓ (与源码注释掉的行一致) if (a3) sub_1B04(a3); // setNonCompilable(backupMethod) ✓ } if (a3) v4 = sub_1BB4(a3, a1, 1); // replaceMethod(backup,target,1) ✓ v5 = v4 + sub_1BB4(a1, a2, 0); // res+=replaceMethod(target,hook,0) ✓ __android_log_print(4, "xjhook-Native", "hook and backup done"); // ✓ return v5; // ✓ } // ============================================================ // 差异汇总 // ============================================================ // [1] 函数名丢失: doBackupAndHook → sub_1978 // [2] res变量被拆分为 v4(int) + v5(unsigned int) // 源码: int res; res+=x; res+=y; return res; // 反编: v4=sub(); v5=v4+sub(); return v5; ← 语义相同 // [3] 注释掉的 setNonCompilable(hookMethod) 在反编译中同样缺失 ✓ 8. getArtMethod (sub_1A4C) C // ============================================================ // 源码 (HookMain.c) // ============================================================ static void *getArtMethod(JNIEnv *env, jobject jmethod) { void *artMethod = NULL; if (jmethod == NULL) return artMethod; // 返回NULL if (SDKVersion >= __ANDROID_API_R__) { // >= 30 // SDK30+: 通过反射字段"artMethod"(long类型)获取 artMethod = (void*)(*env)->GetLongField(env, jmethod, fieldArtMethod); // JNI vtable offset: 808 (GetLongField) } else { // SDK<30: 通过FromReflectedMethod获取 artMethod = (void*)(*env)->FromReflectedMethod(env, jmethod); // JNI vtable offset: 56 (FromReflectedMethod) } LOGI("ArtMethod: %p", artMethod); return artMethod; } // ============================================================ // 反编译 (sub_1A4C_00001A4C.txt) // ============================================================ const void *__fastcall sub_1A4C(__int64 a1, __int64 a2) { // ↑env ↑jmethod const void *v3; if (!a2) return 0; // jmethod==NULL ✓ if (SDKVersion < 30) { v3 = (const void*) (*(__int64(__fastcall**)(__int64,__int64)) (*(_QWORD*)a1 + 56LL)) // FromReflectedMethod, vtable[7] ✓ (a1, a2); } else { v3 = (const void*) (*(__int64(__fastcall**)(__int64,__int64,__int64)) (*(_QWORD*)a1 + 808LL)) // GetLongField, vtable[101] ✓ (a1, a2, qword_4C48); // qword_4C48 = fieldArtMethod ✓ } __android_log_print(4, "xjhook-Native", "ArtMethod: %p", v3); // ✓ return v3; } // ============================================================ // 差异汇总 // ============================================================ // [1] fieldArtMethod 符号名丢失 → qword_4C48 // [2] JNI调用方式: // 源码: (*env)->GetLongField(env, jmethod, fieldArtMethod) // 反编: 函数指针通过vtable偏移量直接调用 // (*(_QWORD*)a1 + 808) 对应 GetLongField // (*(_QWORD*)a1 + 56) 对应 FromReflectedMethod // [3] 返回类型: void* → const void* (差异无实质影响) // [4] 函数名丢失: getArtMethod → sub_1A4C 9. backupAndHookNative C // ============================================================ // 源码 (HookMain.c) // ============================================================ jboolean Java_lab_galaxy_yahfa_HookMain_backupAndHookNative( JNIEnv *env, jclass clazz, jobject target, jobject hook, jobject backup) { if (!doBackupAndHook( getArtMethod(env, target), getArtMethod(env, hook), getArtMethod(env, backup) )) { (*env)->NewGlobalRef(env, hook); // 防止hook方法被GC if (backup) (*env)->NewGlobalRef(env, backup); // 防止backup方法被GC return JNI_TRUE; // == 1 } else { return JNI_FALSE; // == 0 } } // ============================================================ // 反编译 (Java_lab_galaxy_xjhook_HookMain_backupAndHookNative_000018C0.txt) // ============================================================ __int64 __fastcall Java_lab_galaxy_xjhook_HookMain_backupAndHookNative( __int64 a1, __int64 a2, // env, clazz __int64 a3, __int64 a4, __int64 a5) { // target, hook, backup __int64 v5; // getArtMethod结果(backup) __int64 v7; // getArtMethod结果(hook) __int64 v8; // getArtMethod结果(target) v8 = sub_1A4C(a1, a3); // getArtMethod(env, target) ✓ v7 = sub_1A4C(a1, a4); // getArtMethod(env, hook) ✓ v5 = sub_1A4C(a1, a5); // getArtMethod(env, backup) ✓ if ((unsigned int)sub_1978(v8, v7, v5)) { // doBackupAndHook失败 return 0; // JNI_FALSE ✓ } else { // NewGlobalRef: vtable offset 168 (*(void(__fastcall**)(__int64,__int64))(*(_QWORD*)a1 + 168LL))(a1, a4); // hook ✓ if (a5) (*(void(__fastcall**)(__int64,__int64))(*(_QWORD*)a1 + 168LL))(a1, a5); // backup ✓ return 1; // JNI_TRUE ✓ } } // ============================================================ // 差异汇总 // ============================================================ // [1] 返回值判断逻辑反转: // 源码: if (!doBackupAndHook()) { 成功分支 } else { 失败 } // 反编: if (sub_1978()) { 失败分支 } else { 成功 } // ← IDA将条件取反,但语义完全等价 // // [2] NewGlobalRef通过vtable偏移调用: // 源码: (*env)->NewGlobalRef(env, hook) // 反编: (*(_QWORD*)a1 + 168)(a1, a4) ← vtable[21]=NewGlobalRef // // [3] 包名差异: yahfa → xjhook // [4] 注释丢失: "keep a global ref so that the hook method would not be GCed" 10. findMethodNative C // ============================================================ // 源码 (HookMain.c) // ============================================================ jobject Java_lab_galaxy_yahfa_HookMain_findMethodNative( JNIEnv *env, jclass clazz, jclass targetClass, jstring methodName, jstring methodSig) { const char *c_methodName = (*env)->GetStringUTFChars(env, methodName, NULL); // vtable offset: 1352 const char *c_methodSig = (*env)->GetStringUTFChars(env, methodSig, NULL); // vtable offset: 1352 (同一函数) jobject ret = NULL; // 先尝试实例方法 jmethodID method = (*env)->GetMethodID(env, targetClass, c_methodName, c_methodSig); // vtable offset: 264 if (!(*env)->ExceptionCheck(env)) { // vtable offset: 1824 ret = (*env)->ToReflectedMethod(env, targetClass, method, JNI_FALSE); // vtable offset: 72, 最后参数0=JNI_FALSE } else { (*env)->ExceptionClear(env); // vtable offset: 136 // 再尝试静态方法 method = (*env)->GetStaticMethodID(env, targetClass, c_methodName, c_methodSig); // vtable offset: 904 (GetStaticMethodID) if (!(*env)->ExceptionCheck(env)) { // vtable offset: 1824 ret = (*env)->ToReflectedMethod(env, targetClass, method, JNI_TRUE); // vtable offset: 72, 最后参数1=JNI_TRUE } else { (*env)->ExceptionClear(env); // vtable offset: 136 } } (*env)->ReleaseStringUTFChars(env, methodName, c_methodName); // vtable: 1360 (*env)->ReleaseStringUTFChars(env, methodSig, c_methodSig); // vtable: 1360 return ret; } // ============================================================ // 反编译 (Java_lab_galaxy_xjhook_HookMain_findMethodNative_00001718.txt) // ============================================================ __int64 __fastcall Java_lab_galaxy_xjhook_HookMain_findMethodNative( __int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5) { __int64 v6; // GetMethodID结果(第一次) __int64 v7; // GetStaticMethodID结果 __int64 v8; // ret __int64 v9; // c_methodSig __int64 v10; // c_methodName // GetStringUTFChars(methodName) v10 = (*(__int64(__fastcall**)(__int64,__int64)) (*(_QWORD*)a1 + 1352LL))(a1, a4); // ✓ offset 1352 // GetStringUTFChars(methodSig, 0) v9 = (*(__int64(__fastcall**)(__int64,__int64,_QWORD)) (*(_QWORD*)a1 + 1352LL))(a1, a5, 0); // ✓ 同偏移,多参数0 v8 = 0; // ret=NULL ✓ // GetMethodID v6 = (*(__int64(__fastcall**)(__int64,__int64,__int64,__int64)) (*(_QWORD*)a1 + 264LL))(a1, a3, v10, v9); // ✓ offset 264 // ExceptionCheck if ((*(unsigned __int8(__fastcall**)(__int64)) (*(_QWORD*)a1 + 1824LL))(a1)) { // 有异常 ✓ // ExceptionClear (*(void(__fastcall**)(__int64)) (*(_QWORD*)a1 + 136LL))(a1); // ✓ offset 136 // GetStaticMethodID v7 = (*(__int64(__fastcall**)(__int64,__int64,__int64,__int64)) (*(_QWORD*)a1 + 904LL))(a1, a3, v10, v9); // ✓ offset 904 // ExceptionCheck again if ((*(unsigned __int8(__fastcall**)(__int64)) (*(_QWORD*)a1 + 1824LL))(a1)) { // 仍有异常 (*(void(__fastcall**)(__int64)) (*(_QWORD*)a1 + 136LL))(a1); // ExceptionClear ✓ } else { // ToReflectedMethod(..., JNI_TRUE=1) v8 = (*(__int64(__fastcall**)(__int64,__int64,__int64,__int64)) (*(_QWORD*)a1 + 72LL))(a1, a3, v7, 1); // ✓ } } else { // ToReflectedMethod(..., JNI_FALSE=0) v8 = (*(__int64(__fastcall**)(__int64,__int64,__int64,_QWORD)) (*(_QWORD*)a1 + 72LL))(a1, a3, v6, 0); // ✓ } // ReleaseStringUTFChars x2 (*(void(__fastcall**)(__int64,__int64,__int64)) (*(_QWORD*)a1 + 1360LL))(a1, a4, v10); // ✓ offset 1360 (*(void(__fastcall**)(__int64,__int64,__int64)) (*(_QWORD*)a1 + 1360LL))(a1, a5, v9); // ✓ return v8; } // ============================================================ // 差异汇总 // ============================================================ // [1] 所有JNI函数调用转为vtable偏移, 对应关系: // GetStringUTFChars → offset 1352 // GetMethodID → offset 264 // ExceptionCheck → offset 1824 // ExceptionClear → offset 136 // ToReflectedMethod → offset 72 // GetStaticMethodID → offset 904 // ReleaseStringUTFChars→ offset 1360 // // [2] 变量命名: c_methodName→v10, c_methodSig→v9, ret→v8 // [3] GetStringUTFChars第二个调用在源码传NULL,反编译传0 (等价) 11. HookMain_init C // ============================================================ // 源码 (HookMain.c) —— 关键差异部分 // ============================================================ void Java_lab_galaxy_yahfa_HookMain_init(JNIEnv *env, jclass clazz, jint sdkVersion) { SDKVersion = sdkVersion; LOGI("init to SDK %d", sdkVersion); switch (sdkVersion) { // SDK 21 (L) case __ANDROID_API_L__: // 21 OFFSET_ArtMehod_in_Object = 4 * 2; // = 8 OFFSET_entry_point_from_interpreter_in_ArtMethod = OFFSET_ArtMehod_in_Object + 4 * 4; // = 8+16 = 24 OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = OFFSET_entry_point_from_interpreter_in_ArtMethod + 8 * 2; // = 24+16 = 40 break; // SDK 22 (L_MR1) case __ANDROID_API_L_MR1__: // 22 OFFSET_ArtMehod_in_Object = 4 * 2; // = 8 OFFSET_entry_point_from_interpreter_in_ArtMethod = roundUpToPtrSize(OFFSET_ArtMehod_in_Object + 4*7); // roundUp(8+28)=roundUp(36)=40 OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = OFFSET_entry_point_from_interpreter_in_ArtMethod + pointer_size*2; // = 40+16 = 56 break; // SDK 23 (M) case __ANDROID_API_M__: // 23 OFFSET_ArtMehod_in_Object = 0; OFFSET_entry_point_from_interpreter_in_ArtMethod = roundUpToPtrSize(4*7); // roundUp(28) = 32 OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = OFFSET_entry_point_from_interpreter_in_ArtMethod + pointer_size*2; // = 32+16 = 48 break; // SDK 24,25 (N, N_MR1) case __ANDROID_API_N__: // 24 case __ANDROID_API_N_MR1__: // 25 OFFSET_ArtMehod_in_Object = 0; OFFSET_access_flags_in_ArtMethod = 4; OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = roundUpToPtrSize(4*4+2*2) + pointer_size*3; // roundUp(20)+24 = 24+24 = 48 break; // SDK 26 (O) case __ANDROID_API_O__: // 26 OFFSET_ArtMehod_in_Object = 0; OFFSET_access_flags_in_ArtMethod = 4; OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = roundUpToPtrSize(4*4+2*2) + pointer_size*2; // roundUp(20)+16 = 24+16 = 40 break; // SDK 27 (O_MR1) —— 注意: 会fall-through到O的代码! case __ANDROID_API_O_MR1__: // 27 kAccCompileDontBother = 0x02000000; // ← 修改全局常量 // fall-through到O OFFSET_ArtMehod_in_Object = 0; OFFSET_access_flags_in_ArtMethod = 4; OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = roundUpToPtrSize(4*4+2*2) + pointer_size*2; // = 40 break; // SDK 28,29 (P,Q) —— fall-through到Q/P/R公共块 case __ANDROID_API_P__: // 28 case __ANDROID_API_Q__: // 29 kAccCompileDontBother = 0x02000000; OFFSET_ArtMehod_in_Object = 0; OFFSET_access_flags_in_ArtMethod = 4; // SDK<31: roundUp(4*4+2*2)+pointer_size = roundUp(20)+8 = 24+8 = 32 OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = 32; break; // SDK 30 (R) —— 获取fieldArtMethod case __ANDROID_API_R__: // 30 classExecutable = (*env)->FindClass(env, "java/lang/reflect/Executable"); fieldArtMethod = (*env)->GetFieldID(env, classExecutable, "artMethod", "J"); // fall-through到P/Q kAccCompileDontBother = 0x02000000; OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = 32; break; // SDK 31-36 (S+) case __ANDROID_API_S__: // 31 // ... 35, 36 kAccPreCompiled = 0x00800000; // ← 修改全局常量 // fall-through到R classExecutable = (*env)->FindClass(env, "java/lang/reflect/Executable"); fieldArtMethod = (*env)->GetFieldID(env, classExecutable, "artMethod", "J"); kAccCompileDontBother = 0x02000000; // SDK>=31: roundUp(4*3+2*2)+pointer_size = roundUp(16)+8 = 16+8 = 24 OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod = 24; break; default: LOGE("not compatible with SDK %d", sdkVersion); break; } setupTrampoline(OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod); } // ============================================================ // 反编译 (Java_lab_galaxy_xjhook_HookMain_init_000014C8.txt) // ============================================================ __int64 __fastcall Java_lab_galaxy_xjhook_HookMain_init(__int64 a1, __int64 a2, int a3) { __int64 v3; // FindClass结果 SDKVersion = a3; __android_log_print(4, "xjhook-Native", "init to SDK %d", a3); // ✓ switch (a3) { case 21: dword_4C50 = 8; // OFFSET_ArtMehod_in_Object ✓ dword_4C5C = 24; // OFFSET_entry_point_from_interpreter ✓ dword_4C58 = 40; // OFFSET_entry_point_from_quick ✓ return setupTrampoline((unsigned int)dword_4C58); // ✓ case 22: dword_4C50 = 8; // ✓ dword_4C5C = 40; // ✓ dword_4C58 = 56; // ✓ return setupTrampoline((unsigned int)dword_4C58); case 23: dword_4C50 = 0; // ✓ dword_4C5C = 32; // ✓ dword_4C58 = 48; // ✓ return setupTrampoline((unsigned int)dword_4C58); case 24: case 25: dword_4C50 = 0; // ✓ dword_4C54 = 4; // OFFSET_access_flags ✓ dword_4C58 = 48; // ✓ return setupTrampoline((unsigned int)dword_4C58); case 26: goto LABEL_8; // fall-through到O处理 ✓ case 27: dword_4BFC = 0x2000000; // kAccCompileDontBother ✓ LABEL_8: // SDK26/27共用 dword_4C50 = 0; dword_4C54 = 4; dword_4C58 = 40; // ✓ return setupTrampoline((unsigned int)dword_4C58); case 28: case 29: goto LABEL_4; // ← 注意: SDK28跳过了kAccCompileDontBother的设置! case 30: goto LABEL_3; // ← 先执行FindClass/GetFieldID case 31: case 32: case 33: case 34: case 35: case 36: dword_4BF8 = 0x800000; // kAccPreCompiled ✓ LABEL_3: v3 = (*(__int64(__fastcall**)(__int64,const char*)) (*(_QWORD*)a1 + 48LL))(a1, "java/lang/reflect/Executable"); // FindClass, vtable offset 48 ✓ qword_4C48 = (*(__int64(__fastcall**)(__int64,__int64,const char*,const char*)) (*(_QWORD*)a1 + 752LL))(a1, v3, "artMethod", "J"); // GetFieldID, vtable offset 752 ✓ LABEL_4: dword_4BFC = 0x2000000; // kAccCompileDontBother ✓ dword_4C50 = 0; dword_4C54 = 4; if (a3 < 31) dword_4C58 = 32; // SDK28-30: offset=32 ✓ else dword_4C58 = 24; // SDK31+: offset=24 ✓ break; default: __android_log_print(6, "xjhook-Native", "not compatible with SDK %d", a3); // ✓ break; } return setupTrampoline((unsigned int)dword_4C58); // ✓ } // ============================================================ // 差异汇总 // ============================================================ // [1] 全局变量符号名丢失对照表: // OFFSET_ArtMehod_in_Object → dword_4C50 // OFFSET_access_flags_in_ArtMethod → dword_4C54 // OFFSET_entry_point_from_quick_compiled_code_in_ArtMethod → dword_4C58 // OFFSET_entry_point_from_interpreter_in_ArtMethod → dword_4C5C // kAccCompileDontBother → dword_4BFC // kAccPreCompiled → dword_4BF8 // fieldArtMethod → qword_4C48 // // [2] SDK28(P)处理差异: // 源码: SDK28 fall-through到SDK29,设置kAccCompileDontBother // 反编: SDK28 goto LABEL_4, 在LABEL_4处设置dword_4BFC=0x2000000 // ← 语义相同,LABEL_4包含了kAccCompileDontBother的赋值 // // [3] JNI函数通过vtable调用: // FindClass → (*(_QWORD*)a1 + 48) // GetFieldID → (*(_QWORD*)a1 + 752) // // [4] 计算值已预计算为常量,宏展开信息丢失: // roundUpToPtrSize(4*4+2*2)+pointer_size*2 → 40 // roundUpToPtrSize(4*3+2*2)+pointer_size → 24 12. getThread (naked函数) C // ============================================================ // 源码 (utils.c) —— aarch64分支 // ============================================================ jlong __attribute__((naked)) Java_lab_galaxy_yahfa_HookMain_00024Utils_getThread(JNIEnv *env, jclass clazz) { __asm__( "mov x0, x19\n" // ART线程指针保存在x19寄存器 "ret\n" ); // naked: 无函数序言/尾声, 无栈帧 } // ============================================================ // 反编译 (Java_lab_galaxy_xjhook_HookMain_00024Utils_getThread_00002120.txt) // ============================================================ __int64 Java_lab_galaxy_xjhook_HookMain_00024Utils_getThread() { __int64 v0; // x19 ← IDA识别出使用了x19,但无法表达naked语义 return v0; // IDA将 "mov x0,x19; ret" 简化为返回x19的值 } // ============================================================ // 差异汇总 // ============================================================ // [1] __attribute__((naked)) 完全丢失 // - 源码无函数序言(prologue)/尾声(epilogue) // - 反编译误加了标准函数框架 // // [2] 内联汇编信息完全丢失 // 源码: __asm__("mov x0, x19\n" "ret\n") // 反编: return v0 (仅保留了x19→返回值的语义) // // [3] 参数丢失: (JNIEnv *env, jclass clazz) → 无参数 // naked函数参数不在C级别处理,IDA无法推断 13. isValidAddress (sub_2778) C // ============================================================ // 源码 (utils.c) // ============================================================ static int isValidAddress(const void *p) { if (!p) return 0; int ret = 1; // 默认成功 int fd = open("/dev/random", O_WRONLY); // O_WRONLY=1 size_t len = sizeof(int32_t); // = 4 if (fd != -1) { if (write(fd, p, len) < 0) { ret = 0; // 写失败才设为0 } close(fd); } else { ret = 0; // open失败设为0 } return ret; } // ============================================================ // 反编译 (sub_2778_00002778.txt) // ============================================================ bool __fastcall sub_2778(const void *a1) { int fd; _BOOL4 v3; if (a1) { fd = __open_2("/dev/random", 1); // O_WRONLY=1 ✓ if (fd == -1) return 0; // ✓ v3 = write(fd, a1, 4u) >= 0; // ← 与源码有细微差异! // 源码: write<0 时ret=0 (write==0也算成功) // 反编: write>=0 时v3=true (语义相同,write返回写入字节数) close(fd); return v3; } return 0; } // ============================================================ // 差异汇总 // ============================================================ // [1] 控制流重构: // 源码: int ret=1; if(fd!=-1){...} else{ret=0} return ret // 反编: if(fd==-1) return 0; v3=write>=0; return v3 // ← 逻辑等价,但结构不同 // // [2] open函数: open() → __open_2() // (编译器将open替换为带安全检查的__open_2) // // [3] 返回类型: int → bool (_BOOL4) // // [4] write结果判断: // 源码: if(write(fd,p,len) < 0) ret=0 → write<0才失败 // 反编: v3 = write(fd,a1,4u) >= 0 → write>=0为成功 // ← 语义完全等价 14. visiblyInit 逻辑对比 C // ============================================================ // 源码 (utils.c) // ============================================================ jint Java_lab_galaxy_yahfa_HookMain_00024Utils_visiblyInit( JNIEnv *env, jclass clazz, jlong thread) { if (!shouldVisiblyInit()) return 0; // SDK<30 直接返回 // 懒加载: 未初始化时调用findInitClassSymbols if (!classLinker || !MakeInitializedClassesVisiblyInitialized) { if (findInitClassSymbols(env) != 0) { LOGE("failed to find symbols: classLinker %p, " "MakeInitializedClassesVisiblyInitialized %p", classLinker, MakeInitializedClassesVisiblyInitialized); return 1; // 失败 } } LOGI("thread is at %p", thread); MakeInitializedClassesVisiblyInitialized(classLinker, (void*)thread, 1); return 0; // 成功 } // ============================================================ // 反编译 (Java_lab_galaxy_xjhook_HookMain_00024Utils_visiblyInit_00002158.txt) // ============================================================ __int64 __fastcall Java_lab_galaxy_xjhook_HookMain_00024Utils_visiblyInit( __int64 a1, __int64 a2, const void *a3) { // ↑thread (jlong→const void*) if ((unsigned int)sub_24C8(a1, a2)) { // shouldVisiblyInit() ✓ // 条件合并: (classLinker && MakeInit) || !findInitClassSymbols() if (qword_4C70 && off_4C78 // classLinker && MakeInit已初始化 || !(unsigned int)sub_2224(a1)) { // 或 findInitClassSymbols()成功 __android_log_print(4, "xjhook-Native", "thread is at %p", a3); // ✓ off_4C78(qword_4C70, a3, 1); // MakeInitializedClassesVisiblyInitialized ✓ return 0; } else { // 两个条件都失败: 已初始化检查失败 且 findInitClassSymbols失败 __android_log_print(6, "xjhook-Native", "failed to find symbols: classLinker %p, " "MakeInitializedClassesVisiblyInitialized %p", (const void*)qword_4C70, off_4C78); return 1; } } return 0; } // ============================================================ // 差异汇总 // ============================================================ // [1] 最关键差异: 条件结构重组 // // 源码逻辑(if-then嵌套): // if (!classLinker || !MakeInit) { // 未初始化 // if (findInitClassSymbols() != 0) // 初始化失败 // { LOGE; return 1; } // } // // 到达这里: 已初始化 OR 初始化成功 // LOGI; MakeInit(...); return 0; // // 反编逻辑(条件合并): // if ( (classLinker && MakeInit) // 已初始化 // || !findInitClassSymbols() ) { // OR 初始化成功 // LOGI; MakeInit(...); return 0; // } else { // LOGE; return 1; // } // ← 语义等价,但结构完全不同 // // [2] thread参数类型: jlong → const void* (反编译类型推断) // // [3] 符号名丢失: // classLinker → qword_4C70 // MakeInitializedClassesVisiblyInitialized → off_4C78 // findInitClassSymbols → sub_2224 // shouldVisiblyInit → sub_24C8 全局变量对照总表 text ┌─────────────────────────────────────────────────────────────────────────┐ │ 全局变量符号名对照 │ ├──────────────┬──────────────────────────────────────────────┬───────────┤ │ 反编译地址 │ 源码变量名 │ 典型值 │ ├──────────────┼──────────────────────────────────────────────┼───────────┤ │ dword_4BF8 │ kAccPreCompiled │ 0x800000 │ │ dword_4BFC │ kAccCompileDontBother │ 0x2000000 │ │ dword_4C00 │ kAccFastInterpreterToInterpreterInvoke │0x40000000 │ │ dword_4C04 │ kAccNative │ 0x100 │ │ byte_4C11 │ trampoline[9] │ (动态) │ │ byte_4C12 │ trampoline[10] │ (动态) │ │ qword_4C48 │ fieldArtMethod (jfieldID) │ (动态) │ │ dword_4C50 │ OFFSET_ArtMehod_in_Object │ 0/8 │ │ dword_4C54 │ OFFSET_access_flags_in_ArtMethod │ 4 │ │ dword_4C58 │ OFFSET_entry_point_from_quick_compiled_code │ 24~56 │ │ dword_4C5C │ OFFSET_entry_point_from_interpreter │ 0/24~40 │ │ qword_4C60 │ currentTrampolineOff │ (动态) │ │ qword_4C68 │ trampolineSpaceEnd │ (动态) │ │ qword_4C70 │ classLinker │ (动态) │ │ off_4C78 │ MakeInitializedClassesVisiblyInitialized │ (动态) │ │ SDKVersion │ SDKVersion │ 21~36 │ └──────────────┴──────────────────────────────────────────────┴───────────┘
免责声明
本文档所有内容仅供安全研究、学术交流与技术学习使用,严禁用于任何未经授权的逆向破解、网络攻击、隐私窃取、恶意软件开发及其他违反《中华人民共和国网络安全法》《数据安全法》等法律法规的行为,使用者应确保已获得目标软件权利人的合法授权并自行承担因使用本文档内容所产生的一切法律责任与后果,作者不对任何直接或间接损害承担任何责任,继续阅读即视为您已知悉并同意上述全部条款。
浙公网安备 33010602011771号