看雪新人第一次发帖,大家多多指教。 之前只知道Fishhook是Facebook家的通过修改懒加载表(GOT表)的函数的指向达到hook的框架,但是没有真正的探究过代码。动态调试的一下Fishhook探究一下原理。
修改外部引用的函数在(懒)加载表中指向的目标函数的地址,修改为自定义代码的地址 实现上,主要分两步走: 1.通过MachO文件的Load Commands段中定义的各个段中的偏移计算出,Dynamic Symbol Table(Indirect Symbol),Symbol Table (Symbols)和String Table这三个段的起始地址。
2.遍历Lazy Symbol pointers中的所有函数定义,从中查找需要hook的函数(通过函数名的字符串比较,这也是为什么需要String Table段,查找字符串的过程见下面的备注1)。如果是需要被修改的函数,那么修改此函数在(懒)加载表(GOT表)中指向的地址为自己代码实现的地址以达到Hook。
计算3个段的起始地址: Fishhook计算3个段的起始地址。 这里先贴上MachO文件的Load Commands段示意图: 从上向下分析fish的逻辑:
首先计算了_LinkEdit的起始地址,因为LC_SYMTAB和LC_DYSYMTAB的Offset都是依据_LinkEdit的起始地址的。 然后根据_linkedit的起始地址,计算这3个段的起始地址。
首先,Load Command中的显示: 1.linkedit_segment->vmaddr = 0x100010000 2.linkedit_segment->fileoff = 0x10000
调试时,lldb中执行image list -o -f查看内存当中的基地址: slide = 0x2cb0000
接下面是动态调试的计算值: 2.1> 根据代码计算:linkedit_base = (0x4510000 + 0x100010000 - 0x10000) = 4367384576 动态调试时,内存中的值也为 4367384576:
2.2> symtab_cmd->symoff = 67160 根据代码计算:symtab = (4367384576+67160).toString(16) = 0x104520658 动态调试,Xcode动态调试如上图,显示的symtab也为:0x104520658
2.3> symtab_cmd->stroff = 70632 根据代码计算:strtab =(4367384576 + 70632).toString(16) = 0x1045213e8 动态调试,Xcode动态调试如上图,显示的strtab = "";
2.4> dysymtab_cmd->indirectsymoff = 70424 根据代码计算:indirectsymoff = (4367384576 + 70424) = 0x104521318
这样,就计算到了Symbols Table,Indirect Symbols,Strings三个表的起始地址。这样准备工作就做好了,下面开始进入真正的got表hook。
根据上图贴的代码,计算3个值后,开始遍历Header,从Load Command中寻找_DATA下的S_LAZY_SYMBOL_POINTERS和S_NON_LAZY_SYMBOL_POINTERS,即懒加载表和非懒加载段,这两个段中保存这函数调用时的真正指向,最终的目的也是修改这两个段中想要hook的函数的指向。
找到后,根据代码,进入关键的perform_rebinding_with_section函数。将计算的3个函数的段起始地址,ALSR地址,(懒)加载表的地址,传入的想要hook的函数rebingings链表传给这个函数,
开始从Indirect Symbols中查找想要hook的函数(通过函数名比较),如果找到了,那么就修改对应的在(懒)加载表中符号的指向。
这是perform_rebinding_with_section函数的实现:
首先计算了indirect_symbol_bindings的地址,这是(懒)符号表在内存中的起始地址,计算方法是偏移量+(懒)加载表中的addr,这里动态调试查看: indirect_symbol_bindings =(0x4510000 + 4295000064)= 0x104518000,这个值就是(懒)加载表中在内存中的基地址。
下面进入循环,循环传入的(懒)加载表,这里也相当于循环Indirect Symbols,因为这两个表中的函数的index是相同的,这里详细见下面备注2。 对于(懒)加载表中的定义的每一个函数,得到这个函数在Indirect Symbols的data值(uint32_t symtab_index = indirect_symbol_indices[i];),然后再通过data找到symbol table中的该函数在字符串表中的偏移量(uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;),最终找到在字符串表中找到该函数的名称(char *symbol_name = strtab + strtab_offset;)。
程序接下来,开始循环想要hook的函数的链表,如果链表中函数名和当前遍历的函数的名称相同(strcmp() == 0),那么就修改此函数在got表中的指向为传入的被修改的函数的地址(indirect_symbol_bindings[i] = cur->rebindings[j].replacement;):
这里拿一个循环进行动态调试观察过程。例如当i = 0时: symtab_index = 181,indirect_symbol_indices[i] =181; 代码中计算:uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; 这里动态调试即为:strtab_offset = symtab[181].n_un.n_strx;(Symbol Table中各项的定义见下面备注3) 根据图中显示,strtab_offset为0x22E,为558。 那么String table的起始地址+558即为这个函数的函数名。
通过前面的分析,知道替换got表即为indirect_symbol_bindings[i] = cur->rebindings[j].replacement;这行代码,那么在此前后断点,就知道got表想要被hook的函数前后究竟有什么变化,这里以hook NSlog举例:
这是我的调用代码:
GOT表中NSlog指向的地址为: ASLR + (懒)加载表里的偏移量,这里为: 0x1003f8000+ 0xc000 = 0x100404000。0x100404000存储着NSlog的函数指针。*(0x100404000)存储着NSlog的代码。
0x100404000的值为: 0x100404000存储着0x01a97ff7f0,寻这个地址的值,发现是NSLog实现。 disassemble --start-address 0x01a97ff7f0
接着执行indirect_symbol_bindings[i] = cur->rebindings[j].replacement;代码,在这之后,got表即被修改了指向,下面验证一下:
执行后,再查看0x100404000存储的值: 0x100404000存储的值为0x01003fd5ec,*(0x01003fd5ec)可以找到改变后的"NSlog"(自定义的myNSlog):
通过遍历(懒)加载表中定义的函数,找到函数的函数名的逻辑如下(上面第三节的最后也动态调试了)。遍历懒加载表中的函数,找到Indirect symbol表中该函数的index,通过该index找到symbol表中该函数的定义,symbol表中定义了函数在字符串表中函数的偏移,通过偏移找到字符串表中该函数的名称。
遍历懒符号表和遍历Inderect表使用了相同的index,因为两个函数的定义的index相同,下面可以验证一下: 下面是Lazy Symbol Pointers的定义: 下面是Indirect Symbols中定义的函数: 可以看到,顺序都为NSlog,NSStringFromClass.....
Symbol table中符号表的定义如下,其中n_strx即为该函数在字符串表中的偏移量。
参考链接: 1.段的地址计算过程 2.字符串寻址过程
/
/
Find base symbol
/
string table addresses
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);
/
/
Get indirect symbol table (array of uint32_t indices into symbol table)
uint32_t
*
indirect_symtab
=
(uint32_t
*
)(linkedit_base
+
dysymtab_cmd
-
>indirectsymoff);
/
/
Find base symbol
/
string table addresses
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);
/
/
Get indirect symbol table (array of uint32_t indices into symbol table)
uint32_t
*
indirect_symtab
=
(uint32_t
*
)(linkedit_base
+
dysymtab_cmd
-
>indirectsymoff);
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2020-9-22 16:32
被LeoW丨编辑
,原因: