Android PLT Hook 笔记

PLT Hook,全称是 Procedure Linkage Table Hook,是 Native 层最经典、最稳定的 Hook 技术之一。它不直接修改函数指令,而是通过修改 ELF 文件中的 GOT 表,来"偷梁换柱",实现对外部函数调用的拦截。

下面我们结合与 Inline Hook 的对比,从底层原理到代码落地,把它彻底讲透。

🎯 为什么选择 PLT Hook?

在 Native Hook 的众多流派中,PLT Hook 因其独特的优势和局限性,有自己最擅长的应用场景。

特性维度 PLT Hook (GOT/PLT Hook) Inline Hook
核心原理 修改GOT表中的函数地址 修改函数前几条指令,写入跳转代码
实现复杂度 较低。不涉及指令修复,逻辑清晰 极高。需处理指令重定位、多架构兼容、指令缓存等
稳定性 极高。业界公认成熟稳定,可部署到生产环境 相对较低。易出现兼容性问题
Hook范围 仅能拦截对动态库的外部调用 (如fopen、pthread_create) 可以 Hook 任意函数,包括内部函数和静态函数
典型应用 I/O监控、网络监控、线程监控(如 Matrix、xHook) 复杂的性能监控、游戏外挂、逆向工程

简单来说,如果你的目标是监控应用调用了哪些系统 API(如文件读写、网络请求、线程创建),PLT Hook 是首选,因为它足够简单和安全。

⚙️ 核心原理:从ELF到GOT的"移花接木"

PLT Hook 的整个流程,依赖于 Linux 系统动态链接的实现机制。我们可以把它的工作流程拆解为三步:

  • 第一步:查找目标函数的"门牌号" (GOT表偏移) 在 ELF 文件中,.rel.plt 节(重定位表)专门记录了所有需要重定位的外部函数信息。通过 readelf -r 命令,我们可以找到目标函数(如 fwrite)在 GOT 表中的偏移量,例如 0x2FE0。
  • 第二步:定位运行时的"真实地址" (基址 + 偏移) 当 SO 被加载到内存后,它的起始地址(基址)是动态变化的。我们可以通过读取 /proc/<pid>/maps 文件,找到目标 SO 的基址。 运行时函数GOT地址 = 基址 + 偏移量 。这个地址里,存放的就是真正 fwrite 函数的地址。
  • 第三步:执行"移花接木" (修改GOT)
    1. 权限修改 :代码段通常是只读的。我们需要先调用 mprotect 将包含目标 GOT 项的内存页权限改为可写(PROT_READ | PROT_WRITE)。
    2. 地址替换 :直接向计算出的 GOT 地址中,写入我们自定义的 Hook 函数(如 my_fwrite)的地址。
    3. 缓存同步 :ARM 架构有指令缓存,为确保 CPU 读取到最新的指令,需调用 __builtin___clear_cache 清除缓存。

完成这三步后,当程序执行流通过 PLT 跳转到 GOT 表获取 fwrite 地址时,拿到的就是 my_fwrite 的地址,从而成功进入我们的 Hook 函数。

💻 实战代码要点

虽然实际项目中更推荐直接使用成熟的框架(如爱奇艺的 xHook 、微信 Matrix 的 ELF Hook 或 Facebook 的 PLT Hook),但理解其核心代码有助于你更好地驾驭它们。一个典型的 PLT Hook 核心操作如下:

c 复制代码
// 假设已通过maps解析出目标函数在GOT表中的运行时地址:got_addr
// 假设自定义的Hook函数为:my_function

// 1. 修改内存页权限
uintptr_t page_start = PAGE_START((uintptr_t)got_addr);
mprotect((void*)page_start, PAGE_SIZE, PROT_READ | PROT_WRITE);

// 2. 替换GOT表项 (got_addr中存储的就是原函数地址)
void** got_item = (void**)got_addr;
void* original_function = *got_item; // 如果需要原函数,先保存
*got_item = (void*)my_function;       // 替换为Hook函数

// 3. 清除指令缓存
__builtin___clear_cache((void*)page_start, (void*)PAGE_END(got_addr));

💡 总结

PLT Hook 之所以经典,是因为它巧妙地利用了系统已有的动态链接机制。它不是侵入式地修改代码,而是"欺骗"了程序查找函数地址的过程,因此稳定性极高。在 Android 性能优化的战场上,它是你监控 I/O、网络、线程等系统调用的"特种部队",精准且可靠。

相关推荐
三少爷的鞋13 小时前
AI 时代,我们都将成为通才型开发者:只懂 Android,已经不够了
android
Dovis(誓平步青云)17 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
晚风叙码19 小时前
MySQL 数据类型详解:从数值到字符串,一篇讲透
android·mysql·adb
传奇开心果编程19 小时前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓1 天前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer