Joker is a quick and dirty iOS kernelcache handling utility I've written to assist in my reverse engineering. Apple tries their damn hardest to make reversing the kernel as hard as possible: With every release, more symbols are stripped. The kernelcache, being prelinked, requires less symbols to begin with (and tables in memory, as all LINKEDIT segments, are jettisoned). And - let's not forget - the kernelcache is encrypted. 32-bit kernelcaches can be decrypted thanks to the holy work by @xerub and others, but no 64-bit kernelcache keys exist (publicly), and the only way to "see" the kernel is by dumping it.
This hasn't stopped jailbreakers in the past, and will hopefully not stop us in the future. joker is another humble contribution I can provide to the community, and to all reversers out there. It uses the same machlib as its sibling jtool, and the two in fact can finally play well together (see below)
The tool is primarily designed for iOS (naturally), but since the XNU data structures are pretty much identical, it works pretty well with some switches (-m, -s) on the OS X kernel, as well.
With no arguments, joker will strive to explain itself:
With just a filename, it will provide you with a brief identification of the file in question, by looking at the LC_SOURCE_VERSION. It will also print the sysent table address.
Since 32-bit kernelcaches can be obtained in entirety, before LINKEDIT and PRELINK_INFO are removed, joker can both list as well as extract kexts (what I call 'kextract'):
To extract, simply specify the name of the kext , not the bundle ID (annoying because of spaces, I know - but I'll add that in later)
Note that the kernelcache you see in OS X (shoved deep in /System/Library/Caches/com.apple.kext.caches/Startup/kernelcache) is lzss or lzvn (10.10+) compressed, so this won't work there - it doesn't make sense to implement decompression when you have the free floating kexts in /System/Library/Extensions.
32 and 64-bit (OS X too!)
Using -s will get you a full dump of the __sysctl_set section. This is useful because the sysctl descriptors have plenty of useful kernel addresses - both of the variables behind the sysctl as well as any handlers:
Oh, and - this works on OS X kernels too.
Hands down the most useful symbolication is via "-m", which symbolicates Mach traps and UN*X syscalls. joker will automatically suppress enosys and "old" syscalls which don't really do anything, but will nonetheless provide you with the addresses:
The latest and greatest feature (besides 64-bit) is JTool support - the latter can now work with "companion files", which are simply text files which follow a simple convention (0xaddress:symbol:comment). You can use the new -j to auto-create that file
This is especially useful since IDA pro 6.5, for all its amazing features (and hefty price tag) provides this helpful notification when loading a kernel dump :
lso, Mach traps are back - task_for_pid is especially useful (to apply the tfp0 patch, which brings back the kernel_task to userland.
Mach uses messages, and message serialization uses MIG. The Mach Interface Generator generates the boilerplate code required for parsing messages, and - as can be expected - leave easily recognizable patterns all over the place. joker now recognizes the dispatch tables left behind (mig_e), and proceeds to symbolicate the functions as well.
Note that the functions symbolicated are the proxies - the actual functions that do the work are called from these. But it's fairly simple to figure out, because the proxies basically call the message check functions, then the ...internal ones.
This is primarily useful for iOS, of course, but all the symbols are stripped there. So the ..internal pattern can be more readily seen with the OS X xnu, wherein the internal functions are exported (but the mig ones from the tables aren't:
Again, note I use x86_64 here just to illustrate the idea because you can see the internal function is _thread_swap_mach_voucher, which means joker got the MIG dispatch correctly. To get this on iOS, by cross-correlating ARM64 and x86_64 assembly it's trivial to symbolicate the rest..
With iOS 10 and OS X 12 out, and the kernel segment layout modified, it's time for an improved joker! v3.0 is worthy of a major number increment, because it features integration with my machlib (the engine of disarm and jtool), which allows joker to disassemble in order to find functions! Presently I've implemented matching for the "low hanging fruit" (functions taking in strings), but this can be expanded for complex cases and smart pattern matching.
You might be wondering if joker hasn't lost its raison d'être, with Apple (intentionally/accidentally) providing a decrypted kernelcache. I believe it hasn't - namely:
kexts are still fused in tight - so you need -k/-K
Mach Traps & MIG, syscalls and sysctls aren't exported - so you need -m, -s and -S respectively. I've updated the syscall table, btw, for all 520 syscalls, and almost all MIGs.
A lot of other interesting symbols aren't exported. XNU has O(20k) symbols, of which iOS XNU only exports O(4k) - those needed by kexts
Decompilation is not the usual functionality you'd want, and is also a bit slow. So try -j and let it take its time. You'll see:
In some cases (notable, PE_parse_boot_argn), joker is more accurate than the actual symbol table, since the exported symbol is a wrapper over the real implementation, which is the one used frequently. If you want more symbols supported, let me know via the website forum, please.
Joker 3.0 also does MACF policies - presently only for Sandbox.kext , as AMFI's is now loaded in code (but *does* do AMFI as well in dumps). This gets the new hooks in Sandbox 570 (which I've highlighted below) and looks like this:
Other enhancements in 3.0 are JTOOLDIR=... so you can extract to elsehwere than /tmp, and a couple of really exciting features (like the auto-detection of IOUserClients), but that won't be out until the final version. The 2nd Beta adds detection of secure monitor (SMC) and the ARM exception vector, long missing in 64-bit
I use joker extensively (and exclusively, for lack of IDA) when writing my notes from iOS/OSX, now in its third year, and I hope you find it useful.
Again, if you encounter a bug/need a feature, don't just dismiss this and fire up IDA - tell me, and I'll gladly fix/add. I build my tools around my use cases, which is why I can't get everything
Now gets names of all kexts right, by either method #1 or method #2
Fixed that Annoying ID=... bug I had
joker now auto-symbolicates stubs in kexts!!! (most) stubs point to exported symbols in the kernel proper, and thanks to AAPL we now have those exports! joker auto follows the disassembly of stubs, e.g.
As you can see, machlib can follow the registers, and figure out the addresses. So, if the symbol for what's in X16 is exported, you can find it easily:
But.. why should you need to do this yourself? joker now does this automatically!
Zeng BanXian - this one's for you :-)
Minor but useful change: If you feed joker an uncompressed (decrypted) kernelcache it will still complain, but will extract the KPP Mach-O at its end for you to /tmp. Still no reliable split-kexts, but I'm almost there (got other KPP-ressing matters to deal with..). Get beta here.
Split Kexts (iOS 10b2+)
Operates directly on a compressed kernelcache. I used the LZSS.c code from Apple's old iBoot. Also, ARM64 version is back, so you can run it on an iOS10 64-bit device directly!
Joker gets in the zone - Mach zones, that is:
- Not 4.0 because I still don't have USerClients working
- Updated for iOS 11 β (Still not tested on MacOS 13, but should work)
- Sandbox decomp likely broken (haven't tested, but there are more operation names , like dynamic-code-generation...I know, I'll fix it)
Ok. So user clients are working :-) Specifically, here's how to use this:
When kextract™ing an extension, joker will produce a companion file. The major change is that it now also scans __DATA_CONST.__const (where IO objects are) to find plenty of references back into the main kernel. It symbolicates all of those, and this gets you the IO* and OS* methods when you follow up with a jtool --jtooldir ... -d .. on the kext (remember to get the right dir for the companion file). It also detects the common structure with the layout of function pointers , which can then be identified as the IOExternalMethodDispatch struct.
I DO NOT provide you user client names (yet). But I do provide a simple user_client_x_method_y format, like so:
so you get the idea.
IF YOU WOULD LIKE TO HELP submit the names of UserClient methods you find through The forum , and future versions of Joker will provide those hard coded. But with 150+ drivers, I can't do this unless people help.
v3.99 - update and minor fix for iOS 11β1
v3.2 - decompilation now successfully gets all zones by decompiling zinit and tracking the return value to globals
v3.1 - works on compressed kernelcaches (not iPhone 7 bvx2 (LZFSE), but classic complzss)
v3.0 - kext support, split kext, MACF Policies, and preliminary sandbox decomp
-- Minor fix to specify output dir for kextraction, as JOKER_DIR=
-- validation on memem in case signature can't be found..
v2.3 - Almost fully integrated with MachLib - Correctly identifies all kexts, even in a dump
v2.2.1 - kextraction on 64 bit works! Can also ID the kexts, despite absence of __PRELINK_INFO. Use "all" for all kexts.
v2.1.1 - preliminary kextraction from a 64 bit dump (sans __PRELINK_INFO), and fix for Billy's Bug
v2.1 - (re)Added Mach trap table, MIG subsystems (+150 symbols!)
v2.0 - jtool integration (write to companion file)
BackEndBilly (for telling me about the kext extraction bug)
Use it at will. You have my blessing, and I won't complain. A nice thank you would be appreciate if you find it useful, as well as feature requests, if you have any.
joker is open source, so you can see the patterns in the kernelcache it locks on in order to its work. Machlib (its dependency) isn't, but I can license the latter if you're interested.