fishhook 是一个非常简单的库,它能够使 iOS 模拟器和设备上运行的 Mach-O 二进制文件中的符号动态重新绑定。这提供了类似于在 OS X 上使用 DYLD_INTERPOSE
的功能。在 Facebook,我们发现它作为调试/跟踪目的钩子调用 libSystem 的方法很有用(例如,审核文件描述符的双关问题)。
一旦您将 fishhook.h
/fishhook.c
添加到项目中,您可以根据以下方式重新绑定符号
#import <dlfcn.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "fishhook.h"
static int (*orig_close)(int);
static int (*orig_open)(const char *, int, ...);
int my_close(int fd) {
printf("Calling real close(%d)\n", fd);
return orig_close(fd);
}
int my_open(const char *path, int oflag, ...) {
va_list ap = {0};
mode_t mode = 0;
if ((oflag & O_CREAT) != 0) {
// mode only applies to O_CREAT
va_start(ap, oflag);
mode = va_arg(ap, int);
va_end(ap);
printf("Calling real open('%s', %d, %d)\n", path, oflag, mode);
return orig_open(path, oflag, mode);
} else {
printf("Calling real open('%s', %d)\n", path, oflag);
return orig_open(path, oflag, mode);
}
}
int main(int argc, char * argv[])
{
@autoreleasepool {
rebind_symbols((struct rebinding[2]){{"close", my_close, (void *)&orig_close}, {"open", my_open, (void *)&orig_open}}, 2);
// Open our own binary and print out first 4 bytes (which is the same
// for all Mach-O binaries on a given architecture)
int fd = open(argv[0], O_RDONLY);
uint32_t magic_number = 0;
read(fd, &magic_number, 4);
printf("Mach-O Magic Number: %x \n", magic_number);
close(fd);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
Calling real open('/var/mobile/Applications/161DA598-5B83-41F5-8A44-675491AF6A2C/Test.app/Test', 0)
Mach-O Magic Number: feedface
Calling real close(3)
...
dyld
通过更新 Mach-O 二进制文件中特定部分的指针来绑定懒惰和非懒惰的符号。 fishhook 通过确定 rebind_symbols
传递给每个符号名的位置以更新,然后写入相应的替换来重新绑定这些符号。
对于给定的图像,__DATA
段可能包含两个与动态符号绑定相关的部分:__nl_symbol_ptr
和 __la_symbol_ptr
。 __nl_symbol_ptr
是一个指向非懒加载数据(这些在库加载时绑定)的指针数组,而 __la_symbol_ptr
则是一个指向导入函数的指针数组,通常在第一次调用该符号时由称为 dyld_stub_binder
的例程填充(也可以在启动时让 dyld
绑定这些)。为了找到与这些部分中某个特定位置相对应的符号名称,我们需要跳过几个间接层。对于两个相关部分,从 <mach-o/loader.h>
的 struct section
提供的(在 reserved1
字段中)到所谓的间接符号表的偏移量。位于二进制 __LINKEDIT
段中的间接符号表,实际上是一个数组,其中包含指向符号表的索引(也在 __LINKEDIT
中),其顺序与非懒和懒符号部分中的指针顺序相同。因此,给定的 struct section nl_symbol_ptr
对应于该部分第一个地址的符号表中的索引是 indirect_symbol_table[nl_symbol_ptr->reserved1]
。符号表本身是一个 struct nlist
数组(参见 <mach-o/nlist.h>
),其中每个 nlist
包含一个指向 __LINKEDIT
中字符串表的索引,其中实际存储了符号名称。因此,对于每个指向 __nl_symbol_ptr
和 __la_symbol_ptr
的指针,我们能够找到相应的符号,然后找到相应的字符串,将其与所请求的符号名称进行比较,如果匹配,则将部分中的指针替换为替换值。