97964c3e0c9439134bf74599873c1fe1
安全攻与防篇:Hook与反Hook

1.概述

1.1 Hook 是什么?

Hook 也叫钩子,在 Windows 系统中,一切皆消息,比如按了一下键盘,也是一个消息.Hook 的意思是勾住,也就是在消息过去之前,可以先把消息勾住,不让其传递,然后我们可以优先处理,加入自己的逻辑。简单的说,Hook 就是提供了一个入口,能够针对不同的消息或者 API ,在执行前,先执行我们的代码

1.2 Hook 的方式

1.2.1 方法交换

利用 OC 的 Runtime 特性,动态改变 SEL(方法编号)和 IMP(方法实现)的对应关系,达到 OC 方法调用流程改变的目的。该方式主要用于 OC方法。

1.2.2 fishhook

fishhook 是 Facebook 提供的一个动态修改链接 Mach-O 文件的工具。利用 MachO 文件加载原理,通过修改懒加载和非懒加载两个表的指针达到 C 函数 HOOK 的目的。

1.2.3 Cydia Substrate

Cydia Substrate 原名为 Mobile Substrate ,它的主要作用是针对 OC 方法、C 函数以及函数地址进行 HOOK 操作。当然它并不是仅仅针对 iOS 而设计的,安卓一样可以用。Cydia Substrate 由三部分组成。

MobileHooker

用于 HOOK,它定义了一系列的宏和函数,底层调用 OC 的 Runtime 和 fishhook 来替换系统或者目标应用的函数。其中有两个函数:

  • MSHookMessageEx 主要作用于 OC 方法。
void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result)
  • MSHookFunction 主要作用于 C 和 C++ 函数,Logos 语法的 %hook 就是对此函数做了一层封装。
void MSHookFunction(voidfunction,void* replacement,void** p_original)

MobileLoader

MobileLoader 用于加载第三方 dylib 到运行的应用程序中。启动时 MobileLoader 会根据规则把指定目录的第三方的动态库(也就是我们写的破解程序)加载进去。

Safe Mode

因为APP程序质量参差不齐崩溃再所难免,破解程序本质是 dylib,寄生在别人进程里。 系统进程一旦出错,可能导致整个进程崩溃,崩溃后就会造成 iOS 瘫痪。所以 CydiaSubstrate 引入了安全模式,在安全模式下所有基于 CydiaSubstratede 的三方 dylib 都会被禁用,便于查错与修复。

2.fishhook

关于 fishhook 的详细内容,可以参看 核心知识篇:Fishhook 解读。这里再对 fishhook 实现的原理,做一个简单的说明。

我们知道,Mach-O 文件是由 DYLD 动态加载的,ASLR(ARM 汇编基础:Block 和 OC 方法汇编) 技术,使得每次 Mach-O 文件加载的地址都是随机的。当 Mach-O 内部需要调用系统函数的时候,会先在 __DATA 段中建立一个指针,指向外部函数,然后在运行的时候,DYLD 进行动态绑定,将 Mach-O 文件 __DATA 段中的指针,指向外部函数,这也叫做 PIC(position-independent code,地址无关代码)。

基于上面的理解,我们来编写一个 Demo,来看一下 fishhook 对指针的重新绑定。

- (void)gof_rebindingFunc {
    NSLog(@"Gof");  // 加这个是因为 NSLog 是懒加载绑定,所以先绑定查看一下系统函数 NSLog 的指针

    struct rebinding gofLog;
    gofLog.name = "NSLog";
    gofLog.replacement = GofNSLog;
    gofLog.replaced = (void *)&sysNSLog;

    struct rebinding rebs[1] = {gofLog};
    rebind_symbols(rebs, 1);  //在这行添加一个断点
}


/// 自定义的 Log 函数
/// @param format 参数
void GofNSLog(NSString *format, ...) {
    sysNSLog(@"%@ %@", name, format);
}

/// 定义一个变量,保存系统的函数指针
static void (*sysNSLog)(NSString *format, ...);

运行工程,然后按如下步骤操作。

  • 第一步:打开生成的 app 文件中的可执行文件,在 __DATA 段的 __la_sysbol_ptr 中,找到 NSLog 的偏移地址,这里是 0xC000。
  • 第二步:在 LLDB 中,通过 【image list】指令,查看可执行文件在内存中的偏移地址,这里是「0x0000000104024000」。
(lldb) image list
[  0] 102572D3-626C-3C7C-A9CD-10B1C2F1A5B0 0x0000000104024000 /Users/GofLee/Library/Developer/Xcode/DerivedData/GofFishhook-eamgtrmlkfpmsigdlmhkuygtoplh/Build/Products/Debug-iphoneos/GofFishhook.app/GofFishhook 
  • 第三步:符号重绑定前,查看一下 NSLog 内存里保存的指针地址,并输出指针地址处的函数。可以看到这时候,指向的是系统的 NSLog 函数。
(lldb) x 0x0000000104024000+0xc000
0x104030000: 7c cf 63 b1 01 00 00 00 dc 1b 63 b1 01 00 00 00  |.c.......c.....
0x104030010: ec 4d 36 b5 01 00 00 00 50 a5 02 04 01 00 00 00  .M6.....P.......
(lldb) dis -s 0x1b163cf7c
Foundation`NSLog:
    0x1b163cf7c <+0>:  pacibsp 
    0x1b163cf80 <+4>:  sub    sp, sp, #0x20             ; =0x20 
    0x1b163cf84 <+8>:  stp    x29, x30, [sp, #0x10]
    0x1b163cf88 <+12>: add    x29, sp, #0x10            ; =0x10 
    0x1b163cf8c <+16>: adrp   x8, 254391
    0x1b163cf90 <+20>: ldr    x8, [x8, #0xc00]
    0x1b163cf94 <+24>: ldr    x8, [x8]
    0x1b163cf98 <+28>: str    x8, [sp, #0x8]
  • 第四步:符号重绑定后,查看一下 NSLog 内存里保存的指针地址,并输出指针地址处的函数。可以看到重绑定后,指向的是我们自定义的 GofNSLog 函数。
(lldb) x 0x0000000104024000+0xc000
0x104030000: 18 93 02 04 01 00 00 00 dc 1b 63 b1 01 00 00 00  ..........c.....
0x104030010: ec 4d 36 b5 01 00 00 00 fc cc ef b0 01 00 00 00  .M6.............
(lldb) dis -s 0x104029318
GofFishhook`GofNSLog:
    0x104029318 <+0>:  sub    sp, sp, #0x40             ; =0x40 
    0x10402931c <+4>:  stp    x29, x30, [sp, #0x30]
    0x104029320 <+8>:  add    x29, sp, #0x30            ; =0x30 
    0x104029324 <+12>: mov    x8, #0x0
    0x104029328 <+16>: stur   x8, [x29, #-0x8]
    0x10402932c <+20>: sub    x9, x29, #0x8             ; =0x8 
    0x104029330 <+24>: stur   x0, [x29, #-0x10]
    0x104029334 <+28>: mov    x0, x9

3.通过符号查找字符串

这里我们用 WeChat 的 MachO 文件做一个演示:通过符号来查找对应的字符串 ABAddressBookCopyArrayOfAllPeople。

  • 第一步:找到懒加载符号表和与之相对应的动态符号表。

  • 第二步:通过 Indirect Symbols(动态符号表) 中 Data 值(上面是 0x63,对应十进制为 99),在 Symbol Table 符号表中找到对应函数。
  • 第三步:在 String Table 中,根据 Symbol Table 中的 Data(上面是 0x2FF4),加上 String Table 的起始地址(这里是 0x851AE8C,相加结果为 0x851DE80),找到具体的字符串。

top Created with Sketch.