iOS应用逆向工程:分析与实战
上QQ阅读APP看书,第一时间看更新

第2章

越狱iOS平台简介

相较于iOS应用的高层表象,人们对其底层实现更感兴趣,这也是大家进行逆向工程的原动力。

但是,我们也都知道,原版iOS是个对开发者封闭的黑盒子,直到Redsn0w等越狱组织对iOS进行破解、越狱之后,这个盒子才被揭开,神秘的iOS系统得以完全展现在我们面前。

2.1 iOS系统架构

对于原版(即未越狱)iOS,Apple官方并未提供直接访问iOS文件系统的任何入口,iOS开发者只需要遵循规定,参考文档即可完成工作,所以,纯粹的App Store开发者可能对iOS系统的目录结构一无所知。

因为沙箱及权限的关系,从App Store下载的App不能访问除自己目录以外的绝大多数文件。这时,可以借助Xcode,通过点击Xcode→Organizer→Devices浏览指定App的目录结构,如图2-1所示。但是,这种方式也仅能访问/var/mobile/Applications下的App目录,而对系统里的其他目录仍是束手无策。

图2-1 使用Xcode查看App的目录结构

在越狱iOS中,用户可通过安装强大的第三方文件管理工具,如iFunBox、iExplorer或iFile等,访问iOS全系统文件。图2-2是用iFile查看iOS文件系统的界面,它可以直接读取和修改系统中的任何文件,不用再顾虑严格的沙箱与权限限制。

至此,逆向工程的大门已经向我们敞开了,一起来看看吧。

2.1.1 iOS目录结构

iOS是由Mac OS X演化而来的,而Mac OS X则是基于UNIX操作系统的,这三者虽然有很大区别,但它们血脉相连。从Filesystem Hierarchy Standard(文件系统层次标准)和hier(7)中,可以一窥iOS目录结构设计的参考标准。

图2-2 iFile 浏览文件目录

Filesystem Hierarchy Standard(以下简称FHS)为类UNIX操作系统的文件目录结构制定了一套标准,制定这套标准的初衷之一是为了能让用户预知文件或目录的存放位置;Mac OS X在此基础上形成了自己的hier(7)框架。类UNIX操作系统的常见目录结构如下所示。

❏/根目录,以斜杠表示,其他所有文件和目录在根目录下展开。

❏/bin:"binary"的简写,存放提供用户级基础功能的二进制文件,如ls、ps等。

❏/boot:存放能使系统成功启动的所有文件,这些文件一般在内核用户程序开始执行前得到调用。在iOS中此目录为空。

❏/dev:"device"的简写,存放BSD设备文件。每个文件代表系统的一个块设备或字符设备,一般来说,“块设备”以块为单位传输数据,如硬盘;而“字符设备”以字符为单位传输数据,如调制解调器。

❏/sbin:"system binaries"的简写,存放提供系统级基础功能的二进制文件,如netstat、reboot等。

❏/etc:"et cetera"的简写,存放系统脚本及配置文件,如passwd、hosts等。在iOS中,/etc是一个符号链接,实际指向/private/etc。

❏/lib:存放系统库文件、内核模块及设备驱动等。iOS中此目录为空。

❏/mnt:"mount"的简写,存放临时的文件系统挂载点。iOS中此目录为空。

❏/private:存放两个目录,分别是/private/etc和/private/var。

❏/tmp:临时目录。在iOS中,/tmp是一个符号链接,实际指向/private/tmp。

❏/usr:包含了大多数用户工具和程序。/usr/bin包含那些/bin和/sbin中未出现的基础功能,如nm、killall等;/usr/include包含所有的标准C头文件;/usr/lib存放库文件。

❏/var:"variable"的简写,存放一些经常更改的文件,比如日志、用户数据、临时文件等。其中/var/mobile/Applications下存放了所有App Store App,是要重点关注的目录之一。

上述目录中的内容多用于系统底层,逆向难度较大,作为初学者,暂时不用在其中投入太多精力。从学和练的角度出发,循序渐进,由易到难,从我们熟悉的内容开刀,效率更高。

作为iOS开发者,日常操作所对应的功能模块大多来自iOS的独有目录,如下所示。

❏/Applications:存放所有的系统App和来自Cydia的App,不包括App Store App。越狱的过程把/Applications变成了一个符号链接,实际指向/var/stash/Applications,如图2-3所示。这个目录也是我们要重点关注的目录之一。

❏/Developer:/Developer相对没那么重要,它的出现完全是因为我们在Xcode连接iOS设备时选择了"Use for Development",这个目录的结构如图2-4所示。

❏/Library:用来存放系统App的数据。其中最需要关注的是/Library/MobileSubstrate目录,因为这个目录中存放了所有基于MobileSubstrate的插件,其结构如图2-5所示。

在iOS系统中,MobileSubstrate是一个提供hook功能的基础平台,运行在这个平台上的插件通常被称为tweak。

图2-3 /Applications目录

图2-4 /Developer目录

图2-5 /Library/MobileSubstrate目录

从图2-5中的文件列表中也可看到,/Library/MobileSubstrate下通常有3类文件。

❏dylib:即Dynamic Library,也就是tweak插件。

❏plist:用于配合dylib使用的filter文件,指定注入目标,其格式如下(将在第3章中详细介绍):

Filter = {
  Bundles = (com.apple.springboard);
};

❏disabled:被SBSettings禁用的tweak文件,图2-5中的Weixin.disabled,其实就是把Weixin.dylib改了个名字,不让MobileSubstrate加载而已。

❏/System:iOS文件系统中最重要的目录之一,包含了大量的系统组件,其目录结构如图2-6所示。其中需要重点关注的有:

〇/System/Library/Frameworks和/System/Library/PrivateFrameworks:存放iOS中的各种framework,SDK文档里提及的各种framework只是冰山一角,还有数不清的未公开功能等待我们去挖掘。

〇/System/Library/CoreServices里的SpringBoard.app:也就是桌面管理器,是用户与系统交流的最重要的中介。

〇/System/Library/PreferenceBundles:其中的各种bundle提供了“设置”中的绝大多数功能,也是iOS逆向工程入门阶段练习的好目标。

❏/User:用户目录,实际指向/var/mobile,其目录结构如图2-7所示。

这个目录里存放了大量的用户数据,比如:

❏/var/mobile/Media/DCIM:照片目录。

❏/var/mobile/Library/SMS:短信目录。

❏/var/mobile/Library/Mail:邮件目录。

❏/var/wireless/Library/CallHistory:通话记录。这个稍微特殊一点,在iOS 4时代,通话记录是存放在/var/mobile/Library/CallHistory下的。

另外一个非常重要的子目录就是/var/mobile/Applications,里面存放的是通过App Store下载的App,如图2-8所示。

图2-6 System/Library目录

图2-7 /var/mobile目录

图2-8 /var/mobile/Applications目录

在对iOS文件系统的目录结构有了初步了解之后就能够针对感兴趣的功能定位其对应的文件,也可以开展下一步的工作了。

2.1.2 iOS文件权限

iOS是一个多用户操作系统。“用户”是一个抽象的概念,它代表对操作系统的所有权和使用权,比如,mobile用户是无法调用reboot命令重启iOS系统的,而root用户则可以;“组”是用户的一种组织方式,一个组可以包含多个用户,一个用户也可以属于多个组。

iOS中的每个文件都有一个属主用户和一个属主组,或者说这个用户和这个组拥有这个文件;每个文件都具有一系列权限,简单地说,权限的作用在于说明文件的属主用户能做什么,属主组能做什么,以及其他所有人能做什么。iOS用3bit来表示文件的使用权,从高位到低位分别是r(read)权限、w(write)权限以及x(execute)权限。文件与用户的关系存在以下三种可能性:

❏此用户是属主用户。

❏此用户不是属主用户,但在属主组里。

❏此用户既不是属主用户,又不在属主组里。

因此,需要3×3bit来表示一个文件的权限,其中1bit如果置1,则代表生效,置0代表无效。例如,111101101代表rwxr-xr-x,即该文件的属主用户拥有r、w、x权限,而属主组和其他所有人只具有r和x权限;同时,二进制的111101101转换成十六进制是755,也是一种常见的权限表示法。

2.2 iOS程序类型

越狱iOS中最常见的程序有Application、Dynamic Library和Daemon三类。在当前阶段,逆向工程的目标仍是这三类程序,对它们的了解越深入,逆向工程就会越顺利。这三类是不同类型的程序,分工不同,其目录结构和文件权限也有较大区别。下面一起来看看吧。

2.2.1 Application

Application就是我们常说的应用程序,简称App,其意义早已为所有iOS开发人员熟知。不过,在本节中还是要对Application的概念作一个完整的回顾和补充。

1.bundle

在iOS这个体系中,bundle的概念来源于NeXTSTEP,其实它就是一个按某种标准结构来组织的目录,其中包含可执行程序以及运行所需的资源。App、framework和PreferenceBundle都是以bundle的形式存在的,当你将某个bundle确立为逆向目标后,绝大多数逆向资源都可以在bundle内找到,大大减少了逆向工程的工作量。

2.App的结构

在App Store中只能发布App这一种类型的程序,绝大多数iOS开发者对App的结构很熟悉;但在越狱iOS平台上,还有两类App形式的存在:WeeApp(即依附于Notification Center的App)和PreferenceBundle(即依附于Settings的App),它们的结构与App类似,常见于Cydia平台,这里也把它们看做是App。

framework的结构与App类似,但framework的bundle中存放的是一个动态链接库,而不是可执行程序;相对来说,framework在逆向工程中的地位比App更高,因为一个App的绝大多数功能都是通过调用framework提供的接口来实现的。

在逆向工程时,主要关注App中的以下三个部分。

(1)Info.plist

Info.plist记录App的基本信息,如bundle identifier(唯一标识符)、可执行文件名、图标文件名等。

这个文件之所以如此重要,是因为后面的开发调试过程中会利用MobileSubstrate对App进行hook(钩取、注入),获取正确的bundle identifier是成功逆向的第一前提。图2-9是WhatsApp的Info.plist。

图2-9 Info.plist详情

(2)Executable(可执行程序)

App的执行入口,也是逆向工程最主要的目标之一,得到它之后,马上斧钺加身,class-dump、IDA还有GDB等重磅武器都会攻击它。图2-10是笔者开发的一个联系人信息优化App,打开之后即可在目录结构中看到对应的可执行文件(即Info.plist中的"Executable file")。

图2-10 App的目录内容

(3)Resources(资源文件)

图标、图片、声音、配置文件、nib文件以及各种App运行时用到的资源文件,同样可见于图2-10。其中,各种本地化字符串(.strings)是定位逆向目标的重要线索。

3./Applications和/var/mobile/Applications

/Applications目录用于存放系统App及从Cydia下载的App,如图2-11所示,在此目录下有来自Cydia的iFile.app和iOS自带的MobileMail.app。

而/var/mobile/Applications目录存放的则是从App Store下载的App。

图2-11 Applications目录

两者都是App文件,目录结构区别不大,但前者的属主用户和属主组一般是root和admin,后者的属主用户和属主组都是mobile,因而它们拥有不同的权限。不同的越狱工具对两者sandbox(沙箱)的影响不同,一般来说,前者的sandbox比后者的限制要少很多。

顺便提一下,/Applications目录中App安装包的格式多为deb,而/var/mobile/Applications目录中App安装包的格式则为ipa,由此又引申出了iOS平台上的不同安装包格式。

(1)deb

deb格式是Debian 系统(包含Debian 和Ubuntu)专属安装包格式,配合APT 软件管理系统使用,是当前Linux下非常流行的一种安装包格式,是由Cydia 作者Jay Freeman(saurik)移植到iOS中的。

由于UNIX类系统对权限、所有者、组的要求很严格,而deb 格式安装包又经常会涉及系统比较底层的操作,可以获取较高的权限,所以从Cydia下载的App都是deb格式的。

(2)ipa

ipa格式是苹果公司在iOS 平台上推出的专属软件安装包,在2.0的固件发布后才正式投入使用,是目前iOS中唯一的官方安装包格式。

ipa的文件权限很小,sandbox限制很大,访问资源十分有限,这一点早已为广大开发人员所熟知。

(3)pxl

pxl格式起源于Mac 系统上的pkg 安装包,在1.x固件时代被广泛应用,曾经是iOS平台上唯一的软件安装包,现在只有91手机助手等少数平台在使用,基本上可以忽略它的存在。

4.sandbox

通俗地说,iOS中的sandbox就是一种访问限制机制。它是iOS最核心的安全部件之一,其实现很复杂,这里不过多讨论其实现细节,只列举两个主要现象供大家参考。

(1)文件访问

总的来说,sandbox会将App的文件访问限制在这个App的bundle内部,因此一个App不知道其他App的存在,更别说访问它们了。

不过,App可以通过framework来访问诸如照片、歌曲、通讯录等系统数据。

(2)硬件调用

App可以通过framework来调用摄像头、GPS模块等硬件。跟文件访问一样,因为这些操作要通过framework来进行,所以App的作用范围还是在iOS的控制之内。

在初学阶段,我们的目标不是逆向sandbox,所以知道以上两点就够用了。在iOS逆向工程中,越狱操作已经破除了iOS的绝大多数安全限制,而这一点往往被我们忽略了。有时会碰到一些看似奇怪的问题,比如某个tweak不能写文件,或者通过objc_getClass函数获取不到类等,在确保自己的代码没有问题的前提下,这时就要回过头来检查这些问题是不是由对权限或者sandbox的误解造成的。

2.2.2 Dynamic Library

大部分的iOS开发者可能都没有接触过Dynamic Library的概念,因为面向App Store的App用不到它。但是放眼望去,Cydia中的tweak插件无一不是以Dynamic Library的形式工作的,正是这些tweak插件的存在让我们能够随意定制自己的iOS。所以,有必要了解Dynamic Library的基本知识。

Static Library的概念很简单,在一个App启动时,系统会把App的代码和它所链接的Static Library一股脑放进分配好的内存空间里,不过因为一次性加载的内容过多,会造成App启动慢。

Dynamic Library则相对“智能”一些,只有当App要用到这个Library时,系统才把这个Library加载进内存。具体来说,当一个App启动时,iOS内核创建一个新进程,然后把App的代码加载到新进程的内存空间里;同时内核还会启动Dynamic Loader(/usr/lib/dyld),把App需要的Dynamic Library加载进App的内存空间。

值得一提的是,虽然Dynamic Library的功能非常强大,但其本身并不是可执行程序,而是以类似插件的形式存活在一个App中的,是这个App的一部分。所以,Dynamic Library的权限及内存空间是由加载它的那个App决定的,比如你写了一个Instagram的tweak插件,用来将你喜欢的图片保存在本地,如果你的保存目录是/var/mobile/Applications/InstagramGUID/Documents,那么这样操作没有问题,你的tweak插件能够非常好地工作;如果你想保存在/Developer下,而你又没注意或忘记了这个tweak插件是存活在一个App Store App里,那么可能在兴高采烈地保存了一大堆美图,准备回头细细品味时,却发现/Developer里啥也没有——你的写操作都被sandbox给禁掉了。

2.2.3 Daemon

相信本书的绝大部分读者从接触iOS开发的第一天起,就不断被Apple灌输这样一个观念——iOS中没有真正的后台多任务,你的App在后台将被大大限制从iOS7开始,官方已经支持真正的后台多任务了。。如果你是一个纯粹的App Store开发者,坚信并坚守这个观念,那么它将是你的App通过Apple审核的助推剂;但既然你阅读了这本书,想要了解一些官方文档没有阐述的事实,那么你就要保持冷静,谨慎思考,让我们一起回想一下iPhone上的一些现象。

1)当我们正在用iPhone上网或刷微博,接到一个电话,所有其他操作会立即中断,系统第一时间将接听电话的界面呈现在用户面前。如果iOS中没有后台多任务,系统是如何迅速响应这个来电的呢?

2)相信很多越狱手机上都装了360、QQ手机助手等类似的软件,用来拦截垃圾短信、骚扰电话。出于对巨头们的不完全信任,以及同台竞技的小小需求,笔者也做了一款这样的手机助手软件——SMSNinja。如果iOS中没有后台多任务,SMSNinja这类助手软件如何保证自己能及时处理收到的短信呢?

3)Backgrounder是一款帮助App实现真正后台运行的强大插件,有了它,再也不用担心因为push功能的不给力而漏收即时信息啦!如果iOS中没有真正的后台多任务,Backgrounder怎么会存在呢?

这些现象无一不说明iOS中是存在真正的后台多任务的,那么难道是Apple说错了?并不是!对于App Store App来说,当用户按下home键,程序就进入后台了,大多数功能都会被暂停;也就是说,对于遵纪守法的App Store开发者来说,可以把iOS看做是没有真正后台多任务的系统,因为你唯一能干的事不支持后台多任务。但iOS是基于Mac OS X的,后者又跟所有类UNIX操作系统一样,有daemon(即守护进程,Windows称Service)的概念,随着越狱iOS全文件系统的开放,daemon也展现在我们面前。

daemon存在的意义就是后台运行,为用户使用操作系统提供各种“守护”,如imagent保障iMessage的正确收发,mediaserverd处理几乎所有的音频、视频,syslogd记录系统日志等。

iOS系统中的daemon主要由一个可执行文件和一个plist文件构成。iOS的根进程是/sbin/launchd,会在开机或接到命令时检查/System/Library/LaunchDaemons和/Library/Daemons下所有符合格式规定的plist文件,然后按需启动对应的daemon。这里的plist文件与App中的Info.plist文件作用类似,记录了daemon的基本信息。

相对于App,daemon提供的功能要底层得多,逆向难度也要大得多,如果对某个daemon的认识不够成熟,随意更改它可能导致系统崩溃。当你在iOS逆向工程上有了一定的积累后,就可以挑战这些daemon了,虽然相比App,花费的精力会更多,但它们也会给你更丰厚的回报。例如,前段时间刚刚发布的“iOS第一款电话录音软件”Audio Recorder就是通过逆向/usr/sbin/mediaserverd这个daemon实现的。

2.3 小结

本章介绍了iOS系统架构和iOS中的常见程序类型,App Store开发者一般不需要了解也接触不到这些知识,容易形成概念盲区。本章旨在科普那些在逆向工程中非常重要但Apple官方闭口不提的iOS系统知识点,为App Store开发者打开iOS系统中的另一扇门。

就本章所涉及的知识点,将其内容扩充成一整本书都不为过,但作为逆向工程的初学者,了解这些内容就已足够为我们的逆向之路开一个好头了。