iOS的hook介绍,fishhook的调试理解

原创文章
声明:作者声明此文章为原创,未经作者同意,请勿转载,若转载,务必注明本站出处,本平台保留追究侵权法律责任的权利。
全栈老韩
全栈工程师,擅长iOS App开发、前端(vue、react、nuxt、小程序&Taro)开发、Flutter、React Native、后端(midwayjs、golang、express、koa)开发、docker容器、seo优化等。

HOOK

Method Swizzle

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

fishhook

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

hook C函数

引入facebookfishhook.hfishhook.c

复制代码
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // HOOK 交换
    /**
     struct rebinding {
     const char *name; // 需要HOOK的函数名称,C字符串
     void *replacement; // 新函数的地址
     void **replaced; // 用来保存原始函数地址的指针
     };
     */
    struct rebinding nslog;
    nslog.name = "NSLog";
    nslog.replaced = (void *) &sys_nslog; // 保存系统函数的地址的指针的指针
    nslog.replacement = myNSlog; // 新函数的地址
    
    struct rebinding rebs[1] = {nslog};
    /**
     用于重新绑定符号
     一次性交换多个函数,
     arg1:存放rebinding结构体的数组
     arg2:数组的长度
     */
    rebind_symbols(rebs, 1);
}

// ----更改系统的NSLog函数调用
// 函数指针,用来保存原始的函数地址!
static void(*sys_nslog)(NSString *str, ...);


// 定义一个新的函数
void myNSlog(NSString *str, ...) {
    str = [str stringByAppendingString:@"\n钩上了!"];
    // 由于系统的内部实现不知道
    sys_nslog(str);
}

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

hook 自定义的C函数 (勾不住)

复制代码
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self funcDemo];
}

// 交换自定义函数
- (void)funcDemo {
    rebind_symbols((struct rebinding[1]){{"func", newFunc, (void *)&funcP}}, 1);
}
// 保存原始函数的指针
static void(*funcP)(const char *);

// 新函数
void newFunc(const char * str) {
    NSLog(@"勾住了");
    funcP(str);
}
// 自定义的函数
void func(const char * str) {
    NSLog(@"%s", str);
}

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

底层汇编解释

  • C函数是静态的
  • PIC 位置独立代码
  • DYLD 将可执行文件加载到内存中,将NSLog的地址绑定到符号symbol,一开始符号symbol都是0x0,当APP被DYLD加载到内存中运行的时候,DYLD会把symbol中的指针指向内存中的UIKit, UIFoundation中的函数地址。

在NSLog函数的hook代码中,在rebind操作时打一个断点,然后在xCode终端看底层汇编的内容:

  1. 命令image list可以查看fishhook.app的exec可执行文件在内存中的地址:
复制代码
image list
[  0] DC3F7324-1654-3F1B-8A0A-2E8E9B228968 0x0000000108bf5000 /Users/hanweixing/Library/Developer/Xcode/DerivedData/FishHook-egipaudxwxzvacdyvhhoisbqvjio/Build/Products/Debug-iphonesimulator/FishHook.app/FishHook 
[  1] CE635DB2-D47E-3C05-A0A3-6BD982E7E750 0x000000010bbd1000 /usr/lib/dyld 
[  2] 528E1F55-F655-3533-99B9-7EAE1DAE5D07 0x0000000108c01000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim 
...

得到可执行文件的内存地址:0x0000000108bf5000

2. 通过MachOView软件,打开fishhook.app的可执行文件exec,在__la_symbol_ptrLazy Symbol Pointers中可以看到NSLog的内存偏移:00004028

  • 在Xcode中读取NSLog对应的symbol地址:
复制代码
(lldb) x 0x0000000108bf5000+0x4028
0x108bf9028: a0 79 bf 08 01 00 00 00 89 b4 f7 08 01 00 00 00  .y..............
0x108bf9038: 36 0f d8 0c 01 00 00 00 e6 79 bf 08 01 00 00 00  6........y......
  • 取前面的8个字节,然后倒序拼接,就可以将symbol中的值转换成汇编代码:(可以看到这是系统Foundation中的NSLog)
复制代码
(lldb) dis -s 0x0108bf79a0
Foundation`NSLog`:
    0x108bf79a0: pushq  $0x0
    0x108bf79a5: jmp    0x108bf7990
    0x108bf79aa: pushq  $0xd
    0x108bf79af: jmp    0x108bf7990
    0x108bf79b4: pushq  $0x118                    ; imm = 0x118 
    0x108bf79b9: jmp    0x108bf7990
  • 将断点跳一步,继续在Xcode中进行调试,读取最新的symbol中的值,可以发现被替换成了我们替换的函数实现。
复制代码
(lldb) x 0x0000000108bf5000+0x4028
0x108bf9028: e0 6d bf 08 01 00 00 00 89 b4 f7 08 01 00 00 00  .m..............
0x108bf9038: 36 0f d8 0c 01 00 00 00 31 71 91 0b 01 00 00 00  6.......1q......
(lldb) dis -s 0x0108bf6de0
FishHook`myNSlog:
    0x108bf6de0 <+0>:  pushq  %rbp
    0x108bf6de1 <+1>:  movq   %rsp, %rbp
    0x108bf6de4 <+4>:  subq   $0x30, %rsp
    0x108bf6de8 <+8>:  movq   $0x0, -0x8(%rbp)
    0x108bf6df0 <+16>: leaq   -0x8(%rbp), %rax
    0x108bf6df4 <+20>: movq   %rdi, -0x10(%rbp)
    0x108bf6df8 <+24>: movq   %rax, %rdi
    0x108bf6dfb <+27>: movq   -0x10(%rbp), %rsi
(lldb)

Cydia Substrate (主要用在逆向)

Cydia Substrate原名为Mobile Substrate,它的主要作用是针对OC方法、C函数以及函数地址进行HOOK操作。当然它不仅仅是针对iOS而设计的,安卓一样可以用。
官方地址:http://www.cydiasubstrate.com/

暂无评论,快来发表第一条评论吧