-
-
[原创]Frida hook 系统点位原理分析 --Dlopen
-
发表于: 2025-6-6 14:03 112
-
dlopen 是LInux/Unix 系统用于动态加载共享库(.so 文件) 核心API, 属于libdl 库的一部分,它允许程序在 运行时动态加载和使用共享库中的函数和符号,而无需在编译时静态连接。
使用场景
- Python 的
ctypes
、Java 的JNI
等通过dlopen
调用 C/C++ 库 - 在不重启服务的情况下更新库文件
- 按需加载某些功能模块,减少启动时间和内存占用
- 游戏引擎,浏览器,IDE 等通过动态加载插件(.so) 扩展功能
- Eg: 加载图像解码插件(如 JPEG,PNG)
插件系统
延迟加载
热更新
跨语言调用
完整使用示例
#include <dlfcn.h> #include <stdio.h> int main() { // 1. 加载共享库 void* handle = dlopen("./libmath.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "Error opening library: %s\n", dlerror()); return 1; } // 2. 获取函数地址 typedef int (*add_func_t)(int, int); add_func_t add = (add_func_t)dlsym(handle, "add"); if (!add) { fprintf(stderr, "Error finding symbol: %s\n", dlerror()); dlclose(handle); return 1; } // 3. 调用函数 int result = add(3, 4); printf("Result: %d\n", result); // 输出: Result: 7 // 4. 卸载库 dlclose(handle); return 0; }
dlopen (POSIX标准函数 即符合操作系统接口标准)
- filename:共享库路径
- flag:加载方式标志位 ,控制加载行为(常用值:
RTLD_LAZY
(延迟绑定)、RTLD_NOW
(立即绑定)) 控制符号解析时机 - 成功返回句柄 , 失败返回 NULL
- dlsym 在运行加载共享库(dlopen)后,通过符号名称(函数名或全局变量名)获取内存地址
dlsym(void* handle, Const char* symbol)
找不到符号的原因
奔溃或空指针访问
dlerror() 动态库调试关键工具,通过返回描述性错误信息辅助开发者快速定位问题。
- handle 句柄
- symbol 符号名称
- 成功返回符号内存地址,失败返回NULL , 可通过dlerror()获取错误地址
- 符号未导出(被隐藏或静态声明), 或共享库路径错误或未正确加载
- 对于android7以上 ,可能会因为命名空间限制访问失败
- 系统强制使用android_dlopen_ext
- Android 共享库默认不导出所有符号尤其系统库,会导致dlsym 查找失败
- 未检测dlopen 返回值直接调用
- 调用函数与实际不符(参数或返回值类型错误)
- 库文件路径问题
- 符号未找到
- 权限问题
- 符号冲突
- 返回一个描述错误的字符串,如果没有错误返回NULL
常见错误信息
- 动态库函数调用后要立即调用,否则会丢失错误信息
- 避免重复调用 ,连续调用两次第二次返回NULL(第一次已经清除错误状态)
- 动态加载共享库: 运行时加载.so 文件(eg: libc.so 或 自定义so)
获取符号地址: 可以通过dlsym 获取库中的函数或变量地址
dlopen(filename, flag)
Android_dlopen_ext (dlopen 会在内部调用android_dlopen_ext)
- filename:共享库路径
- flag:加载标志位
extinfo: 扩展参数 (一个android_dlextinfo 结构体)
- 命名空间
- 其他扩展标志 eg: ANDROID_DLEXT_RESERVED_ADDRESS 在加载库时,预留特定的内存地址范围,确保库加载到指定的虚拟地址。
- 成功返回句柄失败返回NULL
- App 需要自定义隔离命名空间
- 系统应用可访问 , 通过get_system_namespace()
- ROOT 通过Ptrace 或 动态连接器接口强制切换
- 命名空间隔离, 限制不同应用或组件对共享库的访问权限,通过命名空间限制动态库的搜索路径和可见性
命名空间组成
命名空间类型
- Dlopen 隐式绑定命名空间 , 默认绑定到当前线程的默认命名空间
命名空间获取方式:
- 搜索路径 ,(eg:/system/lib) 无法加载不在搜索路径下的库
- 允许的库列表, 明确指定命名空间下允许加载的库名称(eg: libc.so),防止加载未授权的私有库 (如系统内部的libbase.so)
- 链接器命名空间 , 控制不同命名空间之间的库共享关系。 如 system 和 default 之间可能隔离,但某些共享组件可能允许跨命名空间访问
示例:
- libandroid_runtime.so Android Framework 核心库(如 ActivityThread 的 JNI 实现)。
- libbinder.so Binder 机制的核心实现,供系统服务(如 AMS)使用。
- Defalut : 普通应用默认命名空间,受限较多
System:系统服务和关键组件使用的命名空间,包含所有系统库
- Isolated : 为特定功能(如: webview, MedioCode)创建的隔离环境,限制库访问权限
- 通过create_namespace() 创建命名空间
- get_defalut_namespace() or get_system_namespace()
自定义命名空间
Android 特有扩展: dlopen 的增加版本 , 支持命名空间隔离
命名空间控制:允许指定加载库的命名空间,实现严格的库隔离(Android 7.0+以上)
android_dlopen_ext(filename, flag, extinfo)
android_namespace_t* ns = create_namespace( "my_namespace", // 命名空间名称 "/data/local/tmp/lib", // 自定义搜索路径 ANDROID_NAMESPACE_TYPE_ISOLATED, // 隔离类型 完全隔离命名空间,禁止与其他空间 共享库依赖 NULL, // 不允许继承父命名空间的库 0 // 标志位 );
赞赏
他的文章
赞赏
雪币:
留言: