jtool - Taking the O out of otool(1)

What is this?

The jtool command is meant to meet and exceed the functionality to XCode's otool(1), picking up along the way additional Mach-O commands such as atos(1), dyldinfo(1), nm(1), segedit(1), pagestuff(1), strings(1) , and even codesign(1) and the informal ldid. jtool also provides novel features, such in-binary search functionality, symbol injection and a disassembler functionality with (limited but constantly improving) emulation capabilities. It also provides color output. Most importantly, it can be run on a variety of platforms - OS X, iOS, and even Linux. It is ENTIRELY FREE for use of any type, and the latest version can always be found right here.

Although jtool was built around my own use cases, other people have found it useful as well. The amount of options it supports has grown significantly. With the release of MOXiI 2 (soon!), I've decided to finally bring jtool to (almost) version 1.0, as well as properly document its myriad capabilities, some of which may not be readily discernible, in the form of examples. Running jtool with no arguments will show all the options, which I realize can be a tad intimidating:

This is jtool v(almost 1.0) (Moscow) - with partial ARMv7k disassembly and SLC symbol resolution, compiled on Jan  8 2017 20:33:01

Usage: jtool [options] _filename_

OTool Compatible Options:
   -arch ..            	For fat (universal) binaries: i386,x86_64[h], arm[v[67]]
   -h                  	print header (ELF or Mach-O)
   -f                  	print fat header
   -l                  	List sections/commands in binary
   -L                  	List shared libraries used (like LDD)
   -v[v]               	Verbose output. -vv is even more verbose. -vvv might be more than you can handle :-)

New Options:
   --pages             	Show file page map (similar to pagestuff(1))
   -a _offset_         	Find virtual address corresponding to file offset _offset
   -e[xtract] _name_   	Extract section/segment _name_ in binary, or file _name_ from shared cache
   -e[xtract] arch     	Extract selected architecture from FAT file. Specify arch with -arch .. or ARCH=
   -e[xtract] signature	Extract code signature from binary
   -F [_string_]       	find all occurrences of _string_ in binary
      -Fs _string_     	also show search results (experimental)
   -S                  	List Symbols (like NM)
      -Sa _address_ _symbolname_	Add symbolname manually for address (to .jtool)
      -Sd _symbolname_ (not yet)	Remove symbolname for address (to .jtool)
   -p _addr_[,_size_]  	Peek at _size_ bytes in virtual _address_ in binary (like OD, but on memory)

dyldinfo Compatible Options:
   -bind               	print addresses dyld will set based on symbolic lookups
   -lazy_bind          	print symbols which dyld will lazily set on first use
   -weak_bind          	print symbols which dyld must coalesce
   -export             	print addresses of all symbols this file exports
   -opcodes            	print opcodes used to generate the rebase and binding information
   -function_starts    	print table of function start addresses
   -data_in_code       	print any data-in-code inforamtion

Destructive Options (will write output to /tmp):
   -m                  	Modify
     __SEGMENT[.__section],[_offset][,size]	(null)
   -r                  	Remove/Resize (Experimental)
     -rL _dylib/soname_      	Library
     -rC _Load_Command_#_    	Load Command
   -/+pie              	Toggle Position Independent Executable (ASLR)
   -/+lcmain           	Toggle pre-Mountain-Lion/iOS6 compatibility (LC_UNIXTHREAD/LC_MAIN)
   -/+enc              	Mark as decrypted/encrypted (toggles cryptid of LC_ENCRYPTION_INFO[64])

Disassembly/Dump Options:
   -d[_arg_[,size]]    	disassemble/dump (experimental) -  _arg_ may specify address/section/symbol/Obj-C class. size is optional
     -dA [_arg_[,size]]	Disassemble as ARM code (32-bit instructions)
     -dT [_arg_[,size]]	Disassemble as Thumb/Thumb-2 code (16/32-bit instructions)
     -dD [_arg_[,size]]	Dump (even on a text segment)
     -do [_arg_[,size]]	Dump/Disassemble from offset, rather than address
     -d objc           	Dump objective-C classes in binary, if any
   -D : As -d, but attempts to decompile only (i.e. shows only C-level code, no disassembly	(null)
   -opcodes            	Also dump opcode bytes
   --jtooldir _path_   	path to search for companion jtool files (default: $PWD).
			Use this to force create a file, if one does not exist

Code Signing Options:
   --sig               	Show code signature in binary (if any)
   --sign [adhoc]      	self-sign with no certificate
   --ident _ident_     	provides identity (fake, of course)
   --appdir            	Set App Path (for code signing and/or verification)
   --ent               	Show entitlements in binary (if any)

Advanced Options:
   --pcrelative        	show addresses as PC relative offset
   --slide  _slide_    	slide text by _slide_ bytes (may be negative)
   --rebase _address_  	rebase text to this address (destructive)
   --inplace           	Perform destructive operations in place (instead of out.bin) for the brave
   --version           	Show tool version and compilation date

Output Options:
   --html              	Output as HTML (implies color)
   --curses            	Output as Color using ncurses (can also set JCOLOR=1)

Environment variables: JDEBUG=1, NOPSUP=1 (suppress NOPs in disassembly), NOOBJC=1 (avoid Obj-C crashes)

Note: Experimental features may not be available in public version of this tool

So let's go over options, one by one:

Important: Jtool has been thoroughly tested, but I still rely on your bug reports to tackle some of the more exotic animals in the Mach-O menagerie. If you have an interesting binary which JTool is struggling with or if it (*gulp*) crashes, PLEASE LET ME KNOW. I can't fix bugs I don't know about.. You might want to run jtool again with NOOBJC=1 to disable the (experimental) objective-C support which is still buggy

There are so many useful features to jtool that I can't put them all in the man page anymore. Truthfully, nobody reads the man anyway (since you have to move jtool.1 to /usr/share/man/man1). So I finally put up a decent HTML document. That, too, is growing out of proportion, so even here not everything is covered, but here's a hyperlinked ToC to what is:

Otool Compatible Options

When I started work on jtool, I thought I would make it a drop-in replacement for otool, so some of its options are in fact identical. Those include -L (to show library dependencies, similar to ldd), and -f (for FAT headers). Others are compatible, but deviate. For example: -h for the Mach-O header, will print the details about the header not on a single line, but on multiple ones:

Zephyr:~ morpheus$ otool -h /bin/ls
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x80           2    19       1816 0x00200085

Zephyr:~ morpheus$ jtool -h  /bin/ls
Magic:	64-bit Mach-O
Type:	executable
CPU:	x86_64
Cmds:	19
size:	1816 bytes
Flags:	0x200085

The reason I deviated is to make jtool more grep(1) friendly, so you could isolate the line containing the attribute you want. In the case of the most common option (-l), however, it actually makes sense to combine the output from otool's multiple lines into less lines, in order to make it more friendly. Case in point:

$ otool -l /bin/ls                $ jtool -l /bin/ls
Load command 0                    LC 00: LC_SEGMENT_64          Mem: 0x000000000-0x100000000      __PAGEZERO
      cmd LC_SEGMENT_64           LC 01: LC_SEGMENT_64          Mem: 0x100000000-0x100005000      __TEXT
  cmdsize 72                             Mem: 0x100004430-0x100004604            __TEXT.__stubs  (Symbol Stubs)
  segname __PAGEZERO                     Mem: 0x100004430-0x100004604            __TEXT.__stubs  (Symbol Stubs)
   vmaddr 0x0000000000000000             Mem: 0x100004920-0x100004b10            __TEXT.__const  
   vmsize 0x0000000100000000             Mem: 0x100004b10-0x100004f66            __TEXT.__cstring        (C-String Literals)               
  fileoff 0                              Mem: 0x100004430-0x100004604            __TEXT.__stubs  (Symbol Stubs)
 filesize 0                        LC 02: LC_SEGMENT_64          Mem: 0x100005000-0x100006000      __DATA
  maxprot 0x00000000                         ....
 initprot 0x00000000
   nsects 0
    flags 0x0
Load command 1
      cmd LC_SEGMENT_64
  cmdsize 552
  segname __TEXT
   vmaddr 0x0000000100000000
   vmsize 0x0000000000005000
  fileoff 0
 filesize 20480
  maxprot 0x00000007
 initprot 0x00000005
   nsects 6
    flags 0x0

otool's output is simply terrible, in that it is thinly spread over multiple lines. jtool condenses it so you can clearly see what matters, and take less space (or scrolling through pages and pages of more(1) or less(1)). Another benefit is using grep:

# Finding details about a segment with otool(1):
Zephyr:~ morpheus$ otool -l /bin/ls | grep LC_SEGM
      cmd LC_SEGMENT_64
      cmd LC_SEGMENT_64
      cmd LC_SEGMENT_64
      cmd LC_SEGMENT_64
# Really helpful, right? Yes, you could do that with grep -A, but I find this simpler: 
Zephyr:~ morpheus$ jtool -l -v /bin/ls | grep LC_SEGM
LC 00: LC_SEGMENT_64          Mem: 0x000000000-0x100000000	File: Not Mapped	---/---	__PAGEZERO
LC 01: LC_SEGMENT_64          Mem: 0x100000000-0x100005000	File: 0x0-0x5000	r-x/rwx	__TEXT
LC 02: LC_SEGMENT_64          Mem: 0x100005000-0x100006000	File: 0x5000-0x6000	rw-/rwx	__DATA
LC 03: LC_SEGMENT_64          Mem: 0x100006000-0x100009000	File: 0x6000-0x8750	r--/rwx	__LINKEDIT

Notice in the above use of -v, which adds verbosity - in the case of LC_SEGMENT commands, the file mappings.

For universal (fat) files, jtool provides the same -arch switch to single out a particular architecture. There are several differences from otool:

The last option there seems unusual - after all, how many architectures can there be in a fat file? Yeah, well, I thought so too, until I encountered TaiG's 8.4 Jailbreak, which not only uses fat binaries of some 26-27 architectures, but also uses duplicate entries - confusing otool since it only matches the first one - but jtool can single out a particular slice this way. (q.v my writeup on that jailbreak here for an example).

dyldinfo(1) compatible options

The dyldinfo(1) utility is really useful to explore the inner workings of Apple's dynamic loader. But it's not available for iOS. jtool can match most of dyldinfo's options easily, and can in fact emulate it busybox-style, if you symbolically link dyldinfo to it and call it:

Zephyr:~ morpheus$ ln -s `which jtool` /tmp/dyldinfo
Zephyr:~ morpheus$ !$
Usage: dyldinfo [-arch <arch>] <options> <mach-o file>
-dylibs           print dependent dylibs
-rebase           print addresses dyld will adjust if file not loaded at preferred address
-bind             print addresses dyld will set based on symbolic lookups
-weak_bind        print symbols which dyld must coalesce
-lazy_bind        print addresses dyld will lazily set on first use
-function_starts  print table of function start addresses
# Call jtool with some dyldinfo option:
Zephyr:~ morpheus$ /tmp/dyldinfo -bind /bin/ls
bind information:
segment section          address        type    addend dylib            symbol
__DATA  __got            0x100005000    pointer      0 libSystem.B.dylib    __DefaultRuneLocale
__DATA  __got            0x100005008    pointer      0 libSystem.B.dylib    ___stack_chk_guard
__DATA  __got            0x100005010    pointer      0 libSystem.B.dylib    ___stderrp
__DATA  __got            0x100005018    pointer      0 libSystem.B.dylib    ___stdoutp
__DATA  __got            0x100005020    pointer      0 libSystem.B.dylib    _optind
__DATA  __nl_symbol_ptr  0x100005028    pointer      0 libSystem.B.dylib    dyld_stub_binder
# Call the real binary:
Zephyr:~ morpheus$ dyldinfo -bind /bin/ls
bind information:
segment section          address        type    addend dylib            symbol
__DATA  __got            0x100005000    pointer      0 libSystem        __DefaultRuneLocale
__DATA  __got            0x100005008    pointer      0 libSystem        ___stack_chk_guard
__DATA  __got            0x100005010    pointer      0 libSystem        ___stderrp
__DATA  __got            0x100005018    pointer      0 libSystem        ___stdoutp
__DATA  __got            0x100005020    pointer      0 libSystem        _optind
__DATA  __nl_symbol_ptr  0x100005028    pointer      0 libSystem        dyld_stub_binder
# Show weak binds: (Thanks Guhyeon)
Zephyr:~ morpheus$ jtool -weak_bind /usr/sbin/weakpass_edit  
bind information:
segment section          address    index  dylib            symbol
__DATA  __la_symbol_ptr  0x100003040           this-image    __ZdlPv
__DATA  __la_symbol_ptr  0x100003048           this-image    __Znwm

Since jtool basically mimics dyldinfo 1:1, refer to the latter's man(1) page for more detail. I use -function_starts, -bind and -lazy_bind most often.

New Options


You may be familiar with the pagestuff(1) command, which dumps the layout of a Mach-O binary. Syntax is odd, placing the filename as the first argument and a bit crude:

$ pagestuff ~/Documents/iOS/JB/Pangu9/pguntether -a | more
File Page 0 contains fat file headers
File Page 1 contains empty space in the file between:
    fat file headers and
    Mach-O file for armv7
File Page 8 contains empty space in the Mach-O file for armv7 between:
    Mach-O headers and
    contents of section (__TEXT,__text)
File Page 9 contains contents of section (__TEXT,__text) (armv7)
Symbols on file page 9 virtual address 0x91f8 to 0xa000
... this little piggy went to the river...

jtool --pages provides (what I believe to be) nicer output, like so:

# Cut to the chase:
Phontifex-Magnus:~ root# ARCH=arm64 jtool --pages /pguntether
0x0-0x38000	__TEXT
0x53e8-0x34f50	__TEXT.__text
0x34f50-0x35490	__TEXT.__stubs
0x35490-0x359e8	__TEXT.__stub_helper
0x359e8-0x36a11	__TEXT.__const
0x36a11-0x379cf	__TEXT.__cstring
0x379cf-0x37e30	__TEXT.__objc_methname
0x37e30-0x37e64	__TEXT.__gcc_except_tab
0x37e64-0x37ffc	__TEXT.__unwind_info
0x38000-0x3c000	__DATA
0x38000-0x38048	__DATA.__got
0x38048-0x383c8	__DATA.__la_symbol_ptr
0x383d0-0x38530	__DATA.__const
0x38530-0x389d0	__DATA.__cfstring
0x389d0-0x389d8	__DATA.__objc_imageinfo
0x389d8-0x38b80	__DATA.__objc_selrefs
0x38b80-0x38bd8	__DATA.__objc_classrefs
0x38bd8-0x3b99c	__DATA.__data
0x3c000-0x3f120	__LINKEDIT
0x3c000-0x3c030	Rebase Info     (opcodes)
0x3c030-0x3c2d8	Binding Info    (opcodes)
0x3c2d8-0x3cb98	Lazy Bind Info  (opcodes)
0x3cb98-0x3ce70	Exports                  
0x3ce70-0x3d008	Function Starts
0x3d008-0x3d080	Code Signature DRS
0x3d008-0x3d008	Data In Code
0x3d080-0x3dcd0	Symbol Table
0x3dcd0-0x3e074	Indirect Symbol Table
0x3e074-0x3e974	String Table
0x3e980-0x3f120	Code signature


Sometimes you're just interested in a quick lookup of an offset to an address, without having to sift through pagestuff(1) or jtool --pages output. That's where -a comes in:

Phontifex-Magnus:~ root# ARCH=arm64 jtool -a 0x38b81 /pguntether 
Offset 38b81 in file will be loaded at 100038b81 (__DATA.__objc_classrefs)
Phontifex-Magnus:~ root# ARCH=arm64 jtool -a 0x81 /pguntether 
Requested offset 129 falls in Load Command 1 (LC_SEGMENT_64)


-S does the same thing that nm(1) does. The option is simply -S (uppercase) and -v for full info (as in, which dylib the symbol is in). For nm, that -v is -m. Unlike dyldinfo, the options aren't compatible.

What's really useful here is the quick scriptability, like if you're looking for a particular symbol across many binaries:

# If something calls SecTaskCopyValueForEntitlement, then it checks entitlements:
# (though there are other ways, like calling csops[_audittoken] directly..)
Phontifex-Magnus:/usr/libexec root# for d in /usr/libexec/* ; do  \
     if jtool -S $d 2>/dev/null | grep SecTaskCopy > /dev/null; then \
        echo $d checks entitlements...; \
     fi;  \
/usr/libexec/OTATaskingAgent checks entitlements...
/usr/libexec/adid checks entitlements...
/usr/libexec/configd checks entitlements...
/usr/libexec/crash_mover checks entitlements...
/usr/libexec/demod checks entitlements...
/usr/libexec/demod_helper checks entitlements...
/usr/libexec/keybagd checks entitlements...
/usr/libexec/locationd checks entitlements...
/usr/libexec/lockbot checks entitlements...
/usr/libexec/lskdd checks entitlements...
/usr/libexec/lskdmsed checks entitlements...
/usr/libexec/mobile_obliterator checks entitlements...
/usr/libexec/mobileassetd checks entitlements...
/usr/libexec/nlcd checks entitlements...
/usr/libexec/pfd checks entitlements...
/usr/libexec/pkd checks entitlements...
/usr/libexec/rolld checks entitlements...
/usr/libexec/securityd checks entitlements...
/usr/libexec/timed checks entitlements...
/usr/libexec/transitd checks entitlements...
/usr/libexec/webinspectord checks entitlements...

Well, not quick, maybe (it grows on you), but certainly efficient.

(Luca, I'm working on getting that symlink issue so you can ln -s ......jtool /usr/bin/nm :-)

Code Signing Options

Code signing options are, IMHO, the 2nd most useful feature of JTool. With iOS security revolving around code signatures and entitlements, it's important to have a way to quickly determine what given entitlements a binary possesses and how it is signed. OS X has codesign(1), but I find it crude (at best) - and what more there's no port to iOS, where it's really necessary.


The --sig option allows you to validate or display a code signature. With no more arguments, it will display the signature blobs, and perform silent validation:

Phontifex-Magnus:~ root# jtool --sig /System/Library/CoreServices/SpringBoard.app/SpringBoard
Blob at offset: 7453808 (47168 bytes) is an embedded signature
Code Directory (36570 bytes)
		Version:     20100 					# 20100 or the older 20001
		Flags:       adhoc					# adhoc (iOS System) or none
		CodeLimit:   0x71bc70					# Code signature maximum reach
		Identifier:  com.apple.springboard (0x30)		# App/Bundle identifier
		CDHash:	     8dcf2b2e1839d86068672ac51386d6f6692eb7c4	# Code Directory Hash (calculated)
		# of Hashes: 1820 code + 5 special			# Code and special slots
		Hashes @170 size: 20 Type: SHA-1			# Offset of Hash slot 0
 Empty requirement set (12 bytes)					# Blob 1
Entitlements (10515 bytes) (use --ent to view)				# Blob 2
Blob Wrapper (8 bytes) (0x10000 is CMS (RFC3852) signature)		# Blob 3

As usual, -v will be more verbose, dumping more detail about the sub-blobs (specifically, offsets) as well as every page hash in the CodeDirectory (including special slots, if any):

Phontifex-Magnus:~ root# jtool -v --sig /System/Library/CoreServices/SpringBoard.app/SpringBoard 
Blob at offset: 7453808 (47168 bytes) is an embedded signature of 47149 bytes, and 4 blobs
	Blob 0: Type: 0 @44: Code Directory (36570 bytes)
		Version:     20100
		Flags:       adhoc (0x2)
		CodeLimit:   0x71bc70
		Identifier:  com.apple.springboard (0x30)
		CDHash:	     8dcf2b2e1839d86068672ac51386d6f6692eb7c4
		# of Hashes: 1820 code + 5 special
		Hashes @170 size: 20 Type: SHA-1
			Entitlements blob:	52a9a55ab0c40a12e3a915bcea29d602419a21f8 (OK)
			Application Specific:	Not Bound
			Resource Directory:	c0c8a907e2663ee1dc33a137fbfb55f253bcf464 (OK)
			Requirements blob:	3a75f6db058529148e14dd7ea1b4729cc09ec973 (OK)
			Bound Info.plist:	f852d93c0a643f9d774e7d2057664d769d96d528 (OK)
			Slot   0 (File page @0x0000):	34f05f5bc893e54b69c7a3f87137f86f49a09e5d (OK)
			Slot   1 (File page @0x1000):	e515677a23b37411199258e68da4a03cd184ffae (OK)
			Slot 1818 (File page @0x71a000):	e563b1335082da705a949c7c62c15c6da47876b2 (OK)
			Slot 1819 (File page @0x71b000):	496567ae0e21a025eccb859c9d2e20bacb88075d (OK)
	Blob 1: Type: 2 @36614:  Empty requirement set (12 bytes)
	Blob 2: Type: 5 @36626: Entitlements (10515 bytes) (use --ent to view)
	Blob 3: Type: 10000 @47141: Blob Wrapper (8 bytes) (0x10000 is CMS (RFC3852) signature)

jtool will usually find the special slot resources automatically, but if not, you might want to help a little by specifying --appdir


If you want to examine the entitlements claimed by a binary, all it takes it --ent. Same as codesign -d --ent, but more accurate, because the latter also presents the blob header (as junk <FA><DE>qq^@^@)^S):

Phontifex-Magnus:~ root# jtool  --ent /System/Library/CoreServices/SpringBoard.app/SpringBoard 
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	# ....

Once again, this is designed with shell scripting and grep(1) in mind, so you can do a for file in /usr/libexec/*; do jtool --ent $file; done or other pattern, to get all those undocumented entitlements used by Apple all over the place (I'm also in the process of compiling those into a database I'll share freely when I'm done).


The --sign option is an option I've had for a long time internally, and finally released. It is meant to self-sign binaries much in the same way as Saurik's ldid. As with ldid, you can specify an entitlements file to be inserted (using --ent filename). The result is designed to be indistinguishable, once you get past the (temporary) warning/disclaimer:

morpheus@Zephyr (~)$  jtool --sign binary
  * Warning: Code signatures are still defined as Beta. Lots of minutiae to deal with, *
  * and it isn't as easy as you might think to get things right with all these hashes. *
  *                                                                                    *
  * I suggest you use --sig -v to validate your pseudo-signed binaries.                *
  * Try JDEBUG=1 if you want to follow along.                                          *
Warning: Destructive option. Output (397920 bytes) written to out.bin
morpheus@Zephyr ()$  ldid -S binary
morpheus@Zephyr ()$  ls -l binary out.bin
-rwxr-xr-x  1 root  staff  397920 Oct 18 03:30 binary
-rwxr-xr-x  1 root  staff  397920 Oct 18 03:30 out.bin
morpheus@Zephyr ()$  diff out.bin binary

morpheus@Zephyr ()$  echo $?

There's a reason why that warning is there, and why the default is to write out to an out.bin, rather than in-place - code signatures are a risky business, and may be hard or impossible to reproduce if lost. Be warned.

Rather than using codesign(1) or codesign_allocate(1) internally, jtool performs the entire sequence step by step, so you can see for yourself how code signing ensues - using JDEBUG=1:

morpheus@Zephyr (~)$ JDEBUG=1 ARCH=armv7 jtool  --sign --ent ent.xml  /tmp/a
Very last section ends at 0xc11c, so that's where the code signature will be
Aligning to 16 byte offset - 0xc120
Allocating Load Command
First section offset is 7ea4; Mach header size is 580
Patching header to reflect inserted command @580
Patching __LINKEDIT to reflect new size of file
Setting LC fields
Allocating code signature superblob of 669 bytes, with 3 sub-blobs..
Setting LC_CODE_SIGNATURE's blob size to match CS Blob size..
Creating Code Directory with 13 code slots and 5 special slots
Calculating Hashes to fill code slots..
Need to pad 288 bytes to page size in last page (because code signature is also in this page)
Padding to page size with 3808 bytes
Calculating (modified) last page hash
Adding empty requirements set to 447
Filling the special slot (-2) for requirements blob...
 Copying entitlements blob to 459
Filling the special slot (-5) for entitlement blob...
 Crafting New Mach-O
Inserting 669 bytes Blob at 49440, bringing new file size to 50109
Warning: Destructive option. Output (50109 bytes) written to out.bin

DYLD Shared Caches

Apple prelinks most dylibs and plugins into a "Shared Library Cache". The SLC is located in /var/db/dyld (OS X) and /System/Library/Caches/com.apple.dyld (iOS). The OS X cache also has a "map", but the iOS one doesn't have a map.

In iOS, there are no free floating dylibs - it's all in the cache. This makes caches very important, and there are quite a few "decaching" tools. JTool doesn't offer comprehensive support, only the stuff I find useful. In particular:

# Identifying a shared cache file
Phontifex-Magnus:~ root# jtool  /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64
File is a shared cache containing 1007 images (use -l to list)
# Finding the map of a shared cache file (formerly, --map which isn't needed anymore
Phontifex-Magnus:~ root# jtool -l /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64
File is a shared cache containing 1007 images
   0:        180028000 /System/Library/AccessibilityBundles/AXSpeechImplementation.bundle/AXSpeechImplementation
   1:        180030000 /System/Library/AccessibilityBundles/AccessibilitySettingsLoader.bundle/AccessibilitySettingsLoader
   2:        18003c000 /System/Library/AccessibilityBundles/AccountsUI.axbundle/AccountsUI
   3:        180040000 /System/Library/AccessibilityBundles/AddressBookUIFramework.axbundle/AddressBookUIFramework
   4:        180048000 /System/Library/AccessibilityBundles/CameraKit.axbundle/CameraKit
   5:        180058000 /System/Library/AccessibilityBundles/CameraUI.axbundle/CameraUI
   6:        180064000 /System/Library/AccessibilityBundles/HearingAidUIServer.axuiservice/HearingAidUIServer
   7:        180074000 /System/Library/AccessibilityBundles/MapKitFramework.axbundle/MapKitFramework
   8:        180080000 /System/Library/AccessibilityBundles/MediaPlayerFramework.axbundle/MediaPlayerFramework
# And the -h (header) option, I added after Pangu 9. See anyhing different, boys and girls? *hint* *hint*
Phontifex-Magnus:~ root# jtool -h /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64           
File is a shared cache containing 1007 images (use -l to list)
6 mappings starting from 152, Mapping Count: 1007. 344 Images starting from 0
DYLD base address: 0, Code Signature Address: 25a4c000 (2f0f02 bytes)
Slide info: 1ca18000 (1a4000 bytes)
Local Symbols: 204c8000 (5584000 bytes)
mapping r--/r--    0MB        180000000 -> 180028000         (25d40000-25d68000)
mapping r-x/r-x  384MB        180028000 -> 1980a4000         (28000-180a4000)
mapping rw-/rw-   73MB        19a0a4000 -> 19ea18000         (180a4000-1ca18000)
mapping r--/r--   12MB        1a0a18000 -> 1a16b0000         (1ca18000-1d6b0000)
mapping r--/r--    0MB        1a16b0000 -> 1a16b4000         (25d68000-25d6c000)
mapping r--/r--   46MB        1a16b4000 -> 1a44c8000         (1d6b4000-204c8000)

Jtool can easily decache files - simply use -e dylib on a cache file to get your dylib. Note that the extracted dylib will be big - circa 50MB - because JTool does not deconstruct the merged __LINKEDIT segment.

But why extract??? A key feature of Jtool that other decachers do not have, is its ability to work on a dylib while still in the cache! This not only saves you disk space, but also allows you to see how cached dylibs interact with eachother (e.g. cross dylib calls). To use this feature, simply specify ":" as a delimiter between the cache and the dylib name. All the standard features (e.g. -l, -S, etc), work, but the really useful feature is -d. For example:

root@phontifexMagnus (~)# jtool -d /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64:libMobileGestalt | more
Found but not extracting - setting File Start to 16a10000
Disassembling from file offset 0x11a4, Address 0x196a111a4 
Processing cached file from offset 16a38000  size: 25d44000
   196a111a4    ADRP   x8, 32640                ; ->R8 = 0x19e991000 
   196a111a8    STR    X0, [X8, #280]           ; *0x19e991118 = X0 ARG0
   196a111ac    RET                     
   196a111b0    ORR    W1, WZR, #0x1            ; ->R1 = 0x1 
   196a111b4    B      _func_196a111b8  ; 196a111b8

Objective-C Support

jtool now recognizes objective-C. Use "-d objc" to get a list of classes:

# Use with -v for more detail, without just the classlist
Zephyr:~ morpheus$  jtool -d objc ~/Documents/RE/SpringBoard9.2 

Using -v here will actually reverse the classes, a la classdump-Z, though with a bit more detail, and - as usual - color :-). You can also specify a classname as the argument to -d, and jtool is smart enough to figure out you want to reverse it:

(Yes, I know that the protocols aren't demangled yet - I'm working on that)

AND you can then use -d to get to a particular method. To get buy the horrid +/-/[/spaces, you can use C++ like syntax:

Note that this is done by reconstructing the classes without linking with Apple's Objective-C library so it also works on the Linux version! However, because I do the class walking by myself it might be A) still a bit slow (Springboard is a prime example, with countless classes) and B) possibly buggy. You can use NOOBJC=1 to disable objective-c, but please let me know if you encounter bugs here.

Darn Cool Options

MIG Detection (v0.98.9999 02/02/2016)

No special switches for this one - If jtool detects you're dumping a __DATA.__const segment of a MIG enabled binary (e.g. the kernel or one of the many daemons of /usr/libexec), it will automatically ferret out the MIG dispatch tables. In full color, too :-)

You can try this on the OS X kernel for fun (that's /System/Library/Kernels/kernel), but doing so on the iOS kernel is even more useful. That said, there's also Joker for that.

Misc. Features (Jan 2017)

  • Significant speedup when companion files are detected
  • More robust OBJ-C handling - won't crash as much. And please see note below on reporting crashes!
  • New option: -D: to decompile - like -d, but will only show "; " lines, i.e. lines that Jtool decompiled. You could always do that with grep(1), but color got confusing.

    Can your IDA do this:

  • (err... maybe some IDAPython can do it. Dunno. But jtool does it for free :-)
  • Forget extracting from the Shared Library Cache! jtool can now resolve all symbols in the cache, even when they are in a different dylib! This is super useful because AAPL's reconstructed libraries (in ~/Library/Developer/Xcode/*DeviceSupport) lose external symbols and mess up some __DATA refs. Note that SLC files still have private symbols <redacted>. Working on it.
  • Stack emulation

    Emulating the stack for functions is something I've been putting off for a long time. But recently I've decided it's time.. And by only following STR/STP I've managed to go a reasonably long way: jtool can now detect blocks created on the fly, and tell you what the function they contain is. For example, dispatch_[a]sync or xpc_connection_set_event_handler:

    Better still, it can follow mach_msg construction on the fly. This is really super useful - One of my @TODOs for MOXiI Vol.1 is to document all of SpringBoard, Backboard, and the other daemon's MIGs. You can see how much time it saves me to just do this:

    # Note that:
    # A) you need to disable color for the regexp to work (because of curses sequences)
    # B) the regexp is then "mach_msg(" (decompiled function) or "begins with _" (function label)
    # C) jtool does everything in the cache, no need to extract!
    morpheus@Zephyr (~/Documents/Work/JTool) % JCOLOR=0 jtool -d dyld_shared_cache_arm64:BackBoardServices |
    |                                           egrep "(_mach_msg\(|^_)" | less
    _BKSRestartActionOptionsDescription: # No mach_msg here 
    _BKSTouchDeliveryPolicyServerGetProxyWithErrorHandler: # No mach_msg here either..
    ; _mach_msg(6000000)
    ; _mach_msg(6000001)
    ; _mach_msg(6000002)
    ; _mach_msg(6000003)
    morpheus@Zephyr (~/Documents/Work/JTool) % jtool -d dyld_shared_cache_arm64:BackBoardServices |
                                                egrep "(_mach_msg\(|^_)"       
    ; _mach_msg(6000000)
    ; _mach_msg(6000001)
    ; _mach_msg(6000002)
    ; _mach_msg(6000056)
    ; _mach_msg(6000057)
    ; _mach_msg(0)  # OK, so it's not perfect -- I don't follow FP operations (yet)!
    ; _mach_msg(6000059)
    ; _mach_msg(6000059)
    ; _mach_msg(6000061)
    ; _mach_msg(6001000)
    ; _mach_msg(6001001)

    decompilation improvements

    At least till I can support arbitrary function definitions, I provided the option to define a suffix to any arbitrary function in the companion file: see below

    This way jtool will not just follow arguments, but will spit them out when the function is called. Currently supported types are CFString, cstring (which get you error messages, complains, and logger functions

    There's much more, but I'll pick this up later. If you use jtool and find bugs, don't just complain silently - LET ME KNOW and I'll be glad to fix. Likewise if you have any suggestions for improvement. Remember - I build the functionality around my own use cases - and I'll be happy to adjust if you have any others.
    And I don't care if you use IDA or IDA can do this (if they can't, they'll adopt this in their next version) or there's IDAPython, or whatever. This is the tool I use, I find it useful, and I'm encouraging others to use it (freely, unlike IDA/Hopper and their ilk) and suggest improvements.

    More to come:

    Frequently Asked Questions