首页
社区
课程
招聘
[原创]经典 Frida 检测 libmsaoaidsec.so 绕过
发表于: 2025-12-8 16:59 2949

[原创]经典 Frida 检测 libmsaoaidsec.so 绕过

2025-12-8 16:59
2949

案例:某红薯(v9.8.0)

在面对 Frida 检测导致的闪退时,首要任务是找到是哪个 SO 库触发了检测。
通常,反调试逻辑会在 JNI_OnLoad​ 或 .init_array 中执行。如果 App 在加载某个特定的 SO 后立即崩溃,那么该 SO 极大概率就是检测逻辑的所在地。

通过 Hook 系统底层的动态库加载函数,监控 App 启动过程中加载了哪些 SO 文件。观察 App 崩溃(闪退)前的最后一条加载记录,即可锁定目标。

在 Android 7.0 (Nougat) 及更高版本中,系统加载动态库的底层实现主要依赖 android_dlopen_ext​。相比标准的 dlopen,它提供了更丰富的扩展能力(如从文件描述符加载、指定内存空间加载等),是系统加载器的必经之路。

函数原型

使用 spawn 模式启动 App 并注入上述脚本:

现象
控制台快速打印出一系列 SO 加载日志,随后 App 突然闪退,Frida 会话断开。

日志片段

image

结论​:
加载日志停留在 ​libmsaoaidsec.so。这说明当系统尝试加载并初始化该库时,触发了其内部的反调试机制,导致进程自杀。

确定了 libmsaoaidsec.so​ 是检测元凶后,我们需要找出具体是哪段代码在执行检测。通常,反调试逻辑不会在主线程运行(会卡顿 UI),而是创建一个独立的 子线程 (pthread) 进行轮询检测。

因此,我们需要 Hook 系统底层的线程创建函数 pthread_create​,监控由 libmsaoaidsec.so 发起的所有线程创建行为,并获取其执行函数的入口地址。

这是 Linux/Android 创建线程的标准 POSIX API。

我们重点关注 ​start_routine(arg2) ,它直接指向了线程要执行的代码逻辑。

运行脚本后,当控制台输出如下信息时,我们便成功捕获了检测线程:

image

通过输出我们可以看到一共捕获到了3条关于 libmsaoaidsec.so 创建线程的数据,其函数在so中的偏移量分别是:

在定位到检测线程的来源后,最简单粗暴且有效的绕过方式是 ​ “拒绝执行”

由于 libmsaoaidsec.so​ 中的检测逻辑通常在子线程中死循环运行,我们可以 Hook pthread_create​,当识别到请求来自该 SO 库时,​直接返回 0​(假装创建成功),但不调用原始的 pthread_create 函数。这样,反调试线程永远不会被创建,而主程序的逻辑却以为线程已正常启动。

为什么是 return 0
pthread_create​ 的函数原型定义中,返回值 0 表示 ​Success(成功),而非 0 的值表示错误码。
反调试逻辑通常会检查返回值:

通过返回 0,我们完美欺骗了上层逻辑,使其认为“监控线程”正在正常运行。

副作用与风险
目前的脚本采用的是“核弹级”处理:​禁止该 SO 创建任何线程

进阶:精确打击 (基于 Offset)
如果发现“一刀切”导致 App 功能异常,请使用 第 2 步 中获取的 Offset 进行精确过滤:

成功绕过,程序没用被 Kill ,且程序正常运行:

image

Android 系统加载一个 SO 库的顺序如下:

很多强壳或检测库(如 libmsaoaidsec.so​)会将反调试检测放在 .init_proc​ 或 .init_array​ 中。
如果我们在 android_dlopen_ext​ 的 onLeave​(即加载完成)后再去 Hook JNI_OnLoad​,此时 .init 系列函数早已执行完毕,检测已经触发(App 已闪退),为时晚矣。

既然 onLeave​ 太晚,我们需要在 android_dlopen_ext​ 的 onEnter​ 之后,但在 .init​ 函数执行期间介入。
最佳策略是:寻找一个在 .init_proc中被调用的外部导入函数(Import)进行 Hook

so 未加载完全,无法 Hook 导出函数,也无法通过 so 基址 + 偏移的方式 Hook 函数,所以只能通过 Hook 导入函数进行绕过。

通过 IDA 反编译 libmsaoaidsec.so​ 的 .init_proc 函数:

我们发现 .init_proc​ 的入口处调用了 ​ __system_property_get​。这是一个非常好的 Hook 锚点!只要我们 Hook 这个系统函数,当它被调用且参数为 ro.build.version.sdk​ 时,我们就可以断定:​现在正是 libmsaoaidsec.so执行初始化的时刻

此时 SO 已经在内存中(基址已确定),但后续的检测线程还没来得及创建。

我们的思路如下:

App 正常启动,日志显示 Patch 成功,且没有再出现闪退。

image

面对在 .init​ 段做检测的 SO,android_dlopen_ext+ __system_property_get 是一套非常经典的组合拳。它能帮我们卡住 SO 初始化的咽喉,实现最完美的早期注入。

b98K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6S2M7X3y4Z5x3%4u0F1y4s2u0Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8U0t1H3x3U0g2Q4x3V1j5H3x3#2)9J5c8U0l9J5i4K6u0r3k6Y4u0A6k6r3q4Q4x3U0g2q4y4#2)9J5y4f1u0n7i4K6t1#2z5e0g2Q4x3U0g2q4z5q4)9J5y4f1u0r3i4K6t1#2z5o6N6Q4x3U0g2q4i4K6u0W2i4K6u0W2i4K6u0W2

void* android_dlopen_ext(
    const char* filename,        // args[0]: SO 文件路径
    int flag,                    // args[1]: 加载标志
    const android_dlextinfo* extinfo // args[2]: 扩展信息结构体
);
void* android_dlopen_ext(
    const char* filename,        // args[0]: SO 文件路径
    int flag,                    // args[1]: 加载标志
    const android_dlextinfo* extinfo // args[2]: 扩展信息结构体
);
(function () {
    // 辅助函数:获取格式化时间
    function getTime() {
        return new Date().toLocaleTimeString();
    }
 
    // 辅助函数:终端颜色高亮
    function color(str) {
        return "\x1b[36m" + str + "\x1b[0m";
    }
 
    // 1. 查找 android_dlopen_ext 导出地址
    // 第一个参数传 null 表示在所有加载的模块中查找
    var dlopen = Module.findExportByName(null, "android_dlopen_ext");
     
    if (!dlopen) {
        console.log("[-] android_dlopen_ext not found. Try hooking dlopen instead.");
        return;
    }
 
    console.log("\n[*] Sniffer started on android_dlopen_ext...\n");
 
    Interceptor.attach(dlopen, {
        onEnter: function (args) {
            try {
                // args[0] 是 char* 类型的路径字符串
                var pathPtr = args[0];
                if (pathPtr.isNull()) return;
 
                var soPath = pathPtr.readCString();
                // 过滤无效路径
                if (!soPath || soPath.trim() === "") return;
 
                // 过滤掉系统库,只关注 /data/ 下的应用私有库(可选)
                // if (soPath.indexOf("/system/") !== -1) return;
 
                console.log(`[${getTime()}] Thread:${Process.getCurrentThreadId()} Loading => ${color(soPath)}`);
 
            } catch (e) {
                console.log("[Error] " + e);
            }
        }
    });
})();
(function () {
    // 辅助函数:获取格式化时间
    function getTime() {
        return new Date().toLocaleTimeString();
    }
 
    // 辅助函数:终端颜色高亮
    function color(str) {
        return "\x1b[36m" + str + "\x1b[0m";
    }
 
    // 1. 查找 android_dlopen_ext 导出地址
    // 第一个参数传 null 表示在所有加载的模块中查找
    var dlopen = Module.findExportByName(null, "android_dlopen_ext");
     
    if (!dlopen) {
        console.log("[-] android_dlopen_ext not found. Try hooking dlopen instead.");
        return;
    }
 
    console.log("\n[*] Sniffer started on android_dlopen_ext...\n");
 
    Interceptor.attach(dlopen, {
        onEnter: function (args) {
            try {
                // args[0] 是 char* 类型的路径字符串
                var pathPtr = args[0];
                if (pathPtr.isNull()) return;
 
                var soPath = pathPtr.readCString();
                // 过滤无效路径
                if (!soPath || soPath.trim() === "") return;
 
                // 过滤掉系统库,只关注 /data/ 下的应用私有库(可选)
                // if (soPath.indexOf("/system/") !== -1) return;
 
                console.log(`[${getTime()}] Thread:${Process.getCurrentThreadId()} Loading => ${color(soPath)}`);
 
            } catch (e) {
                console.log("[Error] " + e);
            }
        }
    });
})();
frida -U -f com.xingin.xhs -l hook.js
frida -U -f com.xingin.xhs -l hook.js
(function () {
 
    function now() {
        return new Date().toLocaleTimeString();
    }
 
    // 格式化输出对齐
    function align(label, value) {
        return (label + ":").padEnd(14, " ") + value;
    }
 
    function color(str) {
        return "\x1b[36m" + str + "\x1b[0m";
    }
 
    var targetOnly = false;   // 若设置为 true,则过滤系统 so,只显示第三方库
 
    // 1. 获取 pthread_create 函数地址
    var pthread_create_addr = Module.findExportByName(null, "pthread_create");
    if (!pthread_create_addr) {
        console.log("[-] pthread_create not found.");
        return;
    }
 
    // 2. 定义原函数的 Native 原型,以便后续调用
    // int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
    var pthread_create_native = new NativeFunction(
        pthread_create_addr, "int", ["pointer", "pointer", "pointer", "pointer"]
    );
 
    console.log("\n===== pthread_create monitor started =====\n");
 
    // 3. 使用 Interceptor.replace 替换原函数
    Interceptor.replace(pthread_create_addr, new NativeCallback(function (p0, p1, start_routine, arg) {
 
        // --- 核心逻辑:反查模块信息 ---
        // 通过线程入口地址 (start_routine) 查找它属于哪个 SO 文件
        var module = Process.findModuleByAddress(start_routine);
         
        var soName = module ? module.name : "unknown";
        // 计算偏移量:函数绝对地址 - 模块基址 = 静态偏移 (IDA 中的地址)
        var offset = module ? "0x" + start_routine.sub(module.base).toString(16) : "N/A";
 
        // 过滤逻辑
        if (targetOnly && soName.indexOf("lib") === 0 && soName.indexOf("libc") >= 0) {
            return pthread_create_native(p0, p1, start_routine, arg);
        }
 
        // 捕获到目标 SO 创建线程时打印
        if (soName.indexOf("msaoaidsec") !== -1) {
             console.log("----------------------------------------");
             console.log(align("Time", now()));
             console.log(align("Thread TID", Process.getCurrentThreadId())); // 当前发起创建的线程
             console.log(align("Target SO", color(soName)));
             console.log(align("Entry Addr", start_routine));
             console.log(align("Offset (IDA)", color(offset))); // 重点关注这个偏移
             console.log(align("Arg", arg));
             console.log("----------------------------------------");
        }
 
        // 4. 必须执行原函数,否则应用会崩溃或线程无法创建
        return pthread_create_native(p0, p1, start_routine, arg);
 
    }, "int", ["pointer", "pointer", "pointer", "pointer"]));
 
})();
(function () {
 
    function now() {
        return new Date().toLocaleTimeString();
    }
 
    // 格式化输出对齐
    function align(label, value) {
        return (label + ":").padEnd(14, " ") + value;
    }
 
    function color(str) {
        return "\x1b[36m" + str + "\x1b[0m";
    }
 
    var targetOnly = false;   // 若设置为 true,则过滤系统 so,只显示第三方库
 
    // 1. 获取 pthread_create 函数地址
    var pthread_create_addr = Module.findExportByName(null, "pthread_create");
    if (!pthread_create_addr) {
        console.log("[-] pthread_create not found.");
        return;
    }
 
    // 2. 定义原函数的 Native 原型,以便后续调用
    // int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
    var pthread_create_native = new NativeFunction(
        pthread_create_addr, "int", ["pointer", "pointer", "pointer", "pointer"]
    );
 
    console.log("\n===== pthread_create monitor started =====\n");
 
    // 3. 使用 Interceptor.replace 替换原函数
    Interceptor.replace(pthread_create_addr, new NativeCallback(function (p0, p1, start_routine, arg) {
 
        // --- 核心逻辑:反查模块信息 ---
        // 通过线程入口地址 (start_routine) 查找它属于哪个 SO 文件
        var module = Process.findModuleByAddress(start_routine);
         
        var soName = module ? module.name : "unknown";
        // 计算偏移量:函数绝对地址 - 模块基址 = 静态偏移 (IDA 中的地址)
        var offset = module ? "0x" + start_routine.sub(module.base).toString(16) : "N/A";
 
        // 过滤逻辑
        if (targetOnly && soName.indexOf("lib") === 0 && soName.indexOf("libc") >= 0) {
            return pthread_create_native(p0, p1, start_routine, arg);
        }
 
        // 捕获到目标 SO 创建线程时打印
        if (soName.indexOf("msaoaidsec") !== -1) {
             console.log("----------------------------------------");
             console.log(align("Time", now()));
             console.log(align("Thread TID", Process.getCurrentThreadId())); // 当前发起创建的线程
             console.log(align("Target SO", color(soName)));
             console.log(align("Entry Addr", start_routine));
             console.log(align("Offset (IDA)", color(offset))); // 重点关注这个偏移
             console.log(align("Arg", arg));
             console.log("----------------------------------------");
        }
 
        // 4. 必须执行原函数,否则应用会崩溃或线程无法创建
        return pthread_create_native(p0, p1, start_routine, arg);
 
    }, "int", ["pointer", "pointer", "pointer", "pointer"]));
 
})();
int pthread_create(
    pthread_t *thread,            // arg0: 指向线程标识符的指针
    const pthread_attr_t *attr,   // arg1: 线程属性
    void *(*start_routine) (void *), // arg2: 【关键】线程启动后执行的函数指针
    void *arg                     // arg3: 传递给启动函数的参数
);
int pthread_create(
    pthread_t *thread,            // arg0: 指向线程标识符的指针
    const pthread_attr_t *attr,   // arg1: 线程属性
    void *(*start_routine) (void *), // arg2: 【关键】线程启动后执行的函数指针
    void *arg                     // arg3: 传递给启动函数的参数
);
0x1c544, 0x1b8d4, 0x26e5c
0x1c544, 0x1b8d4, 0x26e5c
(function () {
 
    // 辅助函数:获取当前时间
    function now() {
        return new Date().toLocaleTimeString();
    }
 
    // 1. 获取 pthread_create 地址
    var pthread_create_addr = Module.findExportByName(null, "pthread_create");
    if (!pthread_create_addr) {
        console.log("[-] pthread_create not found.");
        return;
    }
 
    // 2. 定义原函数用于后续调用
    var pthread_create_native = new NativeFunction(
        pthread_create_addr, "int", ["pointer", "pointer", "pointer", "pointer"]
    );
 
    console.log("\n===== pthread_create killer started =====\n");
 
    // 3. 替换 (Replace) 原函数
    Interceptor.replace(pthread_create_addr, new NativeCallback(function (p0, p1, start_routine, arg) {
 
        // 反查模块信息
        var module = Process.findModuleByAddress(start_routine);
        var soName = module ? module.name : "unknown";
        var offset = module ? "0x" + start_routine.sub(module.base).toString(16) : "N/A";
 
        // --- 核心绕过逻辑 ---
        // 检查线程入口函数是否属于 libmsaoaidsec.so
        if (soName.indexOf("libmsaoaidsec.so") !== -1) {
             
            // 打印日志,确认拦截生效
            console.log(`[+] \x1b[31mBLOCKED\x1b[0m Detection Thread from: ${soName} | Offset: ${offset}`);
             
            // 【关键】直接返回 0
            // 在 C 语言标准中,pthread_create 返回 0 代表“成功”
            // 我们欺骗 App 说线程创建成功了,但实际上什么都没做
            return 0;
        }
 
        // 对于其他正常的线程请求,放行并执行原函数
        return pthread_create_native(p0, p1, start_routine, arg);
 
    }, "int", ["pointer", "pointer", "pointer", "pointer"]));
 
})();
(function () {
 

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-12-9 17:15 被xiusi编辑 ,原因:
收藏
免费 92
支持
分享
最新回复 (69)
雪    币: 132
活跃值: (1547)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
666
2025-12-8 17:53
0
雪    币: 390
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
6666
2025-12-8 18:16
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
66666
2025-12-8 19:37
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
666666
2025-12-8 21:40
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
感谢分享
2025-12-8 23:54
0
雪    币: 1140
活跃值: (1240)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
mb_ldbucrik 
  6 楼
感谢分享
2025-12-9 00:00
0
雪    币: 5
活跃值: (3610)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
66666
2025-12-9 09:44
0
雪    币: 293
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
6666666666
2025-12-9 09:59
0
雪    币: 1548
活跃值: (1715)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
感谢分享
2025-12-9 10:35
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
1
2025-12-9 10:36
0
雪    币: 104
活跃值: (7074)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
tql
2025-12-9 10:48
0
雪    币: 209
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
感谢分享
2025-12-9 11:18
0
雪    币: 377
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
666
2025-12-9 11:33
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
666
2025-12-9 11:39
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
很给力,感谢分享!
2025-12-9 12:21
0
雪    币: 29
活跃值: (1355)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
很给力,感谢分享!
2025-12-9 13:44
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
666
2025-12-9 16:43
0
雪    币: 347
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
学习学习
2025-12-10 09:42
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
非常好
2025-12-10 13:54
0
雪    币: 2572
活跃值: (3802)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
学习学习
2025-12-10 14:01
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
22
最新版YY也是使用的这个版本的libmsaoaidsec.so,因为pthread_create的几个偏移都是一模一样的,但是patch这几个地址以后APP直接白屏卡死,过一会就再次crash了。
2025-12-10 14:18
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
666
2025-12-10 15:25
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
24
666
2025-12-10 17:20
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
25
666
2025-12-10 17:32
0
游客
登录 | 注册 方可回帖
返回