知道了 HOOK 的原理,接下来我们就可以做一些代码的基本防护了。我们今天基本防护要达到的目的是:自己的 Method Swizzle 保留! 外面注入的 HOOK 失效!
我们的需求:
我要在内部 HOOK
btnClick1:
,在外部 HOOKbtnClick2:
。我要保证内部的 HOOK 生效,外部的 HOOK 不生效。
创建基本防护类
首先,创建一个工程,假设该工程名叫“基本防护”,我们可以在 ViewController
类里添加两个按钮点击方法:
-(IBAction)btnClick1:(id)sender{
NSLog(@"按钮1调用了!!");
}
-(IBAction)btnClick2:(id)sender{
NSLog(@"按钮2调用了!!");
}
接下来,我们要专门建一个 HOOK 管理的类 HookMgr
,导入"fishhook.h"
和
#import "hookMgr.h"
#import "fishhook.h"
#import
@implementation hookMgr
//专门HOOK
+(void)load
{
NSLog(@"hookMgr---load");
//内部用到的交换代码!
#我们可以把项目中所有用 runtime 去修改的代码都写到这个地方。
Method old = class_getInstanceMethod(objc_getClass("ViewController"), @selector(btnClick1:));
Method new = class_getInstanceMethod(self, @selector(click1Hook:));
method_exchangeImplementations(old, new);
//基本防护
# runtime 用于交换的方法有两个:
# 1. method_exchangeImplementations
# 2. method_getImplementation、method_setImplementation
# 为了反 HOOK,我们会对这两个方法都进行防护
struct rebinding bd;
bd.name = "method_exchangeImplementations";
bd.replacement = myExchang;
bd.replaced = (void *)&exchangeP;
// method_getImplementation
// method_setImplementation
struct rebinding bd1;
bd1.name = "method_getImplementation";
bd1.replacement = myExchang;
bd1.replaced = (void *)&getIMP;
struct rebinding bd2;
bd2.name = "method_setImplementation";
bd2.replacement = myExchang;
bd2.replaced = (void *)&setIMP;
struct rebinding rebindings[] = {bd,bd1,bd2};
rebind_symbols(rebindings, 3);
}
//保留原来的交换函数
IMP _Nonnull (*setIMP)(Method _Nonnull m, IMP _Nonnull imp);
IMP _Nonnull (*getIMP)(Method _Nonnull m);
void (*exchangeP)(Method _Nonnull m1, Method _Nonnull m2);
//新的函数
void myExchang(Method _Nonnull m1, Method _Nonnull m2){
NSLog(@"检测到了HOOK!!!");
//强制退出!
exit(1);
}
-(void)click1Hook:(id)sendr{
NSLog(@"原来APP的HOOK保留!!");
}
@end
上面代码,当点击btnClick1:
的时候,会执行交换后的click1Hook:
的方法。点击btnClick2:
的时候,执行的还是btnClick2:
的方法。
生成ipa包
上面就是所有基本防护的代码,我们把编译后的 app 包拿出来,新建一个Payload
文件夹,把 app 包放入该文件夹里。在终端执行zip -ry Hook.ipa Payload
命令,生成一个 ipa 包。
现在,我们自己的基本防护的项目已经写完了,假设上面的 ipa 包是别人在某个平台下载下来的。他现在要通过逆向来 HOOK 我们项目里的方法了。
创建一个类来HOOK我们的基本防护类
新建一个工程或MonkeyDev工程,假设该工程名叫“HOOK基本防护”,按照文章 iOS逆向工程 - 代码注入 实现 Framework 注入。在 Framework 类"Hook.m"
里写上如下 HOOK 代码:
#import "Hook.h"
#import
@implementation Hook
+(void)load
{
NSLog(@"hook---load");
//内部用到的交换代码!
Method old = class_getInstanceMethod(objc_getClass("ViewController"), @selector(btnClick2:));
Method new = class_getInstanceMethod(self, @selector(click2Hook:));
method_exchangeImplementations(old, new);
}
-(void)click2Hook:(id)send{
NSLog(@"交换成功!!");
}
@end
由于,我们的防护代码里已经把系统 runtime 的用于交换的方法都交换成了我们自己的方法myExchang
,所以别人如果用 runtime 用于交换的方法来 HOOK 我们的方法,就会执行myExchang
方法。程序就会强制退出!
遇到的问题
想法不错,但是如果此时别人 HOOK 我们的方法时,你会发现我们的方法被悲剧的 HOOK 住了。原因在于代码的执行顺序。程序会依次执行:
- “HOOK基本防护”工程的 Framework 类
Hook
- “基本防护”工程的 ViewController 类
ViewController
- “基本防护”工程的 AppDelegate 类
AppDelegate
- “基本防护”工程的 HookMgr 类
HookMgr
问题解决
可以看到,别人的 HOOK 都已经成功了,我们才做防护,这样还有何意义呢。所以,我们要做的就是改变代码的执行顺序。我们可以自己搞一个动态库antiHook
,这个动态库专门用来做防护。然后把fishhook
和HookMgr
两个类都拖到antiHook
文件夹底下。再次重复【生成ipa包】步骤。
此时,程序的执行顺序会变为:
- “基本防护”工程的 HookMgr 类
HookMgr
- “HOOK基本防护”工程的 Framework 类
Hook
- “基本防护”工程的 ViewController 类
ViewController
- “基本防护”工程的 AppDelegate 类
AppDelegate
可以看到,程序会先加载我们的防护代码,然后再加载三方注入的动态库,这样就做到了代码防护。
如果我们还想要在自己的基本防护的工程里 HOOK 该怎么办呢?
把整个工程里所有用到
1. method_exchangeImplementations
2. method_getImplementation、method_setImplementationexchangeP
方法的地方全部改为 exchangeP
方法就可以了。
最后,如果还想 HOOK 做过基本防护的工程该怎么办呢?
有一个思路,就是改变加载顺序。在你的防护动态库antiHook
调用之前先调用三方注入的动态库。也就是修改 Mach-O 文件。