iPhone apps purchased through the AppStore live in your iTunes library under the folder "Mobile Applications". Each app is stored in a zip archive with a .IPA extension. You can simply rename the file to .ZIP and decompress to view the contents. I'll use the game Fieldrunners as the example in this blog, which is in my opinion, the best iPhone game available. Decompressing and loading Payload\Fieldrunners.app\Fieldruners into IDA 5.4 will properly parse the Mach-O binary, list some symbols and provide you with very little and very odd looking disassembled code. Examining the string table reveals next to nothing. This is because the binary is encrypted, the app is in an unacceptable state for reverse engineering. The iPhone loader is responsible for decryption at run-time so I figured my best bet would be to jailbreak my phone and get on the actual device. Jailbreaking is an impressively easy operation these days, requiring only a few minutes with QuickPWN and installing some basic necessities like OpenSSH and GDB. Once on the device, you have to find your target applications directory and make a working copy of it:
# cd /private/var/mobile/Applications/
# find ./ -iname \*.app | grep Field
CA838FFC-8D74-4DB3-AB99-9410A7E860B7/Fieldrunners.appThe executable is a 32-bit Mach-O file which consists of 3 main regions. A header, followed by load commands, followed by segments/sections. Here is an illustration (not my own, found it on Google):

One of the load commands is responsible for actually decrypting the binary. Let's take a look through the load commands via the 'otool' utility. The output shown here is from the load command specific to crypto:
# otool -l Fieldrunners
Load command 9
cmd LC_ENCRYPTION_INFO
cmdsize 20
cryptoff 4096
cryptsize 258048
cryptid 1The cryptsize field is important for us as we'll see in a minute. If we wanted to strip the crypto entirely we would have to null out the cryptid, but as we are just interested in getting to the code we will leave it alone. The next step is to load the binary under GDB, set a breakpoint at the start address of 0x2000, let the loader decrypt and then dump the decrypted code. This tactic is very similar to how you would handle most executable packers such as UPX, ASPack, etc...
# gdb ./Fieldrunners
GNU gdb 6.3.50.20050815-cvs (Tue Nov 11 11:06:48 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "--host=arm-apple-darwin9 --target="...
warning: --arch option not supported in this gdb.
Reading symbols for shared libraries ................ done
gdb$ bp *0x2000
Breakpoint 1 at 0x2000
gdb$ r
Removing symbols for unused shared libraries . done
Reading symbols for shared libraries .++...................... done
Breakpoint 1, 0x00002000 in ?? ()Ok we're at the start address, let's take a look at the first batch of instructions and see if we're dealing with legitimate code:
# before decryption:
gdb$ x/20i 0x2000
0x2000: stfcse f7, [pc], {151}0x2004: cdp 4, 5, cr9, cr5, cr1, {4}0x2008: strcsb r2, [r8, #-2813]!
0x200c: ldrvsbt r2, [r6], r0, lsr #1
0x2010: strmib sp, [r0, #-116]!
0x2014: ldmlsdb ip!, {r1, r3, r5, r6, r7, r8, r9, sl, pc}^0x2018: cdppl 4, 4, cr10, cr15, cr5, {4}0x201c: cmnhi r0, pc, lsl ip
0x2020: eorvc r2, r9, #50331648 ; 0x3000000
0x2024: undefined instruction 0xf8faa207
0x2028: strgt r5, [fp], -r5, ror #1
0x202c: bleq 0x1becd8c
0x2030: ldrnebt lr, [r0], #2163
0x2034: stclsl 9, cr11, [r5, #-4]
0x2038: swine 0x006f99a2
0x203c: strplb r0, [lr, #584]
0x2040: undefined instruction 0xf755c9a3
0x2044: andvs ip, lr, #176 ; 0xb0
0x2048: smlalttge ip, fp, sl, sp
0x204c: ldrhih lr, [r9], pc
# after decryption:
gdb$ x/20i 0x2000
0x2000: ldr r0, [sp]
0x2004: add r1, sp, #4 ; 0x4
0x2008: add r4, r0, #1 ; 0x1
0x200c: add r2, r1, r4, lsl #2
0x2010: bic sp, sp, #7 ; 0x7
0x2014: mov r3, r2
0x2018: ldr r4, [r3], #4
0x201c: cmp r4, #0 ; 0x0
0x2020: bne 0x2018
0x2024: ldr ip, [pc, #24] ; 0x2044
0x2028: add ip, pc, ip
0x202c: ldr ip, [ip]
0x2030: blx ip
0x2034: ldr ip, [pc, #12] ; 0x2048
0x2038: add ip, pc, ip
0x203c: ldr ip, [ip]
0x2040: bx ip
0x2044: ldreqd lr, [r3], -r0
0x2048: andeq lr, r3, r4, asr #31
0x204c: str ip, [sp, #-4]!You can clearly see the difference, now that we have the decrypted code we need to dump it out of memory from 0x2000 through 0x2000 + 'cryptsize', or 0x41000:
gdb$ dump binary memory /var/root/Fieldrunners.app/dump 0x2000 0x41000
# ls -l dump
-rw-r--r-- 1 root wheel 258048 Mar 5 12:16 dumpHaving verified that the dump size matches the previous cryptsize we can now write the decrypted code back into the Mach-O binary using dd:
# dd seek=4096 bs=1 conv=notrunc if=dump of=Fieldrunners
258048+0 records in
258048+0 records out
258048 bytes (258 kB) copied, 18.5638 s, 13.9 kB/sNow we have a suitable file for reverse engineering. Objective-C code is a completely different animal than what I'm used to. You're pretty much dead in the water with cross-references as almost every call you see is siphoned through msgSend(). For some further information on reversing Mac binaries in general, take a look at the slides from Cameron's talk at RECON.
