caller); 这里是对于soinfo的赋值

构架检查 同时在 find_library 的位置,因为我们一直希望干的事情就是HOOK loadLibrary参数来类比于HOOK dlopen Java.perform(() = { console.log("\n====== 环境信息 ======"); console.log(` 进程架构: ${Process.arch}`); console.log(` 当前线程ID: ${Process.getCurrentThreadId()}`); const Build = Java.use("android.os.Build"); console.log("\n====== 模块信息 ======"); const libhoudini = Module.findBaseAddress("libhoudini.so"); if (!libhoudini) { console.error("[!] libhoudini.so 未加载"); return; } console.log(` libhoudini.so 基址: ${libhoudini}`); console.log("\n====== NativeBridgeItf 符号信息 ======"); const NativeBridgeItf = Module.findExportByName("libhoudini.so",共获提升! PastorBonus + 1 + 1 谢谢@Thanks! Quincy379 + 1 + 1 谢谢@Thanks! zhoumeto + 1 + 1 谢谢@Thanks! fengbolee + 2 + 1 欢迎分析讨论交流,启动server服务 adb push frida-server /data/local/tmp/adb shellsuchmod 777 /data/local/tmp/frida-server-16.0.11-android-x86_64./data/local/tmp/frida-server-16.0.11-android-x86_64 image-20250327090423595.png (65.43 KB, flags,好比逆向过程中把每一个指令进行拦截然后用对应的架构语言去解释,不知道偏移地址就进行爆破HOOK, 例如, DBT) 作用 :实时将 ARM 指令逐块翻译为 x86_64 指令, static void* dlopen_ext(const char* filename,这里的最终的libmsaoaidsec.so, 下载次数: 2) 下载附件 2025-4-7 12:52 上传 这里是因为是以spwned启动的b站,后续执行直接调用缓存, caller, 我类比于hook dlopen的方法想去HOOK NativeBridgeLoadLibrary 想去得到对应的frida检测的so文件。

preZygoteFork: 0x88 }; Object.keys(offsets).forEach(name = { console.log(` ${name} addr: ${NativeBridgeItf.add(offsets[name]).readPointer()}`); }); // Hook loadLibraryExt Interceptor.attach(NativeBridgeItf.add(offsets.loadLibraryExt).readPointer(), 下载次数: 4) 下载附件 2025-4-7 12:52 上传 NativeBridgeItf 是 NativeBridgeItf是 Native Bridge 的核心接口表 ,共获提升! mengxinb + 1 + 1 受益匪浅 NewType + 1 + 1 用心讨论。

patching..."); setTimeout(hook_pthread_create。

loadLibrary: 0x10,会调用 android_dlopen_ext 返回 __loader_android_dlopen_ext。

所以HOOK代码也略微的进行了修改 function hook_dlopen() { // Hook 所有可能的 dlopen 变体 const dlopenFuncs = [ 'android_dlopen_ext', "NativeBridgeItf"); if (!NativeBridgeItf) { console.error("[!] 找不到 NativeBridgeItf 符号"); return; } console.log(` NativeBridgeItf 符号地址: ${NativeBridgeItf}`); console.log(` NativeBridgeItf 符号地址 偏移寻址 : ${libhoudini.add(0x0701D00)}`); console.log("\n====== 结构体指针信息 ======"); const callbacks = NativeBridgeItf.readInt(); console.log(` version: ${callbacks}`); const padding = NativeBridgeItf.add(0x4).readInt(); console.log(` padding: ${padding}`); const initialize = NativeBridgeItf.add(0x8).readPointer(); console.log(` initialize addr: ${initialize}`); const loadLibrary = NativeBridgeItf.add(0x10).readPointer(); console.log(` loadLibrary addr: ${loadLibrary}`); const getTrampoline = NativeBridgeItf.add(0x18).readPointer(); console.log(` getTrampoline addr: ${getTrampoline}`); const isSupported = NativeBridgeItf.add(0x20).readPointer(); console.log(` isSupported addr: ${isSupported}`); const getAppEnv = NativeBridgeItf.add(0x28).readPointer(); console.log(` getAppEnv addr: ${getAppEnv}`); const isCompatibleWith = NativeBridgeItf.add(0x30).readPointer(); console.log(` isCompatibleWith addr: ${isCompatibleWith}`); const getSignalHandler = NativeBridgeItf.add(0x38).readPointer(); console.log(` getSignalHandler addr: ${getSignalHandler}`); const unloadLibrary = NativeBridgeItf.add(0x40).readPointer(); console.log(` unloadLibrary addr: ${unloadLibrary}`); const getError = NativeBridgeItf.add(0x48).readPointer(); console.log(` getError addr: ${getError}`); const isPathSupported = NativeBridgeItf.add(0x50).readPointer(); console.log(` isPathSupported addr: ${isPathSupported}`); const unused_initAnonymousNamespace = NativeBridgeItf.add(0x58).readPointer(); console.log(` unused_initAnonymousNamespace addr: ${unused_initAnonymousNamespace}`); const createNamespace = NativeBridgeItf.add(0x60).readPointer(); console.log(` createNamespace addr: ${createNamespace}`); const linkNamespaces = NativeBridgeItf.add(0x68).readPointer(); console.log(` linkNamespaces addr: ${linkNamespaces}`); const loadLibraryExt = NativeBridgeItf.add(0x70).readPointer(); console.log(` loadLibraryExt addr: ${loadLibraryExt}`); const getVendorNamespace = NativeBridgeItf.add(0x78).readPointer(); console.log(` getVendorNamespace addr: ${getVendorNamespace}`); const getExportedNamespace = NativeBridgeItf.add(0x80).readPointer(); console.log(` getExportedNamespace addr: ${getExportedNamespace}`); const preZygoteFork = NativeBridgeItf.add(0x88).readPointer(); console.log(` preZygoteFork addr: ${preZygoteFork}`);}); image-20250327213130139.png (744.91 KB。

同时, 下载次数: 1) 下载附件 2025-4-7 12:52 上传 新的问题 在模拟器中, createNamespace: 0x60, image-20250327081307720.png (488.7 KB,所以程序崩溃了,共获提升! 5omggx + 1 用心讨论,共获提升! taylorchen + 1 + 1 用心讨论, 1) uint32_t padding; // 对齐填充 (64位下可能不存在) // 函数指针表 bool (*initialize)(const struct NativeBridgeRuntimeCallbacks* runtime_cbs);// 0x8 (64位) void* (*loadLibrary)(const char* libpath,实现按照不同的操作(像加减乘除, extinfo, extinfo, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join('\n') + '\n'); } } } }); interceptors.push(interceptor); console.log(`Hooked ${funcName} at ${funcPtr}`); } }); // 返回所有拦截器以便后续管理 return interceptors;}// 延迟执行以避免错过早期加载的库setImmediate(hook_dlopen); image-20250327113344240.png (487.01 KB。

发现是没问题的 image-20250327090613016.png (306.48 KB, path); // 打印调用栈可以帮助定位谁加载了这个库 console.log(Thread.backtrace(this.context, 1. 低版本(Android 5.0~7.0) 核心函数集中在偏移 0x0 ~ 0x28 struct NativeBridgeItf { // 基础字段 uint32_t version; // 版本号 (e.g., 100); } } });}function call_function(){ console.log("alearly patch frida");}function hook_pthread_create() { var pthread_create = Module.findExportByName("libc.so", 2 或 3) uint32_t padding; // 基础函数(与低版本相同) bool (*initialize)(...); // 0x8 void* (*loadLibrary)(...); // 0x10 // 扩展函数(新增) void* (*loadLibraryExt)(const char* libpath, int flags。

所以去还原了NativeBridgeItf这个结构体, unloadLibrary: 0x40,来确定对于构架的检测 image-20250327144832227.png (71.21 KB。

共获提升! ioyr5995 + 1 + 1 我很赞同! yhu123 + 1 + 1 谢谢@Thanks! shanshan258 + 1 + 1 我很赞同! 156608225 + 2 + 1 鼓励转贴优秀软件安全工具和文档! allspark + 1 + 1 用心讨论, 下载次数: 4) 下载附件 2025-4-7 12:52 上传 进入 JavaVMExt::LoadNativeLibrary 方法后,实际上对于这些的知识上。

initialize: 0x8。

但是我们其实实际上需要的参数就是loadLibrary参数,所以对应的这个 NativeBridgeItf 也是对应的高版本上的结合体,共获提升! mz135135 + 1 + 1 用心讨论,也是成功绕过了 但是确实有了一些对于android虚拟机的理解, const android_dlextinfo* extinfo, 下载次数: 4) 下载附件 2025-4-7 12:52 上传 然后就是常规的操作了, 0x1b8d4, onLeave(retval) { console.log(`[NB] 返回句柄: ${retval}`); }});}); 发现实际上是没有这个函数的 image-20250327175223748.png (255.61 KB。

是由frida检测的,那么我们其实可以去考虑得到这个结构体在对应偏移的位置去得到相应的地址 image-20250327203903554.png (13.28 KB, offset: ${thread_ptr.sub(libmsaoaidsec.base)}`); [0x1c544。

javaLoader, void* extinfo);// 0x18 void* (*getTrampolineExt)(...); // 0x20 void* (*createNamespace)(...); // 0x28 // ... 其他扩展函数}; 引入扩展接口 loadLibraryExt , void* __loader_android_dlopen_ext(const char* filename, padding: 0x4, 下载次数: 5) 下载附件 2025-4-7 13:03 上传 这里终于是得到了最终的结果了, 真机测试 这里去尝试了HOOK了 b站 的dlopen函数,共获提升! huangxy07ai52 + 1 + 1 谢谢@Thanks! lin5789 + 1 我很赞同! adjclubyb + 1 + 1 用心讨论, 通过搜索可以得知是主要依赖于 动态二进制翻译 和 系统级的兼容层技术 动态二进制翻译(Dynamic Binary Translation,同时也是AOSP源码中的函数,init之后 的。

就是对于加载过的so文件的输出结果了 绕过frida检测 【新提醒】bilibili XHS frida检测分析绕过 - 吾爱破解 - 52pojie.cn 这里选择的APP是 哔哩哔哩的7.76.0,对应偏移固定(如 64 位环境下偏移 0x10) 2. 高版本(Android 8.0+) struct NativeBridgeItf { uint32_t version; // 版本号 (e.g.。

{ onEnter: function (args) { var pathptr = args[0]; if (pathptr !== undefined pathptr != null) { var path = ptr(pathptr).readCString(); console.log("[LOAD]",按照的就是内存错误之类的信息,在 X86或者是X86_64 的架构的模拟器可以去实现 ARM架构 的指令集, new NativeCallback(() = console.log(`Interceptor.replace: 0x${offset.toString(16)}`),会去判断是否对于触发Native Bridge 核心决策点 :选择原生 dlopen 或 Native Bridge 加载 image-20250327144020937.png (168.45 KB。

需要进行Native Bridge转换时,打印了一串字符串 总结: 这里只是对于在模拟器上如何实现跨架构进行so加载的探究流程, const void* caller_addr) {ScopedPthreadMutexLocker locker(void* result = do_dlopen(filename, 下载次数: 3) 下载附件 2025-4-7 12:52 上传 Native Bridge 触发 在 OpenNaitveLibrary函数 中,共获提升! lover1989 + 1 我很赞同! lingyi223 + 1 + 1 热心回复! yuze221022 + 1 + 1 我很赞同! iceman8501 + 1 + 1 用心讨论, path); // 检查是否是可疑的检测库 if (path (path.includes("libtt") || path.includes("libbili") || path.includes("security") || path.includes("protect"))) { console.warn("!!! Possible Frida detection library loaded:", 免费评分 参与人数 24 吾爱币 +23 热心值 +24 理由 Gcna + 1 + 1 用心讨论, caller); 这里是对于soinfo的赋值,。

位运算这些),通过解析 SO 文件的 ELF 头 (e_machine 字段)判断架构是否匹配, funcName); if (funcPtr) { let interceptor = Interceptor.attach(funcPtr, 下载次数: 3) 下载附件 2025-4-7 12:53 上传 这里绕过了frida检测,对于不同架构下的处理, flags, linkNamespaces: 0x68,参数: ${args[0]}`); } });}}); 但是这里出现了内存报错 image-20250327191723879.png (251.21 KB, { onEnter: function(args) { var libName = Memory.readCString(args[0]); console.log(` loadLibraryExt called with: ${libName}`); if (libName.includes("libmsaoaidsec.so")) { console.log("hooking libmsaoaidsec.so"); hook_system_property_get(); } },利用HOOK loadLibraryExt去实现得到在模拟器中加载过的so文件 image-20250327223501072.png (2.06 MB, 这里实际上一直在报错,自己的一些猜测,而是进入NativeBridgeLoadLibrary image-20250327160216764.png (38.46 KB。

产生过这样的代码 Java.perform(() = {// 加载 Native Bridge 库const libnb = Module.load("libhoudini.so");// 获取 NativeBridgeLoadLibrary 函数地址const NBLoadLib = Module.getExportByName("libhoudini.so",同时尝试了最简单的frida注入操作,就需要去利用Native Bridge 转换架构实现跨架构执行操作, 'dlopen',可以发现的是。

"android_dlopen_ext"), onLeave: function(retval) { console.log(` loadLibraryExt returned: ${retval}`); } });});function hook_system_property_get() { var addr = Module.findExportByName(null, image-20250327084351655.png (61.27 KB, { onEnter: function(args) { var pathptr = args[0]; if (pathptr !pathptr.isNull()) { var path = ptr(pathptr).readCString(); console.log("[LOAD]",所以还是去尝试过直接注入frida, "__system_property_get"); if (!addr) { console.log("__system_property_get not found"); return; } console.log("hooking __system_property_get"); Interceptor.attach(addr,也是得到了和之前手机端一样的结果了 之后也是复现了自己在手机端的绕过frida检测,需要是去探究android真机和模拟器在so层执行流程之间的差别。

吾爱破解论坛有你更精彩! leokyer + 1 + 1 用心讨论, 梳理真机下的执行流程 我们首先是通过System.load()进入 @CallerSensitivepublic static void load(String filename) { Runtime.getRuntime().load0(Reflection.getCallerClass(),然后修改代码,这个frida检测的so是libmsaoaidsec.so, loadLibraryExt: 0x70,看看执行得到的so文件是什么 function hook_dlopen() { var interceptor = Interceptor.attach(Module.findExportByName(null, 缓存翻译后的代码,但是能够实现注入 程序流程探究 按照我自己对于这个作业的要求, const char* shorty);// 0x18 (64位) bool (*isCompatibleWith)(uint32_t bridge_version); // 0x20 (64位) void* (*getNativeAddress)(void* arm_address); // 0x28 (64位) // ... 其他扩展函数}; 结构体中的函数指针名通常为 loadLibrary ,以及对应的PC端安装frida,最后到达 find_libraries 执行最后的处理, extinfo, 下载次数: 4) 下载附件 2025-4-7 13:02 上传 通过deepseek来查看了相关可能出现的问题 image-20250327084509215.png (32.53 KB,确实对于安卓虚拟机有了一些认识,也是得益于资料的搜索, const void* caller_addr) {return dlopen_ext(filename。

const android_dlextinfo* extinfo。

{ onEnter: function(args) { var thread_ptr = args[2]; if (thread_ptr.compare(libmsaoaidsec.base) 0 || thread_ptr.compare(libmsaoaidsec.base.add(libmsaoaidsec.size)) = 0) { console.log(`pthread_create other thread: ${thread_ptr}`); } else { console.log(`pthread_create libmsaoaidsec.so thread: ${thread_ptr}, 同时也能理解为什么在android虚拟机或者是云手机会需要逆向了, 下载次数: 4) 下载附件 2025-4-7 12:52 上传 可以看到这里是固定的。

"void",会调用 find_library() 进行 SO 文件的真正加载, 系统调用转发 ARM 程序发出的系统调用(如文件读写)会被捕获, "pthread_create"); if (!pthread_create) { console.log("pthread_create not found"); return; } var libmsaoaidsec = Process.findModuleByName("libmsaoaidsec.so"); if (!libmsaoaidsec) { console.log("libmsaoaidsec.so not found"); return; } console.log(`libmsaoaidsec.so base: ${libmsaoaidsec.base}`); Interceptor.attach(pthread_create,但是并没有NativeBridgeLoadLibrary函数 Java.perform(() = { let libnb = Module.enumerateExportsSync("libhoudini.so"); libnb.forEach(exp = console.log(exp.name));}); 打印了一下符号 image-20250327180712354.png (399.77 KB,发现在libmsaoaidsec.so里面开启了 三个线程 绕过的操作就是直接 patch了这三个线程 绕过的 Java.perform(() = { console.log("\n====== 环境信息 ======"); console.log(` 进程架构: ${Process.arch}`); console.log(` 当前线程ID: ${Process.getCurrentThreadId()}`); const libhoudini = Module.findBaseAddress("libhoudini.so"); if (!libhoudini) { console.error("[!] libhoudini.so 未加载"); return; } console.log(` libhoudini.so 基址: ${libhoudini}`); const NativeBridgeItf = Module.findExportByName("libhoudini.so",实现一个架构的转变,然后进入 nativeLoad() 函数,我们能够去利用dlopen去实现对于加载的so文件进行打印 模拟器测试 由于模拟器是X86_64的构架模拟的android机来实现的对于程序的执行, 下载次数: 4) 下载附件 2025-4-7 12:52 上传 初始化 Native Bridge 当发现了对应的架构不符合ARM。

我们能否去定位到这个so文件呢? 提出了疑问, caller_addr);return result;} do_dlopen(filename。

path) } } } ) return interceptor}setImmediate(hook_dlopen) image-20250327111335099.png (291.77 KB, soinfo* si = find_library(ns,同时在这里开始调用so的.init_proc函数,这里假设第一个参数为要加载的库路径 var libName = Memory.readCString(args[0]); console.log(" loadLibraryExt called with library name: " + libName); }, filename.c_str(),那么我们如何像在真机端这种去 HOOK dlopen 函数就能够去得到 dlopen的参数从而得到加载的so文件名 ? 那么是否我们能够去HOOK NativeBridgeLoadLibrary 函数呢?NativeBridgeLoadLibrary是在 Native Bridge 库 里面的函数,ARM 的 open() 系统调用会映射为 x86_64 的 sys_open(), isPathSupported: 0x50,在 MuMu 模拟器中执行 ARM 架构的 SO 文件,比如这种跨架构的过程, 下载次数: 5) 下载附件 2025-4-7 12:52 上传 这里开始去了解了对于这个 NativeBridgeItf 结构体的细节内容, 下载次数: 3) 下载附件 2025-4-7 12:52 上传 对比结果引发的思考和问题 其实是可以发现模拟器也是挂掉了, unused_initAnonymousNamespace: 0x58, { onEnter: function(args) { // 根据实际函数原型判断参数, "NativeBridgeItf"); if (!NativeBridgeItf) { console.error("[!] 找不到 NativeBridgeItf 符号"); return; } console.log(` NativeBridgeItf 符号地址: ${NativeBridgeItf}`); console.log(` NativeBridgeItf 符号地址 偏移寻址 : ${libhoudini.add(0x0701D00)}`); console.log("\n====== 结构体指针信息 ======"); const callbacks = NativeBridgeItf.readInt(); console.log(` version: ${callbacks}`); const padding = NativeBridgeItf.add(0x4).readInt(); console.log(` padding: ${padding}`); const initialize = NativeBridgeItf.add(0x8).readPointer(); console.log(` initialize addr: ${initialize}`); const loadLibrary = NativeBridgeItf.add(0x10).readPointer(); console.log(` loadLibrary addr: ${loadLibrary}`); const getTrampoline = NativeBridgeItf.add(0x18).readPointer(); console.log(` getTrampoline addr: ${getTrampoline}`); const isSupported = NativeBridgeItf.add(0x20).readPointer(); console.log(` isSupported addr: ${isSupported}`); const getAppEnv = NativeBridgeItf.add(0x28).readPointer(); console.log(` getAppEnv addr: ${getAppEnv}`); const isCompatibleWith = NativeBridgeItf.add(0x30).readPointer(); console.log(` isCompatibleWith addr: ${isCompatibleWith}`); const getSignalHandler = NativeBridgeItf.add(0x38).readPointer(); console.log(` getSignalHandler addr: ${getSignalHandler}`); const unloadLibrary = NativeBridgeItf.add(0x40).readPointer(); console.log(` unloadLibrary addr: ${unloadLibrary}`); const getError = NativeBridgeItf.add(0x48).readPointer(); console.log(` getError addr: ${getError}`); const isPathSupported = NativeBridgeItf.add(0x50).readPointer(); console.log(` isPathSupported addr: ${isPathSupported}`); const unused_initAnonymousNamespace = NativeBridgeItf.add(0x58).readPointer(); console.log(` unused_initAnonymousNamespace addr: ${unused_initAnonymousNamespace}`); const createNamespace = NativeBridgeItf.add(0x60).readPointer(); console.log(` createNamespace addr: ${createNamespace}`); const linkNamespaces = NativeBridgeItf.add(0x68).readPointer(); console.log(` linkNamespaces addr: ${linkNamespaces}`); const loadLibraryExt = NativeBridgeItf.add(0x70).readPointer(); console.log(` loadLibraryExt addr: ${loadLibraryExt}`); const getVendorNamespace = NativeBridgeItf.add(0x78).readPointer(); console.log(` getVendorNamespace addr: ${getVendorNamespace}`); const getExportedNamespace = NativeBridgeItf.add(0x80).readPointer(); console.log(` getExportedNamespace addr: ${getExportedNamespace}`); const preZygoteFork = NativeBridgeItf.add(0x88).readPointer(); console.log(` preZygoteFork addr: ${preZygoteFork}`); Interceptor.attach(loadLibraryExt,只是对于整个模拟器so加载流程做了一个小小的判断,把所有可能loadLibrary出现在NativeBridgeItf的结构体的偏移都进行hook Java.perform(() = {const NativeBridgeItf = Module.findExportByName("libhoudini.so"。

getAppEnv: 0x28,自定义架构去定义自己的加密函数。

下载次数: 2) 下载附件 2025-4-7 13:03 上传 Java.perform(() = { console.log("\n====== 环境信息 ======"); console.log(` 进程架构: ${Process.arch}`); console.log(` 当前线程ID: ${Process.getCurrentThreadId()}`); const Build = Java.use("android.os.Build"); console.log("\n====== 模块信息 ======"); const libhoudini = Module.findBaseAddress("libhoudini.so"); if (!libhoudini) { console.error("[!] libhoudini.so 未加载"); return; } console.log(` libhoudini.so 基址: ${libhoudini}`); console.log("\n====== NativeBridgeItf 符号信息 ======"); const NativeBridgeItf = Module.findExportByName("libhoudini.so"。

共获提升! 查看全部评分 ,但是我们需要其实是HOOK, []) ); }); } } });} image-20250327233428501.png (2.23 MB,所以我还是打算去老老实实的 看源码 IDA分析结构体 这里对于这些结合体老老实实看看是什么参数 image-20250327205853669.png (70.49 KB。

AOSP源码中的对于模拟器的执行流程处理 我以前发表过Android真机端的so文件的真实的执行流程的 【新提醒】Android SO文件加载过程探究 - 吾爱破解 - 52pojie.cn 但是这篇文章只是局限于了对于android真机也就是arm的处理,可能替代或补充 loadLibrary,但是还是会启动frida-server的服务的,但是在模拟器端这个so甚至于没有被 'android_dlopen_ext', 下载次数: 4) 下载附件 2025-4-7 12:52 上传 这里不知道是不是模拟器的特点,但是不知道的是loadLibrary对于这个结构体中的偏移位置。

"NativeBridgeItf"); if (!NativeBridgeItf) { console.error("[!] 找不到 NativeBridgeItf 符号"); return; } console.log(` NativeBridgeItf 符号地址: ${NativeBridgeItf}`); // 遍历结构体指针 console.log("\n====== 结构体指针信息 ======"); const offsets = { version: 0x0, 下载次数: 5) 下载附件 2025-4-7 12:52 上传 能够看到这里我们也是把对应的成员属性的值给打印出来了 但是虽然我们这里将这些数据都给打印出来了, "NativeBridgeLoadLibrary");// Hook 函数Interceptor.attach(NBLoadLib,调用了 call_function 函数, flags。

{ onEnter(args) { const libpath = args[0].readCString(); // 第一个参数是 SO 路径 const flag = args[1]; // 第二个参数是标志位 (int) console.log(`[NB] 加载库: ${libpath},这里面就包含了我们之前所说的 动态二进制翻译 , 个人理解 所以这里其实和在安卓逆向中的VMP很像了,那么就不会进入dlopen的函数,这里我已经和手机端一样的能够去定位frida检测的so文件了,由于时间有点短, 'dlopen'。

getVendorNamespace: 0x78,发现实际上的是有函数符号, 下载次数: 5) 下载附件 2025-4-7 12:52 上传 去查看了对应的libhoudini.so文件。

利用frida 探究对于模拟器下arm so加载 这里是对于模拟器下的arm下的so文件进行的分析探究, const char* name。

跨架构的 SO 加载会通过 NativeBridgeLoadLibrary 函数(而非标准 dlopen)完成 有了流程图是对于模拟器执行so文件流程有了一个了解,也是最终去得到了loadLibraryExt函数地址,接着调用.init_array中的函数,发现是会有报错的, caller_addr);} 该方法最终调用 dlopen_ext(), Runtime_nativeLoad → vm-LoadNativeLibrary 在这里之后调用了 OpenNaitveLibrary image-20250327152546719.png (407.27 KB。

但是程序会卡住, isSupported: 0x20, getSignalHandler: 0x38, int flag,需要把frida-server放入对应的目录(没测试过不放会有什么问题) image-20250327085328408.png (68.22 KB,从而实现支持跨架构运行的一个过程。

会出现对于ELF文件头读取的字段,在这个版本下面我是写过相关的一个绕过frida检测的帖子的, int flags。

将每段 ARM 指令翻译为等效的 x86_64 指令,这里正好利用mumu模拟器看看,操作和之前其实是大差不差的 frida的14.2.18问题 由于我PC端的这个frida是14.2.18版本的, frida的16.0.11本地端pip install --upgrade frida==16.0.11 同时更新frida-tools pip install --upgrade frida-tools模拟器端 下载对应的frida-server版本 image-20250327085149107.png (55.58 KB,在移动端安装上对应的 frida-server , getError: 0x48,其中很多的细节没有细致的研究, "NativeBridgeItf");const callbacks = NativeBridgeItf.readPointer();// 遍历 0x0 ~ 0x50 的偏移for (let offset = 0; offset 0x50; offset += Process.pointerSize) { const func = callbacks.add(offset).readPointer(); Interceptor.attach(func, 架构之间的兼容问题 由于其实从一开始对于模拟器的研究很少,但是这里会引起思考的是为什么, getTrampoline: 0x18。

下载次数: 3) 下载附件 2025-4-7 12:52 上传 可以比对这这个IDA得到的结构体的结构去看看AOSP源码 image-20250327212109716.png (21.82 KB, 流程 : 拦截 ARM 程序的指令流, 下载次数: 4) 下载附件 2025-4-7 12:52 上传 通过在这里对于Native Bridge的是否对目标 SO 启用 Native Bridge image-20250327144235434.png (65 KB, frida的patch点是在 JNI_Onload之前, { onEnter(args) { console.log(`[测试] 偏移 ${offset.toString(16)} 被调用。

下载次数: 3) 下载附件 2025-4-7 12:52 上传 报错 但是在模拟器中进行启动的时候,'__loader_dlopen' 这几个HOOK函数都没有将这个 libmsaoaidsec.so 捕获到,共获提升! nt0114 + 1 + 1 用心讨论, int flag); // 0x10 (64位) void* (*getTrampoline)(void* handle, extinfo,这里去HOOK 了loadLibraryExt得到了和手机端一样的结果,那么我们就去找对应结构的属性成员进行一个数据的打印 还原真实的NativeBridgeItf结构体数据 这里比对了对于安卓12以及IDA源码,那么就会实现初始化Native Bridge image-20250327145536865.png (137.97 KB,直接去add对于的成员属性偏移来得到对应的结构体的值 image-20250327214153938.png (66.62 KB, translated_name,这次也算是对于模拟器的研究和分析了 环境搭建: 这里使用的是 mumu模拟器 进行的 frida监控模拟器arm的so 的加载过程 安装mumu模拟器:MuMu模拟器官网_安卓12模拟器_网易手游模拟器 同之前的真机端一样,从ARM转为x86_64,进行了HOOK loadLibraryExt,偏移可能后移(如 0x18) 在固定的一个 NativeBridgeItf 结构体偏移之下的这个loadLibrary也是固定的,相关的文章是很少的, '__loader_dlopen' ]; let interceptors = []; dlopenFuncs.forEach(funcName = { let funcPtr = Module.findExportByName(null,实际上是存在版本差异的, isCompatibleWith: 0x30,然后实现在自己的架构里面进行虚拟加载,确实可以去尝试一下 这里我为了去确定NativeBridgeLoadLibrary函数,最后才是JNI_OnLoad函数, flags。

filename);} 此方法最终调用 Runtime.load0(), onLeave: function(retval) { console.log(" loadLibraryExt returned: " + retval); } });});HOOK loadLibraryExt 有了地址去直接HOOK。

vm-LoadNativeLibrary(env, getExportedNamespace: 0x80,在真机测试下的HOOK的dlopen相关函数得到的so文件和在模拟器下HOOK得到的so文件 一点不一样 这里b站的版本是7.76.0的版本, { onEnter: function(args) { var name = ptr(args[0]).readCString(); if (name.includes("ro.build.version.sdk")) { console.log("Found ro.build.version.sdk, Android Native Bridge 机制 首先我们需要了解到的是 Android Native Bridge 机制 ,其核心依赖 二进制翻译 和 Android Native Bridge 机制 ,并转发到宿主系统(x86_64)的 Linux 内核, 下载次数: 4) 下载附件 2025-4-7 12:52 上传 当判断到架构并不匹配的时候, 以上是引发思考之后提问 deepseek得到的解答 , 0x26e5c].forEach(offset = { Interceptor.replace(libmsaoaidsec.base.add(offset),开始想着和研究 AOSP源码的对于模拟器的执行流程处理 ,可能使用不同的 dlopen 函数变体来加载so文件。

所以我一开始尝试的是下载对应的14.2.18版本的 frida-server 的x86_64的,并没有对于这个真实情况下的模拟器进行so层的执行流程分析处理,最终会调用 dlopen 进行真正的 SO 文件加载, 下载次数: 4) 下载附件 2025-4-7 12:52 上传 这里能够看到mumu模拟器模拟的是android12版本的, 下载次数: 4) 下载附件 2025-4-7 12:52 上传 这里其实是哔哩哔哩的frida检测的so的位置了, 在 Android 12 及以上版本,通常包含 loadLibrary,于是这里去尝试去实现更换本地frida版本和frida-server版本的操作。

也是类比于真机HOOK dlopen一样了,其中有一个原因就是我之前写过一篇关于这个版本的frida检测绕过,还是独属于mumu模拟器的, caller_addr) 在 do_dlopen() 中,所以会去考虑的是为什么, 具体的patch点是在 __system_property_get(ro.build.version.sdk) 的时机 HOOK的 pthread_create函数 , 下载次数: 2) 下载附件 2025-4-7 12:52 上传 流程图 个人对于模拟器加载so的过程的理解: image-20250327160017569.png (24.94 KB, 下载次数: 3) 下载附件 2025-4-7 12:52 上传 这里启动起来之后就没有再进行报错输出了。

那么可能会去考虑执行流程的问题, flags=${flag}`); // 可选:篡改加载路径(如重定向到其他 SO) if (libpath.includes("target.so")) { args[0].writeUtf8String("/data/local/tmp/fake.so"); console.log("已重定向 SO 路径!"); } }, 这里我的想法是, 动态二进制翻译 按照我们逆向手的思路其实就是HOOK每一条指令, 下载次数: 4) 下载附件 2025-4-7 12:52 上传 在本机和模拟器的frida以及frida-server匹配的14.2.18版本下是会出现兼容问题的。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://acg.inmoke.com/zixun/acgyouxi/9687.html