fishhook 0.2

fishhook 0.2

测试已测试
语言语言 CC
许可证 BSD
发布最后发布2016年1月

Facebook, Inc. 维护。



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 的指针,我们能够找到相应的符号,然后找到相应的字符串,将其与所请求的符号名称进行比较,如果匹配,则将部分中的指针替换为替换值。

查找懒或非懒指针表中给定条目名称的过程如下: 图形解释