plt hook 项目代码分析和最小实现
2022-04-05 #re #plt #elf虽然知道可以通过魔改 plt 表实现 hook 函数,但是不知道具体实现...碰巧最近用到了 plthook 这个库,于是我就研究了下 plthook 这个库,并且仿照着这个库重新实现一份代码。
原理分析
根据命名规则,.rela.plt 表是负责 plt 表的重定位信息。.rela 表中有每项都有 2 个成员,r_offset 指向了该重定位入口所要修正的位置的第一个字节的虚拟地址, r_info 则是表示相关信息,可以通过 ELF[32|64]_R_SYM
获得其指向的 .dynsym 项的下标。
.dynsym 表(动态链接表)中存放动态链接需要的信息,可以通过 .dynsym 表从 .strtab 表中获得要链接的函数名字。
项目代码分析
经过阅读代码后,我大致了解了 plthook 在 Linux/x86_64 平台上的逻辑。
由于项目的代码支持多个 UNIX 系统(Windows 下则是实现了 IAT Hook),所以原项目中有一堆的宏和封装,下面的逻辑只对应着 Linux/x86_64 平台。
- 获取
link_map
- 对动态库使用
dlinfo(hndl, RTLD_DI_LINKMAP, &lmap)
- 对可执行文件使用
_r_debug.r_map
结构体
- 对动态库使用
- 通过
link_map
获取相关 section 的信息。- 获取 "DT_SYMTAB DT_STRTAB DT_STRSZ DT_JMPREL DT_PLTRELSZ" 的相关信息
- 在 rela 表中获得其在 dynsym 中的位置,然后通过 dynsym 获得名字
- 如果是被替换函数,就通过修改 PLT 表进行替换。
重新复现
首先是通过 dlopen
dlinfo
获得 link_map
。使用 sysconf
则是获得了页的大小。
/// @brief Initlize plthook_info
/// @param info empty plthook_info variable
/// @param name library name, NULL means program itself
plthook_status
然后是从 link_map
中获得各个 section("DT_SYMTAB DT_STRTAB DT_STRSZ DT_JMPREL DT_PLTRELSZ")的信息,供后续使用。
// Get information from link_map's address and dyn table
info->base_addr = map->l_addr;
size_t rela_size = 0;
for
info->rel_cnt = rela_size / sizeof;
return status;
}
然后是替换函数。主要流程就是通过 .rela.plt 表找到函数在 .dynsym 段的位置,然后再通过 .dynsym 段获取函数名字,如果是要替换的函数的话,就用 mprotect 将段加上可写权限,并通过写入 r_offset
指向的地址进行魔改。
/// @brief Replace function with new function
plthook_status
完整代码:https://github.com/chenx6/gadget/tree/master/plt_hook
将代码和 test_main.c
一起编译(别忘了加上 -ldl)后,应该可以看到 atoi
的返回值从正确的 "123456" 变成了 "114514",说明这个 POC 实现成功了。也可以通过用 ((constructor)) 修饰函数,通过 so 注入进行修改。