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

  • 首先回顾一下OC的动态特性

    • 并不是直接调用方法的实现地址,而是通过一张selector和imp的映射表去找

    • 静态语言如C,是直接通过地址访问

      • 但外部C函数调用是通过符号绑定来的

2.1 rebind_symbols

/*
 * 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

/*
 * 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

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

- (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函数?

定义一个这样的函数

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

修改rebinding信息

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前后分别下断点,打开汇编断点。

第一个断点:

-> 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函数在非懒加载符号的表里面(程序运行就绑定好了)

Last updated