ios开发

fishHook原理----(1)

2021-05-04  本文已影响0人  spyn_n

  为了探索fishHOOK的原理,我们首先要探索一下其HOOK流程。我们首先新建个工程fishHookDemo,编写HOOK系统函数NSLog的代码。在HOOK之前和之后分别调用NSLog方法,我们都是到由于aslr的和虚拟地址存,在编译的时候外部C函数的真实地址是不知道的,DYLD在加载一个MachO文件的时候,需要重新找到Code Function框架的NSLog的地址。在如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"123");
    
    struct rebinding psyLog;
    psyLog.name = "NSLog";
    psyLog.replacement = psy_Log;
    // 原函数
    psyLog.replaced = (void *)&sysLog;
    
    struct rebinding psysh[] = {
        psyLog
    };
    
    rebind_symbols(psysh, 1);
    
    NSLog(@"456");
}

// 函数指针
static void (*sysLog)(NSString *format, ...);
//新函数
void psy_Log(NSString *format, ...)
{
    format = [format stringByAppendingString:@"被 psyLog Hook了!"];
    sysLog(format);
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 变异的时候不知道,NSLog的真实地址
    NSLog(@"这是NSLog");
}

  在NSLog第一次调用的时候下断点。然后通过LLDB查看一下,在MachO文件中内存地址和数据。通过image list拿到基地址0x000000010036c000,找到FishHookDemoMachO文件,使用MachOView打开,找到符号表中NSLog,其offset是00008010,对应的Data是0x10000650c(这个是加过slide的,基地址也是加过slide的,所以为了不重复添加,去掉前面的1,取0x650c),0x650c + 0x000000010036c000 = 0x010037250c。我们也可以使用LLDB命令通过偏移8010验证一下,读取内存数据:memory read 0x000000010036c000+0x8010,可看到结果是一样的,都是0x010037250c


反编译0x010037250c

  我们还可以反编译看一下汇编指令:dis -s 0x010037250c,发现它会 b 到地址0x1003724f4,通过计算(0x1003724f4 - 0x000000010036c000)我们得到偏移值0x64F4,查看MachO,也就是最后都 b 到了一段代码。

image.png
这段代码干了什么?我们看到最后会br x16,这个x16对应的地址是0x100008008(也就是偏移8008),我们找到偏移8008是在Non-Lazy Symbol Pointers非懒加载表中,调用的是dyld的dyld_stub_binder函数完成符号绑定操作,将NSLog与实际内存地址绑定:
image.png

  过掉断点在调用fishHook函数rebind_symbols之前,看一下偏移0x8010内存的数据memory read 0x000000010036c000+0x8010,发现他已经变成0x01fe90444c,反编译看一下,这个地址,确实就是实际的NSLog的地址,如下图:

image.png

将断点下在调用rebind_symbols之后的第二次调用NSLog之前位置,看一下偏移0x8010内存的数据memory read 0x000000010036c000+0x8010,此时已经变成0x100371578,反编译看一下,这个地址就是实际的psy_Log的地址,成功的实现了NSLogHOOK,如下图:

image.png

我们还发现,系统的C函数,fishHook可以所以实现HOOK,但是自定义的C函数,我们却不能简单的通过fishHook实现。这个符号绑定过程就是PIC技术,那PIC技术就是简单的完成符号和地址的绑定吗?我们思考一下,为什么rebind_symbols之后第二次调用NSLog,内存memory read 0x000000010036c000+0x8010地址指向的不是一段代码(像第一次调用一样,如上图:“反编译0x010037250c”),而是我们要的psy_Log,难道是rebind_symbols做了什么事?其实不是,我们可以新建个工程验证一下,什么都不做,也不引入fishHook直接调用两次NSLog,发现第二次调用也是一样的指向NSLog的地址。我们再通过MachOView分析发现在Section的_TEXT,stubs,在偏移63EC有一个对应的NSLog,其对应的Data又是干嘛的呢(如下图)?我们称之为“桩”,data1F2003D510E1005800021FD6其实是一段代码的指令符号,通过这个桩去执行一段代码。不光是NSLog_NSStringFromClass等后面的,data都是1F2003D510E1005800021FD6,说明所有系统外部C函数的符号绑定都是先通过一样的桩。然后找到Lazy Symbol Pointers懒加载符号的Data在决定是否进行符号绑定操作,还是已经指向实际的地址。

image.png

总结一下

1、程序一直行,进行非懒加载的符号绑定,调用dyld_stub_binder函数;
2、当我们第一次调用NSLog的时候,首先会到找桩去执行一段代码,这段代码就是去找到懒加载里面的值去执行,然后它默认会 b 到另一段代码,就是去找non_Lazy Symbol去找dyld_stub_binder的真实函数的地址完成符号绑定,第一次绑定完成之后,完成了第一次调用;
3、当第二次调用NSLog的时候,还是会找到桩去执行一段代码,代码会跳到懒加载里面的Data的值去执行,但是这个时候,Data里面的值指向了NSLog的真实地址,不再是指向查找dyld_stub_binder的真实函数的地址完成符号绑定;
4、外部函数先找桩;
那为什么要建符号表呢?因为外部函数的地址不确定,需要符号变在运行的时候实现查找到真实的地址执行。
5、并不是所有的C语言调用都是静态的,C的外部函数有时候就是对符号的重绑定;

上一篇下一篇

猜你喜欢

热点阅读