iOS逆向工程iOS 开发OC 底层

Fishhook 学习笔记

2018-05-20  本文已影响200人  bland_Xu

一、Fishhook 是什么?

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

二、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文件加载的地址不一样,我们大家都知道,系统的动态库缓存区的地址也是变化的,那每次dyld加载动态库的时候是怎样找到依赖动态库进行加载的呢?

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


image.png

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

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

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

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

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


image.png

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

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

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

四、通过符号找到字符串

在上面我们知道了系统的动态库与我们的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,在表中差得字符串以“_”开头,以“.”结尾。所得的字符串就是外部函数地址所对应的函数名。

上一篇下一篇

猜你喜欢

热点阅读