经历过最近轰动一时的 Xcode Ghost 事件后,可以看出,即便大如腾讯这般的企业,在面对 APP 的安全性时,态度也是不够严谨的。各大媒体却将矛头指向了苹果手机(_标题:苹果手机不安全_),这种低俗的竞争手段还真让人汗颜。作为开发人员,我觉得非常有必要修习安全相关的知识,即便不是精通,起码要有些常识。
这篇文章,便是在普及 iOS 安全的基本常识,而你目前关于这方面的认知,可能都是错的!
逆向有多难
谈及一个应用的安全,其实更多的便是关于一个应用的逆向,所谓逆向便是通过一系列的手法,从原始的可执行二进制文件中分析出有漏洞的地方,从而进行窜改,以达到不可告人的目的。逆向有多难呢?对于未涉及该领域的人来说,常常处于两个极端,一种认为非常简单,一种认为难如登天。
曾经有个人对我说,他可以在一分钟内将我手头上的一份可执行文件向上抽象,并画出它们的类交互图。当时,我弱弱的问了一句:什么是向上抽象?然后被他狠狠的鄙视了,并且问旁边的人,公司招我来做啥。这样的言论出自一个技术人员的口中,这让人非常担忧,不假思索的态度真心不适合从事需要严谨对待的程序设计。他所说的向上抽象便是进行逆向分析,而我只是在质疑他的言论,质疑点很多,但大体如下:
- 我质疑你根本不懂
Objective-C
,如何进行类交互图绘制?难道你能在一分钟里绘制出一份如蜘蛛网般的objc_msgSend
? - 我质疑你根本就没搞清楚
PE
、ELF
、Mach-O
之间的区别,甚至含义 - 我质疑你调试的能力,只接触过 gdb,很难让我相信你能在一台越狱手机上通过 lldb 完成所需的调试操作
那么逆向到底有多难?当然,肯定不会如上述言论中的那般简单,就算我们是拿到全部源码也达不到那般速度。从复杂度而言,它并不复杂,只是可能会非常繁琐,接下来的示例,可能会改变你一直以来的看法。
你所认为的安全手段
在一个应用中,面对一些必须持久化的敏感数据,为了安全起见,大家通常都会对这些数据进行加密存储,防止用户窃取一些类似Token
、AppKey
,甚至更为重要的数据。那么下面就模拟一个这样的应用,我们要存储一个用户对象,用户对象中有一个需要受保护的凭证字段,该凭证主要用于和服务端进行通讯。
下面是该对象的接口代码:
1 | @interface User : NSObject |
下面是实现部分的相关代码:
1 | @interface User () <NSCoding> |
可以看到,我们这里进行了非常强大(_SuperPower_)的加解密持久化操作,相信大家肯定都知道这个是可以被攻破的,但是,我们要攻破这样一个存储内容,需要多大气力呢?
不下于三种的攻破方式
要攻破这样的防护,其实非常简单,以至于可以有很多不同的方式来攻破,以下我们例举一些非常有代表性的手法。一般在进行破解之前,我们会通过class-dump
导出所有的Objective-C
头文件,当然,从 AppStore 上下载的应用,苹果都是经过加密的,可以用Clutch
或其他工具进行解密,这可是非常简单的步骤,然后便可以导出所有的Objective-C
头文件了,以下是我们导出的User
类文件:
1 | @interface User : NSObject <NSCoding> |
一切都暴露无疑,那么,有了这样一个头文件,我们就可采用不同的手段来进行攻破了。
Cycript 神器
cy
一直都是 saurik 大神的御用前缀,Cycript
也是他非常具有代表性的作品之一,详细内容我就不再介绍了,相信有兴趣的通过搜索引擎可以很容易的了解到。我们这里,就通过该工具就可以非常容易的解密到User
中所存储的内容,具体方式如下:
这个过程可以说,非常简单,只要将Cycript
附加的进程,然后直接调用相关类的方法,我们就可以获取到需要得到的信息。
lldb 调试
除了上面所的方式,我们还可以使用lldb
进行调试,获取到我们相应的信息,具体步骤如下:
依然是十分的方便,豪不费力气的我们就获得到了想要的信息,可以想象这个所谓的加密解密是多么的不堪一击。除了po
之外,我们还可以通过增加符号断点,然后读取寄存器中的值,也是可以达到相同的效果。通过窜改寄存器中的值,我们可以扰乱原有设定的程序逻辑,比如窜改identifier
,如果服务端安全性没有做好,这时候我们可以冒充其它用户进行相应的网络操作了。
无处不在的 Hook
除了上面对User
对象操作的方式之外,我们甚至完全不用考虑User
的存在,因为这个凭证最终是需要通过网络请求进行发送的,即便是使用 https 也无妨,因为我们不需要通过抓包就可以提前获取到请求的内容。这便是进行 Hook 操作,通过MobileSubstrate
配合theos
,我们可以非常方便的编写自己的 tweak,从而 Hook 一些我们感兴趣的方法,比如这里我们就可以 Hook 掉 NSURLRequest
设置请求头的方法,将内容 Dump 出来,这里偷懒一下,就不进行具体的演示操作了。
矛盾所在
看了上面操作,是不是对原先这样的设计产生质疑了?其实,如果是这种程度的防护,根本就是多余的操作,因为一旦攻击者对你存储的数据感兴趣,那么你这样费尽苦心的加解密对攻击者而言根本没有任何意义,而普通用户更不会关心你数据是否是加密存储。那么,问题究竟出在那?
问题通常存在于客户端将锁和钥匙都放在了一起,很多时候这都是无可奈何的做法,那么所能做的便是将这钥匙藏得更难发现点,但,终归会有开锁的时候,这时攻击者会偷瞄到你钥匙存放的地方,整个防线便崩溃了。就算你有特别的技巧,在开锁的时候让自己隐身,但锁一打开,攻击者可能就直奔进去,拿走了自己想要的东西。
就比如我们要防止 MP3 音频数据被盗,使用了非常复杂的加密算法,社么矩阵啊,什么向量啊,但最终客户端还是需要调用系统 API 进行播放的,无论是CoreAudio
还是AudioTookbox
中的方法,都可以被攻击者 Hook 掉,从而 Dump 出原始的 PCM 数据,你的加密只是降低播放性能,增加自己的工作量而已。
没有绝对的安全,就我所知的所有 iOS 安全防护,也都有相应破解手段,只不过是更加繁琐了一点而已。只要你的应用防不过操作系统,那么肯定就有破解的手段,当然,你的应用最终是需要进行正确执行的,所以操作系统肯定会完全知情,这便是所有的矛盾所在。
何去何从
那么,难道我们就这样放任不管了么?我们应该尽可能的将安全数据存放置于服务器中,并且所有核心的校验也都应该是服务器进行的,这样客户端便没有了后顾之忧。就算你窜改数据,服务端也是不会认账的,自然,客户端那些毫无意义的加密解密操作便可以去掉了。
安全是一个比较庞大的话题,本篇通过非常简单的一个实例普及了下常识性的内容,如果进行深入研究,那么你会发现一些更鲜为人知的黑魔法,加油吧,少年!