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:
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:
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:
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'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:
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:
jtool will refuse to touch a fat binary unless you specify the architecture
You can specify the architecture with -arch, or specify ARCH= as an environment variable. This makes it useful to set a default.
You can specify an architecture by number
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:
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.
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:
jtool --pages provides (what I believe to be) nicer output, like so:
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:
-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:
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:
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):
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):
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:
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:
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:
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:
jtool now recognizes objective-C. Use "-d objc" to get a list of classes:
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.
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.
More to come:
Interfacing from your own code with machlib
Frequently Asked Questions
Is this open source? No. Neither is my other reversing tool, Dextra (for Android). Free, yes. But not open source.
*Why* is this not open source? Is Hopper? Is IDA? So I'm in good company :-) jtool started as a simple otool -l, but things spiraled out of control when I started doing the disassembly. There's just too much original code in there for me to just share with whatever license, that others can rip and not even mention credit in passing. Sorry. I speak from bitter experience here. It's still FREE though, which is more than what most tools (save, of course, otool) can say.
Can I write plugins? better. You can script it with shell commands (it's designed to be grep(1) and other filter-friendly), and you can write entire programs to use jtool's logic - machlib and disarm - as dylibs.
What's up with color? Why is it disabled when I pipe, and messy when I JCOLOR=1 and pipe? Because color is done via curses. It's the default when you DON't pipe (i.e. jtool detects the stdout is a tty), and when you do it's disabled unless you insist (JCOLOR=1 or export...). But if you do that, use less -R so less (which is more) can handle the sequences. Or try --html for colorized, hyperlinked HTML output you can save to a file.
What's the deal with ARM32/Thumb? Though the disassembler originally was for 32, when it became clear it's a choice between my sanity and support ARM32/Thumb, I opted for the former. This way I can write you other books and tools. jtool actually is pretty good still with ARM, but it's not officially supported. ARM64 is, though, and is fairly comprehensive (sans FP/SIMD, which only SpringBoard and a few other daemons use. I'll get to that at some point, if I get stranded for hours with absolutely no Internet and nothing else to do). Update: ARMv7k is kind of back
Why not use foundstone or some other disassembly framework? Because it wouldn't be Jtool then, now, would it? :-) The code is 100% original and mine, sharing no sources what-so-ever with any other code, open or closed (save for obvious include files)
How do we contribute? By suggesting improvements, asking for features, or reporting bugs on the OS X Book Forum
What can we do that will be COUNTER productive, and not in any way contribute? You can bitch on Twitter whenever you encounter a crash:
and you can NOT read the manual page which distinctly says that jtool does crash in some cases. I wish you could use otool instead, but AAPL ruined what was good in it by rewriting it to use objdump.
Seriously, IF JTOOL CRASHES FOR YOU, LET ME KNOW AND I'LL FIX IT. COMPLAINING ACHIEVES NOTHING, AND THERE IS AN INSANE AMOUNT OF LOGIC IN JTOOL, SO YEAH, IT AIN'T PERFECT. But hey - it leaves otool in ashes, and it's FREE. I don't expect thanks, but just slamming the tool and not even reporting a bug is being a douche.
When is version 1.0 coming out? With MOXiI 2.0 :-)
And what about that MOXIiI 2.0 already!? Hey. Just one person here. And I DO have a life to maintain outside of this. And it's a herculean effort that will make MOXiI 1.0 look obsolete and entry-level. So soon. It'll be worth the wait. I promise. Present guesstimate is for late March-April of this year (2016)