iOS 逆向开发17:HOOK原理下(HOOK OC方法)

iOS 逆向开发 文章汇总

目录

  • 一、符号表之间的关联
  • 二、去符号
  • 三、恢复符号
  • 四、使用fishhook HOOK系统MethodSwizzle方法,以检测APP是否被HOOK
  • 五、使用MonkeyDev进行重新签名和代码注入
  • 六、总结


一、符号表之间的关联

懒加载符号表、间接符号表、符号表、字符串表之间的关联

懒加载符号表
间接符号表
符号表
字符串表

懒加载符号表间接符号表index一一对应
接符号表保存了在符号表中的index
符号表中保存了在字符串表中的index

上面的流程和fishhook查找符号的流程图一致:The process of looking up the name of a given entry in the lazy or non-lazy pointer tables looks like this:


二、去符号

一般情况下我们的APP会去掉所有符号(不包括间接符号)、动态库会保留全局符号供外部调用。

Xcode 默认是去掉所有符号的,但是Deployment Postprocessing 需要设置为Yes,否者只有在打包时才会去掉所有符号。

符号表中只有间接符号了,本地符号和全局符号都去掉了

添加如下代码:

运行APP后断点是不起作用的,因为去掉了所有符号。添加NSLog符号断点可以断住,但是去掉符号后函数名会变为___lldb_unnamed_symbol2

如果调用的是OC方法可以添加符号断点并通过参数寄存器查看方法名和参数
还可以通过地址断点(ASLR+地址偏移值)、恢复符号表等方法查看方法名


三、恢复符号

- (void)differtest {
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"第一次外部函数的调用!");
    
    NSLog(@"第二次外部函数的调用!");
}

- (void)test1 {
    NSLog(@"第一次外部函数的调用!");
}

- (void)test {
    [self test1];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self test];
}

虽然去掉所有符号后符号表中没有符号了,但是类的方法列表中还是存在。因此通过方法列表,类名列表等信息可以恢复出相关的符号


通过restore-symbol工具恢复符号(C函数、静态函数不能恢复,只能恢复Runtime相关的)

执行代码./restore-symbol -o symbolDemo2 symbolDemo后即可恢复符号,恢复符号的MachO还需要重新签名打包


四、使用fishhook HOOK系统MethodSwizzle方法,以检测APP是否被HOOK

目标:APP中可以MethodSwizzle方法,破解APP时直接调用MethodSwizzle方法会失败。

4.1 新建项目AntiHook模拟我们自己的APP、添加两个按钮并关联方法

代码如下:

- (IBAction)btnClick1:(id)sender {
    NSLog(@"按钮1调用!");
}

- (IBAction)btnClick2:(id)sender {
    NSLog(@"按钮2调用了!");
}

防护代码需要写在Framework中,因为Framework加载比主工程中的代码加载更快,别人注入的HOOK代码在Framework加载之后、在主工程之前。

添加主Framework

// AntiHookCode.m

#import "fishhook.h"

@implementation AntiHookCode

+ (void)load {
    //exchange方法防护
    struct rebinding exchange;
    exchange.name = "method_exchangeImplementations";
    exchange.replacement = my_exchange;
    exchange.replaced = (void *)&exchangeP;
    
    //set、get方法防护
    //setIMP
    struct rebinding setIMP;
    setIMP.name = "method_setImplementation";
    setIMP.replacement = my_exchange;
    setIMP.replaced = (void *)&setIMP_p;
    
    //getIMP
    struct rebinding getIMP;
    getIMP.name = "method_getImplementation";
    getIMP.replacement = my_exchange;
    getIMP.replaced = (void *)&getIMP_p;

    
    struct rebinding bds[] = {exchange,setIMP,getIMP};
    rebind_symbols(bds, 3);
    
}

//指针!这个可以暴露给外接!我自己的工程使用!!
void (*exchangeP)(Method _Nonnull m1, Method _Nonnull m2);

IMP _Nonnull (*setIMP_p)(Method _Nonnull m, IMP _Nonnull imp);
IMP _Nonnull (*getIMP_p)(Method _Nonnull m);


void my_exchange(Method _Nonnull m1, Method _Nonnull m2){
    NSLog(@"检测到了HOOK!");
}

@end

HookMgr.h中暴露AntiHookCode.h头文件

HookMgr.h设置为Public供外界使用

ViewController.m:自己项目交换按钮2的实现

#import 

- (void)viewDidLoad {
    [super viewDidLoad];
    //HOOK
    exchangeP(class_getInstanceMethod(self.class, @selector(btnClick2:)), class_getInstanceMethod(self.class, @selector(test)));
}

- (void)test {
    NSLog(@"本工程的HOOK!很顺利!!!");
}

4.2 新建项目HookDemo用来HOOK我们的APP

将编译好的AntiHook拷贝到HookDemo项目中的APP/Payload目录中(真机编译)

终端进入APP目录
zip -ry WeChat.ipa Payload (ipa的名字这里沿用之前的)
删除Payload目录

HookDemo项目编译运行到手机中

添加注入代码:模拟别人HOOK按钮1

添加脚本执行

运行项目将APP安装到手机、控制台会直接输出:检测到了HOOK!
点击按钮1也不会执行test方法。这样就达到了HOOK APP方法失败的目的
点击按钮2会显示本工程的HOOK!很顺利!!!


五、使用MonkeyDev进行重新签名和代码注入

项目地址、安装方法

安装好后重新打开Xcode就有如下功能(安装MonkeyDev后Xcode如果打开崩溃,删除Xcode重装一次就可以了)

MonkeyDev

5.1 重签名

  • 选择MonkeyApp模板创建项目MonkeyDemo并运行安装APP
  • 将需要重签名的ipa or app 拖到TargetApp目录中
  • 运行项目MonkeyDemo到手机即可进行重签名

5.2 代码注入:OC方法HOOK

修改MonkeyDemoDylib.xm文件的type

HOOK APP中ViewController的BtnClick1方法

Monkey实际上使用的是MethodSwizzle 中的method_getImplementation/method_setImplementation方法


六、总结

  1. HOOK:钩子,改变程序的执行流程的一种技术
  • MethodSwizzle
    • 利用OC的运行时(Runtime) 特性修改SEL和IMP (函数指针) 对应关系。达到HOOK OC方法的目的
    • method_exchangelMP... 交换两个IMP
    • class_replaceMethod替换某个SEL的IMP (如果没有该方法,就添加。相当于替换掉这个方法)
    • method_getlmplementation、method_setlmplementation 获取和设置某个方法的IMP (很多三方框架都使用)
  • fishhook
    • Facebook提供的一个工具,利用MachO文件的加载原理,动态修改懒加载和非懒加载两个符号表!
  • Cydia Substrate
    • 一个强大的框架。
  1. fishhook
  • 可以HOOK 系统的函数,但是无法HOOK自定义的函数
  • 原理:
    • 共享缓存
      • iOS系统有一块特殊的位置,存放公用动态库。动态库共享缓存(dyld shared cache)
    • PIC技术
      • 由于外部的函数调用, 在我们编译时刻是没法确定其内存地址的。
      • 苹果就采用了PIC技术(位置无关代码)。在MachO文件DATA段,建立两张表,懒加载和非懒加载表,里面存放执行外部函数的指针!
      • 首次调用懒加载函数, 会去找执行代码。首次执行会执行binder函数!
  • 通过字符找到懒加载表
    • fshhook利用stringTable --> Symbols --> indirect Symbols --> 懒加载符号表之间的对应关系。通过重绑定修改指针的值达到HOOK的目的。
  1. 反H0OK基本防护
  • 利用fishhook修改Methodswizzle 相关函数来检测是否被HOOK
  • 防护代码要最先被加载,否则先HOOK就修改完毕了,防护无效。
  • 原始工程编写的Framework库会优先于注入库加载。所以适合写防护代码。

你可能感兴趣的:(iOS 逆向开发17:HOOK原理下(HOOK OC方法))