fishhook源码分析

  1. 云栖社区>
  2. 博客列表>
  3. 正文

fishhook源码分析

阿呆少爷 2018-08-10 11:17:47 浏览589 评论0

摘要: 最早了解到[fishhook](https://github.com/facebook/fishhook)是看了下面两篇文章之后,顿时让我觉得这是一个非常好的东西。总共210行代码,收获了1500+个star,神作啊。 1. [iOS Lazy Binding](http://www.atatech.org/articles/68014),使用fishhook拦截NSSetUncaughtE

最早了解到fishhook是看了下面两篇文章之后,顿时让我觉得这是一个非常好的东西。总共210行代码,收获了1500+个star,神作啊。

  1. iOS Lazy Binding,使用fishhook拦截NSSetUncaughtExceptionHandler函数解决NSUncaughtExceptionHandler被修改的问题。
  2. 聊聊苹果的Bug - iOS 10 nano_free Crash,通过fishhook替换malloc相关的函数尝试解决crash。

OC的runtime非常强大,可以玩很多黑魔法。而操作系统也会提供一些基础设施,比如Solaris/Mac DTrace和Linux systemtap,通过编写脚本分析各种系统调用的情况。相比之下,C语言没有runtime,也玩不出什么花来。不过fishhook提供了一种很好的方式,让我们得以做一些有意义的工作。花了点时间研究一下fishhook的源代码,也能增加一下对Mach-O执行格式的理解。

_dyld_register_func_for_add_image

fishhook首次替换利用了_dyld_register_func_for_add_image在动态库加载完成之后,做一次符号地址的替换,简化了替换逻辑。当然fishhook也支持在App启动之后遍历所有的动态库做符号替换。

/*
 * The following functions allow you to install callbacks which will be called   
 * by dyld whenever an image is loaded or unloaded.  During a call to _dyld_register_func_for_add_image()
 * the callback func is called for every existing image.  Later, it is called as each new image
 * is loaded and bound (but initializers not yet run).  The callback registered with
 * _dyld_register_func_for_remove_image() is called after any terminators in an image are run
 * and before the image is un-memory-mapped.
 */
extern void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide))    __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);

我们App目前在用_dyld_register_func_for_add_image将所有要加载的动态库都在控制台打印出来了。

Added: 0x101afe000 (0x8000) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/libBacktraceRecording.dylib <0B27D656-0E24-3133-BAA2-3B3EC2409C04> time: 1488353483592

Added: 0x101b0c000 (0x8000) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator10.2.sdk/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib <31AD1037-C99E-3141-AAD1-CF345BA42E72> time: 1488353483592

Mach-O

Mach-O的格式如下所示。每个segment里面包含了很多section。__DATA segment的section尤其多,__la_symbol_ptr是其中一个。

screenshot.png

fishhook的原理是遍历__DATA segment里面__nl_symbol_ptr__la_symbol_ptr两个section里面的符号,通过Indirect Symbol Table、Symbol Table、String Table的配合,找到自己要替换的函数,然后做替换。整个过程如下所示。

screenshot.png

MachOView

MachOView(附件里面有)看Mach-O格式比较直观。

screenshot.png

otools && nm

通过otools和nm也可以对可执行程序做很多分析。

$ otool -l /Users/henshao/Library/Developer/Xcode/DerivedData/FishHookTest-elsztizpkmqzuedzxtjbslvzndzj/Build/Products/Debug-iphonesimulator/FishHookTest.app/FishHookTest  
/Users/henshao/Library/Developer/Xcode/DerivedData/FishHookTest-elsztizpkmqzuedzxtjbslvzndzj/Build/Products/Debug-iphonesimulator/FishHookTest.app/FishHookTest:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x00           2    20       2760 0x00200085
Load command 0
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __PAGEZERO
   vmaddr 0x0000000000000000
   vmsize 0x0000000100000000
  fileoff 0
 filesize 0
  maxprot 0x00000000
 initprot 0x00000000
   nsects 0
    flags 0x0
Load command 1
      cmd LC_SEGMENT_64
  cmdsize 792
  segname __TEXT
   vmaddr 0x0000000100000000
   vmsize 0x0000000000004000
  fileoff 0
 filesize 16384
  maxprot 0x00000007
 initprot 0x00000005
   nsects 9
    flags 0x0
Section
  sectname __text
   segname __TEXT
      addr 0x00000001000018c0
      size 0x0000000000000f7a
    offset 6336
     align 2^4 (16)
    reloff 0
    nreloc 0
     flags 0x80000400
 reserved1 0
 reserved2 0
Section
  sectname __stubs
   segname __TEXT
      addr 0x000000010000283a
      size 0x0000000000000096
    offset 10298
     align 2^1 (2)
    reloff 0
    nreloc 0
     flags 0x80000408
 reserved1 0 (index into indirect symbol table)
 reserved2 6 (size of stubs)
Section
  sectname __stub_helper
   segname __TEXT
      addr 0x00000001000028d0
      size 0x000000000000010a
    offset 10448
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x80000400
 reserved1 0
 reserved2 0
Section
  sectname __objc_methname
   segname __TEXT
      addr 0x00000001000029da
      size 0x0000000000000a13
    offset 10714
     align 2^0 (1)
    reloff 0
    nreloc 0
     flags 0x00000002
 reserved1 0
 reserved2 0
Section
  sectname __objc_classname
   segname __TEXT
      addr 0x00000001000033ed
      size 0x000000000000003c
    offset 13293
     align 2^0 (1)
    reloff 0
    nreloc 0
     flags 0x00000002
 reserved1 0
 reserved2 0
Section
  sectname __objc_methtype
   segname __TEXT
      addr 0x0000000100003429
      size 0x000000000000082a
    offset 13353
     align 2^0 (1)
    reloff 0
    nreloc 0
     flags 0x00000002
 reserved1 0
 reserved2 0
Section
  sectname __cstring
   segname __TEXT
      addr 0x0000000100003c53
      size 0x00000000000001cb
    offset 15443
     align 2^0 (1)
    reloff 0
    nreloc 0
     flags 0x00000002
 reserved1 0
 reserved2 0
Section
  sectname __entitlements
   segname __TEXT
      addr 0x0000000100003e1e
      size 0x0000000000000190
    offset 15902
     align 2^0 (1)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __unwind_info
   segname __TEXT
      addr 0x0000000100003fb0
      size 0x0000000000000050
    offset 16304
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Load command 2
      cmd LC_SEGMENT_64
  cmdsize 1192
  segname __DATA
   vmaddr 0x0000000100004000
   vmsize 0x0000000000001000
  fileoff 16384
 filesize 4096
  maxprot 0x00000007
 initprot 0x00000003
   nsects 14
    flags 0x0
Section
  sectname __nl_symbol_ptr
   segname __DATA
      addr 0x0000000100004000
      size 0x0000000000000010
    offset 16384
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000006
 reserved1 25 (index into indirect symbol table)
 reserved2 0
Section
  sectname __got
   segname __DATA
      addr 0x0000000100004010
      size 0x0000000000000008
    offset 16400
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000006
 reserved1 27 (index into indirect symbol table)
 reserved2 0
Section
  sectname __la_symbol_ptr
   segname __DATA
      addr 0x0000000100004018
      size 0x00000000000000c8 //0xc8 = 200,200/8=25。
    offset 16408
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000007
 reserved1 28 (index into indirect symbol table)
 reserved2 0
Section
  sectname __objc_classlist
   segname __DATA
      addr 0x00000001000040e0
      size 0x0000000000000010
    offset 16608
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x10000000
 reserved1 0
 reserved2 0
Section
  sectname __objc_protolist
   segname __DATA
      addr 0x00000001000040f0
      size 0x0000000000000010
    offset 16624
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __objc_imageinfo
   segname __DATA
      addr 0x0000000100004100
      size 0x0000000000000008
    offset 16640
     align 2^2 (4)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __objc_const
   segname __DATA
      addr 0x0000000100004108
      size 0x0000000000000be0
    offset 16648
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __objc_selrefs
   segname __DATA
      addr 0x0000000100004ce8
      size 0x0000000000000018
    offset 19688
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x10000005
 reserved1 0
 reserved2 0
Section
  sectname __objc_classrefs
   segname __DATA
      addr 0x0000000100004d00
      size 0x0000000000000008
    offset 19712
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x10000000
 reserved1 0
 reserved2 0
Section
  sectname __objc_superrefs
   segname __DATA
      addr 0x0000000100004d08
      size 0x0000000000000008
    offset 19720
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x10000000
 reserved1 0
 reserved2 0
Section
  sectname __objc_ivar
   segname __DATA
      addr 0x0000000100004d10
      size 0x0000000000000008
    offset 19728
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __objc_data
   segname __DATA
      addr 0x0000000100004d18
      size 0x00000000000000a0
    offset 19736
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __data
   segname __DATA
      addr 0x0000000100004db8
      size 0x00000000000000c0
    offset 19896
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Section
  sectname __bss
   segname __DATA
      addr 0x0000000100004e78
      size 0x0000000000000018
    offset 0
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000001
 reserved1 0
 reserved2 0
Load command 3
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __LINKEDIT
   vmaddr 0x0000000100005000
   vmsize 0x0000000000005000
  fileoff 20480
 filesize 17040
  maxprot 0x00000007
 initprot 0x00000001
   nsects 0
    flags 0x0
Load command 4
            cmd LC_DYLD_INFO_ONLY
        cmdsize 48
     rebase_off 20480
    rebase_size 200
       bind_off 20680
      bind_size 296
  weak_bind_off 0
 weak_bind_size 0
  lazy_bind_off 20976
 lazy_bind_size 568
     export_off 21544
    export_size 200
Load command 5
     cmd LC_SYMTAB
 cmdsize 24
  symoff 21776
   nsyms 162
  stroff 24580
 strsize 3152
Load command 6
            cmd LC_DYSYMTAB
        cmdsize 80
      ilocalsym 0
      nlocalsym 121
     iextdefsym 121
     nextdefsym 8
      iundefsym 129
      nundefsym 33
         tocoff 0
           ntoc 0
      modtaboff 0
        nmodtab 0
   extrefsymoff 0
    nextrefsyms 0
 indirectsymoff 24368
  nindirectsyms 53
      extreloff 0
        nextrel 0
      locreloff 0
        nlocrel 0
Load command 7
          cmd LC_LOAD_DYLINKER
      cmdsize 32
         name /usr/lib/dyld (offset 12)
Load command 8
     cmd LC_UUID
 cmdsize 24
    uuid 0F497FE2-E79F-3376-BCFE-3E51F2AB6112
Load command 9
      cmd LC_VERSION_MIN_IPHONEOS
  cmdsize 16
  version 10.2
      sdk 10.2
Load command 10
      cmd LC_SOURCE_VERSION
  cmdsize 16
  version 0.0
Load command 11
       cmd LC_MAIN
   cmdsize 24
  entryoff 9920
 stacksize 0
Load command 12
          cmd LC_LOAD_DYLIB
      cmdsize 88
         name /System/Library/Frameworks/Foundation.framework/Foundation (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 1349.13.0
compatibility version 300.0.0
Load command 13
          cmd LC_LOAD_DYLIB
      cmdsize 56
         name /usr/lib/libobjc.A.dylib (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 228.0.0
compatibility version 1.0.0
Load command 14
          cmd LC_LOAD_DYLIB
      cmdsize 56
         name /usr/lib/libSystem.dylib (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 1238.0.0
compatibility version 1.0.0
Load command 15
          cmd LC_LOAD_DYLIB
      cmdsize 80
         name /System/Library/Frameworks/UIKit.framework/UIKit (offset 24)
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 3600.6.21
compatibility version 1.0.0
Load command 16
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/Frameworks (offset 12)
Load command 17
      cmd LC_FUNCTION_STARTS
  cmdsize 16
  dataoff 21744
 datasize 32
Load command 18
      cmd LC_DATA_IN_CODE
  cmdsize 16
  dataoff 21776
 datasize 0
Load command 19
      cmd LC_CODE_SIGNATURE
  cmdsize 16
  dataoff 27744
 datasize 9776

#这里面_OBJC_CLASS_xxx都是外部符号。还有些依赖的函数不在__la_symbol_ptr里面,比如___stack_chk_guard和dyld_stub_binder。
$ nm -um /Users/henshao/Library/Developer/Xcode/DerivedData/FishHookTest-elsztizpkmqzuedzxtjbslvzndzj/Build/Products/Debug-iphonesimulator/FishHookTest.app/FishHookTest 
                 (undefined) external _NSStringFromClass (from Foundation)
                 (undefined) external _OBJC_CLASS_$_UIResponder (from UIKit)
                 (undefined) external _OBJC_CLASS_$_UIViewController (from UIKit)
                 (undefined) external _OBJC_METACLASS_$_NSObject (from libobjc)
                 (undefined) external _OBJC_METACLASS_$_UIResponder (from UIKit)
                 (undefined) external _OBJC_METACLASS_$_UIViewController (from UIKit)
                 (undefined) external _UIApplicationMain (from UIKit)
                 (undefined) external ___memcpy_chk (from libSystem)
                 (undefined) external ___stack_chk_fail (from libSystem)
                 (undefined) external ___stack_chk_guard (from libSystem)
                 (undefined) external __dyld_get_image_header (from libSystem)
                 (undefined) external __dyld_get_image_vmaddr_slide (from libSystem)
                 (undefined) external __dyld_image_count (from libSystem)
                 (undefined) external __dyld_register_func_for_add_image (from libSystem)
                 (undefined) external __objc_empty_cache (from libobjc)
                 (undefined) external _close (from libSystem)
                 (undefined) external _dladdr (from libSystem)
                 (undefined) external _free (from libSystem)
                 (undefined) external _malloc (from libSystem)
                 (undefined) external _memset (from libSystem)
                 (undefined) external _objc_autoreleasePoolPop (from libobjc)
                 (undefined) external _objc_autoreleasePoolPush (from libobjc)
                 (undefined) external _objc_msgSend (from libobjc)
                 (undefined) external _objc_msgSendSuper2 (from libobjc)
                 (undefined) external _objc_release (from libobjc)
                 (undefined) external _objc_retainAutoreleasedReturnValue (from libobjc)
                 (undefined) external _objc_storeStrong (from libobjc)
                 (undefined) external _open (from libSystem)
                 (undefined) external _printf (from libSystem)
                 (undefined) external _read (from libSystem)
                 (undefined) external _strcmp (from libSystem)
                 (undefined) external _strnlen (from libSystem)
                 (undefined) external dyld_stub_binder (from libSystem)

一点点注释

#import "fishhook.h"
#import <stdio.h>

#import <dlfcn.h>
#import <stdlib.h>
#import <string.h>
#import <sys/types.h>
#import <mach-o/dyld.h>
#import <mach-o/loader.h>
#import <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

struct rebindings_entry {
    struct rebinding *rebindings;
    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 = malloc(sizeof(struct rebindings_entry));
    if (!new_entry) {
        return -1;
    }
    new_entry->rebindings = 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;
}

static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
                                           section_t *section,
                                           intptr_t slide,
                                           nlist_t *symtab,
                                           char *strtab,
                                           uint32_t *indirect_symtab) {
    printf("size: %llu\n", section->size);

    uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;

    //slide+section->addr 就是符号地址的数组
    void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);

    //遍历section里面的每一个符号
    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的名字。
        uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
        char *symbol_name = strtab + strtab_offset;
        if (strnlen(symbol_name, 2) < 2) {
            continue;
        }

        printf("symbol_name: %s\n", symbol_name);

        struct rebindings_entry *cur = rebindings;
        while (cur) {
            for (uint j = 0; j < cur->rebindings_nel; j++) {
                if (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;
    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;

    //遍历Mach-O的segment非常有意思,要通过Load Command来遍历。

    //从下面的代码来看,SEG_LINKEDIT这个segment非常重要,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;
        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) {
            symtab_cmd = (struct symtab_command*)cur_seg_cmd;
        } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {
            dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;
        }
    }

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

    // Find base symbol/string table addresses
    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);

    // Get indirect symbol table (array of uint32_t indices into symbol table)
    uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);

    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;
        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;
            }

            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) {
                    perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
                }
                if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
                    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) {

    Dl_info image_info;
    int result = dladdr(header, &image_info);
    if (result == 0) {
        printf("Could not print info for mach_header: %p\n\n", header);
        return;
    }
    printf("added dylib: %s\n", image_info.dli_fname);

    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, header, slide);
    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) {

        //启动的时候做函数替换
        _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;
}

通过日志可以看出,主程序和所有动态库的open和close都被替换掉了。

dylib: /Users/henshao/Library/Developer/CoreSimulator/Devices/BFC573F8-E181-4C9F-92CC-87F5E06F5B3C/data/Containers/Bundle/Application/C1226A56-043D-4029-B034-2B8D85C1D3CE/FishHookTest.app/FishHookTest

sectname: __nl_symbol_ptr, segname: __DATA
size: 16
symbol_name: dyld_stub_binder

sectname: __got, segname: __DATA
size: 8
symbol_name: ___stack_chk_guard

sectname: __la_symbol_ptr, segname: __DATA
size: 200
symbol_name: _NSStringFromClass
symbol_name: _objc_autoreleasePoolPop
symbol_name: _objc_autoreleasePoolPush
symbol_name: _objc_msgSend
symbol_name: _objc_msgSendSuper2
symbol_name: _objc_release
symbol_name: _objc_retainAutoreleasedReturnValue
symbol_name: _objc_storeStrong
symbol_name: ___memcpy_chk
symbol_name: ___stack_chk_fail
symbol_name: __dyld_get_image_header
symbol_name: __dyld_get_image_vmaddr_slide
symbol_name: __dyld_image_count
symbol_name: __dyld_register_func_for_add_image
symbol_name: _close
symbol_name: _dladdr
symbol_name: _free
symbol_name: _malloc
symbol_name: _memset
symbol_name: _open
symbol_name: _printf
symbol_name: _read
symbol_name: _strcmp
symbol_name: _strnlen
symbol_name: _UIApplicationMain

参考文章

  1. fishhook 源码分析
  2. Mach-O的动态链接相关知识
  3. 趣探 Mach-O:文件格式分析
  4. 趣探 Mach-O:加载过程
  5. 通过 GDB 调试理解 GOT/PLT
  6. DTrace
  7. Mac及Linux动态库延迟加载对比

用云栖社区APP,舒服~

【云栖快讯】诚邀你用自己的技术能力来用心回答每一个问题,通过回答传承技术知识、经验、心得,问答专家期待你加入!  详情请点击

网友评论

阿呆少爷
文章9篇 | 关注12
关注
阿里云推出的一款移动App数据统计分析产品,为开发者提供一站式数据化运营服务 查看详情
基于全网公开发布数据、传播路径和受众群体画像,利用语义分析、情感算法和机器学习,分析公众对品... 查看详情
阿里巴巴自主研发的海量数据实时高并发在线分析云计算服务,使得您可以在毫秒级针对千亿级数据进行... 查看详情
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效... 查看详情
阿里云总监课正式启航

阿里云总监课正式启航