fishhook------源碼解讀

 

在  fishhook------原理分析 中,驗證了fishhook符號重綁定的實現原理,這裏主要分析一下fishhook的代碼實現.

準備

理論知識

Mach-O的文件佈局

Mach-O文件一共有三個部分:

  • Header:header包含了文件的基本信息,實際上不會映射到虛擬內存空間上去;
  • Load Commands:這部分確定了文件數據在虛擬內存上的佈局,包含各個部分的映射到虛擬空間上的虛擬地址,虛擬空間大小,實際文件在基地址上的偏移量,實際文件大小,包含section的數量等.
  • Data: 這個段是真實的數據段,按照在Load Commands的定義被加載到虛擬內存中去.

符號表的查找

Mach-O的符號有兩部分,一部分是非懶加載符號表(__ln_symbol_ptr),指向非延時綁定的指針數組,這部分符號在動態庫加載時已經完成綁定,是非延時的;另一部分是懶加載符號表(__la_symbol_ptr),指向延時綁定的指針數組,這部分符號一般是在第一次調用時有_dyld_stub_binder去查找並填充(也有可能是啓動時進行綁定).

無論是那種方式符號表,都由統一的規則去尋找對應的符號:

  • 在Load Commands段中,__DATA,__la_symbol_ptr(或者__ln_symbol_ptr)中存在存在一個偏移值reserved1,這個偏移值確定了對應的符號表(__ln_symbol_ptr或者__la_symbol_ptr)中的元素在重定向符號表(Indirect Symbols)中的起始索引位置startIndex;
  • 在DATA段中,__la_symbol_ptr(或者__ln_symbol_ptr)指針數組中存儲了各個不同的符號信息.對於每一個符號信息在該數組中的索引relativeIndex,就是在重定向符號表中以startIndex爲起始位置,relativeIndex處的元素.即利用index=startIndex+relativeIndex,在重定向符號表中,獲取索引爲index位置對應的偏移值offsetInSymbols;
  • 在符號表(Symbols)中,獲取到索引offsetInSymbols處的值,即爲該符號在符號在字符串表(String Table)中的位置偏移量offset;
  • 根據字符串表的起始位置,加上偏移量offset即可得到該符號的值.

具體的查找過程在 fishhook------原理分析 中有詳細的描述.

源碼中的實現拆分

區分不同指令集對應架構對應的數據結構

Mach-O中可以包含多種架構,但是不同的架構在數據結構的定義上會有不同,所以爲了兼容不同的指令集架構,需要通過紅定義來區分不同架構下使用的數據結構,方便統一做處理:

#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

Load Command中數據包含的數據結構

在Load Commands中,

  • 最外層的數據主要使用數據結構
struct load_command {
	uint32_t cmd;		/* type of load command */
	uint32_t cmdsize;	/* total size of command in bytes */
};

來表示,其中cmd是一個特殊的整型類型,主要有以下類型:

/* Constants for the cmd field of all load commands, the type */
#define	LC_SEGMENT	0x1	/* segment of this file to be mapped */
#define	LC_SYMTAB	0x2	/* link-edit stab symbol table info */
#define	LC_SYMSEG	0x3	/* link-edit gdb symbol table info (obsolete) */
#define	LC_THREAD	0x4	/* thread */
#define	LC_UNIXTHREAD	0x5	/* unix thread (includes a stack) */
#define	LC_LOADFVMLIB	0x6	/* load a specified fixed VM shared library */
#define	LC_IDFVMLIB	0x7	/* fixed VM shared library identification */
#define	LC_IDENT	0x8	/* object identification info (obsolete) */
#define LC_FVMFILE	0x9	/* fixed VM file inclusion (internal use) */
#define LC_PREPAGE      0xa     /* prepage command (internal use) */
#define	LC_DYSYMTAB	0xb	/* dynamic link-edit symbol table info */
#define	LC_LOAD_DYLIB	0xc	/* load a dynamically linked shared library */
#define	LC_ID_DYLIB	0xd	/* dynamically linked shared lib ident */
#define LC_LOAD_DYLINKER 0xe	/* load a dynamic linker */
#define LC_ID_DYLINKER	0xf	/* dynamic linker identification */
#define	LC_PREBOUND_DYLIB 0x10	/* modules prebound for a dynamically */
				/*  linked shared library */
#define	LC_ROUTINES	0x11	/* image routines */
#define	LC_SUB_FRAMEWORK 0x12	/* sub framework */
#define	LC_SUB_UMBRELLA 0x13	/* sub umbrella */
#define	LC_SUB_CLIENT	0x14	/* sub client */
#define	LC_SUB_LIBRARY  0x15	/* sub library */
#define	LC_TWOLEVEL_HINTS 0x16	/* two-level namespace lookup hints */
#define	LC_PREBIND_CKSUM  0x17	/* prebind checksum */

/*
 * load a dynamically linked shared library that is allowed to be missing
 * (all symbols are weak imported).
 */
#define	LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)

#define	LC_SEGMENT_64	0x19	/* 64-bit segment of this file to be
				   mapped */
#define	LC_ROUTINES_64	0x1a	/* 64-bit image routines */
#define LC_UUID		0x1b	/* the uuid */
#define LC_RPATH       (0x1c | LC_REQ_DYLD)    /* runpath additions */
#define LC_CODE_SIGNATURE 0x1d	/* local of code signature */
#define LC_SEGMENT_SPLIT_INFO 0x1e /* local of info to split segments */
#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */
#define	LC_LAZY_LOAD_DYLIB 0x20	/* delay load of dylib until first use */
#define	LC_ENCRYPTION_INFO 0x21	/* encrypted segment information */
#define	LC_DYLD_INFO 	0x22	/* compressed dyld information */
#define	LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD)	/* compressed dyld information only */
#define	LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */
#define LC_VERSION_MIN_MACOSX 0x24   /* build for MacOSX min OS version */
#define LC_VERSION_MIN_IPHONEOS 0x25 /* build for iPhoneOS min OS version */
#define LC_FUNCTION_STARTS 0x26 /* compressed table of function start addresses */
#define LC_DYLD_ENVIRONMENT 0x27 /* string for dyld to treat
				    like environment variable */
#define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
#define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */
#define LC_SOURCE_VERSION 0x2A /* source version used to build binary */
#define LC_DYLIB_CODE_SIGN_DRS 0x2B /* Code signing DRs copied from linked dylibs */
#define	LC_ENCRYPTION_INFO_64 0x2C /* 64-bit encrypted segment information */
#define LC_LINKER_OPTION 0x2D /* linker options in MH_OBJECT files */
#define LC_LINKER_OPTIMIZATION_HINT 0x2E /* optimization hints in MH_OBJECT files */
#define LC_VERSION_MIN_TVOS 0x2F /* build for AppleTV min OS version */
#define LC_VERSION_MIN_WATCHOS 0x30 /* build for Watch min OS version */
#define LC_NOTE 0x31 /* arbitrary data included within a Mach-O file */
#define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) /* used with linkedit_data_command, payload is trie */
#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) /* used with linkedit_data_command */

所以可以依據struct load_command中的cmd變量來確定對一個的Load Command命令屬於那種類型,然後再使用不同的類型來進行轉化.例如如果是LC_SEGEMENT或者LC_SEGMENT_64類型就可以使用數據結構

# 64位架構下
struct segment_command_64 { /* for 64-bit architectures */
	uint32_t	cmd;		/* LC_SEGMENT_64 */
	uint32_t	cmdsize;	/* includes sizeof section_64 structs */
	char		segname[16];	/* segment name */
	uint64_t	vmaddr;		/* memory address of this segment */
	uint64_t	vmsize;		/* memory size of this segment */
	uint64_t	fileoff;	/* file offset of this segment */
	uint64_t	filesize;	/* amount to map from the file */
	vm_prot_t	maxprot;	/* maximum VM protection */
	vm_prot_t	initprot;	/* initial VM protection */
	uint32_t	nsects;		/* number of sections in segment */
	uint32_t	flags;		/* flags */
};
# 32位架構下
struct segment_command { /* for 32-bit architectures */
	uint32_t	cmd;		/* LC_SEGMENT */
	uint32_t	cmdsize;	/* includes sizeof section structs */
	char		segname[16];	/* segment name */
	uint32_t	vmaddr;		/* memory address of this segment */
	uint32_t	vmsize;		/* memory size of this segment */
	uint32_t	fileoff;	/* file offset of this segment */
	uint32_t	filesize;	/* amount to map from the file */
	vm_prot_t	maxprot;	/* maximum VM protection */
	vm_prot_t	initprot;	/* initial VM protection */
	uint32_t	nsects;		/* number of sections in segment */
	uint32_t	flags;		/* flags */
};

進行類型轉化;如果是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 */
};

進行轉化;如果LC_DYSYMTAB,就可使用數據結構

struct dysymtab_command {
    uint32_t cmd;	/* LC_DYSYMTAB */
    uint32_t cmdsize;	/* sizeof(struct dysymtab_command) */
    uint32_t ilocalsym;	/* index to local symbols */
    uint32_t nlocalsym;	/* number of local symbols */
    uint32_t iextdefsym;/* index to externally defined symbols */
    uint32_t nextdefsym;/* number of externally defined symbols */
    uint32_t iundefsym;	/* index to undefined symbols */
    uint32_t nundefsym;	/* number of undefined symbols */
    uint32_t tocoff;	/* file offset to table of contents */
    uint32_t ntoc;	/* number of entries in table of contents */
    uint32_t modtaboff;	/* file offset to module table */
    uint32_t nmodtab;	/* number of module table entries */
    uint32_t extrefsymoff;	/* offset to referenced symbol table */
    uint32_t nextrefsyms;	/* number of referenced symbol table entries */
    uint32_t indirectsymoff; /* file offset to the indirect symbol table */
    uint32_t nindirectsyms;  /* number of indirect symbol table entries */
    uint32_t extreloff;	/* offset to external relocation entries */
    uint32_t nextrel;	/* number of external relocation entries */
    uint32_t locreloff;	/* offset to local relocation entries */
    uint32_t nlocrel;	/* number of local relocation entries */

};	

進行轉化等等.

  • 一個記載指令(load_command)可以包含多個Section,所以第二層的數據主要可以使用是數據結構segment_command_t來表示:
# 32位架構下
struct segment_command { /* for 32-bit architectures */
	uint32_t	cmd;		/* LC_SEGMENT */
	uint32_t	cmdsize;	/* includes sizeof section structs */
	char		segname[16];	/* segment name */
	uint32_t	vmaddr;		/* memory address of this segment */
	uint32_t	vmsize;		/* memory size of this segment */
	uint32_t	fileoff;	/* file offset of this segment */
	uint32_t	filesize;	/* amount to map from the file */
	vm_prot_t	maxprot;	/* maximum VM protection */
	vm_prot_t	initprot;	/* initial VM protection */
	uint32_t	nsects;		/* number of sections in segment */
	uint32_t	flags;		/* flags */
};
# 64位架構下
struct segment_command_64 { /* for 64-bit architectures */
	uint32_t	cmd;		/* LC_SEGMENT_64 */
	uint32_t	cmdsize;	/* includes sizeof section_64 structs */
	char		segname[16];	/* segment name */
	uint64_t	vmaddr;		/* memory address of this segment */
	uint64_t	vmsize;		/* memory size of this segment */
	uint64_t	fileoff;	/* file offset of this segment */
	uint64_t	filesize;	/* amount to map from the file */
	vm_prot_t	maxprot;	/* maximum VM protection */
	vm_prot_t	initprot;	/* initial VM protection */
	uint32_t	nsects;		/* number of sections in segment */
	uint32_t	flags;		/* flags */
};
  • Symbols 中的元素對應了一個新的結構體:
struct nlist {
	union {
#ifndef __LP64__
		char *n_name;	/* for use when in-core */
#endif
		uint32_t n_strx;	/* index into the string table */
	} n_un;
	uint8_t n_type;		/* type flag, see below */
	uint8_t n_sect;		/* section number or NO_SECT */
	int16_t n_desc;		/* see <mach-o/stab.h> */
	uint32_t n_value;	/* value of this symbol (or stab offset) */
};

/*
 * This is the symbol table entry structure for 64-bit architectures.
 */
struct nlist_64 {
    union {
        uint32_t  n_strx; /* index into the string table */
    } n_un;
    uint8_t n_type;        /* type flag, see below */
    uint8_t n_sect;        /* section number or NO_SECT */
    uint16_t n_desc;       /* see <mach-o/stab.h> */
    uint64_t n_value;      /* value of this symbol (or stab offset) */
};

利用這個結構體可以獲取到對應索引處的偏移地址strtab_offset,假設string table的起始地址strtab,就可獲取到對應的符號.

開始擼代碼

  • 獲取所有的image(target module)

在一個應用中會有多個image文件,也就是Mach-O文件.如果獲取應用中所有的target module文件呢?

    // 包含在<mach-o/dyld.h>中
    uint32_t countOfAllTargetModules = _dyld_image_count();
    for(uint32_t i = 0; i < countOfAllTargetModules; i++) {
        //獲取name
        const char *imageName = _dyld_get_image_name(i);
        //獲取偏移量(ASLR導致)
        intptr_t silde = _dyld_get_image_vmaddr_slide(i);
        //獲取mach_header
        mach_header_t *header;
        header =  (mach_header_t *)_dyld_get_image_header(i);
        Dl_info info;
        dladdr(header, &info);
        
        NSLog(@"dli_fname == %s", info.dli_fname);
        NSLog(@"dli_sname == 0x%016lx", (uintptr_t)info.dli_fbase);
        
    }

當然還有另一箇中方法.系統提供了一個加載image的回調,可以自定義回調處理函數來接收系統記載的image.

static void _rebind_symbols_for_image(const struct mach_header *header,
                                      intptr_t slide) {
    mach_header_t *mach_header = (mach_header_t *)header;
    Dl_info info;
    dladdr(mach_header, &info);
    NSLog(@"path:%s, base address=0x%06lx", info.dli_fname, (uintptr_t)info.dli_fbase);
}

# 然後在需要的地方註冊回調函數.這個方法無論你在哪裏註冊都會回調,而不一定是要在image加載之前註冊
_dyld_register_func_for_add_image(&_rebind_symbols_for_image);
  • 獲取Mach-O文件中的所有Load Command,並找出LC_SEGMENT(__LINKEDIT),LC_SYMTAB,LC_DYSYMTAB以及LC_UUID,並輸出UUID的值.
static void _rebind_symbols_for_image(const struct mach_header *header,
                                      intptr_t slide) {
    uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
    /* 或者使用
     uintptr_t cur = (uintptr_t)((mach_header_t *)header + 1);
     */
    struct load_command *cur_cmd;
    segment_command_t *linkedit_segment = NULL;
    struct symtab_command* symtab_cmd = NULL;
    struct dysymtab_command* dysymtab_cmd = NULL;
    struct uuid_command *uuid_command = NULL;
    for(int i = 0; i < header->ncmds; i++, cur += cur_cmd->cmdsize) {
        cur_cmd = (struct load_command *)cur;
        if (cur_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
            //當前load_command是一個Segment
            segment_command_t *seg = (segment_command_t *)cur_cmd;
            if (strcmp(seg->segname, SEG_LINKEDIT) == 0) {
                //找到LC_SEGMENT(__LINKEDIT)
                linkedit_segment = seg;
            }
        } else if (cur_cmd->cmd == LC_SYMTAB) {
            symtab_cmd = (struct symtab_command*)cur_cmd;
        } else if (cur_cmd->cmd == LC_DYSYMTAB) {
            dysymtab_cmd = (struct dysymtab_command*)cur_cmd;
        } else if (cur_cmd->cmd == LC_UUID) {
            uuid_command = (struct uuid_command *)cur_cmd;
        }
    }
    if (linkedit_segment != NULL) {
        NSLog(@"find linkedit_segment");
    }
    if (symtab_cmd != NULL) {
        NSLog(@"find symtab_cmd");
    }
    if (dysymtab_cmd != NULL) {
        NSLog(@"find dysymtab_cmd");
    }
    if (uuid_command != NULL) {
        uint8_t *command = uuid_command->uuid;
        NSString *uuid = [NSString stringWithFormat:@"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", command[0], command[1], command[2], command[3], command[4], command[5], command[6], command[7], command[8], command[9], command[10], command[11], command[12], command[13], command[14], command[15]];
        NSLog(@"uuid == %@", uuid);
        
    }
}
  • 如何獲取在指定表內存中的地址的地址

在結構體struct segment_command(struct segment_command_64)中,有一個vmsize參數,這個參數就是在沒有ASLR技術的情況下對應結構被記載到內存中的地址.所以在有ASLR技術時,只需要加上對應target module的偏移量就slide就是對應的應用內存地址,只需要再減去文件的fileoff就是加載文件的起始地址.其他文件都是在這個地址基礎上加上fileoff就可以獲取到.

    例如選擇__DATA,LC_SEGMENT(__LINKEDIT) 計算起始地址 

    //獲取起始地址
    uintptr_t baseptr = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
    //獲取符號表的地址
    nlist_t *sym_tab = (nlist_t *)(baseptr + symbol_segment->symoff);
    //獲取字符表地址
    char *str_tab = (char *)(baseptr + symbol_segment->stroff);
    //重定向表的地址
    uint32_t *indirect_table = (uint32_t *)(baseptr + dysymbol_segement->indirectsymoff);

 

源碼解析

  • 註冊交換符號實現的方法
//註冊重新綁定符號的回調時機
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
    //將需要hook的結構體鏈接成鏈表結構(之所以使用鏈表是因爲在C語言中,處理這種預先不知道可能有多少個的集合來講,鏈表是比較好的選擇)
  int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);
    //此處的異常主要用來處理malloc開闢空間失敗
  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
    /*
     如果是第一次調用重新綁定符號的方法,就通過註冊添加image(target module)的方法回調來重新綁定符號(這個方法在任何地方註冊都會執行加載image的回調);
     如果不是第一次調用重新綁定符號的方法,就通過遍歷當前已經加載的image來循環綁定符號(如果在此操作執行之後,又重新加載了image,可以通過_dyld_register_func_for_add_image來監聽到);
     
     */
  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;
}
//在方法內部調用rebind_symbols_for_image實現
static void _rebind_symbols_for_image(const struct mach_header *header,
                                      intptr_t slide) {
    rebind_symbols_for_image(_rebindings_head, header, slide);
}
  • 獲取鏈接表,符號表,動態符號表,重定向表在內存中的地址,獲取__la_symbol_ptr以及__ln_symbol_ptr對應的section;
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;
    
    
    //第一次遍歷load Commands主要是爲了找到鏈接表linkedit_segment,符號表(symtab_cmd),動態符號表(dysymtab_cmd)對應的load command
  uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);//使用uintptr_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;
  }

  // 通過使用__LINKEDIT的起始地址找到symbol table/string table在內存中的地址
    
    
  uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
  nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
  char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); //使用char *類型是因爲字符在內存中按照char爲單位進行存儲

  // 獲取重定向表在內存中的位置
  uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); //使用uint32_t *是因爲重定向符號表中數據按照4字節爲單位進行存儲

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

        ////找到__nl_symbol_ptr和__la_symbol_ptr這兩個section
      for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
        section_t *sect =
          (section_t *)(cur + sizeof(segment_command_t)) + j;
        if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
            //遍歷懶加載符號表中的符號,與_rebindings_head進行比較,對匹配的符號進行重新綁定
          perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
        }
        if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
            //遍歷非懶加載符號表中的符號,與_rebindings_head進行比較,對匹配的符號進行重新綁定
          perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
        }
      }
    }
  }
}
  • 查找並替換_rebindings_head中需要替換的符號
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) {
    //section在indirect table中的索引從reserved1處開始
  uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
    //強制類型轉化z,主要是說明給地址處存儲的是還是地址(即指針處內存存儲的值依然是指針)
  void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
  for (uint i = 0; i < section->size / sizeof(void *); i++) {
      //獲取indirext table中對應索引處的值,該值表示在symbole 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;
    }
      //獲取在string table中偏移
    uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
      //獲取在string table中的地址
    char *symbol_name = strtab + strtab_offset;
    struct rebindings_entry *cur = rebindings;
      //遍歷找到需要重新綁定的符號,進行重新綁定
    while (cur) {
      for (uint j = 0; j < cur->rebindings_nel; j++) {
        if (strlen(symbol_name) > 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對應位置
            *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];
          }
            //將自定義實現,存入符號對應的位置
          indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
            //非常古老的C語言跳轉指令,現在很少用
          goto symbol_loop;
        }
      }
      cur = cur->next;
    }
  symbol_loop:;
  }
}

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章