From 113ee0a966cef2066aef376d706c055ad95114b5 Mon Sep 17 00:00:00 2001 From: Kavish Devar Date: Thu, 23 Apr 2026 18:43:54 +0530 Subject: [PATCH] android: fallback to .dynsym when .gnu_debugdata fails --- android/app/src/xposed/cpp/l2c_fcr_hook.cpp | 151 ++++++++++++++------ 1 file changed, 105 insertions(+), 46 deletions(-) diff --git a/android/app/src/xposed/cpp/l2c_fcr_hook.cpp b/android/app/src/xposed/cpp/l2c_fcr_hook.cpp index 4910dfd..2b58719 100644 --- a/android/app/src/xposed/cpp/l2c_fcr_hook.cpp +++ b/android/app/src/xposed/cpp/l2c_fcr_hook.cpp @@ -192,6 +192,65 @@ static uintptr_t getModuleBase(const char* name) { return base; } +static uint64_t findSymbolOffsetDynsym( + const std::vector& elf, + const char* symbol_substring) { + + LOGI("findSymbolOffsetDynsym called with %s", symbol_substring); + + auto* eh = reinterpret_cast(elf.data()); + auto* shdr = reinterpret_cast( + elf.data() + eh->e_shoff); + + const char* shstr = + reinterpret_cast( + elf.data() + shdr[eh->e_shstrndx].sh_offset); + + const Elf64_Shdr* dynsym = nullptr; + const Elf64_Shdr* dynstr = nullptr; + + for (int i = 0; i < eh->e_shnum; ++i) { + const char* secname = shstr + shdr[i].sh_name; + + if (!strcmp(secname, ".dynsym")) + dynsym = &shdr[i]; + if (!strcmp(secname, ".dynstr")) + dynstr = &shdr[i]; + } + + if (!dynsym || !dynstr) { + LOGE("findSymbolOffsetDynsym: dynsym or dynstr not found"); + return 0; + } + + auto* symbols = reinterpret_cast( + elf.data() + dynsym->sh_offset); + + const char* strings = + reinterpret_cast( + elf.data() + dynstr->sh_offset); + + size_t count = dynsym->sh_size / sizeof(Elf64_Sym); + + LOGI("findSymbolOffsetDynsym: scanning %zu symbols", count); + + for (size_t i = 0; i < count; ++i) { + const char* name = strings + symbols[i].st_name; + + if (strstr(name, symbol_substring) && + ELF64_ST_TYPE(symbols[i].st_info) == STT_FUNC) { + + LOGI("findSymbolOffsetDynsym: matched %s @ 0x%lx", + name, (unsigned long)symbols[i].st_value); + + return symbols[i].st_value; + } + } + + LOGI("findSymbolOffsetDynsym: no match for %s", symbol_substring); + return 0; +} + static uint64_t findSymbolOffset( const std::vector& elf, const char* symbol_substring) { @@ -291,9 +350,10 @@ static bool hookLibrary(const char* libname) { reinterpret_cast( file.data() + shdr[eh->e_shstrndx].sh_offset); - LOGI("hookLibrary: parsing ELF header and sections"); - for (int i = 0; i < eh->e_shnum; ++i) { + uint64_t chk_offset = 0; + uint64_t sdp_offset = 0; + for (int i = 0; i < eh->e_shnum; ++i) { if (!strcmp(shstr + shdr[i].sh_name, ".gnu_debugdata")) { LOGI("hookLibrary: found .gnu_debugdata section"); @@ -303,60 +363,59 @@ static bool hookLibrary(const char* libname) { std::vector decompressed; - if (!decompressXZ( + if (decompressXZ( compressed.data(), compressed.size(), decompressed)) { - LOGE("hookLibrary: decompressXZ failed"); - return false; - } - LOGI("hookLibrary: decompressed debug data, size: %zu", decompressed.size()); - uintptr_t base = getModuleBase(libname); - if (!base) { - LOGE("hookLibrary: getModuleBase failed"); - return false; - } - LOGI("hookLibrary: module base: 0x%lx", base); + chk_offset = findSymbolOffset(decompressed, + "l2c_fcr_chk_chan_modes"); - uint64_t chk_offset = - findSymbolOffset(decompressed, - "l2c_fcr_chk_chan_modes"); - - uint64_t sdp_offset = - findSymbolOffset(decompressed, - "BTA_DmSetLocalDiRecord"); - - LOGI("hookLibrary: chk_offset: 0x%lx, sdp_offset: 0x%lx", chk_offset, sdp_offset); - - if (chk_offset) { - void* target = - reinterpret_cast(base + chk_offset); - - hook_func(target, - (void*)fake_l2c_fcr_chk_chan_modes, - (void**)&original_l2c_fcr_chk_chan_modes); - - LOGI("hookLibrary: hooked l2c_fcr_chk_chan_modes"); + sdp_offset = findSymbolOffset(decompressed, + "BTA_DmSetLocalDiRecord"); + } else { + LOGE("debugdata decompress failed"); } - if (sdp_offset) { - void* target = - reinterpret_cast(base + sdp_offset); - - hook_func(target, - (void*)fake_BTA_DmSetLocalDiRecord, - (void**)&original_BTA_DmSetLocalDiRecord); - - LOGI("hookLibrary: hooked BTA_DmSetLocalDiRecord"); - } - - return true; + break; } } - LOGI("hookLibrary: failed for %s", libname); - return false; + if (!chk_offset) { + LOGI("fallback dynsym chk"); + chk_offset = findSymbolOffsetDynsym(file, + "l2c_fcr_chk_chan_modes"); + } + + if (!sdp_offset) { + LOGI("fallback dynsym sdp"); + sdp_offset = findSymbolOffsetDynsym(file, + "BTA_DmSetLocalDiRecord"); + } + + uintptr_t base = getModuleBase(libname); + if (!base) { + LOGE("hookLibrary: getModuleBase failed"); + return false; + } + + if (chk_offset) { + void* target = reinterpret_cast(base + chk_offset); + hook_func(target, + (void*)fake_l2c_fcr_chk_chan_modes, + (void**)&original_l2c_fcr_chk_chan_modes); + LOGI("hooked chk"); + } + + if (sdp_offset) { + void* target = reinterpret_cast(base + sdp_offset); + hook_func(target, + (void*)fake_BTA_DmSetLocalDiRecord, + (void**)&original_BTA_DmSetLocalDiRecord); + LOGI("hooked sdp"); + } + + return chk_offset || sdp_offset; } static void on_library_loaded(const char* name, void*) {