初探反HOOK防护
当
HOOK
第三方App
时,对于OC
方法,一般会使用Method Swizzle
。例如:使用系统提供的method_exchangeImplementations
函数,将两个方法进行交换自己开发的
App
,如果使用fishHook
,预先将method_exchangeImplementations
和自定义函数交换,攻击方在不知道函数名称的情况下,将很难进行HOOK
案例1:
使用
fishHook
交换method_exchangeImplementations
搭建
App
项目,命名为AntiHook
,作为反HOOK
案例防护代码写在
Framework
中更适宜,因为load
方法调用时机比主程序更快,并且可以在别人注入的动态库之前被执行添加
target
,创建Framework
,命名为HookMgr
,设置为动态库在
Framework
中,创建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
在
App
中,打开ViewController.m
文件,写入以下代码:#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
创建
Payload
目录编译项目,将编译后的
AntiHook.app
拷贝到Payload
目录下
使用
zip
命令,将Payload
打包为AntiHook.ipa
zip -ry AntiHook.ipa Payload
案例2:
测试
AntiHook
项目的反HOOK
防护是否有效搭建
App
项目,命名为HookDemo
,作为重签名App
添加
target
,创建Framework
,命名为Hook
,设置为动态库在
Framework
中,创建Inject
文件
打开
Inject.h
文件,写入以下代码:#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
将
appSign.sh
文件、yololib
文件,拷贝到项目根目录。然后在项目根目录,创建App
目录
将
案例1
中,最后打包的AntiHook.ipa
文件,拷贝到App
目录
在
HookDemo
中,选择Build Phases
,在Run Script
中输入:./appSign.sh
真机运行项目,检测到
HOOK
代码
点击
按钮1
,输出的还是原始内容
AntiHook
项目中的防护代码生效,当注入的动态库,使用method_exchangeImplementations
函数时,可以被检测出来,并让攻击方的HOOK
失效
案例3:
在本工程中使用方法交换
来到
AntiHook
项目在
HookMgr
中,打开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
打开
HookMgr.h
文件,写入以下代码:#import <Foundation/Foundation.h> #import <HookMgr/AntiHookCode.h>
在
AntiHook
中,打开ViewController.m
文件,写入以下代码:#import "ViewController.h" #import <HookMgr/HookMgr.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
成功。但对于攻击方,在不知道函数名称的情况下,则很难进行HOOK
上述防护方式,过于简单。它会在
MachO
的字符串表中,存储method_exchangeImplementations
的字符串使用
Hopper
,打开HookMgr
动态库的MachO
文件
攻击方通过字符串,很容易定位到防护代码,然后对其破解。例如,在更早的执行时机,对防护的关键函数
HOOK
,让防护代码失效所以上述方式,不建议使用。高明的防护代码,应该让攻击方很难发现,尽量不留下痕迹。在程序运行过程中,不知不觉的将其干掉
MokeyDev
MokeyDev
:作者刘培庆
,原iOSOpenDev
的升级版,一款非越狱插件开发集成神器主要包括四个模块:
Logos Tweak
:使用theos
提供的logify.pl
工具,将*.xm
文件转成*.mm
文件参与执行。集成了CydiaSubstrate
,可以使用MSHookMessageEx
和MSHookFunction
来Hook
指定地址和OC
函数CaptainHook Tweak
:使用CaptainHook
提供的头文件,进行OC
函数的Hook
以及属性的获取Command-line Tool
:可以直接创建运行于越狱设备的命令行工具MonkeyApp
:这是自动给第三方应用集成Reveal
、Cycript
和注入dylib
的模块。支持调试dylib
和第三方应用,支持Pod
给第三放应用集成SDK
,只需要准备一个砸壳后的ipa
或app
文件即可
安装
MokeyDev
,需要先安装theos
。详情可参见:官方文档 & 安装说明安装中,遇到
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
- 详情可查看:原文地址
案例1
搭建
MokeyApp
项目安装
MokeyDev
成功后,打开Xcode
,创建新项目选择
MokeyApp
创建成功,默认包含
App
和注入的动态库
案例2
使用
MokeyApp
实现应用重签名
MokeyApp
支持.ipa
和.app
格式将
ipa
包,拷贝到MokeyDemo
下的TargetApp
目录中
运行
MokeyDemo
项目,即可安装成功
案例3
使用
MokeyApp
实现代码注入在
MokeyDemoDylib
中,点击MokeyDemoDylib.xm
文件,选择Objective-C++ Source
打开
MokeyDemoDylib.xm
文件,写入以下代码:#import <UIKit/UIKit.h> %hook ViewController - (void)btnClick1:(id)sender { NSLog(@"🍺🍺🍺🍺🍺"); } %end
- 使用
Logos
语法,详情可查看:官方文档真机运行项目,检测到
HOOK
代码
点击
按钮1
,输出的是交换后的内容
HOOK
成功后,调用原始函数- (void)btnClick1:(id)sender { NSLog(@"🍺🍺🍺🍺🍺"); %orig; }
只需要
%orig
一句代码,即可调用
使用
MokeyApp
,在AntiHook
项目中,可以检测到method_exchangeImplementations
函数的使用,说明防护代码有效。至于方法还是被交换了,可能MokeyDev
使用的是get/set
方法
案例4
在
AntiHook
项目中,增加对method_setImplementation
的防护来到
AntiHookCode
项目在
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
编译项目,将
ipa
包,覆盖到MokeyDemo
下的TargetApp
目录中真机运行项目,检测到
HOOK
代码
点击
按钮1
,输出的还是原始内容
当
method_setImplementation
函数被交换,MokeyDev
的HOOK
代码同样失效
在
MokeyDev
中,使用的是Substrate
框架
在
MokeyDemoDylib.xm
中写入的Logos
语法,也是被libsubstrate.dylib
库进行解析
将
.xm
中的代码,解析到MokeyDemoDylib.mm
文件
在
MSHookMessageEx
函数内部,也是通过get/set
方法,对OC
方法进行HOOK
总结:
反
HOOK
防护
- 利用
fishHook
,修改Method Swizzle
相关函数- 不推荐使用,防护痕迹过于明显。会导致其他三方库无法使用
Method Swizzle
- 防护代码需要最先被执行,否则先被
HOOK
,防护代码失效- 原始工程编写的
Framework
库,优先于注入库的加载,适合写防护代码
MokeyDev
- 原
iOSOpenDev
的升级版,一款非越狱插件开发集成神器- 使用
Substrate
框架- 底层通过
get/set
方法,对OC
方法进行HOOK