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

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值