人活在世界上,快乐和痛苦本就分不清。所以我只求它货真价实。
——王小波 《革命时期的爱情》
iOS二进制文件类型
- 在iOS逆向工程初学阶段,我们的目标主要是
Application
、Dynamic Library
(以下简称dylib)和Daemon
这三类二进制文件,对它们的了解越深入,逆向工程就会越顺利。这三类文件分工不同,其目录结构和文件权限也有一些区别。
Application
- Application就是我们最熟悉的App了。虽然对于大多数iOS开发者来说,工作都是在跟App打交道,但在iOS逆向工程中,关注App的侧重点与正向开发还是不尽相同的。了解下面的几个App相关概念,是开始逆向工程前的必备工作。
bundle
bundle
的概念来源于NeXTSTEP
,它不是一个文件,而是一个按某种标准结构来组织的目录,其中包含了二进制文件及运行所需的资源。正向开发中常见的App
和framework
都是以bundle
的形式存在的;在越狱iOS中常见的PreferenceBundle
,可以看成是一种依附于Settings
的App,结构与App类似,本质也是bundle
。Framework
也是bundle
,但framework
的bundle
中存放的是一个dylib
,而不是可执行文件。相对来说,framework
的地位比App更高,因为一个App的绝大多数功能都是通过调用framework提供的接口来实现的。将某个bundle
确立为逆向目标后,绝大多数逆向线索都可以在bundle内找到,这大大降低了逆向工程的复杂度。
App目录结构
- 在iOS逆向工程中,对App目录结构的熟悉程度是决定工作效率的重要因素。App目录的以下三个部分比较重要。
Info.plist
Info.plist
记录了App的基本信息,如bundle identifier
、可执行文件名
、图标文件名
等。可以通过Xcode查看它的值,也可以通过Xcode自带的命令行工具plutil
查看它的值,如下:
1 | plutil -p /Users/snakeninny/Code/iOSSystemBinaries/8.1_iPhone5/SiriViewService.app/Info.plist | grep CFBundleIdentifier |
可执行文件
- 可执行文件的重要性不言而喻,它是App目录下最核心的部分,也是逆向工程最主要的目标。同样可以通过Xcode和plutil两种方式来查看
Info.plist
,定位可执行文件。用Xcode查看Info.plist
的界面,也可以通过Xcode自带的命令行工具plutil
查看它的值,如下:
1 | snakeninny$ plutil -p /Users/snakeninny/Code/iOSSystemBinaries/8.1_iPhone5/SiriViewService.app/Info.plist| grep CFBundleExecutable |
lproj目录
lproj
目录下存放的是各种本地化的字符串(.strings
),是iOS逆向工程的重要线索,也可以用plutil
查看,如下:
1 | plutil -p /Users/snakeninny/Code/iOSSystemBinaries/8.1_iPhone5/SiriViewService.app/en.lproj/Localizable.strings |
系统App VS StoreApp
/Applications/
目录存放系统App
和从Cydia
下载的App(我们把来自Cydia的App视为系统App),而/var/mobile/Containers/
目录存放的则是StoreApp
。虽然两者都是App,但它们在如下方面存在着一些差别。
目录结构
- 两种App的bundle内部目录结构区别不大,都含有
Info.plist
、可执行文件
、lproj目录
等,但是数据目录的位置不同:StoreApp的数据目录在/var/mobile/Containers/Data/
下,以mobile
权限运行的系统App的数据目录在/var/mobile/
下,而以root
权限运行的系统App的数据目录在/var/root/
下。
安装包格式与权限
Cydia App
的安装包格式一般是deb
,StoreApp
的安装包格式一般是ipa
。其中deb
是来自Debian
的安装包格式,由Cydia作者saurik
移植到iOS中,它的属主用户和属主组一般是root和admin,能够以root权限运行;而ipa是苹果为iOS推出的专属App安装包格式,属主用户和属主组都是mobile,只能以mobile权限运行。
沙盒(sandbox)
- 通俗地说,iOS中的沙盒就是一种访问限制机制,我们可以把它看作是权限的一种表现形式,授权文件(entitlements)也是沙盒的一部分。它是iOS最核心的安全组件之一,其实现很复杂,这里不过多讨论其细节。总的来说,沙盒会将App的文件访问范围限制在这个App内部,一个App一般不知道其他App的存在,更别说访问它们了;沙盒还会限制App的功能,例如对iCloud接口的调用就必须经过沙盒的允许。
- 在初学阶段,我们的目标不是沙盒,知道有这样一个东西存在就够了。在iOS逆向工程中,越狱本身已经破除了iOS的绝大多数安全限制,并对沙盒进行了一定程度的扩充,因此我们往往很容易忽略
sandbox
的存在,从而碰到一些看似很奇怪的问题。比如某个tweak
不能写文件,调用了某个函数却没有出现应有的效果,在确保自己的代码没有问题的前提下,就要回过头来检查这些问题是不是因为权限不够,或者沙盒限制造成的。
Dynamic Library
- 大部分iOS开发者的日常工作应该都是写App,估计很少有人写过
dylib
,因此对dylib
的概念很陌生。殊不知,在Xcode工程里导入的各种framework,链接的各种lib
,其实本质都是dylib
。可以用“file”
命令验证一下,如下:
1 | file /Users/snakeninny/Code/iOSSystemBinaries/8.1.1_iPhone5/System/Library/Frameworks/UIKit.framework/UIKit |
- 如果把焦点转移到越狱iOS中,Cydia里的各种
tweak
无一不是以dylib
的形式工作的,正是这些tweak
的存在让我们能够随意定制自己的iOS。在逆向工程中,我们会频繁接触各种dylib
,因此有必要了解一些相关知识。 - 在iOS中,lib分为
static
和dynamic
两种,其中staticlib
在编译阶段成为App可执行文件的一部分,会增加可执行文件的大小。因为App尺寸变大,启动时需要加载的内容变多,所以可能会导致App启动变慢。dylib
则相对“智能”一些,它不会改变可执行文件的大小,只有当App需要用到这个dylib
时,iOS才会把它加载进内存,成为App进程的一部分。 - 值得一提的是,
dylib
虽然充斥在iOS的各个角落,是逆向工程的重要目标类型,但其本身并不是可执行文件,不能独立运行,只能为别的进程服务,而且它们寄生在别的进程里,成为了这个进程的一部分。因此,dylib
的权限是由它寄生的那个App决定的,同一个dylib
寄生在系统App和StoreApp里时的权限是不同的。例如,你写了一个Instagram的tweak,用来把喜欢的图片保存在本地,如果保存目录是/var/mobile/Containers/Data/
下App对应的Documents
目录,那么因为Instagram是一个StoreApp,这样的操作是没有问题的,tweak能够正常工作。而如果保存目录是/var/mobile/Documents
,那么在兴高采烈地保存了一大堆美图,准备回头细细品味时,你就会发现/var/mobile/Documents
里啥图片也没有——操作都被sandbox给禁掉了。
Daemon
相信绝大部分开发者从接触iOS开发的第一天起,就不断被苹果灌输这样一个观念——iOS中没有真正的后台多任务,你的App在后台将被大大限制。如果你是一个纯粹的App Store开发者,坚信并坚守这个观念,那么它将是你的App通过苹果审核的助推剂;但想要在学习逆向工程的同时了解一些官方文档没有阐述的事实,那么你就要保持冷静,理性思考。让我们一起回想一下iPhone上的一些现象。
- 当我们正在用iPhone上网或刷微博时来了一个电话,所有其他操作会立即中断,iOS第一时间将接听电话的界面呈现在我们面前。如果iOS中没有真正的后台多任务,系统是如何实时处理这个来电的呢?
- 对于那些经常收到垃圾短信和骚扰电话的朋友来说,类似于SMSNinja这样的防火墙软件必不可少。如果它不能常驻iOS后台,怎么能够实时地处理并过滤收到的每一条短信呢?
- Backgrounder是一款iOS 5时代的插件,它能够帮助App实现真正的后台运行。有了它,我们再也不用担心因为push功能的不给力而漏收QQ消息啦!
如果iOS中没有真正的后台多任务,Backgrounder怎么会存在呢?
- 这些现象无一不说明iOS实际上存在真正的后台多任务。那么难道是苹果说错了?并不是!对于StoreApp来说,当用户按下home键时,进程就进入后台了,大多数功能都会被暂停;也就是说,对于遵纪守法的App Store开发者来说,可以把iOS看作是没有真正后台多任务的系统,因为你唯一能干的事不支持后台多任务。但iOS源于OSX,后者又跟所有类UNIX操作系统一样,有
daemon
(即守护进程,Windows称Service)的概念。越狱开放了iOS全文件系统,daemon
也得以展现在我们面前。 Daemon
为后台运行而生,给用户提供了各种“守护”,如imagent
保障了iMessage的正确收发,mediaserverd
处理了几乎所有的音频、视频,syslogd
则用于记录系统日志等。iOS中的daemon
主要由一个可执行文
件和一个plist文件
构成。iOS的根进程是launchd
,它会在开机时检查/System/Library/LaunchDaemons
和/Library/LaunchDaemons
下所有格式符合规定的plist
文件,然后启动对应的daemon
。这里的plist文件与App中的Info.plist文件作用类似,即记录daemon
的基本信息,如下:
1 | plutil -p |
- 相对于App,
daemon
提供的功能要底层得多,逆向难度也要大得多,随意改动造成的后果当然也就严重得多,所以白苹果的惨案才会时有发生。在iOS逆向工程初学阶段,请不要把daemon当作练习目标;当你逆向了几个App,有了一定的心得和积累后再挑战这些daemon才是比较明智的选择。相比App,逆向daemon
花费的时间和精力会更多,但更多的付出一定会带来更丰厚的回报。例如,“iOS上的第一款电话录音软件”Audio Recorder就是通过逆向mediaserverd
这个daemon实现的。
总结
- 简单介绍了常见的二进制文件类型,它们都是App Store开发者不需要了解也接触不到的知识,在学习iOS逆向工程时很容易形成概念盲区。
- 本文旨在科普那些在逆向工程中非常重要但苹果官方闭口不提的iOS系统级知识点,从而为AppStore开发者打开iOS逆向工程的这扇窗。