fishhook 源码学习
status
category
date
summary
slug
icon
tags
password
在学习fishhook之前,首先要了解Mach-O文件结构
什么是 fishhook
fishhook 是一个由 Facebook 开源的框架,可以动态修改链接
Mach-O
符号表。demo演示
我们用这样一段C代码来演示:
首先我们构造了一个和原函数签名相同的函数指针
*original_strlen
,然后重新实现了新的new_strlen
函数。在main函数中,创建了一个rebinding
结构体,分别传入了需要hook的函数名,新实现的函数,与原函数签名相同的函数指针。最后通过rebind_symbols
进行符号的重新绑定,运行时就会输出666。源码分析
结构体定义
首先来看一看
rebinding
的定义:这里通过函数名和一个函数签名,可以确定需要hook的是哪个函数,然后用新函数代替它,储存这些信息的数据结构就是一个
rebinding
。一个
rebindings_entry
可以理解为一次hook时的信息入口,它存储了一个rebinding
数组、数组元素的数量和下一个节点。所以这里维护的是一个rebindings_entry
链表,有多少次hook,链表就有多少个节点。rebind_symbols
直接调用的函数实现:
rebind_symbols
是使用fishhook的入口函数,它有两个作用,一是调用prepend_rebindings
进行数据结构的初始化,二是注册了_rebind_symbols_for_image
函数并在新映像加载时回调。如果调用_dyld_register_func_for_add_image
时,系统已经加载了某些映像,则会分别调用它们注册的回调函数。也就是说,在加载、卸载映像,以及为映像注册回调函数时,回调函数都会被调用,所以这个函数通常被用来监控映像和统计系统数据。prepend_rebindings 初始化
prepend_rebindings
初始化了一个新的rebindings_entry
节点并插入链表头部。如果在一个程序中多次调用rebind_symbols
来hook函数,就有多个rebinding
数组需要维护,rebindings_entry
维护的是一个反向链表,每个节点都维护一个rebinding
数组,通过链表可以判断是否是第一次hook。
_rebind_symbols_for_image 准备基址
调用
_dyld_register_func_for_add_image
进行注册时,需要满足特定的回调函数签名格式。这里对Load Commands进行了两次遍历,第一次遍历根据
cmd
找到了三个需要的segment:LC_SEGMENT__LINKEDIT
、LC_SYMTAB
、LC_DYSYMTAB
,为第二次遍历做好了地址计算的准备;第二次遍历找到了SECTION_TYPE
为S_LAZY_SYMBOL_POINTERS
和S_NON_LAZY_SYMBOL_POINTERS
的section,并调用perform_rebinding_with_section
对section中的符号进行处理。
perform_rebinding_with_section 重绑定
这个查找匹配的过程描述起来比较绕口,首先通过符号在
__la_symbol_prt
的index,加上Load Command中__la_symbol_prt
的保留信息reserved1
,得到了Indirect Symbols
中位于index + reversed1
的数据index2,然后在Symbol Table
中index2的位置拿到偏移地址offset,最后拿到String Table
中offset处的数据,这个数据就是函数名。如果函数名匹配,则更改__la_symbol_ptr
表中的函数地址,完成hook。
总结
程序启动时,会链接很多动态库,函数的调用就是通过指令跳转到函数对应的内存地址。因为动态库是运行后开始链接,所以程序并不知道函数在哪里,所以这些函数放在
__DATA,__la_symbol_prt
表中。例如我们现在要调用
printf
函数,表中相应内容并不会直接指向printf
,而是指向了dyld_stub_binder
,它的作用就是计算真正的printf
地址,并且将表中的指针指向修改,这样下次就可以直接调用printf
了,这就是懒加载。而fishhook做的工作,就是在dyld绑定了地址之后再次做一个重绑定,200行左右的代码,只有一行是在修改函数指针,最复杂的逻辑主要是在计算地址和匹配字符串。hook的局限性,就是只能修改
__la_symbol_ptr
表中的函数指向,也就是不能hook静态库中的函数和自定义的函数。安全方面,我们可以通过替换函数的地址是否在映像内,来判断是否是恶意程序注入的hook。Loading...