/classdumpios

classdump-c: Custom macOS / iOS / tvOS port updated to work on iOS 13-16+ supporting chained fixups, can also dump entitlements now as well

Primary LanguageObjective-C

class-dump-c

Building

There are 5 different targets in this Xcode project, the macOS bin & library, an iOS library & bin and a tvOS bin. Choose the respective target and build all of them through Xcode, OR build all the targets using the bundled build scripts for Debug and Release respectively.

Libraries

The core functionality of classdump has also be made into a separate library for iOS and macOS, its still a bit rough around the edges but I primarily did that to use inside HeaderHelperApp, its the app I built to make dumping newer iOS/tvOS version files into the various repos I host with private headers & entitlements.

Background

This project is an amalgam of a few different versions of classdump. Initially based on a version by DreamDevLost made as an iOS port from nygard/class-dump. From there I have manually merged in bits and pieces from various PR's against the original ie #78 and then made a working macOS version again.

Chained Fixups

In iOS 13 Apple introduced some new load commands into dyld (specifically LC_DYLD_CHAINED_FIXUPS and LC_DYLD_EXPORTS_TRIE) But didn't start widely using them until iOS 15, once binaries are built with -fixup_chains any class-dump projects that relied on the old methodolgy would no longer work. This writeup and related code was instrumental in me understanding how to implement this newer methodology.

Nitty gritty

My only prior experience with working on class dump was the cleanup work I did in classdump-dyld, I didn't really fully understand the process or how dyld and mach-o files fundamentally worked, and in getting this project updated and working I have a much better understanding. I can't overstate how much value I found in the resources gathered at this repo granted the open source apple code is available elsewhere for otool's (cctools) et al, it's nice to have a central place of reference.

I usedCDLCDyldInfo as a template for the CDLCChainedFixups that does most of the heavy lifting for the newer process. It walks the fixup chains and stores the binds and the rebases in two separate dictionaries, which are subsequently referenced as applicable. The biggest 'gotchas' of this process were the need byte swap and/or bitshift in random circumstances for inexplicable reasons. The samples I modified from llios's macho-parser section (included in this repo in the samples folder) were instrumental in figuring this process out. Using some of the undocumented flags I added (-v,-d,-F,-z,-x etc..) On these sample files can give a better understanding on what im talking about, and the journey to figure all of this stuff out. The other big piece of the puzzle was making the adjustments for the differing DYLD_CHAINED_PTR_64_OFFSET vs DYLD_CHAINED_PTR_64 pointer_format's when rebinding & rebasing.

otool epiphany

While researching the new LC_DYLD_CHAINED_FIXUPS based world I was experimenting with otool output on the provided 'sample' files to see what kind of output I would get from the commands based around dumping the obj-c portions of the file and I noticed something curious when dumping iOS binaries.

macOS:

macos

iOS

ios

Notice anything different? In the iOS section, even otool has trouble resolving the symbols i.e. 0x4790 (0x10000c460 extends past end of file) Maybe because of entsize differences? (24 v 12)

I also noticed the 'rebased' addresses typically were identical with the upper bits being 'discarded'. ie 0x10000100007cc8 would become 0x100007CC8 So I thought, when I run into these scenarios where the offset would extend past end of file I would discard the upper bits and then re-add the preferredLoadAddress in an attempt to rectify this problem (preferredLoadAddress is re-added as an implementation detail to keep things working the same as the pre chained fixup workflow) Low and behold files that had failed to dump before would finally resolve missing symbols and stop crashing and burning, huzzah!

I apologize if any of my lingo isn't stated properly, this kind of bit/byte shifting chicanery has never been my strong suit, explaining this as best I can.

Entitlements

Another thing I realized when trying to do my normal course of header dump repos, was the usual tools (ldid, jtool) I use to dump entitlements from binaries were all coming up empty on *OS 15 and *OS 16 binaries. At first to rectify this I used a hacky bruteforce route by treating the files as pure text files and used NSScanner's to identify the app entitlements. Upon further analysis I realized there is a new section in the __TEXT segment labeled __entitlements. Turns out saving that section to a file is all that is necessary to dump entitlements off of binaries, I still havent fully deciphered how they used to get dumped, so I threw my old hacky string scanning code as a fallback for the time being, and added the -e flag to dump entitlements as a new feature in classdump-c.

Special Thanks:

Additional Reading:

NOTE: The master branch is now obsolete, the macos branch works on both mobile OSes and macOS