关于ios:iOS逆向-HOOK原理之fishhook

33次阅读

共计 3789 个字符,预计需要花费 10 分钟才能阅读完成。

一、HOOK 概述

1.HOOK 定义

HOOK翻译成中文为“挂钩”、“钩子”,在 iOS 逆向畛域中指的是扭转程序运行流程的一种技术,通过 HOOK 能够让他人的程序执行本人所写的代码

下列示意图就是对 HOOK 性能的形象诠释:

  • 注入恶意代码让用户误以为打卡胜利,理论并没有实现打卡(只批改两头流程)
  • 注入恶意代码让用户误以为网络出问题(开拓新的流程分支)

2.HOOK 形式

在 iOS 中 HOOK 技术有以下几种:

  • Method Swizzling:利用 OC 的 Runtime 个性,动静扭转 SEL(办法编号)IMP(办法实现)的对应关系,达到 OC 办法调用流程扭转的目标
  • fishhook:这是 FaceBook 提供的一个动静批改链接 machO 文件的工具,利用 machO 文件加载原理,通过批改懒加载和非懒加载两个表的指针达到 C 函数 HOOK 的目标
  • Cydia Substrate:原名为Mobile Substrate,它的次要作用是针对 OC 办法、C 函数以及函数地址进行 HOOK 操作,且安卓也能应用

之前曾经介绍过 Method Swizzling 了,OC 的 Runtime 个性让它有了“黑魔法”之称,同时也是局限性所在

三者的区别如下:

  • Method Swizzling只实用于动静的 OC 办法(运行时确定函数实现地址)
  • fishhook实用于动态的 C 办法(编译时确定函数实现地址)
  • Cydia Substrate是一种弱小的框架,只须要通过 Logos 语言(相似于正向开发)就能够进行 Hook,实用于 OC 办法、C 函数以及函数地址

二、fishhook

1.fishhook 的应用

整个 fishhook 就凋谢了一个构造体 rebinding 和两个函数

  • rebind_symbolshook 我的项目中的所有函数名称
  • rebind_symbols_image只 hook 某一个资源库的函数名称

应用 fishhook 的步骤:

  1. 导入头文件
#import "fishhook.h" 
  1. 指定替换函数——rebinding构造体对象
struct rebinding nslog;
nslog.name = "NSLog";
nslog.replacement = fxNSlog;
nslog.replaced = (void *)&sys_nslog; 
  1. 调用 rebind_symbols 从新绑定符号
struct rebinding rebs[] = {nslog};
rebind_symbols(rebs, 1); 

残缺代码如下:

#import "fishhook.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {[super viewDidLoad];

    struct rebinding nslog;
    nslog.name = "NSLog";
    nslog.replacement = fxNSlog;
    nslog.replaced = (void *)&sys_nslog;

    struct rebinding rebs[] = {nslog};
    rebind_symbols(rebs, 1);
}

static void(*sys_nslog)(NSString *format, ...);

void fxNSlog(NSString *format, ...) {format = [format stringByAppendingFormat:@"已 hook"];
    sys_nslog(format);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {NSLog(@"点击屏幕");
}

@end 
  • 定义 rebinding 来指定替换的函数

    • name中指定 Hook 的函数名称(C 字符串)
    • replacement中指定新的函数地址

      • c 函数的名称就是函数指针——动态语言编译时就已确定
    • replaced中寄存原始函数地址的指针

      • 因为 NSLog 在 Foundation 库中,在每个手机的内存地址都不一样,不能通过 CMD+B 就能确定其内存地址
      • 这里 fishhook 在运行的时刻会动静获取到 NSLog 的地址,所以这里定义新的函数指针保留函数实现
      • 应用 & 批改变量外部的值
      • 应用 (void *) 进行类型转换
  • rebind_symbols中须要两个参数

    • 参数一是 rebinding 数组
    • 参数二是 rebinding 数组的长度

作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这有个 iOS 交换群:642363427,不论你是小白还是大牛欢送入驻,分享 BAT, 阿里面试题、面试教训,探讨技术,iOS 开发者一起交流学习成长!

2.fishhook 的局限性

后面介绍了 fishhook 是用来 hook C 函数的,但在此前提下还是有它的局限性——无奈替换自定义函数

  • C 函数 是动态的,在编译时就曾经确定了函数地址(函数实现地址在 MachO 本地文件中)
  • 零碎 C 函数 则是存在着动静的局部

那么为什么零碎级别的 C 函数就能够呢?这就要说到 PIC 技术了

3.PIC 技术

PIC 技术 (Position-independent code),又叫做 地位独立代码 ,是为了 零碎 C 函数 在编译期间可能确认一个地址的一种技术手段

  • 编译时在 MachO 文件中预留出一段空间——符号表(DATA 段中)
  • dyld 把利用加载到内存中时(此时在 Load Commands 中会依赖 Foundation),在符号表中找到了NSLog 函数,就会进行链接绑定——将 FoundationNSLog的实在地址赋值到 DATA 段NSLog符号上

而自定义的 C 函数不会生成符号表,间接就是一个函数地址,所以 fishhook 的局限性就在于只有符号表内的符号能够 hook(从新绑定符号)

4.LLDB 调试

因为 NSLog 是个懒加载的符号,所以加了行代言代码

  1. 通过 image list 打印所有镜像资源——第一个就是过程的内存首地址

  2. 获取到 MachO 中 NSLog 的内存偏移量

应用计算器,内存首地址 + 内存偏移量 = 符号地址 
  1. 通过 memory read 内存地址 读出符号地址

因为 iOS 是小端模式,内存地址 8 位倒着读 
  1. 通过 dis -s 内存地址 查看汇编

此时的符号并没有绑定,因为还没有应用 
  1. 过掉断点(应用 NSLog)再进行查看

此时的 NSLog 符号曾经被绑定 
  1. 再跳到下一步(重绑定符号)进行查看

此时的 NSLog 符号曾经被重绑定了 

5.fishhook 原理摸索

接下来剖析下rebind_symbols

  • prepend_rebindingsrebindings 数组一直增加到 _rebindings_head 链表的头部成为新的头节点
  • 判断链表 _rebindings_head->next 是否为空来判断是否是第一次调用

    • 若首次调用则应用 _dyld_register_func_for_add_image 进行回调监听
    • 若不是则遍历调用_rebind_symbols_for_image
  • 这个函数有个 Int 返回值

    • 如果重定向胜利则返回 0
    • 如果失败则返回 -1

接下来是 _rebind_symbols_for_image 对 MachO 文件的操作:依照规定计算各种表的地址和指针在表中的偏移量

最初一步,perform_rebinding_with_section依据算好的符号表地址和偏移量,找到在符号表中用于指向共享库指标函数的指针,将该指针的值(即指标函数的地址)赋值给 rebinding*replaced,最初批改该指针的值为replacement(新的函数地址)

6.fishhook 原理的实际——在 MachO 中查找函数实现

接下来就依据字符串对应在符号表中的指针,找到其在共享库的函数实现的

  1. NSLog 在 Lazy Symbol Pointers 中位列第一个,下标为 0

  2. Dynamic System Table->Indirect Symbols中的 value 与 Lazy Symbol Pointers 一一对应,此表中的 Data(0xA9=169)即为另一个表的下标

  3. 拿着这个下标在 Symbol Table->Symbols 中找到 NSLog,此时的 Data 对应 String Table Index 中的偏移值(0x0000009B)

  4. 最初在 String Table 中计算表头(0x000061EC)+ 偏移量(0x0000009B),找到 NSLog 的函数地址

7.fishhook 利用场景

既然 fishhook 能够替换 C 函数,那么就能够替换 method_exchangeImplementations 等函数来避免 HOOK,我的项目自身如果须要应用 method_exchangeImplementations 则能够应用新的函数地址来进行操作

  • 攻:能够插入新的动静库提前进行 HOOK 办法,此时 fishhook 的防护就不起作用了
  • 防:也能够提前插入动静库进行 fishhook 替换(逆向注入的动静库个别排在原我的项目的动静库之后),此时 fishhook 代码先于 hook 代码,所以还是 fishhook 还是失效的
  • 攻:釜底抽薪——间接找到 fishhook 的函数地址进行批改,再次运行

写在前面

在平安攻防畛域中,hook 原理起到至关重要的作用,而应用 fishhook 做防护尽管意义不大,然而理解 fishhook 原理对后续的学习还是挺有帮忙的,想理解更多逆向相干常识无妨动动小手,增加一下咱们的交换群 642363427 来为你的技术多添一份荣耀。

正文完
 0