目录
- 一、为什么要加固
- 二、加固的几种方法
- 三、基于念茜如何加固
- 四、其他人的加固方法
前言
APP加固之代码混淆
一、为什么要加固
1、iOS应用安全防护框架技术分析
参考文章:iOS应用安全防护框架技术分析
摘取的有用内容如下:
攻易防难,唯有缜密、多层的防护网络才能可靠的保护我们iOS应用程序的安全。那么,一个完善的iOS应用安全防护框架都要写哪些东西呢?首先,先梳理一下常见的逆向及攻击工具。
1.iOS应用逆向常用工具
Reveal Cycript Class-dump Keychain-Dumper gdb iNalyzer introspy Fishhook removePIE IDA pro or Hopper snoop-it iDB Charles SSL Kill Switch
2.裸奔app的安全隐患
一部越狱的iOS设备,外加上述的逆向工具,给裸奔的iOS应用程序带来哪些威胁呢?任意读写文件系统数据 HTTP(S)实时被监测 重新打包ipa 暴露的函数符号 未加密的静态字符 篡改程序逻辑控制流 拦截系统框架API 逆向加密逻辑 跟踪函数调用过程(objc_msgSend) 可见视图的具体实现 伪造设备标识 可用的URL schemes runtime任意方法调用 ……
3.iOS应用安全防护开源工具
ios-class-guard
ios-class-guard是对抗class-dump的利器,作用是将ObjC类名方法名等重命名为难以理解的字符。
iOS应用安全防护框架概述
针对上述安全隐患,我们的iOS应用安全防护框架需实现的任务大致如下:
防护
ObjC类名方法名等重命名为难以理解的字符
加密静态字符串运行时解密
混淆代码使其难于反汇编
本地存储文件防篡改4.检测
调试状态检测 越狱环境检测 ObjC的Swizzle检测 任意函数的hook检测 指定区域或数据段的校验和检测 自修复 自修复被篡改的数据和代码段
此外,还需要多层的防护,通过高层保护低层的方式来保证整个防护机制不失效。
二、加固的几种方法
1、苹果机审原理及我们可以采用的混淆方法
参考文章:苹果机审原理及我们可以采用的混淆方法
摘取的有用内容如下:
本文只适用于机审 4.3,区分机审和人审请找你们后端或运维看看审核期间有没有IP登陆。
1、机审原理
我们虽然无法得知苹果实际的机审原理,但从程序员的角度还是能分析出一些东西的。
1.1 首先OC和C++代码编译出的二进制文件,有点经验和反编译过的应该都知道:
- 删注释神马的是没用的,因为注释是不会被编译进包里
- 改类名是靠谱的,因为反编译出来能看到类名,改掉它显然是会造成包不一样
- 增改函数也是靠谱的,同样是因为反编译能看到
- 改文件夹或者文件名应该是不太靠谱的,编译的时候会根据路径来引用查找,编译之后应该是根据在包里的相对内存地址来查找类和函数,跟你编译时的文件名称和路径关系应该不大。不过为了方便和代码的统一,更换时可以顺便换了。
1.2 然后是一些资源文件如图片、音效
路径和文件名相当可能或者绝对是有用的,可惜修改代价有点高
文件的md5值以程序员的角度来看才是真正区分文件是否一致的标准,我们有理由相信我们的苹果同行也用了这个来判断是否重复。所以一些修改md5值的操作如添加空行、注释、额外字节,应该也被考虑加上。1.3 最后是相似的判定,应该是相似率高于某个值才认为你跟其他的重复了,针对像改资源文件名这种代价太高的可能暂不考虑的操作,我们只能添加垃圾文件提高总值来降低重复率了。
2、混淆方法
2.1 修改类名文件名
先说下手动操作,无非是在xcode上修改文件名类名,然后在可能引用的地方替换类名和文件名(header),要注意的地方是替换的时候要选中匹配大小写;然后是文件夹名称跟文件名一样的时候,可能文件夹名称也要跟着改名,否则替换之后路径引用可能找不到。
如果是要脚本批量操作,那最好先对工程整理下,确保以下几点能让脚本写的更简单更可靠:
- 要修改的类和文件最好都放到一个文件夹下,万一搞出事不用东找西找,备份和回滚也简单一点
- 类名和文件名尽量带上前缀,这样修改只替换前缀即可,也不太会跟函数名、变量名什么的重复
- 最好过一遍把不能修改类名的列出来,比如外面太多地方调用的、第三方的类库。在写脚本的时候把他们排除在外
脚本的话就是遍历文件,判断前缀、是否排除在外,修改文件名类名,在其他文件中查找替换。用python的话应该不是什么大问题。一个小技巧是改完后可以替换一下xxx.xcodeproj/project.pbxproj里的相应字符串,这样xcode打开工程的时候就不用手动再添加进来。
2.2 添加垃圾函数
OC头文件的声明必然是在@interface @end之间,实现是在@implementation @end之间,C++的大部分应该是以}结尾,直接在相应的地方插入垃圾函数,模板可以直接写个HelloWorld输出个随机字符串。在这一步随机名称是个坑,可以去网上找下常见英文单词,格式化后把太短的、太长的、看着不爽的,最重要的是语言的关键字如break,false,if之类的删掉
2.3添加垃圾类
根据我们猜测的路径应该是不影响打包的,所以我们可以简单的把垃圾类文件都放到同一个文件夹下方便管理,写好2.2后这一步应该就是顺手的事情了 。我不太确定的是如果外部不引用这些垃圾类,编译之后它们会不会因为太独立而被检测认为是垃圾代码。所以保险起见,我实现的时候写了一个单独的头文件include了所有这些生成的垃圾类,然后在外部include了这个单独的头文件
2.4修改资源md5值
资源文件有很多类型,通常来说文本文件添加随机数量的空格或空行应该就可以了。图片的话常见的png和jpg都是有固定的结尾字节块的,png是00 00 00 00 49 45 4E 44 AE 42 60 82,jpg是ffd9,用16进制查看工具打开图片应该能注意到这个规律,也可以参考下常见图片文件格式简析。在结尾字节块添加的内容是不会影响图片本身显示的,我们可以利用这个来改变图片的md5值。音效应该也有相应的格式,期待大佬科普下!
2.5创建资源垃圾文件
跟2.3类似,不过这个最好也随机下创建文件夹显得真实点,一些文本文件是什么格式都有各自定义,png和jpg的话就随机写任意长度的任意字符,最好结尾加上相应的结尾字节块,防止2.5后又执行2.4导致出错。
3、其他事项
上面的基本都能脚本自动化执行,完了后工程名最好也在xcode改下;info.plist会被打包进ipa,最好也多加几个字段上去;target能改也改下方便识别;scheme关联到导出的ipa文件名,不是特别麻烦也顺手改掉;包名、启动页、图标应该都是基本的东西不会被忽略。
这些改完之后,希望你自己也不认识它了!
相关工具会慢慢整理到iosMixTools,欢迎大家讨论和提Issues。
iosMixTools中的好文章实力比拼!四款iOS加固工具评测
2、iOS安全–浅谈关于iOS加固的几种方法
参考文章:
iOS安全–浅谈关于iOS加固的几种方法
作者:Alone_Monkey
书籍:《iOS应用逆向与安全》
新浪微博地址:点击进入
博客地址:点击进入
摘取的内容如下:
关于iOS安全这方面呢,能做的安全保护确实要比Android平台下面能做的少很多。
只要你的手机没越狱,基本上来说是比较安全的,当然如果你的手机越狱了,可能也会相应的产生一些安全方面的问题。就比如我在前面几篇博客里面所介绍的一些IOS逆向分析,动态分析以及破解方法。
但是尽管这样,对IOS保护这方面来说,需求还不是很乏,所有基于IOS平台的加固产品也不是很多,目前看到几种关于IOS加固的产品也有做的比较好的。
最开始关于爱加密首创的iOS加密,http://www.ijiami.cn/ios 个人感觉这只是一个噱头而已,因为没有看到具体的工具以及加固应用,所以也不知道它的效果怎么样了。
后来在看雪上面看到一个http://www.safengine.com/mobile/ 有关于IOS加密的工具,但是感觉用起来太麻烦了,而且让产品方也不是很放心,要替换xcode默认的编译器。
不久前看到偶然看到一个白盒加密的应用http://kiwisec.com/ 也下下来试用了一下,感觉要比上面两个从使用上方面了许多,而且考虑的东西也是比较多的。
好了,看了别人做的一些工具,这里大概说下都有哪些加固方法以及大概的实现吧,本人也是刚接触这个方面不就,可能分析的深度没有那么深入,大家就随便听听吧。
现在的加固工具总的来说都是从以下几个方面来做的:
一、字符串加密:
现状:对于字符串来说,程序里面的明文字符串给静态分析提供了极大的帮助,比如说根据界面特殊字符串提示信息,从而定义到程序代码块,或者获取程序使用的一些网络接口等等。
加固:对程序中使用到字符串的地方,首先获取到使用到的字符串,当然要注意哪些是能加密,哪些不能加密的,然后对字符串进行加密,并保存加密后的数据,再在使用字符串的地方插入解密算法,这样就很好的保护了明文字符串。
二、类名方法名混淆
现状:目前市面上的IOS应用基本上是没有使用类名方法名混淆的,所以只要我们使用class-dump把应用的类和方法定义dump下来,然后根据方法名就能够判断很多程序的处理函数是在哪。从而进行hook等操作。
加固:对于程序中的类名方法名,自己产生一个随机的字符串来替换这些定义的类名和方法名,但是不是所有类名,方法名都能替换的,要过滤到系统有关的函数以及类,可以参考下开源项目:https://github.com/Polidea/ios-class-guard
三、程序代码混淆
现状:目前的iOS应用找到可执行文件然后拖到Hopper Disassembler或者IDA里面程序的逻辑基本一目了然。
加固:可以基于Xcode使用的编译器clang,然后在中间层也就是IR实现自己的一些混淆处理,比如加入一些无用的逻辑块啊,代码块啊,以及加入各种跳转但是又不影响程序原有的逻辑。可以参考下开源项目:https://github.com/obfuscator-llvm/obfuscator/ 当然开源项目中也是存在一些问题的,还需自己再去做一些优化工作。
四、加入安全SDK
现状:目前大多数IOS应用对于简单的反调试功能都没有,更别说注入检测,以及其它的一些检测了。
加固:加入SDK,包括多处调试检测,注入检测,越狱检测,关键代码加密,防篡改等等功能。并提供接口给开发者处理检测结果。
当然除了这些外,还有很多方面可以做加固保护的,相信大家会慢慢增加对IOS应用安全的意识,保护好自己的APP。
三、基于念茜如何加固
参考文章列表:
1、Objective-C代码混淆
参考文章:iOS安全攻防(二十三):Objective-C代码混淆
作者:念茜
书籍:无
新浪微博地址:无
博客地址:点击进入
以下只摘取部分内容,其他内容请自行前往文章博客地址查看。
摘取的内容如下:
混淆的时机:编译前
混淆的方法:
方法名混淆其实就是字符串替换,有2个方法可以,一个是#define,一个是利用tops。利用#define的方法有一个好处,就是可以把混淆结果合并在一个.h中,在工程Prefix.pch的最前面#import这个.h。不导入也可以编译、导入则实现混淆。
单段的selector,如func: ,可以通过#define func 来实现字符串替换。 多段的selector,如a:b:c: ,可以通过分别#define a 、b、c 来实现字符串替换。
操作方法的自我实践:
1、命名个`codeObfuscation.h`文件,将其添加到预编译文件`PrefixHeader.pch`中。
2、`codeObfuscation.h`文件中的内容为需要混淆的类名或方法名的宏
基本上只要这两部即可。
3、类名混淆,自己在最后的`codeObfuscation.h`文件中补充相应的宏即可。(附:由于这种方式还得想类的宏的值定为多少,所以如果是使用念茜脚本的话,我们一般在将含特征量的方法抽取到`fun.list`和转到`codeObfuscation.h`后,将从脚本中的抽取代码即grep那部分先注释掉,然后将我们要定义的其他类或者其他方法补充进`fun.list`中,最后再编译执行一遍脚本即可。)
4、全局混淆,那就使用脚本吧。把念茜脚本中的做特定的限制的部分,即`|sed -n "/^lcof_/p"`给删除掉即可。不过这个我没试验过。
附:经试验本方法对block混淆同样有用。
但是为了省去我们一个个到项目中把要混淆的方法名抽取到codeObfuscation.h
中。我们一般会
①、在项目中对将来要混淆的方法名进行添加一个特征量,如一个`lcof_`前缀。
②、然后在写一个sh脚本,该脚本要做的事情是去项目中将含指定该特征量的方法名提取、转换,并最终写入`codeObfuscation.h`中。念茜中的脚本即是该作用。
③、执行脚本
操作整个过程中的几个易错注意点:
①、`PrefixHeader.h`中未`#import "codeObfuscation.h"`。`#import "codeObfuscation.h"`未放置在`PrefixHeader.h`最前面,导致可能有些混淆类的在该import前就import了,而编译不通过。
②、PrefixHeader.h中未加codeObfuscation.h
③、sh脚本不应打包进项目,即不应有任何target MemberShip,所以未避免不小心在添加时候,勾选了target MemberShip,最好在使用完后将该sh文件从工程中清除。
④、在`Build Phase`配置的`Run Script`,在最后使用完后也顺便清除。毕竟是编译前脚本,打包后的执行文件是不会执行的。
⑤、使用念茜的脚本抽取混淆方法名的时候,会在最后生成一个你`SYMBOL_DB_FILE`所指定的文件。该文件也可删除。其作用你可以理解为是生成我们在`codeObfuscation.h`中的字符串的时候需要使用的一种缓存。
⑥、容易引起app崩溃的注意:使用如interface链接的事件,即使该事件已被我们宏混淆,虽然在classdump和ida上是混淆的,但是在我们使用app的时候发现触发该事件会产生崩溃。
⑦、容易引起app没法进入指定页面效果的注意:通过nibName从xib中生成的控制器,即使我们已将该控制器的类名进行了混淆,但是在最后使用的时候发现无法正常加载xib上的界面元素。
⑧、不要试图去简单混淆静态库暴露在外面的接口方法。
额外补充:class-dump只可处理oc项目,无法处理oc与swift混编的项目。但是ida pro都可以。
执行脚本,书写脚本路径时候常用的宏:
$PROJECT_DIR/$PROJECT_NAME/Resources/PrefixHeader.h
关于将字符串转成其他混淆字符串的方法,除了上述文本中脚本sh的方法,其他可参考:
操作方法的自我总结:
2、上述方法
参考文章:一个说苹果审核对于念茜方法的混淆包给予拒绝通过
重要部分
最近发现了苹果审核对于念茜方法的混淆包给予拒绝通过,解释原因苹果自己有自己的加密方法去保证安全性,防止反编译不需要你去做混淆。我猜测和最近严打马甲包也有关吧。(2018年01月23日)
3、参考文章:一个基于念茜方法的混淆包在被拒之后的改进,最终通过审核的文章
重要部分:
API
首先加密API肯定不能再使用了,苹果都说了,加密特征太过明显。幸好之前已经把所有自定义的API名称都加上了ab_前缀,使得我们写脚本很好识别。我们将所有扫描出的API放到一个plist文件中保存在本地,然后我们建立了6个数组,每个数组中有6个单词,每次从每个数组中随机抽取一个单词。将6个单词拼接成一段方法名保存在另一个plist文件中,当然,在保存前,先去重,如果这个方法名已经用过了,那我们随机再换,这样一共可以生成46656种方法名,对于我们的工程已经够用了。
类前缀替换
这一步主要是更改文件名。程序扫描绝对地址下的所有文件,只要是带”XX”开头的文件都替换成”AB”这种,另外每次替换一个文件都要遍历所有文件,将所有用到这个头文件的文件内容进行更换。
四、其他人的加固方法
附、已使用加固的项目
XXX项目加固
一、采用的方案:如上
二、已涉及到的范围:
- 1、支付敏感信息
- 2、登录的敏感信息
- 3、用于混淆'所混淆的类'是重要类的'原本不需要混淆的类'
三、不去修复的地方:
UPPaymentControl
虽是支付部分,但其是属第三方静态库libPaymentControl.a
,若只混淆暴露出来的.h
文件中的类和方法名,而不去改变编译后的.a
文件,容易出问题,所以不去处理。
四、未来可能需添加涉及的范围:
- 加密库
End
结束!!!欢迎讨论!!!