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