Running Unsupported iOS on Deprecated Devices
Recorded: Nov. 27, 2025, 1:02 a.m.
| Original | Summarized |
Running unsupported iOS on deprecated devices Running unsupported iOS on deprecated devices Created on 26.11.25 I'll show you what iOS is made of iBoot - the bootloader. Has 4 different types for different scenarios - iBSS, iBEC, LLB and iBoot Kernelcache - the OS kernel + kernel extensions (drivers) built into a single binary blob DeviceTree - structured list of hardware used by specific device model + some parameters that specify software behavior. The copy included in an IPSW is more of a template that is heavily modified by iBoot before jumping into kernel Userspace filesystem - tiny restore ramdisk used purely for OS installation or the actual root filesystem of iOS installed persistently Various firmwares for coprocessors, be they internal or external to the main SoC - like, baseband, Wi-Fi, Bluetooth, multitouch and etc. iPhone 3GS tests In iBoot context, load all kexts from filesystem - binary itself + Info.plist The sad outcome: The kernel has all the code to pick them up, but not to actually link... I used /usr/local/bin/kcgen from internal Sierra build (can be found online as "Phoenix A1708.dmg"), but it seems that even latest macOS kextcache can do it (included by default) -c output.bin - output file to write resulting kernelcache to $(cat n18.10A403.kextlist | sed 's/^/--bundle-id /') - this weird expression appends --bundle-id to every line from the file at n18.10A403.kextlist. This is to specify which kexts we'd like to include. How I created such list is described below -arch armv7 - obviously only build armv7 slice -all-personalities - very important flag that prevents irrelevant IOKit personalities to be stripped. "Irrelevant" as in "irrelevant to current machine", meaning everything relevant to iPod touch 3 is going to be stripped -strip-symbols - strips unnecessary symbols. This flag can be omitted theoretically, but I recommend keeping it to make resulting kernelcache smaller -uncompressed - do not apply compression. Since we'll have to change one little thing later, compression would have to be reapplied anyway -- means the rest of the args will point to directories to grab kexts from kernels_kexts_10A63970m/Extensions is a path to a folder containing kexts The little thing to do is to remove fat header. For some reason, it creates a fat Mach-O with a single slice. iBoot doesn't like it, so let's strip it: The kernel cache is ready now! Just needs to be compressed and packaged into Image3 container Remount ramdisk and set umask just like the original one does Call restored_external, but with -server argument, so it doesn't reboot after finishing restore If restore was completed properly, I add a third partition, write the exploit there and set boot-partition to 2 Reboot the device My implementation is available guess where? Yes, in the repository Add matching SpringBoard's hardware feature plist (/System/Library/CoreServices/SpringBoard.app/N18AP.plist in this case) I took the iOS 5.1.1 variant as a base and added iOS 6 specific capabilities I tried to keep original enough Home screen icon order by merging iPod touch 3 iOS 5.1.1 and iPod touch 4 6.x layouts Add multitouch & Wi-Fi firmwares I use versions from 5.1.1 Add Bluetooth firmware and scripts This is more complicated, as those are all hardcoded into /usr/sbin/BlueTool Luckily, they can also be overriden by files in /etc/bluetool - as always check my code for reference I extracted both firmware and scripts from 5.1.1 BlueTool FairPlay daemon is limited to N88AP (iPhone 3GS) It has LimitLoadToHardware key in its' LaunchDaemon plist But if we simply remove the key, it works on iPod touch 3 as well This is important, because otherwise we cannot activate device through Apple's servers This trick will be harder to pull off on iOS 6.1+ because they load LaunchDaemons from a signed cache. Still can be bypassed in many ways - for instance, patching launchd or forcefully loading another plist via launchctl DYLD shared cache patches Product ID map patch iOS 6 brings a concept of "product ID" in the form of a long byte sequence getDeviceVariant() patch MobileGestalt once again messes us up our business Fixing code signature This is much easier than most people think The iBoot exploit john, 2025 |
Running unsupported iOS on deprecated devices This article details the technical process undertaken to successfully run iOS 6 on an iPod touch 3rd generation and, subsequently, explore techniques applicable to other legacy devices. The project, spearheaded by the author, demanded a deep understanding of iOS internals and relied heavily on reverse engineering and adaptation. The intended audience is a college graduate possessing a foundational understanding of operating systems and software development. The work begins with a recap of the core software components of iOS: iBoot, the bootloader, which comes in four variations—iBSS, iBEC, LLB, and iBoot— serving differing boot scenarios. Kernelcache, encompassing the OS kernel and kernel extensions (drivers), is a central element. DeviceTree, a structured list of hardware specifics and parameters, is crucial, often requiring modification during deployment. Userspace filesystems, used for OS installation or root filesystems, and various firmware sets for coprocessors—baseband, Wi-Fi, Bluetooth, and multitouch— contribute to the overall system. A pivotal demonstration involved testing iOS 6 on an iPod touch 3, followed by an attempt to run it on an iPhone 3GS. The author first focused on the DeviceTree, identifying a significant discrepancy between iOS 6 and its predecessor. This was resolved through the creation of a Python script, part of the SundanceInH2A repository, designed to automatically generate diffs between DeviceTrees. This diffing capability could then be applied to modify other DeviceTree configurations. The script focused on identifying and resolving inconsistencies in the ‘nvram-proxy-data’ property, vital for accurate NVRAM population, ensuring the kernel's proper initialization. Due to the iPod touch 3’s unique environment, custom modifications were necessary, cleaning up iPhone-specific differences before applying the DeviceTree to the 5.1.1 base. iBoot played a comparatively straightforward role, requiring only image3 signature patching, boot-args injection, and debug-enabled patching. Dynamic NVRAM-proxy data population was implemented, primarily for non-restore boot scenarios, though acknowledging the ability of restore boots to tolerate a hardcoded NVRAM value. Standard boot-args, specifically `amfi=0xff`, were utilized for code-signing bypass. The most complex undertaking involved the Kernelcache. Recognizing the inability of the iPod touch 3 to officially support iOS 6, the author leveraged the operational rumors surrounding its initial intended inclusion. The strategy centered on mirroring Mac OS X’s approach—dynamically loading kexts at bootloader levels. The execution relied on the iBoot context to load all kexts from the filesystem – binary files and accompanying Info.plist configuration. These were then organized in memory with corresponding entries in the DeviceTree’s memory-map node. A standalone kernel was subsequently booted, which then recognized and loaded these kexts. However, this approach encountered a critical failure – a "kern_return_t kxld_link_file" error, indicative of a problem with kernel linking. To overcome this, the author adopted a "glueing" approach, utilizing a pre-linked kernelcache. This was achieved by repurposing macOS kernelcache generation logic – with the author referencing internal Sierra build tools (Phoenix A1708.dmg) – resulting in the “kcgen” tool. Key options employed included: output file designation (output.bin), the generation of the `--bundle-id` string for each kext, an armv7 architecture-specific build, disabling irrelevant IOKit personalities, stripping unnecessary symbols, and ensuring an uncompressed output (necessitated by later modifications). The output was then "thin" compressed and packaged into an Image3 container. Addressing the issues within the kext lists was determined to be critical. The content of these lists needs to be updated during the process. The author compared the iPhone 3GS iOS 5.1.1 with iOS 6.0, identifying changes to kexts, modifications to bundle IDs and ensuring that pseudo extensions were included. It goes without saying that they are to be found in the SundanceInH2A repository. IOKit personalities required patching, specifically the Wi-Fi kext’s Info.plist. Samples are present in the repository. The restore ramdisk filesystem is fairly standard. Asr was patched as usual, also the option.n88.plist was moved to options.n18.plist to properly lay out partitions. The iBoot exploitation itself was complex, necessitating a complete reimplementation of the rc.boot binary, incorporating determinism for reliability. This effort was undertaken years prior, but required further refinement for this specific project. The root filesystem demands a massive amount of changes. Adding SpringBoard’s hardware feature plist is the first step. A base of iOS 5.1.1 was taken and enhanced with iOS 6-specific capabilities. Home screen icon layout was merged from iPod touch 3 iOS 5.1.1 and the iPod touch 4 6.x layouts. Multitouch and Wi-Fi firmware were sourced from 5.1.1, as were Bluetooth and scripts. BluesTool’s implementation was relied upon, but it can be overridden by files in /etc/bluetool. FairPlay daemon requires workarounds. LimitLoadToHardware key in its LaunchDaemon plist is necessary, but if removed, it works on iPod touch 3 as well. This is critical for device activation through Apple servers, with potential workarounds for iOS 6.1+ via LaunchDaemon patching or forceful plist loading through launchctl. DYLD shared cache patches and a product ID map patch were also necessary. This ensures proper device identification, with the iPhone 3GS assigned the long byte sequence 8784AE8D7066B0F0136BE91DCFE632A436FFD6FB, and a shortened integer form 0x2714. This integer is matched by MobileGestalt’s table, swapping 0x2714 with 0x2715. The device variant was an issue. iOS 6 fails to correctly identify the Wi-Fi transciever vendor, leading to activation failures. The author patched the lookup function to consistently return "A" (in CFString format). Finally, the iBoot exploit itself was reimplemented, addressing a problematic Image3 signature issue encountered in earlier versions. The author concluded with plans for further experimentation, including supporting iPad 1 and iOS 4 on other devices. The project’s success, despite considerable challenges, suggests a replicable approach for exploring unsupported iOS versions on legacy devices, particularly for those interested in reverse engineering and adaptation. |