用到技术 利用dyld相关接口,我们可以注册image装载的监听方法: extern void _dyld_register_func_for_add_image(void (func)(const struct mach_header mh, intptr_t vmaddr_slide)); 调用_dyld_register_func_for_add_image注册监听方法后,当前已经装载的image(动态库等)会立刻触发回调, 之后的image会在装载的时候触发回调。 dyld在装载的时候,会对符号进行bind,而fishhook则会在回调函数中进行rebind。

hook过程 1.先找到SEG_LINKEDIT加载命令 2.获取它加载后在VM中的linkedit_base (_dyld_get_image_vmaddr_slide()//获取内存镜像模块基址) 3.通过基址找到三个表基址 间接符号表 符号表 字符串表 4.找到要 hook中函数符号 5.先保存,再修改为指向我们自己的符号 fishhook.h

#ifndef fishhook_h
#define fishhook_h

#include <stddef.h>
#include <stdint.h>
#include <mach-o/nlist.h>

#if !defined(FISHHOOK_EXPORT)
#define FISHHOOK_VISIBILITY __attribute__((visibility("hidden")))
#else
#define FISHHOOK_VISIBILITY __attribute__((visibility("default")))
#endif

#ifdef __cplusplus
extern "C" {
#endif //__cplusplus


struct rebinding {
  const char *name; //字符串名称
  void *replacement;//替换后的方法
  void **replaced;  //原始的方法(通常要存储下来,在替换后的方法里调用)
};
//在__DATA段中,有两个Sections和动态符号绑定有关:
//
//__nl_symbol_ptr 存储了non-lazily绑定的符号,这些符号在mach-o加载的时候绑定。
//__la_symbol_ptr 存储了lazy绑定的符号(方法),这些方法在第一调用的时候,
//由dyld_stub_binder来绑定,所以你会看到,每个mach-o的non-lazily绑定符号都有dyld_stub_binder。
//两个参数分别是rebinding结构体数组,以及数组的长度
//实现手动绑定函数
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);

/*
 * Rebinds as above, but only in the specified image. The header should point
 * to the mach-o header, the slide should be the slide offset. Others as above.
 */
FISHHOOK_VISIBILITY
int rebind_symbols_image(void *header,
                         intptr_t slide,
                         struct rebinding rebindings[],
                         size_t rebindings_nel);

#ifdef __cplusplus
}
#endif //__cplusplus

#endif //fishhook_h


fishhook.c


//用到技术
//利用dyld相关接口,我们可以注册image装载的监听方法:
//extern void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide));
//调用_dyld_register_func_for_add_image注册监听方法后,当前已经装载的image(动态库等)会立刻触发回调,
//之后的image会在装载的时候触发回调。
//dyld在装载的时候,会对符号进行bind,而fishhook则会在回调函数中进行rebind。

//hook过程
//1.先找到SEG_LINKEDIT加载命令
//2.获取它加载后在内存中的基址 linkedit_base
//3.通过基址找到三个表基址  间接符号表  符号表  字符串表
//4.找到要 hook中函数符号
//5.先保存,再修改为指向我们自己的符号
#include "fishhook.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#ifdef __LP64__
typedef struct mach_header_64 mach_header_t;
typedef struct segment_command_64 segment_command_t;
typedef struct section_64 section_t;
typedef struct nlist_64 nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
#else
typedef struct mach_header mach_header_t;
typedef struct segment_command segment_command_t;
typedef struct section section_t;
typedef struct nlist nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
#endif

#ifndef SEG_DATA_CONST
#define SEG_DATA_CONST  "__DATA_CONST"
#endif
//用于保护要 hook的符号(rebind_symbols传入的参数)
//每次调用,就会在链表的头部插入一个节点
struct rebindings_entry {
  struct rebinding *rebindings;   //hook结点
  size_t rebindings_nel;          //大小
  struct rebindings_entry *next;  //下一个
};
//链表头结点
static struct rebindings_entry *_rebindings_head;
////往链表的头部插入一个节点
static int prepend_rebindings(struct rebindings_entry **rebindings_head,
                              struct rebinding rebindings[],
                              size_t nel) {
    //申请结点空间
  struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry));
  if (!new_entry) {
    return -1;
  }//再申请结点指向的空间
  new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel);
  if (!new_entry->rebindings) {
    free(new_entry);
    return -1;
  }
  //拷贝
  memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel);
  //结点指向改变
  new_entry->rebindings_nel = nel;
  //新的结点下一个指向头结点
  new_entry->next = *rebindings_head;
  //保存结点
  *rebindings_head = new_entry;
  return 0;
}
//进行section中的symbol rebind
//symtab_cmd = symtab_command
//nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
//char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
//dysymtab_cmd=dysymtab_command
//uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
    //从linkedit_segment中获取相应的虚拟地址(VA)和文件偏移,然后互减得到两者之间的偏移量 =加载基址
    //但是在内存中有加载基址随机化 所以要加上 slide随机后的偏移
//uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
//进行section中的symbol rebind
static void perform_rebinding_with_section(
        //结点链表
        struct rebindings_entry *rebindings,
        //节 Symbol Table
        section_t *section,
        //偏移
        intptr_t slide,
        //符号表
        nlist_t *symtab,
        //字符串表
        char *strtab,
        //间接寻址符号表
        uint32_t *indirect_symtab) {
    //读取indirect table中的数据(uint32_t)的数组   Indirect Symbol Table
    //ndirect_symtab 是动态符号表的地址,表中包含动态符号在符号表中的索引。
    //那么 section->reserved1 又是代表什么呢?这里的 section 实际上是指 __DATA 段
    //中包含 __la_symbol_ptr 以及 __nl_symbol_ptr 的 section,它们会在 reserved1
    //字段中记录自身所包含的动态符号在 indirect_symtab 的起始索引,因此通过
    //indirect_symbol_indices 便可以得到 section 所包含的动态符号在符号表中的索引信息。
    //indirect_symbol_bindings 则是代表程序偏移后的相关 section 的虚拟地址,fishhook
    //会在其中寻找指向目标动态符号的指针,然后将其指向我们自己的符号。
  uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
  // VA
  void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
  //遍历indirect table
  for (uint i = 0; i < section->size / sizeof(void *); i++) {
      //找到符号在Indrect Symbol Table表中的值
    uint32_t symtab_index = indirect_symbol_indices[i];
    if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
        symtab_index == (INDIRECT_SYMBOL_LOCAL   | INDIRECT_SYMBOL_ABS)) {
      continue;
    }
    //接着去symbol table里面找到符号的值,进一步获取到符号在String Table的名字。
    //以symtab_index作为下标,访问symbol table  n_strx为string table中的下标
    uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
    //获取到symbol_name  偏移量+String Table的基础偏移量
    char *symbol_name = strtab + strtab_offset;
    bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1];
    //遍历链表,一个个hook
    struct rebindings_entry *cur = rebindings;
    while (cur) {
        //每一个链表的结点包括一个hook的C数组
      for (uint j = 0; j < cur->rebindings_nel; j++) {
        if (symbol_name_longer_than_1 &&
            strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) {//如果名称一致
            //如果没有被替换,并且数据合法,则进行替换 指向我们自己的符号
          if (cur->rebindings[j].replaced != NULL &&
              indirect_symbol_bindings[i] != cur->rebindings[j].replacement) {
              //把函数原来的地址保存起来
            *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];
          }
            //将新函数的地址设置上
          indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
          goto symbol_loop;
        }
      }
      cur = cur->next;
    }
  symbol_loop:;
  }
}

static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
                                     const struct mach_header *header,
                                     intptr_t slide) {
  Dl_info info;
	//    typedef struct dl_info {
//        const char      *dli_fname;     指向包含address的加载模块的文件名
//        void            *dli_fbase;     加载模块的句柄。该句柄可用作dlsym() 的第一个参数
//        const char      *dli_sname;     指向与指定的address最接近的符号的名称
//        void            *dli_saddr;     最接近符号的实际地址
//    } Dl_info;
  if (dladdr(header, &info) == 0) {
    return;
  }

  segment_command_t *cur_seg_cmd;
  segment_command_t *linkedit_segment = NULL;
  struct symtab_command* symtab_cmd = NULL;
  struct dysymtab_command* dysymtab_cmd = NULL;
    //SEG_LINKEDIT这个段非常重要
    //Indirect Symbol Table、Symbol Table、String Table的地址都要基于它获取
  uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
  //第一次遍历找出三个表的基址
  for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
    cur_seg_cmd = (segment_command_t *)cur;
    //找到SEG_LINKEDIT段
    if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
      if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) {
        linkedit_segment = cur_seg_cmd;
      }
    } else if (cur_seg_cmd->cmd == LC_SYMTAB) {
    //struct symtab_command {
    //    uint32_t	cmd;		/* LC_SYMTAB */
    //    uint32_t	cmdsize;	/* sizeof(struct symtab_command) */
    //    uint32_t	symoff;		/* symbol table offset */
    //    uint32_t	nsyms;		/* number of symbol table entries */
    //    uint32_t	stroff;		/* string table offset */
    //    uint32_t	strsize;	/* string table size in bytes */
    //};
      symtab_cmd = (struct symtab_command*)cur_seg_cmd;
    } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {
      //重要字段extrefsymoff   file offset to the indirect symbol table
      dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;
    }
  }

  if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||
      !dysymtab_cmd->nindirectsyms) {
    return;
  }

//因为地址空间加载随机化的缘故,系统在加载程序时,会在其原有的地址空间上进行偏移操作,
//而这个 slide 正是偏移的大小,所以 linkedit_base 代表的是程序被加载后的基地址
  uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
  //linkedit_base+symtab_cmd->symoff是Symbol Table的位置
  nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
  //linkedit_base+symtab_cmd->stroff是String Table的位置  算的是文件中的基址
  char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);

  //获取indriect table的数据(uint32_t类型的数组)
  uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
  //加载命令基址
  cur = (uintptr_t)header + sizeof(mach_header_t);
  //再一次遍历
  //遍历 all 段(加载命令)
  for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
      //下一个 cmd
    cur_seg_cmd = (segment_command_t *)cur;
    if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
        //找到DATA和DATA_CONST segment
      if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 &&
          strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) {
        continue;
      }
      //遍历 all节
      for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
          //找到__nl_symbol_ptr和__la_symbol_ptr这两个section
        section_t *sect =
          (section_t *)(cur + sizeof(segment_command_t)) + j;
        if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
            //进行section中的symbol rebind
          perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
        }
        if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
            //进行section中的symbol rebind
            //symtab_cmd = symtab_command
            //nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
            //char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
            //dysymtab_cmd=dysymtab_command
            //uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
            //slide 为RVA
            //uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
          perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
        }
      }
    }
  }
}
//完成动态库的binding之后,会回调这个函数。
//其中slide跟ALSR(Address space layout randomization)有关系,是一个随机的加载地址。
static void _rebind_symbols_for_image(const struct mach_header *header,
                                      intptr_t slide) {
    rebind_symbols_for_image(_rebindings_head, header, slide);
}
//绑定符号
int rebind_symbols_image(void *header,
                         intptr_t slide,
                         struct rebinding rebindings[],
                         size_t rebindings_nel) {
    struct rebindings_entry *rebindings_head = NULL;
    int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel);
    rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide);
    if (rebindings_head) {
      free(rebindings_head->rebindings);
    }
    free(rebindings_head);
    return retval;
}
//手动绑定
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
  int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);
  if (retval < 0) {
    return retval;
  }
  // If this was the first call, register callback for image additions (which is also invoked for
  // existing images, otherwise, just run on existing images
  if (!_rebindings_head->next) {
      //注册image装载的监听方法
      //当前已经装载的image(动态库等)会立刻触发回调,之后的image会在装载的时候触发回调。
      //dyld在装载的时候,会对符号进行bind,而fishhook则会在回调函数中进行rebind。
    _dyld_register_func_for_add_image(_rebind_symbols_for_image);
  } else {
    uint32_t c = _dyld_image_count();
    for (uint32_t i = 0; i < c; i++) {
        //启动之后也可以做函数替换
      _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
    }
  }
  return retval;
}

//main.m

//
//  main.m
//  fishhook
#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, ...);

// 自定义的close函数
int my_close(int fd) {
    printf("Calling real close(%d)\n", fd);
    return orig_close(fd);
}

// 自定义的open函数
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);
        // 转换为:=====>
        
        struct rebinding binds[2];
        // orig_close是一个函数指针,(void *)&orig_close 是一个返回参数,所以用取地址,(void *)&orig_open也是类似的
        struct rebinding bind1 = {"close", my_close, (void *)&orig_close};
        binds[0] = bind1;
        binds[1] = (struct rebinding){"open", my_open, (void *)&orig_open};
        //重新绑定符号 参数 1 为符号相关信息 参数 2 为个数
        rebind_symbols(binds, 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]));
    }
}
//
//bool ksdl_dladdr(const uintptr_t address, Dl_info* const info) {
//    // 初始 Dl_info
//    info->dli_fname = NULL;
//    info->dli_fbase = NULL;
//    info->dli_sname = NULL;
//    info->dli_saddr = NULL;
//    // image index
//    const uint32_t idx = ksdl_imageIndexContainingAddress(address);
//const struct mach_header* header = _dyld_get_image_header(idx);
//// slide
//const uintptr_t imageVMAddrSlide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx);
//const uintptr_t addressWithSlide = address - imageVMAddrSlide;
//// 段基址
//const uintptr_t segmentBase = ksdl_segmentBaseOfImageIndex(idx) + imageVMAddrSlide;
//
//// 拿到了 Pathname
//info->dli_fname = _dyld_get_image_name(idx);
//// 拿到了基地址
//info->dli_fbase = (void*)header;
//
//// Find symbol tables and get whichever symbol is closest to the address
//// 在符号表中查找哪个符号最接近这个指令的地址
//// nlist
//const STRUCT_NLIST* bestMatch = NULL;
//uintptr_t bestDistance = ULONG_MAX;
//// load commond
//uintptr_t cmdPtr = ksdl_firstCmdAfterHeader(header);
//// header->ncmds 代表所有的加载命令,这里进行遍历,查找 LC_SYMTAB
//for(uint32_t iCmd = 0; iCmd<ncmds; iCmd++) {
//    const struct load_command* loadCmd = (struct load_command*)cmdPtr;
//    if(loadCmd->cmd == LC_SYMTAB) {
//        // symtab_command  LC_SYMTAB
//        const struct symtab_command* symtabCmd = (struct symtab_command*)cmdPtr;
//        // 找到符号表
//        const STRUCT_NLIST* symbolTable = (STRUCT_NLIST*)(segmentBase + symtabCmd->symoff);
//        //  找到字符串表
//        const uintptr_t stringTable = segmentBase + symtabCmd->stroff;
//        // 遍历符号表
//        for(uint32_t iSym = 0; iSym nsyms; iSym++) {
//            // If n_value is 0, the symbol refers to an external object.
//            if(symbolTable[iSym].n_value != 0)
//            {
//                uintptr_t symbolBase = symbolTable[iSym].n_value;
//
//                // addr >= symbol.value 说明这个指令在这个函数入口中
//                // 获取和address的距离找到最接近的一个
//                // 离指令地址addr更近的函数入口地址,才是更准确的匹配项
//                uintptr_t currentDistance = addressWithSlide - symbolBase;
//                if((addressWithSlide >= symbolBase) &&
//                   (currentDistance dli_saddr = (void*)(bestMatch->n_value + imageVMAddrSlide);
//                    // 符号的名字
//                    info->dli_sname = (char*)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx);
//                    break;
//                    }
//                    }
//                    cmdPtr += loadCmd->cmdsize;
//                    }
//
//                    return true;
//                    }

输出

Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/fishhook', 0)
Mach-O Magic Number: feedfacf 
Calling real close(3)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/Info.plist', 0)
Calling real close(3)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/fishhook', 0)
Calling real close(3)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/fishhook', 0)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/fishhook', 0)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/fishhook', 0)
Calling real close(4)
Calling real close(5)
Calling real close(4)
Calling real close(4)
Calling real open('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/share/icu/icudt59l.dat', 0)
Calling real close(4)
Calling real open('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/DeviceTypes/iPhone 8 Plus.simdevicetype/Contents/Resources/capabilities.plist', 0)
Calling real close(4)
Calling real close(4)
Calling real close(4)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/fishhook', 0)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/fishhook', 0)
Calling real close(4)
Calling real close(4)
Calling real close(4)
Calling real open('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/CoreServices/SystemVersion.plist', 0)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Library/Caches/com.apple.UIStatusBar/15F79/images/181535779762958242784650320250782874492', 0)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Library/Caches/com.apple.UIStatusBar/15F79/images/640248145345471683816653893630245676187', 0)
Calling real close(4)
Calling real open('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/UIKit.framework/Info.plist', 0)
Calling real close(4)
Calling real open('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/UIKit.framework/Artwork.bundle/Info.plist', 0)
Calling real close(4)
Calling real open('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/UIKit.framework/Artwork.bundle/Assets.car', 0)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Library/Caches/com.apple.UIStatusBar/15F79/images/64024814534547168383211005250830375305', 0)
Calling real close(4)
Calling real open('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/CoreServices/SystemVersion.plist', 0)
Calling real close(4)
Calling real open('/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Fonts/CoreUI/SFUIText.ttf', 0)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/Base.lproj/Main.storyboardc/Info-8.0+.plist', 0)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/Base.lproj/Main.storyboardc/Info.plist', 0)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/Base.lproj/Main.storyboardc/UIViewController-BYZ-38-t0r.nib', 0)
Calling real close(4)
Calling real open('/Users/liuhailong/Library/Developer/CoreSimulator/Devices/F4E12B93-EB34-4D6B-B5A2-C8175AD859AA/data/Containers/Bundle/Application/9DF317C2-399F-4EF4-87A5-56A718751D3D/fishhook.app/Base.lproj/Main.storyboardc/BYZ-38-t0r-view-8bC-Xf-vdC.nib', 0)
Calling real close(4)
Calling real close(4)

参考: https://www.jianshu.com/p/9e1f4d771e35 https://www.jianshu.com/p/4fa4dd917682 https://blog.csdn.net/Hello_Hwc/article/details/78444203?locationNum=3&fps=1 https://www.aliyun.com/jiaocheng/356052.html