GKLBB

当你经历了暴风雨,你也就成为了暴风雨

导航

应用安全 --- 逆向技巧 之 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     │
└──────────────┴──────────────────────────────────────────────┴───────────┘

 

posted on 2026-04-05 09:02  GKLBB  阅读(9)  评论(0)    收藏  举报