iOS逆向工程——基础篇

iOS文件系统

iOS由OSX演化而来,而OSX又是基于UNIX的,它们都是类UNIX操作系统。类UNIX操作系统有一套标准的文件系统——Filesystem Hierarchy Standard(FHS),其常用目录结构如下所示:

  • /:根目录
  • /bin:binary,存放用户级基础功能的二进制文件,如lsps
  • /boot:存放能使系统成功启动的所有文件。iOS中此目录为空
  • /dev:device,存放BSD设备文件。每个文件代表系统的一个块设备或字符设备。块设备以块为单位传输数据,如硬盘;字符设备以字符为单位传输数据,如调制解调器
  • /sbin:system binaries,存放提供系统级基础功的二进制文件,如netstatreboot
  • /etc:法语Et Cetera(and so on的意思),存放系统脚本及配置文件,如passwdhosts。iOS中,/etc是一个符号链接,实际指向/private/etc
  • /lib:存放系统库文件、内核模块及设备驱动等。iOS中此目录为空
  • /mnt:mount,存放临时的文件系统挂载点。iOS中此目录为空
  • /private:存放两个目录,分别是/private/etc/private/var
  • /tmp:临时目录。iOS中,/tmp是一个符号链接,实际指向/private/var/tmp
  • /usr:包含大多数用户和程序。
    • /usr/bin:包含那些/bin/sbin中未出现的基础功能,如nmkillall
    • /usr/include:包含所有的标准C头文件
    • /usr/lib:存放库文件
  • /var:variable,存放一些经常更改的文件,如日志、用户数据、临时文件等。
    • /var/mobile:存放了mobile用户的文件。逆向工程重点关注目录
    • /var/root:存放了root用户的文件

iOS独有目录

下图所示分别是OSX和iOS的根目录结构,与FHS还是有一定的区别。

对于iOS,其独有的目录如下思维导图所示:

  • /Applications:存放所有的系统App来自于Cydia的App,不包括StoreApp
  • /Developer:如果一台设备连接Xcode后被指定为调试机,Xcode就会在iOS中生成该目录
    • /Developer/Applications
    • /Developer/Library
    • /Developer/Tools
    • /Developer/usr
  • /Library:存放一些提供系统支持的数据
    • /Library/MobileSubstrate/DynamicLibraries存放Cydia安装程序的.plist.dylib文件
  • /System/Library:iOS文件系统中最重要的目录之一,存放大量系统组件
    • /System/Library/Frameworks存放iOS中各种日常使用的framework
    • /System/Library/PrivateFrameworks存放iOS中未公开的私有framework
    • /System/Library/CoreServices/SpringBoard.app:iOS桌面管理器
  • /User:用户目录(mobile用户的home目录),实际指向/var/mobile,存放大量用户数据
    • /var/mobile/Media/DCIM:存放照片
    • /var/mobile/Media/Recording:存放录音文件
    • /var/mobile/Library/SMS:存放短信数据库
    • /var/mobile/Library/Mail:存放邮件数据
    • /var/mobile/Containers存放StoreApp
      • /var/mobile/Containers/Bundle存放所有StoreApp的可执行文件和相关资源
      • /var/mobile/Containers/Data存放所有StoreApp的数据,沙盒目录的真实目录

iOS应用(StoreApp)沙盒

出于安全考虑,iOS系统把每个应用(StoreApp)以及数据都放到一个沙盒(sandbox)里面,应用只能访问自己沙盒目录里面的文件、网络资源等(也有例外,比如系统通讯录、照相机、照片等能在用户授权的情况下被第三方应用访问)。

上图所示为沙盒结构,沙盒在逻辑上包含两个部分:Bundle ContainerData Container,两者在iOS文件系统中的位置是平行的,分别是/var/mobile/Containers/Bundle/var/mobile/Containers/Data。实际开发中,通过NSHomeDirectory()方法获取到沙盒根目录对应的是Data Container的路径。

Bundle Container

bundle的概念源自于NeXTSTEP,它是一个按某种标准结构来组织的目录,其中包含了二进制文件及运行所需的资源。正向开发中常见的App和framework都是以bundle的形式存在。在越狱iOS中常见的PreferenceBundle是一种依附于Settings的App,结构与App类似,本质也是bundle。Framework也是bundle,但framework的bundle中存放的是一个dylib(动态库),而非可执行文件。

在正向开发时,我们上传至App Store的ipa(iPhone Application)包,解压后会有一个Payload目录,其内部又包含一个.app目录,这个目录就是一个App的目录结构,也是一个bundle。

Bundle Container位于/var/mobile/Containers/Bundle/Application/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/主要用于存放静态资源,主要包含以下目录和文件:

  • MyApp.app
    存放可执行文件和资源文件,包括:打包时的资源文件、本地文件、可执行文件、.plist文件。这个目录不会被iTunes同步。

  • iTunesArtWork

  • iTunesMetadata.plist

Data Container

Data Container位于/var/mobile/Containers/Data/Application/YYYYYYYY-YYYY-YYYY-YYYYYYYYYYYY/,主要用于存放App运行时产生的动态数据,其主要包含以下目录和文件:

  • Documents
    存放应用运行时生成的并且需要保存的不可再生数据。注:iTunes或iCloud同步设备时会备份该目录
  • Library
    • Library/Caches
      存放应用运行时生成且需要保存的可再生数据,比如网络请求,用户需要负责删除对应文件。iTunes或iCloud不同步。
    • Library/Preferences
      存放偏好设置。使用NSUserDefaults写的设置数据都会保存在该目录下的一个plist文件中。iTunes或iCloud同步设备时备份该目录。
  • tmp
    存放应用下次启动不再需要的临时文件。当应用不再需要这些文件的时候,需要主动将其删除。(当应用不再运行的时候,系统可能会将此目录清空。)这个目录不会被iTunes同步。

在正向开发时,沙盒目录路径均有相应的获取方式,具体如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 沙盒目录
NSLog(@"%@",NSHomeDirectory());

// MyApp.app
NSLog(@"%@",[[NSBundle mainBundle] bundlePath]);

// Documents
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths objectAtIndex:0];
NSLog(@"%@",docPath);

// Library
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libPath = [paths objectAtIndex:0];
NSLog(@"%@",libPath);

// tmp
NSLog(@"%@",NSTemporaryDirectory());

在逆向工程中,可以通过以下方式获取相关的目录:

1
2
3
4
5
6
// 获取App的Bundle路径
iphone: root# ps -e | grep appname

// 获取App的Data路径
iphone: root# cycript -p appname
cy# directory = NSHomeDirectory()

iOS二进制文件类型

iOS逆向的目标主要包含三类二进制文件:ApplicationDynamic LibraryDaemon

Application

Application,即我们最熟悉的App。App的Bundle目录有以下三个部分比较重要:

  • Info.plist
    Info.plist记录了App的基本信息,如:bundle identifier可执行文件名图标文件名等。

  • 可执行文件
    查看Info.plist,即可定位可执行文件

  • lproj目录
    lproj目录下存放的是各种本地化的字符串。

系统App VS. StoreApp

/Applications/目录下存放系统App(包括CydiaApp);/var/mobile/Containers/目录下存放StoreApp。其区别在于:

  • 目录结构
    两种App的Bundle目录区别不大,都含有Info.plist、可执行文件、lproj目录等。只是Data目录的位置不同:StoreApp的数据目录在/var/mobile/Containers/Data/下,以mobile权限运行的系统App的数据目录在/var/mobile/下,以root权限运行的系统App的数据目录在/var/root/下。

  • 安装格式和权限
    Cydia App的安装格式一般是deb,StoreApp的安装格式一般是ipa。前者的属主用户和属主组一般是root和admin,能够以root权限运行;后者的属主用户和属主组都是mobile,只能以mobile权限运行。

Dynamic Library

Dynamic Library简称dylib,即动态链接库。在正向开发中,在Xcode工程中导入的各种framework,链接的各种lib,其本质都是dylib。

在iOS中,lib分为static和dynamic两种,其中static lib在编译阶段成为App可执行文件的一部分,会增加可执行文件的大小。dylib则不会改变可执行文件的大小,只有当App运行时调用到dylib时,iOS才会把它加载进内存,成为App进程的一部分。

dylib是逆向工程的重要目标类型,但其本身并不是可执行文件,不能独立运行,只能为别的进程服务,而且它们寄生在别的进程里,成为这个进程的一部分。因此,dylib的权限是由它寄生的那个App决定的,同一个dylib寄生在系统App和StoreApp里时的权限是不同的。

越狱iOS中,Cydia里的各种tweak无一不是以dylib的形式工作的。

Daemon

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

参考

  1. 《iOS应用逆向工程(第2版)》
  2. File System Programming Guide
  3. iOS底层基础知识-文件目录结构
  4. iOS APP沙盒目录
  5. Mac OS X 术语表
  6. 了解iOS上的可执行文件和Mach-O格式
  7. 由App的启动说起
  8. Mach-O 可执行文件