iOS安全之三攻三防
Qinz
互联网世界每分钟都在上演黑客攻击,由此导致的财产损失不计其数。金融行业在安全方面的重视不断加深,而传统互联网行业在安全方面并没有足够重视,这样导致开发的APP在逆向开发人员面前等同于裸奔,甚至有些小厂前后端在账号密码解决上采取明文传送,本地存储,这等同于将账号密码直接暴露无疑。当然即便采用加密传送,逆向APP后仍然可以获取到账号密码,让你在神不知鬼不觉的情况下将账号密码发送到了黑客邮箱,所以攻防终究是一个相互博弈的过程。本文主要分析常见的几种攻击和防护手段,通过攻击你可以看到你的APP是如何被一步一步被攻破的。有了攻击,我们针对相应的攻击就是见招拆招了。
一、攻击原理
- 从APPStore下载正式版本版本应用,进行一键砸壳,绝大部分应用均可以脱壳成功。
- 使用脚本或者第三方工具MonkeyDev对应用实现重签名。
- 利用动态调试(LLDB,Cycript,Reveal)和静态分析(反汇编),找到关键函数进行破解。
- Theos编写插件,让使用更加方便。
二、攻守第一回合
1. 第一攻武器:代码注入+method_exchangeImplementations
在shell脚本实现iOS包重签名及代码注入的最后,我们成功使用method_exchange截获微信点击按钮,代码如下:
+(void)load { Method oldMethod = class_getInstanceMethod(objc_getClass("WCAccountLoginControlLogic"), @selector(onFirstViewRegester)); Method newMethod = class_getInstanceMethod(self, @selector(test)); method_exchangeImplementations(oldMethod, newMethod); } -(void)test{ NSLog(@"----截获到微信注册按钮点击------");}
2. 第一防护盾:framwork+fishHook
关于为什么使用framwork而不是直接在代码中创立一个类,并在类的load方法中编写防护代码,起因是自己创立framwork的加载要早于代码注入的framwork,代码注入的framwork的执行要早于自己类load的加载,具体原理请看dyld加载应用启动原理详解。防护代码如下:
防exchange
注意:当我们检查到hook代码时,比较好的解决方式是将该手机的UDID,账号等信息发送给后端服务器,让后端服务器进行封号禁设施解决,而不是直接exit(0)让程序强制退出,由于这样的好处是让黑客很难定位。
三、攻守第二回合
1. 第二攻武器:MonkeyDev
MonkeyDev可以帮助我们更加方便的实现代码重签名和hook,底层是使用了方法交换的SET和GET方法进行hook,关于MoneyDev的使用在逆向iOS系统桌面实现一键清空徽标有讲。同样以截获微信注册按钮为例,hook代码示例如下:
%hook WCAccountLoginControlLogic- (void)onFirstViewRegester:(id)arg{ NSLog(@"---hook-----");}%end
2. 第二防护盾:仍然framwork+fishHook
+(void)load{ //setIMP struct rebinding gt; gt.name = "method_getImplementation"; gt.replacement = my_getIMP; gt.replaced = (void *)&getIMP; //getIMP struct rebinding st; st.name = "method_setImplementation"; st.replacement = my_setIMP; st.replaced = (void *)&setIMP; struct rebinding rebs[2] = {gt,st}; rebind_symbols(rebs, 2); }//保存原来的交换函数IMP (*getIMP)(Method _Nonnull m);IMP (*setIMP)(Method _Nonnull m, IMP _Nonnull imp);IMP my_getIMP(Method _Nonnull m){ NSLog(@"??----检查到了HOOk-----??"); return nil;}IMP my_setIMP(Method _Nonnull m, IMP _Nonnull imp){ NSLog(@"??----检查到了HOOk-----??"); return nil;}
三、攻守第三回合
上面的两次攻击都是通过代码注入来实现hook目的,我们能不能防止第三方代码进行注入呢?答案当然是可以,接下来我们来防止第三方代码注入。
1. 第三防护盾:在编译设置阶段添加字段”-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null”,如下图:
防止第三方代码注入
1.1 添加该字段后在MachO文件就会添加_RESTRICT,__restrict段,如下图:
image.png
1.2 为什么添加这个字段即可以了呢?这里我们就要回归到dyld的源码了,在dyld加载过程中有一个函数hasRestrictedSegment就是用来判断能否存在__RESTRICT,__RESTRICT中能否是__restrict名称,假如是,则会禁止加载第三方注入的库文件,源码如下:
#if __MAC_OS_X_VERSION_MIN_REQUIREDstatic bool hasRestrictedSegment(const macho_header* mh){ const uint32_t cmd_count = mh->ncmds; const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); const struct load_command* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_SEGMENT_COMMAND: { const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; //dyld::log("seg name: %s\n", seg->segname); if (strcmp(seg->segname, "__RESTRICT") == 0) { const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { if (strcmp(sect->sectname, "__restrict") == 0) return true; } } } break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } return false;}#endif
2. 第三攻击武器:直接修改MachO二进制文件
通过Synalyze It!工具更改MachO二进制文件字段,而后重新签名打包就可破坏该防护过程:
修改二进制
3. 第三防护2级护盾:代码过滤,添加白名单。
- 3.1 既然禁止第三方注入代码都很容易被攻破,接下来我们就从代码入手,过滤第三方库注入库,添加白名单,代码如下:
@implementation ViewController+(void)load{ const struct mach_header_64 * header = _dyld_get_image_header(0); if (hasRestrictedSegment(header)) { NSLog(@"---- 防止状态 ------"); }else{ NSLog(@"--- 防护字段被修改了 -----"); } }static bool hasRestrictedSegment(const struct macho_header* mh){ const uint32_t cmd_count = mh->ncmds; const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(struct macho_header)); const struct load_command* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_SEGMENT_COMMAND: { const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; printf("seg name: %s\n", seg->segname); if (strcmp(seg->segname, "__RESTRICT") == 0) { const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { if (strcmp(sect->sectname, "__restrict") == 0) return true; } } } break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } return false;}
- 3.2 原理就是使用系统的函数帮我们检测自己设定的__RESTRICT能否被更改,假如被更改说明我们被Hook了,接下来在被hook的字段中添加自己的解决逻辑就可。
总结:对最后一个防护代码也很容易进行攻击,比方找到hasRestrictedSegment函数,让其直接返回YES。所以建议将该函数进行封装,尽量不要使用Bool作为返回值。综上: 攻和守原本就是一个博弈的过程,没有绝对安全的城墙。
最后附上过滤白名单源码下载,直接拖入工程即用,达到较好的代码防护目的。假如觉得好用请给一个Star。
我是Qinz,希望我的文章对你有帮助。
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » iOS安全之三攻三防