大麦逆向so
解决libmsaoaidsec.so通过dlsym获取pthread_create函数的方案
背景介绍
libmsaoaidsec.so
是一个用于生成和获取手机OAID(Open Anonymous Identifier)的SDK。在移动广告生态中,OAID是替代IMEI等设备标识符的重要方案,几乎所有需要接入广告的APP都需要集成此SDK。
问题描述
在某些场景下,libmsaoaidsec.so
需要通过dlsym
动态获取pthread_create
函数。然而,由于Android系统的限制和动态链接库的加载机制,直接调用dlsym
可能会失败或引发兼容性问题。
libmsaoaidsec.so
版本有很多,而且在很多APP中广泛存在。大致分为两类:
- 通过GOT表导入了
pthread_create
函数,创建了反调试线程。 - 使用
dlsym
动态加载libc.so
库来获取pthread_create
函数指针。
解决方案
动态加载libc.so
并获取函数指针
以下代码展示了如何动态加载libc.so
并获取pthread_create
函数指针。此技术不仅适用于libmsaoaidsec.so
,还可以用于其他高性能应用,例如大麦App的抢票工具。抢票工具通常需要处理高并发请求,因此多线程优化是关键需求:
void *handle = dlopen("libc.so", RTLD_LAZY);
if (handle) {void (*pthread_create_ptr)(...) = dlsym(handle, "pthread_create");if (pthread_create_ptr) {// 创建多个线程处理抢票请求for (int i = 0; i < THREAD_COUNT; i++) {pthread_create_ptr(&threads[i], NULL,抢票逻辑函数, NULL);}} else {// 处理获取失败的情况}dlclose(handle);
}
使用Frida进行动态Hook
以下是一个Frida脚本示例,用于Hook dlsym
并替换pthread_create
函数的返回值。此技术可以用于抢票工具的反调试机制,防止工具被逆向分析:
var pthread_create_ptr = Module.getExportByName(null, "pthread_create");// 备份原始函数
var original_pthread_create = new NativeFunction(pthread_create_ptr, 'int', ['pointer', 'pointer', 'pointer', 'pointer']);var my_pthread_create = new NativeCallback(function (thread_ptr, attr_ptr, start_routine, arg_ptr) {console.log("[*] 自定义 pthread_create 被调用!");console.log(" thread_ptr: " + thread_ptr);console.log(" attr_ptr: " + attr_ptr);console.log(" start_routine: " + start_routine);console.log(" arg_ptr: " + arg_ptr);var find_module = Process.findModuleByAddress(start_routine);console.log("这是pthread_create传入的函数地址,你可以再去hook这个函数看看BLR X8指令的位置,然后NOP掉--> Module: " + find_module.name + " offset:" + start_routine.sub(find_module.base));// 直接返回成功状态return 0;
}, 'int', ['pointer', 'pointer', 'pointer', 'pointer']);Interceptor.attach(Module.getExportByName(null, "dlsym"), {onEnter(args) {this.symbol = Memory.readUtf8String(args[1]);},onLeave(retval) {if (this.symbol.indexOf("pthread_create") !== -1) {console.log("[*] dlsym loaded pthread_create, addr:", retval);var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE);var callerAddress = backtrace[0];var find_module = Process.findModuleByAddress(callerAddress);if (find_module && find_module.name.indexOf("libmsaoaidsec.so") !== -1) {console.log("invoke dlsym |--> Module: " + find_module.name + " offset:" + callerAddress.sub(find_module.base));// 替换返回值为自定义的 pthread_createretval.replace(ptr(my_pthread_create));}}}
});
ELF文件解析与GOT表修改
通过解析libmsaoaidsec.so
的ELF文件,可以定位到pthread_create
的GOT表项。以下是一个Python脚本示例,用于解析ELF文件并修改GOT表。此技术同样适用于抢票工具的动态库加载需求:
import liefbinary = lief.parse("libmsaoaidsec.so")
for symbol in binary.symbols:if symbol.name == "pthread_create":got_entry = binary.get_symbol(symbol.name).valueprint(f"Found pthread_create GOT entry at 0x{got_entry:x}")# 修改GOT表项binary.patch_address(got_entry, 0xdeadbeef) # 替换为目标函数地址binary.write("libmsaoaidsec_patched.so")
绕过内存保护机制
Android系统启用了ASLR和RELRO等内存保护机制,直接修改GOT表可能会失败。可以通过以下方法绕过:
- 禁用RELRO:在编译时添加
-Wl,-z,norelro
选项。 - 动态计算基址:通过
/proc/self/maps
获取模块基址,动态计算GOT表地址。
步骤5:动态调试与性能优化
动态调试与性能优化
使用GDB或LLDB在运行时动态修改函数指针:
gdb -p <pid>
(gdb) set *(void**)0x12345678 = 0xdeadbeef # 替换为目标函数地址
性能优化建议:
- 缓存函数指针:避免频繁调用
dlsym
,缓存函数指针以提高性能。 - 延迟加载:在首次调用时加载
libc.so
,减少启动时间。
抢票工具的技术需求
大麦App的抢票工具通常需要处理高并发请求和快速响应,因此可能涉及以下技术需求:
- 多线程优化:通过
pthread_create
创建多个线程以并发处理抢票请求。 - 动态库加载:可能需要动态加载某些库(如加密库或网络库)以支持抢票逻辑。
- 反调试机制:防止抢票工具被逆向分析或Hook。
技术实现示例
1. 多线程优化
void *handle = dlopen("libc.so", RTLD_LAZY);
if (handle) {void (*pthread_create_ptr)(...) = dlsym(handle, "pthread_create");if (pthread_create_ptr) {// 创建多个线程处理抢票请求for (int i = 0; i < THREAD_COUNT; i++) {pthread_create_ptr(&threads[i], NULL,抢票逻辑函数, NULL);}}dlclose(handle);
}
2. 动态库加载
抢票工具可能需要动态加载加密库以支持安全通信:
void *crypto_handle = dlopen("libcrypto.so", RTLD_LAZY);
if (crypto_handle) {// 获取加密函数指针void (*encrypt_func)(...) = dlsym(crypto_handle, "AES_encrypt");if (encrypt_func) {// 使用加密函数}dlclose(crypto_handle);
}
3. 反调试机制
通过Hook dlsym
防止抢票工具被逆向分析:
Interceptor.attach(Module.getExportByName(null, "dlsym"), {onEnter(args) {this.symbol = Memory.readUtf8String(args[1]);},onLeave(retval) {if (this.symbol.indexOf("ptrace") !== -1) {// 替换为无效函数指针retval.replace(ptr(0x0));}}
});
总结
通过动态加载libc.so
并调用dlsym
,可以安全地获取pthread_create
函数指针。结合ELF文件解析、内存保护绕过和动态调试技巧,我们能够更深入地分析和控制libmsaoaidsec.so
的行为。此方案不仅解决了libmsaoaidsec.so
的需求,还为类似场景(如大麦App抢票工具的多线程优化和反调试机制)提供了参考。