如何在Android中实现原生库函数插桩?
Android GLES函数插桩失效的解决思路
核心原因分析
Android上GLES函数的调用机制和桌面Linux存在差异:应用并非直接链接GLES库的符号,而是通过eglGetProcAddress或动态链接器的dlsym获取函数指针后调用,普通的LD_PRELOAD符号覆盖无法拦截这种动态获取的指针。
具体解决方案
1. 拦截eglGetProcAddress函数
这是最直接的适配方案,所有上层应用和系统组件获取GLES函数指针都依赖这个接口:
- 在插桩库中实现自定义的
eglGetProcAddress,当检测到请求目标函数(如glClear)时,返回自身的插桩函数地址; - 对于其他函数,调用原生
eglGetProcAddress返回原始指针; - 确保插桩库的
eglGetProcAddress被优先调用,可通过修改AOSP的EGL初始化逻辑,强制加载你的库并替换该函数的全局符号。
2. 修改AOSP的EGL驱动加载逻辑
利用可修改AOSP代码的权限,在EGL驱动加载流程中注入插桩逻辑:
- 在
frameworks/native/opengl/libs/EGL/egl.cpp的egl_init_drivers函数中,加载插桩库时使用RTLD_NOW | RTLD_GLOBAL标志,确保库中符号全局可见; - 获取原生GLES库的函数指针表,直接替换表中目标函数(如
glClear)的地址为插桩函数地址; - 注意驱动加载顺序,必须在系统GL驱动初始化前完成符号替换。
3. 针对特定应用的定向注入
因为仅需对少量应用生效,无需全局修改:
- 修改Zygote启动逻辑,在启动目标应用时动态设置
LD_PRELOAD环境变量指向插桩库; - 结合SELinux宽容模式,确保应用进程有权限加载
/data/local/tmp/下的库; - 可在AOSP中添加应用白名单,仅白名单内的应用触发插桩库加载。
4. 动态链接器符号劫持技巧
利用Android动态链接器特性强制覆盖符号:
- 在插桩库的构造函数(
__attribute__((constructor))修饰的函数)中,使用dlsym(RTLD_NEXT, "glClear")获取原生函数地址并保存为全局变量; - 通过PLT(过程链接表)替换技术,将系统GL库中
glClear的PLT条目指向插桩函数; - 需注意处理arm64、arm等不同架构的PLT结构差异。
验证步骤
- 在插桩函数中添加Logcat日志,确认是否被调用;
- 使用
dumpsys meminfo <包名>查看应用加载的库列表,确认插桩库已加载; - 通过
readelf -s或nm检查应用进程的符号表,确认目标函数地址已替换为插桩库中的地址。
内容的提问来源于stack exchange,提问作者artem
相关产品推荐
相关产品推荐

