动态链接库函数调用拦截技术

测试示例

让以一个虚构的例子为例。假设有一个用C语言编写的名为“test”的程序(test.c文件)和一个共享库(libtest.c文件),库中实现了一个libtest()函数。在它们的实现中,程序和库都使用了C语言标准库中的puts()函数(与Mac OS一起提供,包含在libSystem.B.dylib中)。让看一下描述的情况的图解:

任务如下:需要将libtest.dylib库对puts()函数的调用替换为在主程序(test.c文件)中实现的hooked_puts()函数的调用。后者,反过来,可以使用原始的puts()函数。

需要取消执行的更改,即,使libtest()的重复调用导致调用原始的puts()。不允许更改代码或重新编译库,只能更改主程序。调用重定向本身应该只为特定库执行,并且是即时的,而不需要程序重启。

重定向算法

让用文字描述所有操作,因为代码可能尽管有很多注释,但可能仍然不那么清晰:

1. 使用LC_SYMTAB加载命令的数据找到符号表和字符串表。

2. 从LC_DYSYMTAB加载命令中,找出符号表中未定义符号的子集(iundefsym字段)从哪个元素开始。

3. 在符号表中未定义符号的子集中,通过名称找到目标符号。

4. 记住目标符号从符号表开始的索引。

5. 使用LC_DYSYMTAB加载命令的数据找到间接符号表(indirectsymoff字段)。

6. 找出从哪个索引开始映射导入表(__DATA, __la_symbol_ptr节的内容;或__IMPORT, __jump_table — 将有一个)到间接符号表(reserved1字段)。

7. 从这个索引开始,查看间接符号表,并搜索与符号表中目标符号索引相对应的值。

8. 记住目标符号从导入表映射到间接符号表的索引。保存的值是导入表中所需元素的索引。

9. 使用__la_symbol_ptr节(或__jump_table)的数据找到导入表(offset字段)。

10. 有了目标元素的索引,重写地址(对于__la_symbol_ptr)为所需值(或者只是更改CALL/JMP指令为JMP,带有操作数 — 所需函数的地址(对于__jump_table))。

将指出,应该在从文件加载符号表、字符串表和间接符号表之后,以及在内存中读取描述导入表的部分以及执行重定向本身时,只与这些表、字符串和间接符号表一起工作。这是因为符号表和字符串表可能不存在,或者不能在目标Mach-O中显示真实状态。这是因为动态加载器在之前在那里工作,并且它成功地保存了所有必要的符号数据,而没有分配表本身。

重定向实现

是时候将想法转化为代码了。让将所有操作分为三个阶段,以优化搜索所需的Mach-O元素:

#include #include #include "mach_hook.h" #define LIBTEST_PATH "libtest.dylib" void libtest(); int hooked_puts(char const *s) { puts(s); return puts("HOOKED!"); } int main() { void *handle = 0; mach_substitution original; Dl_info info; if (!dladdr((void const *)libtest, &info)) { fprintf(stderr, "Failed to get the base address of a library!\n", LIBTEST_PATH); goto end; } handle = mach_hook_init(LIBTEST_PATH, info.dli_fbase); if (!handle) { fprintf(stderr, "Redirection init failed!\n"); goto end; } libtest(); puts("-----------------------------"); original = mach_hook(handle, "puts", (mach_substitution)hooked_puts); if (!original) { fprintf(stderr, "Redirection failed!\n"); goto end; } libtest(); puts("-----------------------------"); original = mach_hook(handle, "puts", original); if (!original) { fprintf(stderr, "Restoration failed!\n"); goto end; } libtest(); end: mach_hook_free(handle); handle = 0; return 0; }

可以通过以下方式进行测试:

user@mac$ arch -i386 ./test libtest: calls the original puts() ----------------------------- libtest: calls the original puts() HOOKED! ----------------------------- libtest: calls the original puts()
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485