Disclosures and Source Code: Appendix - iPhone Forensics

by Jonathan Zdziarski

This appendix includes details about the procedures and results described in this book that a court may require from law enforcement witnesses, prosecutors, and defendants.
iPhone Forensics book cover

This excerpt is from iPhone Forensics. iPhone Forensics supplies the knowledge necessary to conduct complete and highly specialized forensic analysis of the iPhone, iPhone 3G, and iPod Touch.

buy button

Power-On Device Modifications (Disclosure)

When any computer is turned on, files are read and written. iPhone examiners need only be concerned with what is written, as the iPhone’s filesystem is mounted with the noatime option, even if the option is not specified in /etc/fstab. This option prevents access times from being updated when a file is read or its metadata (such as its name) is changed on the device. Therefore, the access time shown on a file should reflect either its creation or the last time some change was made to the content, allowing you to concentrate on only the files that have been actually changed.

In the likely event that you don’t possess special equipment to physically dump the iPhone’s memory chip, the device must be powered on and booted into its operating system to recover data. Furthermore, the forensic tools described in this book require that the device be rebooted after the toolkit payload is installed.

Just like a desktop operating system, the iPhone’s Leopard operating system performs minor writes to certain files upon booting. The purpose of most writes is to replace or reset existing configuration files, and writes generally don’t add any new data to the filesystem. Some writes, however, append a very minor amount of data to files. Overall, the writes to the filesystem are minimal, but are disclosed here in Table A.1, “Bytes added to files during boot” for integrity.


On iPhone firmware versions lower than or equal to 1.1.2, the mobile directory is replaced with root.

Table A.1. Bytes added to files during boot


Estimated magnitude of change


28 bytes


1275 bytes


121 bytes


3 bytes


76 bytes


3252 bytes


320 bytes


144 appended


Inode only


7168 bytes


783 bytes


730 bytes


1305 bytes


2284 bytes


4380 bytes


Unless otherwise noted, all changes are performed as overwrites to existing data, but this isn’t guaranteed.

In addition to the changes noted in Table A.1, “Bytes added to files during boot”, the files listed in Table A.2, “Bytes added to files during login” may be written to or recreated when someone logs into the device, causing bytes to be added.

Table A.2. Bytes added to files during login


Estimated magnitude of change


468 bytes


1256 bytes

Installation Record (Disclosure)

The forensic toolkit payload installed by iLiberty+ places a set of open source tools onto the otherwise read-only portion of the device, resulting in no destruction to user-level data stored on the device’s media partition. At the time of payload installation, the following files are written to the system (root) partition.


File size may vary depending on the application and payload versions used. Some files are deleted after toolkit installation.

/usr/libexec/ipluspwns (basepack)
-rwxr-xr-x  1 root wheel   25212 Mar 27 08:59 chmod*
-rwxr-xr-x  1 root wheel   38320 Mar 27 08:59 echo*
-rwxr-xr-x  1 root wheel   23292 Mar 27 08:59 iPipe*
-rwxr-xr-x  1 root wheel   14352 Mar 27 08:59 mv*
-rwxr-xr-x  1 root wheel   13760 Mar 27 08:59 reboot*
-rwxr-xr-x  1 root wheel   19128 Mar 27 08:59 rm*
-rwxr-xr-x  1 root wheel 1298880 Mar 27 08:59 sh*
-rwxr-xr-x  1 root wheel   39036 Mar 27 08:59 sleep*
-rwxr-xr-x  1 root wheel   14916 Mar 27 08:59 umount*
-rwxr-xr-x  1 root wheel  141528 Mar 27 08:59 unzip*
     /bin (basepack)
-rwxr-xr-x  1 root  wheel   134152 Mar 27 08:59 awk
-rwxr-xr-x  1 root  wheel    23368 Mar 27 08:59 blcheck
-rwxr-xr-x  1 root  wheel    14368 Mar 27 08:59 cat
-rwxr-xr-x  1 root  wheel    25212 Mar 27 08:59 chmod
-rwxr-xr-x  1 root  wheel    80660 Mar 27 08:59 chown
-rwxr-xr-x  1 root  wheel    19644 Mar 27 08:59 cp
-rwxr-xr-x  1 root  wheel    18972 Mar 27 08:59 cut
-rwxr-xr-x  1 root  wheel    33288 Mar 27 08:59 dd
-rwxr-xr-x  1 root  wheel     9212 Mar 27 08:59 dirname
-rw-r--r--  1 root  wheel     2971 Apr  1 20:25 functions.inc
-rwxr-xr-x  1 root  wheel   158708 Mar 27 08:59 grep
-rwxr-xr-x  1 root  wheel    18056 Mar 31 14:03 iEdit
-rwxr-xr-x  1 root  wheel    20776 Mar 27 08:59 igsm
-rwxr-xr-x  1 root  wheel    13492 Mar 31 14:03 ln
-rwxr-xr-x  1 root  wheel    41028 Mar 27 08:59 ls
-rwxr-xr-x  1 root  wheel    13348 Mar 31 14:03 mkdir
-rwxr-xr-x  1 root  wheel    24244 Mar 27 08:59 plutil
-rwxr-xr-x  1 root  wheel    13760 Mar 27 08:59 reboot
-rwxr-xr-x  1 root  wheel    19172 Mar 27 08:59 rm
-rwxr-xr-x  1 root  wheel    42888 Mar 27 08:59 sed
-rwxr-xr-x  1 root  wheel  1298880 Mar 27 08:59 sh
-rwxr-xr-x  1 root  wheel     9392 Mar 27 08:59 sleep
-rwxr-xr-x  1 root  wheel   260244 Mar 27 08:59 tar
-rwxr-xr-x  1 root  wheel   141528 Mar 27 08:59 unzip
     /bin (payload)
-rwxr-xr-x  1 root  wheel  591364 Mar 16 09:23 bash
-rwxr-xr-x  1 root  wheel   45804 Feb 29 04:55 cat
-rwxr-xr-x  1 root  wheel   74456 Feb 29 04:55 chgrp
-rwxr-xr-x  1 root  wheel   65632 Feb 29 04:55 chmod
-rwxr-xr-x  1 root  wheel   74724 Feb 29 04:55 chown
-rwxr-xr-x  1 root  wheel  159704 Feb 29 04:55 cp
-rwxr-xr-x  1 root  wheel   33288 Apr  7 10:25 dd
-rwxr-xr-x  1 root  wheel  119948 Mar 27 07:48 grep
-rwxr-xr-x  1 root  wheel  115848 Feb 29 04:55 ln
-rwxr-xr-x  1 root  wheel  146360 Feb 29 04:55 ls
-rwxr-xr-x  1 root  wheel   44452 Feb 29 04:55 mkdir
-rwxr-xr-x  1 root  wheel   45900 Feb 29 04:55 mknod
-rwxr-xr-x  1 root  wheel  169368 Feb 29 04:55 mv
-rwxr-xr-x  1 root  wheel   39292 Feb 29 04:55 pwd
-rwxr-xr-x  1 root  wheel   13760 Apr  8 00:35 reboot
-rwxr-xr-x  1 root  wheel  142636 Feb 29 04:55 rm
lrwxr-xr-x  1 root  wheel       4 Apr  8 00:18 sh -> bash
-rwxr-xr-x  1 root  wheel   17004 Feb 27 18:50 sync
     /etc (payload)
-rw-r--r--  1 root  wheel  1418 Jun 12  2006 ssh_config
-rw-r--r--  1 root  wheel  3230 Aug 25  2007 sshd_config
     /sbin (payload)
-rwxr-xr-x  1 root  wheel  185008 Apr  8 00:34 fsck_hfs
-rwxr-xr-x  1 root  wheel   18052 May  7 12:12 md5
-rwxr-xr-x  1 root  wheel   19236 Apr  8 00:34 mount_hfs
-rwxr-xr-x  1 root  wheel   46300 Apr  8 00:35 newfs_hfs
-rwxr-xr-x  1 root  wheel  191976 May  7 12:22 ping
-rwxr-xr-x  1 root  wheel   14916 Apr  8 00:37 umount
     /usr/bin (payload)
-rwsr-xr-x  1 root  wheel   31712 Feb 27 18:50 login
-rwxr-xr-x  1 root  wheel   29520 Apr  8 00:34 nc
-rwxr-xr-x  1 root  wheel   56284 Aug 23  2007 scp
-rwxr-xr-x  1 root  wheel   88876 Aug 23  2007 sftp
-rwxr-xr-x  1 root  wheel  340340 Aug 23  2007 ssh
-rwxr-xr-x  1 root  wheel  103960 Aug 23  2007 ssh-add
-rwxr-xr-x  1 root  wheel   87336 Aug 23  2007 ssh-agent
-rwxr-xr-x  1 root  wheel  134264 Aug 23  2007 ssh-keygen
-rwxr-xr-x  1 root  wheel  198048 Aug 23  2007 ssh-keyscan

     /usr/lib (payload)
lrwxr-xr-x  1 root  wheel      18 Apr  8 00:18 libcurses.dylib ->
-r-xr-xr-x  1 root  wheel   35392 Jan  3 20:31 libhistory.5.2.dylib
lrwxr-xr-x  1 root  wheel      20 Apr  8 00:18 libhistory.5.dylib ->
lrwxr-xr-x  1 root  wheel      20 Apr  8 00:18 libhistory.dylib ->
-rw-r--r--  1 root  wheel   60780 Jan 14 21:44 libintl.8.0.2.dylib
lrwxr-xr-x  1 root  wheel      19 Apr  8 00:18 libintl.8.dylib ->
lrwxr-xr-x  1 root  wheel      19 Apr  8 00:18 libintl.dylib ->
-rw-r--r--  1 root  wheel     801 Jan 14 21:44 libintl.la
-rwxr-xr-x  1 root  wheel  105156 Feb 23 06:30 libncurses++.a
-rwxr-xr-x  1 root  wheel  379360 Feb 23 06:30 libncurses.5.dylib
lrwxr-xr-x  1 root  wheel      18 Apr  8 00:18 libncurses.dylib ->
-r-xr-xr-x  1 root  wheel  239308 Jan  3 20:31 libreadline.5.2.dylib
lrwxr-xr-x  1 root  wheel      21 Apr  8 00:18 libreadline.5.dylib ->
lrwxr-xr-x  1 root  wheel      21 Apr  8 00:18 libreadline.dylib ->
-rwxr-xr-x  1 root  wheel  247684 Jan  4 05:35 libresolv.dylib
lrwxr-xr-x  1 root  wheel      17 Apr  8 00:18 terminfo -> ../share/terminfo

     /usr/libexec (payload)
-rwxr-xr-x  1 root  wheel   59372 Aug 23  2007 sftp-server
-rwxr-xr-x  1 root  wheel  200664 Aug 23  2007 ssh-keysign
-rwxr-xr-x  1 root  wheel   35280 Aug 23  2007 ssh-rand-helper
-r-xr-xr-x  1 root  wheel     425 Dec 20  2006 sshd-keygen-wrapper

     /usr/sbin (payload)
-rwxr-xr-x  1 root  wheel   32784 Apr  8 00:36 fdisk
-rwxr-xr-x  1 root  wheel  414512 Aug 23  2007 sshd

     /Library/LaunchDaemons (payload)
-rw-r--r--  1 root  wheel  828 Feb  4  2006 com.openssh.sshd.plist

Technical Procedure

This section explains some low-level technical details of the operations performed by the iLiberty+ tool. These techniques are intended for those desiring a technical explanation of the procedure or who seek to reproduce or reimplement it, and are not necessary for general forensic examination.

Many different methods have been devised by the iPhone development community to gain access to an iPhone’s operating system, but very few of them are able to do so without destroying evidence, or even destroying the entire filesystem. The technique used in this manual is considered to be forensically safe in that it is cdisclosures-sourcecodeble of accessing the device without corrupting user data.

Unsigned RAM Disks

A RAM disk is a filesystem that resides in memory, and is not physically written on disk. Most Unix kernels are cdisclosures-sourcecodeble of booting the operating system from memory, and most versions of iPhone software also support this.

The technique used by iLiberty+ for iPhone software versions 1.0.2–1.1.4 gains access to the operating system by booting an unsigned RAM disk from the iPhone’s resident memory. This RAM disk is copied into the iPhone’s memory and booted by setting the appropriate kernel flags using Apple’s MobileDevice framework. This section is based specifically on version 7.4.2 of the device framework. Because the function calls change slightly for newer versions of the framework, you will have to install this framework with a copy of iTunes 7.4.2 in order to reproduce the procedure in this section.

Once the unsigned RAM disk is booted, the iPhone’s disk-based filesystem is mounted and the selected payload is copied. Depending on the payload, this could simply enable shell access, or install a surveillance kit or any other type of software. When the device boots back into its normal operating mode, the installed payload will be executed, performing whatever tasks it was designed for.

iLiberty+’s custom RAM disk differs from the RAM disk used by Apple to install software updates and perform restores. The custom iLiberty+ RAM disk consists of a disk image containing the necessary ARM-architecture files to boot and install a custom payload on the iPhone. The RAM disk itself is padded with 0x800 bytes to contain an 8900 header, and may additionally pad between 0xCC2000 and 0xD1000 zero bytes to assist in aligning the execution space of the disk.

Once a custom RAM disk has been assembled, it is executed using private and undocumented function calls within Apple’s MobileDevice framework. In short, this involves the following procedures.

The device is placed into recovery mode either manually (by holding the Home and Power buttons until forced into recovery mode), or by using the MobileDevice function AMDeviceEnterRecovery. The RAM disk image is sent to the device using the private __sendFileToDevice function after looking up its symbol address in the framework.

The following commands are sent to the device using the private __sendCommandToDevice function after looking up its symbol address in the MobileDevice framework. This sets the kernel’s boot arguments to boot from a RAM disk, and specifies the memory address of the approximate location of the custom image copied to the device.

setenv boot-args rd=md0 -s -x pmd0=0x9340000.0xA00000


Depending on the cdisclosures-sourcecodecity and firmware version of the device, different memory addresses may be necessary. The memory address 0x09CC2000.0x0133D000 has also been reported to succeed.

Once the RAM disk has booted and the payload has been delivered, the device can be booted back into normal operating mode by sending the following commands to the device using __sendCommandToDevice:

setenv boot-args [Empty]
setenv auto-boot true


Depending on the version of iPhone firmware, the fsboot command may be replaced with bootx.

Source Code Examples

The following source code illustrates the process of booting an unsigned RAM disk in C. The example waits for the device to be connected in recovery mode and then issues the commands to send and boot a RAM disk as described in the previous section. The RAM disk image and needed framework library are provided by the implementer. This code was designed to run on the Mac OS X operating system running iTunes 7.4.2 MobileDevice framework. Comments are provided inline.

To build this example, use the following command:

$ gcc –o inject-ramdisk inject-ramdisk.c –framework CoreFoundation 
–framework MobileDevice –F/System/Library/PrivateFrameworks

The complete code for inject-ramdisk.c follows:

#include <stdio.h>
#include <mach-o/nlist.h>
#include <CoreFoundation/CoreFoundation.h>

/* Path to the MobileDevice framework is used to look up symbols and 
offsets */

/* Used as a pointer to the iPhone/iTouch device, when booted into 
recovery */
typedef struct AMRecoveryModeDevice *AMRecoveryModeDevice_t;

/* Memory pointers to private functions inside the MobileDevice framework */
typedef int(*symbol)  (AMRecoveryModeDevice_t, CFStringRef) \
    __attribute__ ((regparm(2)));
static symbol sendCommandToDevice;
static symbol sendFileToDevice;

/* Very simple symbol lookup. Returns the position of the function in 
memory */
static unsigned int loadSymbol (const char *path, const char *name)
    struct nlist nl[2];
    memset(&nl, 0, sizeof(nl));
    nl[0].n_un.n_name = (char *) name;
    if (nlist(path, nl) < 0 || nl[0].n_type == N_UNDF) {
        return 0;
    return nl[0].n_value;

/* How to proceed when the device is connected in recovery mode.
* This is the function responsible for sending the ramdisk image and booting
* into the memory location containing it. */

void Recovery_Connect(AMRecoveryModeDevice_t device) {
    int r;

    fprintf(stderr, "Recovery_Connect: DEVICE CONNECTED in Recovery Mode\n");

    /* Upload RAM disk image from file */
    r = sendFileToDevice(device, CFSTR("ramdisk.bin"));
    fprintf(stderr, "sendFileToDevice returned %d\n", r);

    /* Set the boot environment arguments sent to the kernel */
    r = sendCommandToDevice(device,
        CFSTR("setenv boot-args rd=md0 -s -x pmd0=0x9340000.0xA00000"));
    fprintf(stderr, "sendCommandToDevice returned %d\n", r);

    /* Instruct the device to save the environment variable change */
    r = sendCommandToDevice(device, CFSTR("saveenv"));
    fprintf(stderr, "sendCommandToDevice returned %d\n", r);

    /* Invoke boot sequence (bootx may also be used) */
    r = sendCommandToDevice(device, CFSTR("fsboot"));
    fprintf(stderr, "sendCommandToDevice returned %d\n", r);

/* Used for notification only */
void Recovery_Disconnect(AMRecoveryModeDevice_t device) {

    fprintf(stderr, "Recovery_Disconnect: Device Disconnected\n");

/* Main program loop */
int main(int argc, char *argv[]) {
    AMRecoveryModeDevice_t recoveryModeDevice;
    unsigned int r;

    /* Find the __sendCommandToDevice and __sendFileToDevice symbols */
    sendCommandToDevice = (symbol) loadSymbol
        (MOBILEDEVICE_FRAMEWORK, "__sendCommandToDevice");
    if (!sendCommandToDevice) {
        fprintf(stderr, "ERROR: Could not locate symbol: "
            "__sendCommandToDevice in %s\n", MOBILEDEVICE_FRAMEWORK);
        return EXIT_FAILURE;
    fprintf(stderr, "sendCommandToDevice: %08x\n", sendCommandToDevice);

    sendFileToDevice = (symbol) loadSymbol
        (MOBILEDEVICE_FRAMEWORK, "__sendFileToDevice");
    if (!sendFileToDevice) {
        fprintf(stderr, "ERROR: Could not locate symbol: "
            "__sendFileToDevice in %s\n", MOBILEDEVICE_FRAMEWORK);
        return EXIT_FAILURE;

    /* Invoke callback functions for recovery mode connect and disconnect */
    r = AMRestoreRegisterForDeviceNotifications(
    fprintf(stderr, "AMRestoreRegisterForDeviceNotifications returned %d\n", 
    fprintf(stderr, "Waiting for device in restore mode...\n");

    /* Loop */

Once the RAM disk has been injected and booted, iLiberty+’s work is complete and the RAM disk has delivered whatever payload it was written to deliver. The device can then be returned to normal operating mode by issuing the following commands in place of those in the Recovery_Connect function:

/* Reset and save the default boot-related environment variables */
    sendCommandToDevice(device, CFSTR("setenv auto-boot true"));
    sendCommandToDevice(device, CFSTR("setenv boot-args "));
    sendCommandToDevice(device, CFSTR("saveenv"));

    /* Boot the device (bootx may also be used) */
    sendCommandToDevice(device, CFSTR("fsboot"));

The device will now boot into normal operating mode for all subsequent boots.

If you enjoyed this excerpt, buy a copy of iPhone Forensics.