首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
10
8
[原创]Frida 检测 libmsaoaidsec.so 绕过学习
发表于: 2026-1-30 11:40
3900
[原创]Frida 检测 libmsaoaidsec.so 绕过学习
mb_enmenpdg
2026-1-30 11:40
3900
```js frida 16.2.1 android 14 IDA pro 9.1 ``` # b站 7.76版本——8.81版本(最新版) ### SO 库的加载流程详解 Android 系统加载一个 SO 库的顺序如下: 1. **`dlopen`** **/** **`android_dlopen_ext`**:系统调用加载器,将 SO 映射到内存。 2. **`.init`** **/** **`.init_proc`**:执行初始化段的代码。 3. **`.init_array`**:执行初始化数组中的函数(C++ 全局构造函数等)。 4. **`JNI_OnLoad`**:最后执行,通常用于注册 JNI 方法。 ## 定位检测点 ```c function hook_dlopen() { const funcName = "android_dlopen_ext"; const libc = Module.findBaseAddress("libc.so"); var funcPtr = Module.findExportByName(null, funcName); if (funcPtr !== null && funcPtr !== undefined) { console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`); Interceptor.attach(funcPtr, { onEnter: function (args) { this.pathPtr = args[0]; if (this.pathPtr !== null && this.pathPtr !== undefined) { try { // 读取加载的so名称字符串并打印 var path = this.pathPtr.readCString(); console.log("\x1b[36m[dlopen] \x1b[0m" + path); if (path.indexOf("libmsaoaidsec.so") !== -1) { this.isTarget = true; } } catch (e) { console.log("[!] Error reading path string in " + this.funcName); } } }, onLeave: function (retval) { console.log("结束"); } }); } else { console.log("[-] Warning: " + funcName + " not found in exports."); } } function main() { hook_dlopen(); } setImmediate(main); ``` 可以看到除了`libmsaoaidsec.so`,都输出了结束,并且最后是打印`libmsaoaidsec.so`后程序退出,所以`libmsaoaidsec.so`大概率为检测的so文件,并且没有打印出“结束”,所以我们可以确定是检测点在so加载完成之前  可以通过在dlopen结束之后,去HOOK JNI_Onload函数,去判断检测函数在JNI_Onload之前还是之后 ```js function hook_dlopen() { const funcName = "android_dlopen_ext"; const libc = Module.findBaseAddress("libc.so"); var funcPtr = Module.findExportByName(null, funcName); if (funcPtr !== null && funcPtr !== undefined) { console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`); Interceptor.attach(funcPtr, { onEnter: function (args) { this.pathPtr = args[0]; if (this.pathPtr !== null && this.pathPtr !== undefined) { try { // 读取加载的so名称字符串并打印 var path = this.pathPtr.readCString(); console.log("\x1b[36m[dlopen] \x1b[0m" + path); if (path.indexOf("libmsaoaidsec.so") !== -1) { this.isTarget = true; } } catch (e) { console.log("[!] Error reading path string in " + this.funcName); } } }, onLeave: function (retval) { if (this.isTarget) { hook_JNI_OnLoad(); } } }); } else { console.log("[-] Warning: " + funcName + " not found in exports."); } } function hook_JNI_OnLoad() { let module = Process.findModuleByName("libmsaoaidsec.so") Interceptor.attach(module.base.add(0x13A4C), { onEnter(args) { console.log("JNI_OnLoad") } }) } function main() { hook_dlopen(); } setImmediate(main); ``` 发现最后没有打印出JNI_OnLoad,所以肯定是在JNI_OnLoad之前检测的  如果在JNI_OnLoad加载后,执行结果应当如下:会先打印出JNI_OnLoad再退出  所以根据以上的结果,我们可以判断出,检测函数是在JNI_OnLoad之前就开始检测了 所以我们找一个靠前的时间点, 在so初始化的时候我们找一个锚点,在这个锚点再去进行后续的hook,首先我们先找锚点 ## 绕过检测点 打开IDA pro,我们一般情况下都会找 **`__system_property_get`**作为我们的锚点,一般情况下参数为`ro.build.version.sdk` 我们可以直接通过IDA搜索字符串`ro.build.version.sdk`,双击跳转到这个函数   我们可以找到`sub_123F0`,发现其调用了_system_property_get,调用系统属性获取函数,获取 SDK 版本,  查看`sub_123F0`的交叉引用,可以看到在init_proc中,`sub_123F0`被调用了,我们知道init_proc是一个很靠前的时间点,所以可以确认 **`__system_property_get`**就是一个很好的锚点,此时 SO 已经在内存中(基址已确定),但后续的检测线程还没来得及创建  所以我们给出下面的代码先做一个测试: ```js function hook_dlopen() { const funcName = "android_dlopen_ext"; const libc = Module.findBaseAddress("libc.so"); var funcPtr = Module.findExportByName(null, funcName); if (funcPtr !== null && funcPtr !== undefined) { console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`); Interceptor.attach(funcPtr, { onEnter: function (args) { this.pathPtr = args[0]; if (this.pathPtr !== null && this.pathPtr !== undefined) { try { // 读取加载的so名称字符串并打印 var path = this.pathPtr.readCString(); console.log("\x1b[36m[dlopen] \x1b[0m" + path); if (path.indexOf("libmsaoaidsec.so") !== -1) { this.isTarget = true; hook_system_property_get(); //在libmsaoaidsec.so进行初始化的时候hook } } catch (e) { console.log("[!] Error reading path string in " + this.funcName); } } }, onLeave: function (retval) { } }); } else { console.log("[-] Warning: " + funcName + " not found in exports."); } } function hook_JNI_OnLoad() { let module = Process.findModuleByName("libmsaoaidsec.so") Interceptor.attach(module.base.add(0x13A4C), { onEnter(args) { console.log("JNI_OnLoad") } }) } function hook_system_property_get() { var system_property_get_addr = Module.findExportByName(null, "__system_property_get"); if (system_property_get_addr !== null && system_property_get_addr !== undefined) { Interceptor.attach(system_property_get_addr, { onEnter: function (args) { var nameptr = args[0]; if (nameptr) { var name = ptr(nameptr).readCString(); if (name.indexOf("ro.build.version.sdk") >= 0) { console.log("Found ro.build.version.sdk, need to patch"); //这里可以开始进行HOOK } } } }) } } function main() { hook_dlopen(); } setImmediate(main); ``` 可以看到结果打印了`Found ro.build.version.sdk, need to patch`,说明`hook_system_property_get()`在检测函数之前就执行了,说明我们的推断是合理的  接下来就是去hook检测线程了,我们在这个锚点尝试hook `pthread_create`,打印出检测线程的地址 ```js function hook_dlopen() { const funcName = "android_dlopen_ext"; const libc = Module.findBaseAddress("libc.so"); var funcPtr = Module.findExportByName(null, funcName); if (funcPtr !== null && funcPtr !== undefined) { console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`); Interceptor.attach(funcPtr, { onEnter: function (args) { this.pathPtr = args[0]; if (this.pathPtr !== null && this.pathPtr !== undefined) { try { // 读取加载的so名称字符串并打印 var path = this.pathPtr.readCString(); console.log("\x1b[36m[dlopen] \x1b[0m" + path); if (path.indexOf("libmsaoaidsec.so") !== -1) { this.isTarget = true; hook_system_property_get(); } } catch (e) { console.log("[!] Error reading path string in " + this.funcName); } } }, onLeave: function (retval) { } }); } else { console.log("[-] Warning: " + funcName + " not found in exports."); } } function hook_JNI_OnLoad() { let module = Process.findModuleByName("libmsaoaidsec.so") Interceptor.attach(module.base.add(0x13A4C), { onEnter(args) { console.log("JNI_OnLoad") } }) } function hook_pthread_create() { var pthread_create_addr = Module.findExportByName("libc.so", "pthread_create"); console.log("pthread_create addr: ", pthread_create_addr); Interceptor.attach(pthread_create_addr, { onEnter: function (args) { var thread_func_addr = args[2]; var module = Process.findModuleByAddress(thread_func_addr); console.log(`pthread_create thread func: ${module.name}+0x${(thread_func_addr - module.base).toString(16)}`); }, onLeave: function (retval) { } }); } function hook_system_property_get() { var system_property_get_addr = Module.findExportByName(null, "__system_property_get"); if (system_property_get_addr !== null && system_property_get_addr !== undefined) { Interceptor.attach(system_property_get_addr, { onEnter: function (args) { var nameptr = args[0]; if (nameptr) { var name = ptr(nameptr).readCString(); if (name.indexOf("ro.build.version.sdk") >= 0) { console.log("Found ro.build.version.sdk, need to patch"); // hook_pthread_create(); // bypass() //这里可以开始进行HOOK hook_pthread_create(); } } } }) } } function main() { hook_dlopen(); } setImmediate(main); ```  我们直接尝试去nop这些线程 ```js function hook_dlopen() { const funcName = "android_dlopen_ext"; const libc = Module.findBaseAddress("libc.so"); var funcPtr = Module.findExportByName(null, funcName); if (funcPtr !== null && funcPtr !== undefined) { console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`); Interceptor.attach(funcPtr, { onEnter: function (args) { this.pathPtr = args[0]; if (this.pathPtr !== null && this.pathPtr !== undefined) { try { // 读取加载的so名称字符串并打印 var path = this.pathPtr.readCString(); console.log("\x1b[36m[dlopen] \x1b[0m" + path); if (path.indexOf("libmsaoaidsec.so") !== -1) { this.isTarget = true; hook_system_property_get(); } } catch (e) { console.log("[!] Error reading path string in " + this.funcName); } } }, onLeave: function (retval) { } }); } else { console.log("[-] Warning: " + funcName + " not found in exports."); } } function hook_JNI_OnLoad() { let module = Process.findModuleByName("libmsaoaidsec.so") Interceptor.attach(module.base.add(0x13A4C), { onEnter(args) { console.log("JNI_OnLoad") } }) } function hook_pthread_create() { var pthread_create_addr = Module.findExportByName("libc.so", "pthread_create"); console.log("pthread_create addr: ", pthread_create_addr); Interceptor.attach(pthread_create_addr, { onEnter: function (args) { var thread_func_addr = args[2]; var module = Process.findModuleByAddress(thread_func_addr); console.log(`pthread_create thread func: ${module.name}+0x${(thread_func_addr - module.base).toString(16)}`); }, onLeave: function (retval) { } }); } function nopFunc(addr) { Memory.protect(addr, 4, 'rwx'); // 修改该地址的权限为可读可写 var writer = new Arm64Writer(addr); writer.putRet(); // 直接将函数首条指令设置为ret指令 writer.flush(); // 写入操作刷新到目标内存,使得写入的指令生效 writer.dispose(); // 释放 Arm64Writer 使用的资源 console.log("nop " + addr + " success"); } function bypass_detect_func() { var base = Module.findBaseAddress("libmsaoaidsec.so") // jxbank nopFunc(base.add(0x1c544)); nopFunc(base.add(0x1b8d4)); nopFunc(base.add(0x26e5c)); } function hook_system_property_get() { var system_property_get_addr = Module.findExportByName(null, "__system_property_get"); if (system_property_get_addr !== null && system_property_get_addr !== undefined) { Interceptor.attach(system_property_get_addr, { onEnter: function (args) { var nameptr = args[0]; if (nameptr) { var name = ptr(nameptr).readCString(); if (name.indexOf("ro.build.version.sdk") >= 0) { console.log("Found ro.build.version.sdk, need to patch"); // hook_pthread_create(); // bypass() //这里可以开始进行HOOK // hook_pthread_create(); bypass_detect_func(); } } } }) } } function main() { hook_dlopen(); } setImmediate(main); ``` frida已经不退出了  ## 其他锚点 这种寻找锚点的方式,不只可以使用`__system_property_get`作为我们的锚点,还可以使用其他的函数,这边使用gemini找了几个可以尝试作为锚点的函数 | **锚点函数** | **推荐指数** | **触发时机** | **适用场景** | | -- | ------------ | ------ | ----------------------------------- | | **__system_property_get** | ⭐⭐⭐⭐⭐ | 极早 | 几乎所有加固都会读取`ro.build.version`或厂商信息 | | **dlsym** | ⭐⭐⭐⭐⭐ | 极早 | 壳需要隐藏 API 调用时(如隐藏`ptrace`等) | | **prctl** | ⭐⭐⭐ | 较早 | 防止 Dump 或 允许 Ptrace 时 | ### **dlsym** 首先我们在IDA中看dlsym是否在init_proc阶段被调用:找到dlsym,查看它的引用  定位到`sub_9150`,继续查看引用,可以发现在init_proc中被调用了,所以在初始化阶段确实存在dlsym  SO 加壳或做对抗时,为了隐藏导入表,往往会通过 `dlopen`/`dlsym` 动态获取系统函数地址(如 `ptrace`, `open`, `pthread_create`)。**特征参数**:第二个参数是**函数名称字符串,这里编写如下的代码,测试是否会动态导入**`pthread_create` ```js function hook_dlopen() { const funcName = "android_dlopen_ext"; const libc = Module.findBaseAddress("libc.so"); var funcPtr = Module.findExportByName(null, funcName); if (funcPtr !== null && funcPtr !== undefined) { console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`); Interceptor.attach(funcPtr, { onEnter: function (args) { this.pathPtr = args[0]; if (this.pathPtr !== null && this.pathPtr !== undefined) { try { // 读取加载的so名称字符串并打印 var path = this.pathPtr.readCString(); console.log("\x1b[36m[dlopen] \x1b[0m" + path); if (path.indexOf("libmsaoaidsec.so") !== -1) { this.isTarget = true; // hook_system_property_get(); // hook_prctl_anchor() hook_dlsym_anchor(); } } catch (e) { console.log("[!] Error reading path string in " + this.funcName); } } }, onLeave: function (retval) { } }); } else { console.log("[-] Warning: " + funcName + " not found in exports."); } } function hook_dlsym_anchor() { const dlsym_addr = Module.findExportByName(null, "dlsym"); if (dlsym_addr) { Interceptor.attach(dlsym_addr, { onEnter: function (args) { this.symbolName = args[1].readCString(); // 监听壳是否在动态获取pthread_create if (this.symbolName && (this.symbolName.indexOf("pthread_create") >= 0)) { console.log("[Anchor] dlsym finding: " + this.symbolName); // 触发核心 Bypass 逻辑 // bypass_detect_func(); } } }); } } function main() { hook_dlopen(); } setImmediate(main); ``` 可以看到打印了[Anchor] dlsym finding: pthread_create  我们在这个锚点执行我们的bypass方法 ```js function hook_dlopen() { const funcName = "android_dlopen_ext"; const libc = Module.findBaseAddress("libc.so"); var funcPtr = Module.findExportByName(null, funcName); if (funcPtr !== null && funcPtr !== undefined) { console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`); Interceptor.attach(funcPtr, { onEnter: function (args) { this.pathPtr = args[0]; if (this.pathPtr !== null && this.pathPtr !== undefined) { try { // 读取加载的so名称字符串并打印 var path = this.pathPtr.readCString(); console.log("\x1b[36m[dlopen] \x1b[0m" + path); if (path.indexOf("libmsaoaidsec.so") !== -1) { this.isTarget = true; // hook_system_property_get(); // hook_prctl_anchor() hook_dlsym_anchor(); } } catch (e) { console.log("[!] Error reading path string in " + this.funcName); } } }, onLeave: function (retval) { } }); } else { console.log("[-] Warning: " + funcName + " not found in exports."); } } function hook_dlsym_anchor() { const dlsym_addr = Module.findExportByName(null, "dlsym"); if (dlsym_addr) { Interceptor.attach(dlsym_addr, { onEnter: function (args) { this.symbolName = args[1].readCString(); // 监听壳是否在动态获取pthread_create if (this.symbolName && (this.symbolName.indexOf("pthread_create") >= 0)) { console.log("[Anchor] dlsym finding: " + this.symbolName); // 触发核心 Bypass 逻辑 bypass_detect_func(); } } }); } } function main() { hook_dlopen(); } setImmediate(main); ``` 可以看到已经成功绕过,frida未退出  ### **prctl** 这里验证了**`prctl`**,IDA中的调用路径如下: 通过function中搜索函数,定位到函数  开始查看引用,找到`sub_1B144`  查看`sub_1B144`的引用,找到了`sub_1B380`:  继续查看引用,找到`sub_1B924`:  继续往上跟进,找到`sub_1BEC4`:  发现在init_proc中调用了`sub_1BEC4`,说明了`prctl`确实是在初始化阶段被调用了  具体代码如下: ```js function hook_dlopen() { const funcName = "android_dlopen_ext"; const libc = Module.findBaseAddress("libc.so"); var funcPtr = Module.findExportByName(null, funcName); if (funcPtr !== null && funcPtr !== undefined) { console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`); Interceptor.attach(funcPtr, { onEnter: function (args) { this.pathPtr = args[0]; if (this.pathPtr !== null && this.pathPtr !== undefined) { try { // 读取加载的so名称字符串并打印 var path = this.pathPtr.readCString(); console.log("\x1b[36m[dlopen] \x1b[0m" + path); if (path.indexOf("libmsaoaidsec.so") !== -1) { this.isTarget = true; // hook_system_property_get(); hook_prctl_anchor() } } catch (e) { console.log("[!] Error reading path string in " + this.funcName); } } }, onLeave: function (retval) { } }); } else { console.log("[-] Warning: " + funcName + " not found in exports."); } } function hook_pthread_create() { var pthread_create_addr = Module.findExportByName("libc.so", "pthread_create"); console.log("pthread_create addr: ", pthread_create_addr); Interceptor.attach(pthread_create_addr, { onEnter: function (args) { var thread_func_addr = args[2]; var module = Process.findModuleByAddress(thread_func_addr); console.log(`pthread_create thread func: ${module.name}+0x${(thread_func_addr - module.base).toString(16)}`); }, onLeave: function (retval) { } }); } function nopFunc(addr) { Memory.protect(addr, 4, 'rwx'); // 修改该地址的权限为可读可写 var writer = new Arm64Writer(addr); writer.putRet(); // 直接将函数首条指令设置为ret指令 writer.flush(); // 写入操作刷新到目标内存,使得写入的指令生效 writer.dispose(); // 释放 Arm64Writer 使用的资源 console.log("nop " + addr + " success"); } function bypass_detect_func() { var base = Module.findBaseAddress("libmsaoaidsec.so") // jxbank nopFunc(base.add(0x1c544)); nopFunc(base.add(0x1b8d4)); nopFunc(base.add(0x26e5c)); } function hook_prctl_anchor() { const prctl_ptr = Module.findExportByName(null, "prctl"); const PR_SET_DUMPABLE = 4; if (prctl_ptr) { Interceptor.attach(prctl_ptr, { onEnter: function (args) { const option = args[0].toInt32(); // 锚点:检测到尝试禁止内存 dump if (option === 15) { console.log(`[Anchor] prctl(PR_SET_DUMPABLE) detected!`); bypass_detect_func(); } } }); } } function main() { hook_dlopen(); } setImmediate(main); ``` 但是存在一个问题,这个锚点虽然能执行`bypass_detect_func`,但是实测下来无法执行`hook_pthread_create()` ## 其他绕过方法 这时候就有兄弟要问了,有没有更轮椅的方法,有的兄弟,有的 只需要去下载一个<a href="elink@b16K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6k6L8r3q4J5L8$3c8Q4x3V1k6r3L8r3!0J5K9h3c8S2i4K6u0r3M7X3g2D9k6h3q4K6k6i4x3`.">florida</a> 就可以一键绕过检测了 下载对应的版本,直接替换原本的frida-server即可。 ## 参考文章 [绕过最新版bilibili app反frida机制](https://bbs.kanxue.com/thread-281584.htm) [[原创]经典 Frida 检测 libmsaoaidsec.so 绕过](https://bbs.kanxue.com/thread-289359.htm) [[原创]某加固新版frida检测绕过-trace一把嗦](https://bbs.kanxue.com/thread-289545.htm) [[原创] bilibili frida检测分析绕过](https://bbs.kanxue.com/thread-285893.htm) **小白第一次发帖,可能分析和描述中存在错漏,望大佬指点~**
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
#HOOK注入
收藏
・
10
点赞
・
8
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
long_
谢谢你的细致分析,受益匪浅!
2026-8-19 21:32
mb_gytzdazc
这个讨论对我很有帮助,谢谢!
2026-8-13 15:51
dragpn
感谢你的积极参与,期待更多精彩内容!
2026-3-9 23:14
mb_bcgnztsa
为你点赞!
2026-3-5 20:03
0xThawne
为你点赞!
2026-2-24 15:54
mancong
+1
你的分享对大家帮助很大,非常感谢!
2026-2-3 11:09
zhao3261
为你点赞!
2026-1-31 13:37
git_47499test-look
这个讨论对我很有帮助,谢谢!
2026-1-30 19:18
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
6
)
Imxz
雪 币:
112
活跃值:
(9405)
能力值:
( LV2,RANK:10 )
在线值:
发帖
6
回帖
750
粉丝
9
关注
私信
Imxz
2
楼
tql
2026-1-30 14:46
0
嘎嘎真的很棒
雪 币:
716
活跃值:
(3787)
能力值:
( LV3,RANK:30 )
在线值:
发帖
8
回帖
161
粉丝
6
关注
私信
嘎嘎真的很棒
3
楼
call_constructors函数可以让你非常舒服的去Hook JNI_OnLoad之前的检测,这个符号很稳定。
2026-2-3 22:42
1
签个到
雪 币:
17
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
2
粉丝
0
关注
私信
签个到
4
楼
tql
2026-2-4 09:52
0
Mr.ghost
雪 币:
528
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
5
粉丝
0
关注
私信
Mr.ghost
5
楼
你是用原版frida-sever吗, 8.36 bilibil frida 17最后还是会 Process terminated
2026-2-17 12:19
0
mb_upczeeld
雪 币:
6
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
8
粉丝
0
关注
私信
mb_upczeeld
6
楼
666
2026-2-28 20:50
0
mb_gytzdazc
雪 币:
238
能力值:
( LV1,RANK:0 )
在线值:
发帖
2
回帖
2
粉丝
1
关注
私信
mb_gytzdazc
7
楼
florida17实测好像绕不过去
2026-8-13 15:52
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
mb_enmenpdg
1
发帖
3
回帖
0
RANK
关注
私信
他的文章
[原创]Frida 检测 libmsaoaidsec.so 绕过学习
3900
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部