1. 源码分析
///
/// @param rebindings 需要重绑定的函数信息
/// @param section __la_symbol_ptr 或 __nl_symbol_ptr section header (Load Commands -> LC_SEGMENT_64(__DATA) -> Section64 Header)
/// @param slide mach-o 的偏移地址
/// @param symtab Symbols (Symbol Table -> Symbols)
/// @param strtab String Table
/// @param indirect_symtab Indirect Symbols (Dynamic Symbol Table -> Indirect Symbols)
static void rcd_perform_rebinding_with_section(struct rcd_rebindings_entry *rebindings,
section_t *section,
intptr_t slide,
nlist_t *symtab,
char *strtab,
uint32_t *indirect_symtab) {
// indirect_symtab + section->reserved1 定位到 Indirect Symbols 中指定 section 段的符号的起始位置
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
// section 在内存中的真实地址
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
// 遍历 section 中的符号
for (uint i = 0; i < section->size / sizeof(void *); i++) {
// 根据偏移量遍历 Indirect Symbols 中section 段的符号,并获取对应value (即符号在Symbols 中的位置的偏移量 )
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;
}
// 根据偏移量在 Symbols 中读取 String Table Index
uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
// 从String Table读取符号对应的名字
char *symbol_name = strtab + strtab_offset;
// 需要重新绑定的符号信息,rebindings_nel 需要重新绑定的个数
struct rcd_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) = indirect_symbol_bindings[I];
}
indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
goto symbol_loop;
}
}
cur = cur->next;
}
symbol_loop:;
}
}
2. 举个例子
分析现有可执行文件,基于文件分析重绑定 NSLog 的流程
1、从 Load Commands 中查找 __la_symbol_ptr 、 __nl_symbol_ptr 段(section->flags 为 6 、7 )
2、首先查找到__nl_symbol_ptr , 获取 section->reserved1 (上图中的Indirect Sym Index), 得知 __nl_symbol_ptr 中的符号在 Indirect Symbols 的起始位置为 0x1f2 (498)
3、找到 Indirect Symbols ,从起始位置 + 0x1f2 * 4 处开始查找
Indirect Symbols 的起始位置为 0x18b8c8
__nl_symbol_ptr 中的符号在 Indirect Symbols 表的起始位置为 0x18b8c8 + 0x1f2 * 4 ,即为 0x18c090
4、查找到第一个符对应的值为 0x40000000,为特殊值(等于 INDIRECT_SYMBOL_ABS),继而遍历下一个得到 symtab_index = 0x5bbb
5、得到 symtab_index 后再前往Symbol Table,符号在Symbol Table 的位置偏移即为symtab_index
symtab_index = 0x5bbb
Symbol Table 的起始位置为 0x12c3c8
则 符号在Symbol Table 的地址为 0x12c3c8 + 0x5bbb *16,计算可得 0x187f78
6、根据计算的地址查找相关信息,可得知符号名称在 String Table 的偏移量为 0x257c4,即可前往String Table 查找符号对应的名称
String Table 起始位置为 0x18ce34
则符号名称的位置为 0x18ce34 + 0x257c4 == 0x 1b25f8
7、可读取到符号名称为FoundationURLRequestVMn,不等于我们要替换的NSLog,则回到第4步继续遍历下一个
8、符号在Indirect Symbols的顺序与在其在它所属段的顺序是一致的
因此找到名称相同的符号时可根据偏移量直接到所属section中修改符号最终绑定的位置
学习资料
iOS逆向工程 - fishhook原理