Well although it paniked, it’s a good start at least the serial port is still working. Then let’s check the pc address
1 2 3 4 5 6 7 8 9 10 11 12 13
__text:FFFFFFF008024F64 loc_FFFFFFF008024F64 ; CODE XREF: sub_FFFFFFF008024D88+26C↓j __TEXT_EXEC:__text:FFFFFFF008024F64 ; sub_FFFFFFF008024D88+278↓j ... __TEXT_EXEC:__text:FFFFFFF008024F64 LDR X8, [X21] ; Load from Memory __TEXT_EXEC:__text:FFFFFFF008024F68 LDR X8, [X8,#0x138] ; Load from Memory __TEXT_EXEC:__text:FFFFFFF008024F6C MOV X0, X21 ; Rd = Op2 __TEXT_EXEC:__text:FFFFFFF008024F70 MOV X1, X20 ; Rd = Op2 __TEXT_EXEC:__text:FFFFFFF008024F74 BLR X8 ; Branch and Link Register __TEXT_EXEC:__text:FFFFFFF008024F78 MOV X20, X0 ; Rd = Op2 __TEXT_EXEC:__text:FFFFFFF008024F7C CBZ X0, loc_FFFFFFF008024F90 ; Compare and Branch on Zero __TEXT_EXEC:__text:FFFFFFF008024F80 LDR X8, [X20] ; Load from Memory __TEXT_EXEC:__text:FFFFFFF008024F84 LDR X8, [X8,#0x20] ; Load from Memory __TEXT_EXEC:__text:FFFFFFF008024F88 MOV X0, X20 ; Rd = Op2 __TEXT_EXEC:__text:FFFFFFF008024F8C BLR X8 ; Branch and Link Register
After some reversing, I found that that it’s nvram variable loading routine. But we didn’t load any nvram variable! In iOS 14, nvram data can be loaded to devicetree by iBoot
1 2 3 4 5
result = sub_FFFFFFF007FCE6F8("/chosen", qword_FFFFFFF009301760, 0LL, 0LL, 0LL); if ( result ) { v3 = result; v4 = (*(__int64 (__fastcall **)(__int64, constchar *))(*(_QWORD *)result + 328LL))(result, "nvram-proxy-data");
I am just too lazy to reverse the nvram stuff, but since it’s loaded to devicetree, we can dump it from a real device! Now let’s try to find the memory address of devicetree. Luckily, we have
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
typedefstructxnu_boot_args { uint16_t Revision; /* Revision of boot_args structure */ uint16_t Version; /* Version of boot_args structure */ uint64_t virtBase; /* Virtual base of memory */ uint64_t physBase; /* Physical base of memory */ uint64_t memSize; /* Size of memory */ uint64_t topOfKernelData; /* Highest physical address used in kernel data area */ structXNU_Boot_VideoVideo;/* Video Information */ uint32_t machineType; /* Machine Type */ void *deviceTreeP; /* Base of flattened device tree */ uint32_t deviceTreeLength; /* Length of flattened tree */ char CommandLine[256]; /* Passed in command line */ uint64_t bootFlags; /* Additional flags specified by the bootloader */ uint64_t memSizeActual; /* Actual size of memory */ } boot_args;
We need to load trustcache, simply download it from ipsw and load it. Then add the address and size to devicetree and the kernel should be happy enough.
We don’t have that yet, so simply patch it to return after entering the function.
Shell we dance?
Now we can boot userland and launch bash but we can not input anything.
1 2 3 4
Thu Jan 1 00:00:00 1970 localhost com.apple.xpc.launchd[1] (com.apple.xpc.launchd.domain.system) <Error>: Failed to bootstrap path: path = /AppleInternal/Library/LaunchDaemons, error = 2: No such file or directory Thu Jan 1 00:00:00 1970 localhost com.apple.xpc.launchd[1] (com.apple.xpc.launchd.domain.system) <Notice>: exiting bootstrap mode Thu Jan 1 00:00:00 1970 localhost com.apple.xpc.launchd[1] (com.apple.xpc.launchd.domain.system) <Notice>: exiting ondemand mode bash-5.0#
after some debugging we can find that the FIQ handler was never called. That means there might be something wrong with our timer. Comparing the kernel of iOS 13 and iOS 14, I notice that
that is all we need! And we can unpatch the IORTC hack. Now we have an interactive shell!
1 2 3 4 5 6 7 8 9
bash-5.0# uname -a Darwin localhost 20.1.0 Darwin Kernel Version 20.1.0: Fri Oct 30 00:34:17 PDT 2020; root:xnu-7195.42.3~1/RELEASE_ARM64_T8015 iPhone10,3 arm64 D22AP Darwin bash-5.0# sw_vers ProductName: iPhone OS ProductVersion: 14.2 BuildVersion: 18B92 bash-5.0# id uid=0(root) gid=0(wheel) groups=0(wheel) bash-5.0#