Fishhook 学习笔记

一、Fishhook 是什么?

简单来说Fishhook就是hook函数的一种工具,当然它hook的原理和我们熟知的Method Swizzle 方式是不一样的,它是Facebook提供的一个动态修改链接mach-O文件的工具。

  • Method Swizzle 是利用OC的Runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的。主要用于OC方法。
  • Fishhook 是利用MachO文件加载原理,通过修改懒加载和非懒加载两个表的指针达到C函数HOOK的目的。

二、Fishhook的简单使用

首先在github上下载fishhook库:


image.png

解压zip包后,将其中的.c和.h文件拖入你想hook的项目当中,我们所用到的就是这两个文件,当然网上有大把的关于fishhook的源码剖析资料,大家可以自行查阅,这里我就直接上代码了,拖入步骤不作详细介绍。

#import "ViewController.h"
#import "fishhook.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
 
    //定义rebinding 结构体
    struct rebinding rebind = {};
    rebind.name = "NSLog";
    rebind.replacement = hookNSLog;
    rebind.replaced = (void *)&nslogMethod;
    
    //将上面的结构体 放入 reb结构体数组中
    struct rebinding red[]  = {rebind};
    
    /*
     * arg1 : 结构体数据组
     * arg2 : 数组的长度
     */
    
    rebind_symbols(red, 1);
    
}

//定义一个函数指针 用于指向原来的NSLog函数
static void (*nslogMethod)(NSString *format, ...);

void hookNSLog(NSString *format, ...){
    
    format = [format stringByAppendingString:@"被勾住了"];
    
    nslogMethod(format);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"原有NSLog函数");
}

将文件导入项目后,在ViewController.m 文件中写入代码如上,代码目的是:将点击屏幕时执行的NSLog函数,替换成执行hookNSLog函数。
点击屏幕时执行结果:


image.png

我们先不管原理是怎样,大家是不是觉得,这执行结果和以上代码干的事,是不是很像runtime的方法交换。
但是从表面上看,runtime和fishhook的区别,大家都知道使用runtime交换的是OC的方法,而fishhook却是交换C函数,这里就会有个疑惑,C语言不是静态语言吗,为什么fishhook可以直接交换函数呢?
ok ! 在疑问之前,我们先将以上代码稍作修改,在看一下结果。

#import "ViewController.h"
#import "fishhook.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
 
    //定义rebinding 结构体
    struct rebinding rebind = {};
    rebind.name = "funcDlog";
    rebind.replacement = hookNSLog;
    rebind.replaced = (void *)&nslogMethod;
    
    //将上面的结构体 放入 reb结构体数组中
    struct rebinding red[]  = {rebind};
    
    /*
     * arg1 : 结构体数据组
     * arg2 : 数组的长度
     */
    
    rebind_symbols(red, 1);
    
}

//定义一个函数指针 用于指向原来的NSLog函数
static void (*nslogMethod)(NSString *format, ...);

void hookNSLog(NSString *format, ...){
    
    format = [format stringByAppendingString:@"被勾住了"];
    
    nslogMethod(format);
}

void funcDlog(NSString *format, ...){
    
    NSLog(@"%@", format);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    funcDlog(@"原有NSLog函数");
}


@end

来我们看下代码的改动:
我们将原来hook的目标转换了,自己定义了一个函数void funcDlog(NSString *format, ...),在函数调用NSLog函数,而我们这次Hook的目标变成了 funcDlog函数,按照之前的运行结果,猜测这次的运行结果应该还是“ 原有NSLog函数被勾住了”,但是实际运行结果却是:

image.png

结果显示我们的代码并没有hook成功,综合以上两次运行结果,我们发现了,fishhook可以实现runtime做不到的将C函数进行交换,但是我们自己定义的C函数却不能交换,只能hook系统的函数,这是为什么呢?我们来看下一个章节。

三、Fishhook的原理探究

首先在探究fishhook的原理之前,我要清楚几个问题:

  • MachO是怎么加载的
    • 首先我们要知道,MachO文件是被dyld加载的,dyld是动态加载,也负责动态库的加载。我们都知道动态库加载时并不在我们自己的MachO文件内部,是存在系统动态缓存区,当我们MachO文件加载过后会动态加载我们所依赖的那些动态库。
  • ASLR技术
    • 我们的MachO文件在内存当中每次的运行地址是不一样的,MachO文件加载的时地址是随机的,这就是ASLR技术。苹果每次加载MachO文件时会随机分配一个地址,来降低缓冲区溢出。

那问题来了,每次MachO文件加载的地址不一样,我们大家都知道,系统的动态库缓存区的地址也是变化的,那每次dyld加载动态库的时候是怎样找到依赖动态库进行加载的呢?

  • PIC 位置代码独立

    • 当MachO内部需要调用系统的库函数时,会现在MachO文件_DATA段中建立一个指针,指向外部函数,dyld就会将指针与函数进行动态绑定,将MachO中的DATA段中的指针指向外部函数,从而实现加载。

我们来看下第一份代码的MachO文件,如图:


image.png

当MachO内部有两个指针用来加载动态库:

  • Non-Lazy Symbol Pointers :不懒加载指针
  • Lazy Symbol Pointers :懒加载指针

这两个指针就是dyld用来在MachO内部调用系统库函数时来指向外部函数进行动态绑定加载。从上图我们可以看出在Lazy Symbol Pointers指针表中NSLog的位置,以及偏移地址是0x8018。

那我们根据这个偏移地址在第一份代码中进行LLDB调试看看结果如何:

  • 首先我们先在 fishhook的代码执行前设置断点:


    image.png
  • 然后我们先拿到MachO文件在内存中的首地址:0x0000000100154000


    image.png
  • 根据偏移地址拿到指针指向的地址值:


    image.png
  • 根据地址值中的地址进行反汇编,我看下结果:


    image.png

反汇编后结果清楚显示地址指向的是Foundation下的 NSLog函数

ok! 这是还没有进行fishhook前的NSLog ,那我们将断点断到fishhook代码执行后在看结果:


image.png

同样的偏移地址,地址值却发生了变化,反汇编后却发现,地址指向的是fishhookDemo1下的hookNSLog函数。

这就是Fishhook的原理:在dyld加载MachO文件所需要的系统函数时,通过改变MachO文件中的DATA段中指向外部函数的指针所指向的地址,来实现动态交换。

这里就可以解释我们在两次运行结果对比留下的两个疑惑:

  • 为什么fishhook可以交换C函数
    • fishhook 是通过改变dyld加载动态库所用的指针指向的函数地址,来实现的函数交换 ,所以C函数通过dyld动态加载动态库也展现出C函数动态的一面。
  • 为什么我们自己写的C函数fishhook交换不了
    • 我们自己写的C函数不在系统动态库缓存区,而是存在我们自己的MachO文件当中,不经过dyld加载,直接编译成汇编语言,在MachO中的TEXT段中。所以fishhook的原理不能交换我们自己写的C函数。

四、通过符号找到字符串

在上面我们知道了系统的动态库与我们的MachO文件是通过dyld在加载时在MachO文件中的一个指针指向了外部函数来进行加载,但是这只是个地址,我们在代码中调用函数时通过函数名称来调用的,那这个地址和我们调用的函数名称是如何建立起联系的呢?我们先来看下刚才的MachO文件,如图:

图1:


image.png

图2:


image.png

从图1、图2中我们可以看出Lazy Symbol Pointers 表 和 Dyamic Symbol Table 表中的关系是一一对应的,遍历前一张表的index就可以对应到后一张表。注意图2中的对应index行中的 Data 值为0x7F。

图3:


image.png

在Symbol Table 表中先拿到第一条的偏移地址0xC500。

图4:


image.png

在图2中index中获取的Data值为0x7F,这值是图4中index的标号,由于图4中index的偏移为0x10,所以偏移地址+Data对应的偏移地址为0xCCF0,找到当前index的Data值是0xA7。

图5:


image.png

最后在String Table 表中 偏移首地址+0xA7 的值为0x0000D02B,在表中差得字符串以“_”开头,以“.”结尾。所得的字符串就是外部函数地址所对应的函数名。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 13. Hook原理介绍 13.1 Objective-C消息传递(Messaging) 对于C/C++这类静态语...
    Flonger阅读 1,382评论 0 3
  • 13.1 Objective-C消息传递(Messaging) 对于C/C++这类静态语言,调用一个方法其实就是跳...
    泰克2008阅读 1,855评论 1 6
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,669评论 0 27
  • [TOC] 回顾 注入的相关要素: 注入的形式:利用动态库的特性进行注入,包括Framework、Dylib。可以...
    _顺_1896阅读 792评论 0 0
  • 2016年的12月,很冷很冷。我从读书的城市辗转去到广州南站,跨过1458.9km去见你。 那时候,我读书的城市还...
    莫名墨阅读 238评论 7 7