被问到一个标题:假如你做SDK给外部使用,怎样包管提供的函数不被外部hook?
我们知道,iOS中的hook根本原理有两个:
1.OC的动态性,使用 Method Swizzling 举行hook;2.C语言在iOS中的动态性,使用符号重绑定举行hook。以是,我们可以使用OC的Method Swizzling来hook方法,有以下三种方法:
1、方法互换OBJC_EXPORT voidmethod_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0); 2、更换方法OBJC_EXPORT IMP _Nullableclass_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0); 3、setIMP & getIMPOBJC_EXPORT IMP _Nonnullmethod_setImplementation(Method _Nonnull m, IMP _Nonnull imp) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0); OBJC_EXPORT IMP _Nullableclass_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);创建工程,在StoryBord中画两个按钮,然后在ViewController中实现对应的方法:
#import "ViewController.h"@interface ViewController ()@end@implementation ViewController+(void)load{ NSLog(@"ViewController--Load");}- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. }- (IBAction)method1id)sender { NSLog(@"这是方法一"); }- (IBAction)method2id)sender { NSLog(@"这是方法二");}@end接着新创建HookClass类,先实现hook,以class_getInstanceMethod为例:
+ (void)load { //互换方法一 Method old1 = class_getInstanceMethod(objc_getClass("ViewController"), @selector(method1); Method new1 = class_getInstanceMethod(self, @selector(click1Hook1); method_exchangeImplementations(old1, new1); //互换方法二 Method old2 = class_getInstanceMethod(objc_getClass("ViewController"), @selector(method2); Method new2 = class_getInstanceMethod(self, @selector(click1Hook2); method_exchangeImplementations(old2, new2);}实现必要的互换的两个方法
- (void)click1Hook1id)sender { NSLog(@"点击了hook1");}- (void)click1Hook2id)sender { NSLog(@"点击了hook2");}运行代码,点击两个按钮,发现其实现了方法互换:
2022-12-09 18:30:08.954801+0800 HookTest[80560:759752] 点击了hook12022-12-09 18:30:09.892046+0800 HookTest[80560:759752] 点击了hook2那么标题来了,假如我们向外部提供的方法被hook了,造成预期外的效果,那么该怎样防止呢?由于dyld加载步调时候,对于外部符号(比方体系函数)是lazybind加载的,编译的时候并不是绑定真实的地点,而是在运行时动态绑定的,以是可以使用finishhook来hook体系方法。如我们可以先把method_exchangeImplementations先换成我们本身的函数,外部再使用method_exchangeImplementations来互换方法时就失效啦。同理,class_replaceMethod及method_setImplementation & class_getMethodImplementation也是一样。必要留意的是,dyld在加载步调的时候,会先加载动态库,而且是按照MachO文件存储的顺序加载(也就是Xcode链接库的顺序),以是我们要把hook代码放到动态库最前面。完备代码如下:
#import "HookClass.h"#import <objc/runtime.h>#import "fishhook.h"@implementation HookClass//生存原来的互换函数void (* exchangeProtect)(Method _Nonnull m1, Method _Nonnull m2);IMP _Nonnull (* setIMP)(Method _Nonnull m, IMP _Nonnull imp);IMP _Nonnull (* getIMP)(Method _Nonnull m);//新的函数void protectExchange(Method _Nonnull m1, Method _Nonnull m2){ NSLog(@"检测到了hook");}+ (void)load { Method old1 = class_getInstanceMethod(objc_getClass("ViewController"), @selector(method1); Method new1 = class_getInstanceMethod(self, @selector(click1Hook1); method_exchangeImplementations(old1, new1); Method old2 = class_getInstanceMethod(objc_getClass("ViewController"), @selector(method2); Method new2 = class_getInstanceMethod(self, @selector(click1Hook2); method_exchangeImplementations(old2, new2); //在互换代码之前,把全部的runtime代码写完 //防护 struct rebinding bd; bd.name = "method_exchangeImplementations"; bd.replacement=protectExchange; bd.replaced=(void *)&exchangeProtect; struct rebinding bd1; bd1.name = "class_replaceMethod"; bd1.replacement=protectExchange; bd1.replaced=(void *)&exchangeProtect; struct rebinding bd2; bd2.name = "method_setImplementation"; bd2.replacement=protectExchange; bd2.replaced=(void *)&setIMP; struct rebinding bd3; bd3.name = "method_getImplementation"; bd3.replacement=protectExchange; bd3.replaced=(void *)&getIMP; struct rebinding rebindings[]={bd,bd1,bd2,bd3}; rebind_symbols(rebindings, 4); }- (void)click1Hook1id)sender { NSLog(@"点击了hook1"); }- (void)click1Hook2id)sender { NSLog(@"点击了hook2"); }@end再建个HookClass+protect分类来验证,假如外部想hook我们的方法,就会有检测到了hook的提示,代码如下:
#import "HookClass+protect.h"#import <objc/runtime.h>@implementation HookClass (protect)+ (void)load { Method oldM = class_getInstanceMethod([self class], @selector(test)); method_exchangeImplementations(oldM, class_getInstanceMethod([self class], @selector(hookExchange))); class_replaceMethod([self class], @selector(test), class_getMethodImplementation(self.class, @selector(hookReplace)), "v@:"); method_getImplementation(oldM); method_setImplementation(oldM, class_getMethodImplementation(self.class, @selector(myTest)));}- (void)test { NSLog(@"test方法");}- (void)hookExchange { NSLog(@"hookExchange 到了 test");}- (void)hookReplace { NSLog(@"hookReplace 到了 test");}- (void)hookSet { NSLog(@"hookSet 到了 test");}@end运行代码,可以看到,我们的hook防护起到了作用:
2022-12-09 18:29:59.395944+0800 HookTest[80560:759752] 检测到了hook2022-12-09 18:29:59.396107+0800 HookTest[80560:759752] 检测到了hook2022-12-09 18:29:59.396201+0800 HookTest[80560:759752] 检测到了hook2022-12-09 18:29:59.396279+0800 HookTest[80560:759752] 检测到了hook相关资料
iOS安全防护
fishhook 使用及其 hook 原理 |