This write-up uses as an example the Trident project by benjamin-42. The offsets are for the components this particular project requires, but the methodology and the information can easily be adapted to other iOS versions, devices, and projects. Each exploit requires a different set of offsets for various kernel components and each offset is found in a different way, but I believe this information should be useful for beginners. I am gonna use the iPod Touch 5th Generation for this write-up.

I was working on a private iOS 8.4.1 Jailbreak for my iPod Touch 5th Generation for practicing purposes and I’ve decided to use CVE-2016-4655 in order to leak the Kernel Base. The Kernel base will be required, especially since I need to patch a few things up. The iOS 8.4.1 Kernel is randomized using kASLR by iBoot at every boot of the system so we’ll need to calculate the randomized address of the components we wanna patch. Let’s take _kernel_pmap as an example. If I disassemble the kernelcache.release.n78 using IDA Pro or Hopper Disassembler, the _kernel_pmap can be located at address 0x803a311c in the __DATA segment, but this will not remain true for the live running kernel, so I can’t just patch PMAP using that address. In this case, I need to find the offset, add the Kernel Base and calculate the live, slid address of _kernel_pmap.

Of course, _kernel_pmap is not the only component that needs patching in a jailbreak, and for each component, the address you find in IDA or in Hopper won’t match the live Kernel because of kASLR (Kernel Address Space Layout Randomization). Of course, since my example is on iOS 8.4.1, and therefore before the changes implemented in iOS 10 regarding the encryption of the firmware, before we can do any sort of disassembly we need to decrypt the Kernel with the appropriate Key and IV. These can be located, fortunately, on TheiPhoneWiki for all 32-Bit devices and some 64-Bit devices too.

When you’re building a jailbreak, there is no legal notice that you MUST do everything from scratch or otherwise your PC will catch fire. Nope. You can reuse parts of someone else’s code (publicly released open source exploits, PoCs, etc.), as long as you give proper credits and you respect the license. In my case, I was going to use Trident by benjamin-42 on GitHub, but I realized that the offsetfinder.c from his project does not have the offsets for iPod Touch 5th Generation on iOS 8.4.1 which means that the project is totally useless for my device until I find the correct offsets. That was the reason I’ve decided to put together this write-up.

So, without further ado, let’s begin() our journey!

Getting started.

At first, we need to obtain ourselves a copy of the iOS 8.4.1 (or the version you intend to use this for) Kernel. Fortunately, the decryption keys are available, so we don’t need to dump the kernel from the memory of the device (much more painful and hard to do and requires exploit and more coding). However, It’s not pink flowers and unicorns here either. We need to decrypt the kernel using xpwntool. All the tools referred in this write-up will be linked at the end of the article. Before we can decrypt it, we need to grab it. We have two options: Either download the whole iOS 8.4.1 IPSW for iPod Touch 5th Generation (in my case), which may not be that bad depending on your internet speed, or download only the kernelcache.release.n78 which is better for me since I am currently living in a part of Italy where the internet speed is not very great.

Obtaining the kernelcache.release.xx file

For this task, we can use PZB or Partial Zip Browser by @tihmstar. The source code is available at the end of the write-up. Once we have the source code, we can compile it and run it in Terminal. PZB has the following syntax: ./pzb [parameter] <url to zip>.

With this in place, we can first run ./pzb -l <IPSW LINK> and see the name of all the files, including the Kernel file which in my case is called kernelcache.release.n78. Then we can simply run ./pzb -g file_to_download <IPSW LINK> and get the file. My final command would be ./pzb -g kernelcache.release.n78,1_8.4.1_12H321_Restore.ipsw

Now that I have my kernelcache.release.n78 in my User directory (geosn0w directory in my case), I am able to run it through xpwntool to decrypt it. Xpwntool’s syntax for our needs looks like this xpwntool <infile> <outfile> [-k <key>] [-iv <key>] Both the Key and the IV can be found on TheiPhoneWiki on the Firmware page. Each device has a different Key and a different IV for the same iOS version. The reason for that is that the keys used to be generated using the baked in GID key that cannot be retrieved. Using an iBoot or a SecureROM exploit one can access the built-in AES Engine and generate the keys from the KBAGS of the encrypted files. A tedious process that requires 0day on newer devices. Fortunately, Apple stopped encrypting most components in iSO 10.x

My Final command on Xpwntool is /Users/geosn0w/Desktop/ToolChain/Pentesting\ Tools\ \&\ Other/xpwntool /Users/geosn0w/Desktop/kernelcache.release.n78 /Users/geosn0w/Desktop/kernelcache.release.DECRYPTED -k e7904495a19966d622389ce0e1113f4f00e0fc7c0fa65c4d66e79dd12450edf9 -iv c96b701e3dc9ae4d07bf722a4cb50011

The command should create a new file called kernelcache.release.DECRYPTED on my Desktop and the file should be recognized as Mach-O executable arm_v7 if you run it through the file command in terminal. Now I am able to finally open the Kernel file in IDA or in Hopper Disassembler and look for what I need, but what do I need?

Making a note of who’s offsets we need

As I said, I am building a tool for iOS 8.4.1, iPod Touch 5th Generation and I use the Trident project as my beginning point. The project lacks the proper offsets for my device, so I need to find them manually. When I run the project as is, it bails out and says that the device is unsupported. No wonder why there is a function that checks whether the project has the right offsets. No patching can be done without knowing where in the memory is the thing you wanna patch.

Taking a look at the offsetfinder.h header file inside the Trident source code, I can see a list of offsets needed so I know what I need to find. The list goes like this in my case.

// OSSerializer::serialize
uint32_t find_OSSerializer_serialize(void);
// OSSymbol::getMetaClass
uint32_t find_OSSymbol_getMetaClass(void);
// calend_gettime
uint32_t find_calend_gettime(void);
// _bufattr_cpx
uint32_t find_bufattr_cpx(void);
// clock_ops
uint32_t find_clock_ops(void);
// _copyin
uint32_t find_copyin(void);
// BX LR
uint32_t find_bx_lr(void);
// write_gadget: str r1, [r0, #0xc] ; bx lr
uint32_t find_write_gadget(void);
// vm_kernel_addrperm
uint32_t find_vm_kernel_addrperm(void);
// kernel_pmap
uint32_t find_kernel_pmap(void);
// flush_dcache
uint32_t find_flush_dcache(void);
// invalidate_tlb
uint32_t find_invalidate_tlb(void);
// task_for_pid
uint32_t find_task_for_pid(void);
// setreuid
uint32_t find_setreuid(void);
// setreuid cred update
uint32_t find_setreuid_cred_update(void);
// pid_check_addr offset
uint32_t find_pid_check(void);
// posix_check_ret_addr offset
uint32_t find_posix_check(void);
// mac_proc_check_ret_addr offset
uint32_t find_mac_proc_check(void);

Okay, so looks like I have to find 18 different offsets in the kernel just to get the Trident project to work. At first, let’s understand how the real address of what we need is calculated. First, before we can calculate anything, we need to grab the Kernel Base, because everything else will be relative to it. This is where the CVE-2016-4655 is very useful. This vulnerability works all the way up to iOS 9.3.4 (patched in iOS 9.3.5), so we can use this info leak vulnerability to grab the Kernel Base and then calculate the real address of the functions we wanna patch in the live kernel.

Let’s take an example: Let’s assume that _kernel_pmap’s unslid address is 0x803a311c. From the unslid kernel base to 0x803a311c we have an offset of 0x3a211c. This would be the offset we need if the kernel was unslid in the memory, but the kernel is randomized so this offset means nothing. Remember that we considered our offset relative to the unslid Kernel Base? Well, if we grab the slid Kernel Base and we add this offset, we are able to tell exactly where in the memory is the _kernel_pmap. We have to do this exact thing for all remaining offsets.

Adding the device to the list

In my case, Trident already has a structure of type t_target_environment called info_to_target_environment. My device with my iOS Version isn’t part of it so I’ll have to add it manually. My code would look like this.

t_target_environment info_to_target_environment(const char *device_model, const char *system_version) {
    determineTarget("iPhone4,1", "9.0.2", iPhone41_iOS902);
    determineTarget("iPhone4,1", "9.1", iPhone41_iOS910);
    determineTarget("iPod5,1", "8.4.1", iPhone51_iOS841); 
    //I add my device with its iOS version at the end of the list provided by Trident.

Of course, now I also have to navigate to the header file, offsetfinder.h, and modify the enum to contain my device with my iOS version too. Here’s the code.

typedef enum {
    //I add my device at the end of the enum.
} t_target_environment;

Trident uses the right offset for the right device by using a switch case for each device, returning the right offset for the device detected at run-time. Each function for which we need offsets has a switch for each device on each supported version, so we will have to follow the type if we use Trident, but first we need to find an offset.

Finding the offsets

So now that we know which offsets we need, it’s time to get them. Let’s begin with OSSerializer::serialize. For this, the method is pretty simple. Just search for __ZNK12OSSerializer9serializeEP11OSSerialize. After some searching, Hopper or IDA will drop you into a function with that name. The address of the first instruction in the function i s802d5a1c and the assembly line is 802d5a1c ldr r3, [r0, #0x8]. Put your mouse cursor after the address and somewhere on the bottom of the window, Hopper and IDA should tell you the offset. In my case, the offset I am looking for is 0x2d4a1c. That’s the offset we need for uint32_t find_OSSerializer_serialize(void); in Trident.

In order to add this offset to Trident, I’ll navigate to the offsetfinder.c, locate the uint32_t find_OSSerializer_serialize(void); and add my device with the right iOS version and the corresponding offset at the end of the switch case. The code looks like this.

uint32_t find_OSSerializer_serialize(void) {
    switch (target_environment) {
        case iPhone41_iOS902: return 0x317de4;
        case iPhone41_iOS910: return 0x319450;
        case iPhone41_iOS920: return 0x3106fc;
        case iPod51_iOS933: return 0x318388;
        case iPod51_iOS934: return 0x318388;
        case iPod51_iOS841: return 0x2d4a1c; // My offset
        default: abort();

We’re done with this offset. Time to grab the next one on the list, which happens to be OSSymbol::getMetaClass. This one is also relatively simple. We need to look for __ZNK8OSSymbol12getMetaClassEv. The same strategy, you place the coursor after the address of the first instruction inside the function and IDA or Hopper will tell you the offset. In my case, the first instruction and its address are 802d7afc movw r0, #0xd85c , and the offset is 0x2d6afc.

So, now that we have the second offset, we’re going to add it to the swich of the uint32_t find_OSSymbol_getMetaClass(void);. The code for the switch case looks like this.

uint32_t find_OSSymbol_getMetaClass(void) {
    switch (target_environment) {
        case iPhone41_iOS902: return 0x31a5d0;
        case iPhone41_iOS910: return 0x31bc3c;
        case iPad35_iOS934: return 0x3219fc;
        case iPod51_iOS931: return 0x31a934;
        case iPod51_iOS932: return 0x31aa6c;
        case iPod51_iOS933: return 0x31ab90;
        case iPod51_iOS934: return 0x31ab90;
        case iPod51_iOS841: return 0x2d6afc; //I add my offset
        default: abort();

With that one in place, let’s continue our journey to the next offset which happens to be calend_gettime. This is a tricky one. There is no function called this way. IDA and Hopper generate a random name for this function so you can’t find it by searching for its name. For this, I found an interesting method. May not be the best, and I am pretty sure there are better ways but this worked for me. Search for _clock_get_uptime. Once you reach this function, count down 5 functions and the 6th function is the one you’re looking for. You know you found it because it has something like this 8001e0ac bl _clock_get_calendar_nanotime. The same method put the cursor after the address of the first instruction in the function prologue and get the offset, which in my case is 0x1d0a0.

And with this, we’ve just obtained the third offset. Now we have to add it to its proper place in the uint32_t find_calend_gettime(void);. I won’t repeat the same thing, but you got the idea on how you find offsets in the kernel.

Bottom Line

While different exploits require different offsets, most exploits come with a set of offsets for the device and the iOS version on which the researcher tested the exploit. If no instructions are given on how you can find the correct offsets for your device, it suffices to download the Kernel for the iOS version and the device for which the researcher provided offsets and you can use the given offsets to reverse-engineer the methods used to find them, then you can apply these methods to your own device & iOS version.

I am pretty sure there are better ways to find these offsets, especially if these better methods are scripts :P but I still think this write-up will help more beginners like me to understand where these offsets come and how are they obtained.

References and Resources