前言
上篇文章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
。
- 新建App工程项目,命名为
AntiHook
,表示反HOOK案例
。在ViewController
页面中创建两个按钮btn1
和btn2
👇

并添加点击事件
代码👇
#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更快
,并且可以在别人
注入的动态库之前
被执行。
- 添加
target
,选择Framework
,命名为HookManager
,设置为动态库
👇

- 在
HookManager
中,创建AntiHookCode
类,并导入fishhook
👇

- 在
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
- 在项目的文件目录,创建
Payload
文件夹,build工程,将编译后的AntiHook.app
拷贝到Payload目录
下👇

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


1.2 案例验证
接下来,我们来测试AntiHook
项目的反HOOK防护
是否有效?
- 搭建App项目,命名为
HookDemo
,作为重签名App
。

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

- 在
Hook
中,创建Inject
类

- 打开
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-应用重签名)
- 将
appReSign.sh
文件、yololib
文件,拷贝到项目根目录
👇

-
项目根目录
中创建App目录
,将之前打包的AntiHook.ipa文件,拷贝到App目录👇

- 在
HookDemo
中,选择Build Phases
,在Run Script中输入👇
./appReSign.sh

- 真机运行项目👇

点击按钮1👇

上图可见 👉 AntiHook
项目中的防护代码生效
,当注入的动态库,使用method_exchangeImplementations
函数时,可以被检测
出来,并让攻击方的HOOK失效
。
1.3 案例二
回到
AntiHook工程中
使用方法交换。
- 来到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
- 打开
HookManager.h
文件,写入以下代码👇
#import <Foundation/Foundation.h>
#import <HookMgr/AntiHookCode.h>
- 在
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
- 真机
运行
项目,点击按钮2
👇

上图可见,在本工程
中,使用sys_exchange
函数HOOK
成功。
弊端
上述防护方式,过于简单。它会在MachO
的字符串表
中,存储method_exchangeImplementations
的字符串。我们可以使用Hopper
,打开HookManager.framework
的MachO文件👇

所以,攻击方可以通过字符串
,很容易定位
到防护代码,然后对其破解 👉 跟我们上面的案例一样,在更早
的执行时机,对防护的关键函数HOOK
,让防护
代码失效
。因此上述方式,不建议使用。
高明的防护代码,应该让攻击方
很难发现
,尽量不留下痕迹
。在程序运行过程中,不知不觉
的将其干掉。
二、MokeyDev
MonkeyDev是逆向开发中一个常用的工具,作者刘培庆,原iOS OpenDev
的升级版
,一款非越狱插件开发集成神器!
主要包括四个模块👇
-
Logos Tweak
👉 使用theos
提供的logify.pl
工具,将*.xm
文件转成*.mm
文件进行编译。集成了Cydia Substrate
,可以使用MSHookMessageEx
和MSHookFunction
来Hook
指定地址和OC函数
-
CaptainHook Tweak
👉 使用Captain
Hook提供的头文件,进行OC函数
的Hook
以及属性的获取
-
Command-line Tool
👉 可以直接创建运行于越狱设备
的命令行工具
-
MonkeyApp
👉 这是自动给第三方应用集成Reveal
、Cycript
和注入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/
👇

⚠️注意:安装过程中可能会出现错误,请参考👇
https://github.com/theos/theos/wiki/Installation-macOS
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
对应的功能👇

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

可执行以下命令👇
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
插件完成应用重签名
- 新建
MonkeyApp
项目,命名为MonKeyDemo
👇

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

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

- 运行
MokeyDemo
项目,即可重签名安装成功👇

⚠️注意:报错
ld: file not found: /usr/lib/libstdc++.dylib
,请参考
https://github.com/longyoung/libstdc.6.0.9-if-help-you-give-a-star
可在项目路径下验证theos
是否安装成功👇

案例二:代码注入
- 在
MokeyDemoDylib
中,点击MokeyDemoDylib.xm
文件,选择Objective-C++ Source
👇

- 打开
MokeyDemoDylib.xm
文件,写入以下代码👇
#import <UIKit/UIKit.h>
%hook ViewController
- (void)btnClick1:(id)sender {
NSLog(@"🍺🍺🍺🍺🍺");
}
%end
⚠️注意:使用
Logos
语法,详情可查看:官方文档
- 真机运行项目,检测到HOOK代码👇

点击按钮1
看输出👇

上图验证,HOOK
成功,我们再利用%orig
表示调用原始函数
👇
- (void)btnClick1:(id)sender {
NSLog(@"🍺🍺🍺🍺🍺");
%orig;
}

至此,我们创建MokeyApp
,在AntiHook
项目中,可以检测到method_exchangeImplementations
函数的使用,说明防护代码有效
。
案例三:
method_setImplementation
的防护
- 回到
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
- 编译
AntiHook
项目,将AntiHook.app
包copy到MokeyDemo
下的TargetApp
文件夹中👇

- 真机运行
MokeyDemo
项目,检测到HOOK代码👇

点击按钮1
👇

发现,输出的只有原始内容
。因此,当method_setImplementation
函数被Hook后,MokeyDev
的HOOK
代码会失效
!
MokeyDev Hook的原理
接着,我们分析MokeyDev
的HOOK
代码会失效
的原因。
- 在
MokeyDev
中,使用的是Substrate框架
,这点我们可以在MokeyDemo
的包里面验证👇

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

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


而在MSHookMessageEx
函数内部,是通过get/set
方法,对OC方法
进行HOOK
的。详见MSHookMessageEx官网说明。
所以,当method_setImplementation
函数被Hook后,MokeyDev
的HOOK代码
会失效
!
总结
- 反HOOK防护
- 利用
fishHook
,修改Method Swizzle
相关函数-
不推荐
使用,防护痕迹过于明显。会导致其他三方库无法使用Method Swizzle - 防护代码需要
最先
被执行,否则先被HOOK,防护代码失效
-
- 原始工程编写的
Framework库
,优先于注入库的加载,适合写防护代码
- 利用
- MokeyDev
- 原iOS
OpenDev
的升级版,一款非越狱插件开发集成神器 - 使用
Substrate
框架 - 底层通过
get/set
方法,对OC方法
进行HOOK
- 原iOS
网友评论