市面上的App,做过防护的只有那些出名的,大企业的App;通常的App除了登录进行了普通的加密,是一点防护都没有的,今天我们就学习一下最基本的防护。
我们知道,iOS中hook的基本原理是两个:
- OC的动态性,利用 Method Swizzling 进行hook;
- C语言在iOS中的动态性,利用符号重绑定进行hook。
我们这里只介绍第一种,防护OC的方法。这一层只能防护住一些简单的逆向工程师,但是这是也是明白防护的第一步,后面会逐步增加防护的知识。
我们这里进行防护都是利用fishHook完成。
直接防护 method_exchangeImplementations 方法
可能进行防护,我们上来就是想到防护method swizzling 的几个方法,这里通过
method_exchangeImplementations 进行举例。
一、首先书写防护代码:
二、将这个工程打包,新建工程进行破解:
(此种涉及到重签名和代码注入的知识,不明白的新手可以查看我的文章:iOS开发|Framework注入,让别人的软件执行我们的代码)
因为我们知道原来工程的代码,这里我们就不分析了,直接使用
method_exchangeImplementations 进行hook ViewController 中的 test 方法。
天啦!我们明明在防护代码中把
method_exchangeImplementations 方法已经进行了防护,这里注入Framework什么还是hook到了呢?
难道是我们自己写的防护代码出错了?小白一般都那么不自信,我也会这样怀疑自己,来,我们自测一下。
测试:来到防护代码,我们直接在本工程中进行进行hook:
很明显,打印了“我防护了,你别乱来!”,说明我们这里的防hook没有问题。
但是当我们使用Framework注入的方式进行hook的时候,又不起作用了,这是为什么?
在 Framework 进行防护
其实标题已经告诉了我们,应该在Framework中进行hook。原因是什么?
执行顺序
从我们之前分析hook的文章中知道,如果想要更改代码执行的顺序,肯定要在执行之前拿到这个方法。我们这里是通过fishHook进行的hook防护,它其实也是在执行之前,将我们的
method_exchangeImplementations 函数进行了交换,换成了
my_method_exchangeImplementations。
这里实验结果说明:
method_exchangeImplementations 没有被交换成
my_method_exchangeImplementations,至少在破解代码之前没有进行交换。
为了证明这个结论,那么我们来调试一下:在防护代码中创建也创建一个Framework,看执行顺序。
果然,Framework是先执行的。
这样就能够解释了,因为Framework中的代码已经执行了,所以他的hook已经生效了;当执行到防护代码ViewController中的时候,早已经hook完毕了,所以,防护没有起到作用。
这里的原因,其实在DYLD的加载流程中已经解释过了(可以去看我关于DYLD的分析)。DYLD会先加载插入的Framework,然后才是慢慢进入主程序,执行我们所写的代码。
DYLD的加载过程:配置环境——加载共享缓存——初始化MechO——加载插入动态库——初始化主程序——objc_init给DYLD一个回调——load_images——load。
完全防护
上面一步一步我们进行了防护工作,发现插入Framework的执行是在我们代码之前,于是我们想要进行安全防护,肯定是需要将防护代码写在别人恶意执行之前才行。
怎么做呢?
将防护写在Framework中。
一、再次来到防护代码中,将代码转移到Framework中去。
我同样在本工程中进行了检测,发现是可以起到作用的,成功的拦截到了hook。
二、第二步我们将这个App包交给破解代码,然后进行破解。
- 眼镜都不要眨一下,看上面这个图。InjectCode中是我的hook代码,也是在Framework中做的,而且执行了,说明hook的代码走了。
- 那么看下面的log,是“我防护了,你别乱来!”。
说明什么,我们成功了!
没错,hook防护的代码需要写在Framework中。
补充
我们知道,除了方法
method_exchangeImplementations 可以进行hook,还有 class_replaceMethod ,method_setImplementation 和 method_getImplementation 需要进行防护。
首先测试一下,除了
method_exchangeImplementations,其它方式是否可以越过防护。
直接在防护代码中进行测试:
1、class_replaceMethod 方法越过了防护。
2、method_setImplementation 也同样越过了防护。
那么我们将代码完善一下:
+ (void)load { struct rebinding exchange; exchange.name = "method_exchangeImplementations"; exchange.replacement = my_method_exchangeImplementations; exchange.replaced = (void *)&old_method_exchangeImplementations; struct rebinding replace; replace.name = "class_replaceMethod"; replace.replacement = my_class_replaceMethod; replace.replaced = (void *)&old_class_replaceMethod; struct rebinding set; set.name = "method_setImplementation"; set.replacement = my_method_setImplementation; set.replaced = (void *)&old_method_setImplementation; struct rebinding get; get.name = "method_getImplementation"; get.replacement = my_method_getImplementation; get.replaced = (void *)&old_method_getImplementation; struct rebinding rebinds[4] = {exchange, replace, set, get}; rebind_symbols(rebinds, 4); NSLog(@"%s", __func__); } // 定义一个函数指针,用来保留系统的exchange函数 void (*old_method_exchangeImplementations)(Method _Nonnull m1, Method _Nonnull m2); // 定义一个函数指针,用来保留系统的replace函数 IMP _Nullable (*old_class_replaceMethod)(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types); // 定义一个函数指针,用来保留系统的set函数 IMP _Nonnull (*old_method_setImplementation)(Method _Nonnull m, IMP _Nonnull imp); // 定义一个函数指针,用来保留系统的get函数 IMP _Nonnull (*old_method_getImplementation)(Method _Nonnull m); // 我自定义的exchange方法 void my_method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) { NSLog(@"我防护了,你别乱来!"); } // 我自定义的replace方法 IMP _Nullable my_class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types) { NSLog(@"我防护了,你别乱来!"); return nil; } // 我自定义的set方法 IMP _Nonnull my_method_setImplementation(Method _Nonnull m, IMP _Nonnull imp) { NSLog(@"我防护了,你别乱来!"); return nil; } // 我自定义的get方法 IMP _Nonnull my_method_getImplementation(Method _Nonnull m) { NSLog(@"我防护了,你别乱来!"); return nil; }
完善过后,我们将防护App的包装到hook的当中进行测试。
这是hook App中的代码:
+ (void)load { Method oldM = class_getInstanceMethod(NSClassFromString(@"ViewController"), @selector(test)); method_exchangeImplementations(oldM, class_getInstanceMethod([self class], @selector(hookExchange))); class_replaceMethod(NSClassFromString(@"ViewController"), @selector(test), class_getMethodImplementation(self.class, @selector(hookReplace)), "v@:"); method_getImplementation(oldM); method_setImplementation(oldM, class_getMethodImplementation(self.class, @selector(myTest))); } - (void)hookExchange { NSLog(@"hookExchange 到了 test"); } - (void)hookReplace { NSLog(@"hookReplace 到了 test"); } - (void)hookSet { NSLog(@"hookSet 到了 test"); }
运行后的效果:每一个方法都hook住了。
以上就是我们利用fishHook完成的防护hook方案。
总结:
- 防护应该写在注入的Framework中,因为DYLD是先加载的插入动态库,只有在修改之前限制才有作用;
- 防护应该多种方案都想到,这样才会周全,四种OC的 Method Swizzling 都应该考虑到。
面试题持续更新记得关注我哦!
不同的圈子就有不同的学习方式 ;
(qq群搜索):651612063 群密码:111 进群文件可以直接获取大厂面试题