15-Hook原理(二)反Hook防护 & MokeyDev

2,870 阅读7分钟

前言

上篇文章14-Hook原理(一)fishHook中告诉大家了Hook的知识点,重点介绍了fishHook的原理,那么本篇文章就告诉大家如何一步步地做到反HOOK防护,然后给大家介绍逆向开发中一个常用的工具 MonkeyDev,它能够帮助我们很方便地进行重签名和代码注入

一、反Hook防护

当我们要HOOK第三方App的OC方法时,一般会使用Method Swizzle。例如 👉 使用系统提供的method_exchangeImplementations函数将两个方法的imp实现进行交换

此时,我们自己写个App,使用fishHook,预先将method_exchangeImplementations自定义函数进行交换,那么,第三方攻击方想要Hook就很难了。

接下来,我们通过案例演示,如何进行反Hook防护。

1.1 案例准备

使用fishHook交换method_exchangeImplementations

  1. 新建App工程项目,命名为AntiHook,表示反HOOK案例。在ViewController页面中创建两个按钮btn1btn2👇

image.png

并添加点击事件代码👇

#import "ViewController.h"
#import <HookMgr/HookMgr.h>

@implementation ViewController

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

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

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

@end

⚠️注意: 防护代码写在Framework中更适宜,因为load方法调用时机比主程序App更快,并且可以在别人注入的动态库之前被执行。

  1. 添加target,选择Framework,命名为HookManager,设置为动态库👇 image.png

  2. HookManager中,创建AntiHookCode类,并导入fishhook👇 image.png

  3. AntiHookCode.m文件中写入以下代码👇

#import "AntiHookCode.h"
#import "fishhook.h"
#import <objc/message.h>

@implementation AntiHookCode

+(void)load{
   struct rebinding reb;
   reb.name="method_exchangeImplementations";
   reb.replacement=my_exchange;
   reb.replaced=(void *)&sys_exchange;

   struct rebinding rebs[] = { reb };
   rebind_symbols(rebs, 1);
}

void (*sys_exchange)(Method _Nonnull m1, Method _Nonnull m2);

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

@end
  1. 在项目的文件目录,创建Payload文件夹,build工程,将编译后的AntiHook.app拷贝到Payload目录下👇

image.png

  1. 终端中使用zip命令,将Payload打包为AntiHook.ipa👇
zip -ry AntiHook.ipa Payload

image.png

image.png

1.2 案例验证

接下来,我们来测试AntiHook项目的反HOOK防护是否有效?

  1. 搭建App项目,命名为HookDemo,作为重签名App

image.png 2. 同样的,添加target,创建Framework,命名为Hook,设置为动态库👇

image.png

  1. Hook中,创建Inject

image.png

  1. 打开Inject.m文件,写入以下代码👇
#import "Inject.h"
#import <objc/runtime.h>

@implementation Inject

+(void)load{
   Method sysClick1 = class_getInstanceMethod(objc_getClass("ViewController"), @selector(btnClick1:));
   Method myClick1 = class_getInstanceMethod(self, @selector(my_btnClick1));
   method_exchangeImplementations(sysClick1, myClick1);
}

-(void)my_btnClick1{
   NSLog(@"🍺🍺🍺🍺🍺");
}

@end

接下来的就是重签名(具体步骤可参考我之前的文章10-应用重签名

  1. appReSign.sh文件、yololib文件,拷贝到项目根目录👇

image.png

  1. 项目根目录中创建App目录,将之前打包的AntiHook.ipa文件,拷贝到App目录👇 image.png
  2. HookDemo中,选择Build Phases,在Run Script中输入👇

./appReSign.sh

image.png 8. 真机运行项目👇

image.png 点击按钮1👇

image.png

上图可见 👉 AntiHook项目中的防护代码生效,当注入的动态库,使用method_exchangeImplementations函数时,可以被检测出来,并让攻击方的HOOK失效

1.3 案例二

回到AntiHook工程中使用方法交换。

  1. 来到AntiHook项目,在HookManager中,打开AntiHookCode.h文件,写入以下代码👇
#import <Foundation/Foundation.h>
#import <objc/message.h>

CF_EXPORT void (*sys_exchange)(Method _Nonnull m1, Method _Nonnull m2);

@interface AntiHookCode : NSObject

@end
  1. 打开HookManager.h文件,写入以下代码👇
#import <Foundation/Foundation.h>
#import <HookMgr/AntiHookCode.h>
  1. AntiHook中,打开ViewController.m文件,写入以下代码👇
#import "ViewController.h"
#import <HookManager/HookManager.h>
#import <objc/runtime.h>

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];

   Method sysClick2 = class_getInstanceMethod(self.class, @selector(btnClick2:));
   Method myClick2 = class_getInstanceMethod(self.class, @selector(test));
   sys_exchange(sysClick2, myClick2);
}

-(void)test{
   NSLog(@"本工程HOOK,🍺🍺🍺🍺🍺");
}

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

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

@end
  1. 真机运行项目,点击按钮2👇

上图可见,在本工程中,使用sys_exchange函数HOOK成功。

弊端

上述防护方式,过于简单。它会在MachO字符串表中,存储method_exchangeImplementations的字符串。我们可以使用Hopper,打开HookManager.framework的MachO文件👇

image.png 所以,攻击方可以通过字符串,很容易定位到防护代码,然后对其破解 👉 跟我们上面的案例一样,在更早的执行时机,对防护的关键函数HOOK,让防护代码失效。因此上述方式,不建议使用。

高明的防护代码,应该让攻击方很难发现,尽量不留下痕迹。在程序运行过程中,不知不觉的将其干掉。

二、MokeyDev

 MonkeyDev是逆向开发中一个常用的工具,作者刘培庆,原iOS OpenDev升级版,一款非越狱插件开发集成神器!

主要包括四个模块👇

  1. Logos Tweak 👉 使用theos提供的logify.pl工具,将*.xm文件转成*.mm文件进行编译。集成了Cydia Substrate,可以使用MSHookMessageExMSHookFunctionHook指定地址和OC函数
  2. CaptainHook Tweak 👉 使用Captain Hook提供的头文件,进行OC函数Hook以及属性的获取
  3. Command-line Tool 👉 可以直接创建运行于越狱设备命令行工具
  4. MonkeyApp 👉 这是自动给第三方应用集成RevealCycript注入dylib的模块。支持调试dylib第三方应用,支持Pod给第三放应用集成SDK,只需要准备一个砸壳后的ipa或app文件即可

2.1 安装 MonkeyDev

2.1.1 theos安装

例如,Cydia Substrate就是theos中的工具。

  • 使用下面命令行进行安装👇
sudo git clone --recursive https://github.com/theos/theos.git /opt/theos
  • 查看安装结果

查看安装路径/opt/theos/👇

image.png

⚠️注意:安装过程中可能会出现错误,请参考👇 github.com/theos/theos…

2.1.2 Xcode配置

  • 指定Xcode
sudo xcode-select -s /Applications/Xcode.app
  • 安装MonkeyDev插件命令
sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-install)"

至此,这里就安装完成,完成后重启Xcode,在Xcode中会出现MonkeyDev对应的功能👇

image.png

⚠️注意:安装中,遇到xcode 12 Types.xcspec not found错误👇

image.png 可执行以下命令👇

sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/PrivatePlugIns/IDEOSXSupportCore.ideplugin/Contents/Resources /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications

2.2 案例演示

案例一:使用MokeyDev插件完成应用重签名

  1. 新建MonkeyApp项目,命名为MonKeyDemo👇

image.png 创建成功后,工程中默认包含App和注入的动态库👇

image.png 2. 将之前案例的AntiHook.ipa包,拷贝到MokeyDemo工程目录下的TargetApp文件夹中👇

image.png 3. 运行MokeyDemo项目,即可重签名安装成功👇

image.png

⚠️注意:报错ld: file not found: /usr/lib/libstdc++.dylib,请参考 github.com/longyoung/l…

可在项目路径下验证theos是否安装成功👇

image.png

案例二:代码注入

  1. MokeyDemoDylib中,点击MokeyDemoDylib.xm文件,选择Objective-C++ Source👇

image.png 2. 打开MokeyDemoDylib.xm文件,写入以下代码👇

#import <UIKit/UIKit.h>

%hook ViewController

- (void)btnClick1:(id)sender {
   NSLog(@"🍺🍺🍺🍺🍺");
}

%end

⚠️注意:使用Logos语法,详情可查看:官方文档

image.png 3. 真机运行项目,检测到HOOK代码👇

点击按钮1看输出👇

image.png 上图验证,HOOK成功,我们再利用%orig表示调用原始函数👇

- (void)btnClick1:(id)sender {
   NSLog(@"🍺🍺🍺🍺🍺");
  %orig;
}

image.png 至此,我们创建MokeyApp,在AntiHook项目中,可以检测到method_exchangeImplementations函数的使用,说明防护代码有效

案例三:method_setImplementation的防护

  1. 回到AntiHook项目,在HookManager.framework中,打开AntiHookCode.m文件,修改为以下代码👇
#import "AntiHookCode.h"
#import "fishhook.h"

@implementation AntiHookCode

+(void)load{
   
   struct rebinding rebExchange;
   rebExchange.name="method_exchangeImplementations";
   rebExchange.replacement=my_check;
   rebExchange.replaced=(void *)&sys_exchange;
   
   struct rebinding rebSetImp;
   rebSetImp.name="method_setImplementation";
   rebSetImp.replacement=my_check;
   rebSetImp.replaced=(void *)&sys_setImp;

   struct rebinding rebs[] = { rebExchange, rebSetImp };
   rebind_symbols(rebs, 2);
}

void (*sys_exchange)(Method _Nonnull m1, Method _Nonnull m2);
IMP _Nonnull (*sys_setImp)(Method _Nonnull m, IMP _Nonnull imp);

void my_check(Method _Nonnull m1, Method _Nonnull m2){
   NSLog(@"⚠️⚠️⚠️检测到HOOK⚠️⚠️⚠️");
}

@end
  1. 编译AntiHook项目,将AntiHook.app包copy到MokeyDemo下的TargetApp文件夹中👇

image.png 3. 真机运行MokeyDemo项目,检测到HOOK代码👇

image.png 点击按钮1👇

image.png 发现,输出的只有原始内容。因此,当method_setImplementation函数被Hook后,MokeyDevHOOK代码会失效

MokeyDev Hook的原理

接着,我们分析MokeyDevHOOK代码会失效的原因。

image.png

  • MokeyDev中,使用的是Substrate框架,这点我们可以在MokeyDemo的包里面验证👇

image.png

  • MokeyDemoDylib.xm中使用Logos语法写的注入代码👇

也是被libsubstrate.dylib库进行解析👇

image.png

image.png 而在MSHookMessageEx函数内部,是通过get/set方法,对OC方法进行HOOK的。详见MSHookMessageEx官网说明

所以,当method_setImplementation函数被Hook后,MokeyDevHOOK代码失效

总结

  • 反HOOK防护
    • 利用fishHook,修改Method Swizzle相关函数
      • 不推荐使用,防护痕迹过于明显。会导致其他三方库无法使用Method Swizzle
      • 防护代码需要最先被执行,否则先被HOOK,防护代码失效
    • 原始工程编写的Framework库,优先于注入库的加载,适合写防护代码
  • MokeyDev
    • 原iOS OpenDev的升级版,一款非越狱插件开发集成神器
    • 使用Substrate框架
    • 底层通过get/set方法,对OC方法进行HOOK