# 08.Hook

## 一、常见的Hook技术

* Method Swizzle
  * 利用OC的Runtime特性，动态改变SEL和IMP的对应关系，达到OC方法调用流程的改变。主要用于OC方法。
* fishhook
  * Facebook提供的一个动态修改链接mach-O文件的工具。利用MachO文件加载原理，通过懒加载和非懒加载两个表的指针达到C函数Hook的目的。
* Cydia Substrate
  * 主要针对OC方法、C函数以及对函数地址进行Hook操作。不经针对iOS，安卓也可以用。

## 二、fishhook

[fishhook-Github](https://github.com/facebook/fishhook)

* 首先回顾一下OC的动态特性
  * 并不是直接调用方法的实现地址，而是通过一张selector和imp的映射表去找
  * 静态语言如C，是直接通过地址访问
    * 但外部C函数调用是通过符号绑定来的

### 2.1 rebind\_symbols

```C
/*
 * For each rebinding in rebindings, rebinds references to external, indirect
 * symbols with the specified name to instead point at replacement for each
 * image in the calling process as well as for all future images that are loaded
 * by the process. If rebind_functions is called more than once, the symbols to
 * rebind are added to the existing list of rebindings, and if a given symbol
 * is rebound more than once, the later rebinding will take precedence.
 */
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
```

参数列表：

* 需要Hook的rebinding数组
* 长度

### 2.2 rebinding

```C
/*
 * A structure representing a particular intended rebinding from a symbol
 * name to its replacement
 */
struct rebinding {
  const char *name;
  void *replacement;
  void **replaced;
};
```

`name` 需要Hook的函数名称，C字符串 `replacement` 新函数的地址 `replaced` 原始函数的指针

### 2.3 使用 rebind\_symbols hook NSLog

通过查看接口我们需要创建一些数据。

```Swift
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor blackColor];
    // 目标去Hook系统的NSLog函数
    
    struct rebinding nslog;
    nslog.name = "NSLog";
    nslog.replacement = hook_NSLog;
    // 保存NSLog函数的指针
    nslog.replaced = (void *)&sys_NSLog;
    
    struct rebinding bds[] = {nslog};
    
    rebind_symbols(bds, 1);
}

/// 旧函数的指针
static void (*sys_NSLog)(NSString *format, ...);

// 新函数
void hook_NSLog(NSString *format, ...) {
    format = [format stringByAppendingString:@"\n🌈"];
    // 再调用系统的
    sys_NSLog(format);
}

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

```

输出结果：

```
2021-05-05 15:51:44.230666+0800 Hook[1650:239418] RyukieLog
🌈
2021-05-05 15:51:44.430579+0800 Hook[1650:239418] RyukieLog
🌈
2021-05-05 15:51:44.679392+0800 Hook[1650:239418] RyukieLog
🌈
```

Hook成功！

### 2.4 试一试hook C函数？

定义一个这样的函数

```C
void logFun(const char * str) {
    NSLog(@"%s", str);
}
```

修改rebinding信息

```C
struct rebinding nslog;
nslog.name = "logFun";
nslog.replacement = new_logfun;
nslog.replaced = (void *)&old_logfun;

static void(*old_logfun)(const char * str);

void new_logfun(const char * str) {
    str = "🌈";
    // 再调用旧的
    old_logfun(str);
}
```

发现并没有成功！应为这个是通过地址直接访问的。

## 三、重绑定符号

一个问题：编译的时候，NSLog的真实地址知道么？不知道。

在MachO加载进内存时，dyld会先加载依赖的系统库进共享缓存。

NSLog在Foundation框架中，加载后dyld会将NSLog的真实地址告诉MachO。

验证一下fishhook的实现：

![hook](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2Fa8c546a4ab547feb6b3cbd611b74f48b4668bdfd.png?generation=1620206954339651\&alt=media)

在hook前后分别下断点，打开汇编断点。

第一个断点：

`-> 0x10233d234 <+188>: bl 0x10233e320 ; symbol stub for: NSLog`

下一步：

```
->  0x104cae320 <+0>: nop    
    0x104cae324 <+4>: ldr    x16, #0x5cdc              ; (void *)0x0000000104cae464
    0x104cae328 <+8>: br     x16

```

第二个断点：

`-> 0x10233d254 <+220>: bl 0x10233e320 ; symbol stub for: NSLog`

下一步：

```
->  0x104cae320 <+0>: nop    
    0x104cae324 <+4>: ldr    x16, #0x5cdc              ; (void *)0x0000000104cad44c: hook_NSLog at /Users/RyukieW/RyukieSamaProject/Study/逆向/Code/Hook/Hook/Hook/ViewController.m:75
    0x104cae328 <+8>: br     x16
```

分析：

`bl 0x10233e320 ; symbol stub for: NSLog`

这一段是到`0x10233e320`中去寻找符号绑定的地址

第一次的第二步找到了系统原本函数的地址

第二次走到第二步后发现已经是hookNSLog了

## 四、符号绑定的过程

* 间接符号表
  * MachO文件以外的，所有调用系统的都是外部的。
* 本地符号
  * 上架去符号，去的是本地符号
* 全局符号
  * 可以暴露给外界

### 4.1 桩

一块代码！

上面一段中看到的一段汇编指令

`bl 0x10233e320 ; symbol stub for: NSLog`

`0x10233e320`就是NSLog的桩

bl -> 桩 -> 通过符号表去执行代码

> 懒加载符号表全部都是通过桩去进行绑定的

## 五、总结

* 符号绑定过程
  * 外部函数调用是执行桩里的代码
    * 通过懒加载符号表里的地址去执行
  * 懒加载符号表里面默认保存的是binder的代码
    * bingder函数在非懒加载符号的表里面（程序运行就绑定好了）


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ni-xiang/08.hook.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
