Running unsupported iOS on deprecated devices

Created on 26.11.25

Earlier this year I demonstrated running iOS 6 on an iPod Touch 3 – a device that Apple never shipped iOS 6 to, making iOS 5.1.1 the latest build it can run

A few months later I also released a script that creates an installable iOS 6 restore image on that iPod touch model

This article describes the technical details behind this work. Some efficiency is considered in iOS internals

sundanceh2a

I’ll show you what iOS is made of

First, let’s recap what software components iOS includes:

  1. iboot – Bootloader. There are 4 different types for different scenarios – iBSS, iBEC, LLB and iBoot
  2. kernel cache – OS kernel + kernel extensions (drivers) built into a single binary blob
  3. devicetree – Structured list of hardware used by a specific device model + some parameters that specify software behavior. The copy included in IPSW is like a template that is heavily modified by iBoot before going into the kernel.
  4. Userspace File System – Small restore ramdisk Used purely for OS installation or actual root file system Continuous installation of iOS
  5. Various firmware for coprocessors, whether internal or external to the main SoC – e.g., baseband, Wi-Fi, Bluetooth, multitouch, and etc.

iPhone 3GS test

The iPhone 3GS was released the same year as the iPod Touch 3 (2009), and has exactly the same hardware (S5L8920X SOC vs S5L8922XBut most importantly, it actually officially got iOS 6

Before doing anything on the iPod I decided to try booting iOS 5.1.1 on the iPhone with iBoot and iOS 6.0 with DeviceTree and see what was going to break and how.

devicetree

The most broken thing was DeviceTree – a lot of new nodes and properties were added in iOS 6. To fix this in an automated way I wrote a naive python script that decodes and calculates the difference between 2 devicetrees. Such difference can also be applied to any other devicetree

available in script SundanceInH2A repo

As I mentioned above, many things in the device tree are filled in by iBoot at runtime. One of such new properties is nvram-proxy-data In chosen node

The property should contain a raw NVRAM dump – leaving it blank will very quickly cause the kernel to get stuck somewhere

For the iPod touch 3 I also had to clean up differences on iPhone-specific things before applying it to the iPod’s 5.1.1 devicetree.

iboot

In this case iBoot did not require any major changes. Just specific Image3 signature checking patch, boot-args injection and debug-enabled Patch so the kernel will actually respect AMFI boot-args

One important thing is to actually populate nvram-proxy-data Dynamically, at least for normal boots (aka non-restore). Restoring the boot with some random NVRAM hardcoded into the devicetree would be fine, but normal NVRAM will overwrite your actual NVRAM with the random NVRAM if it decides to sync it at some point.

I do it by replacing the call UpdateDeviceTree() With my own little function that says real UpdateDeviceTree()but is also populated by actual nvram-proxy-data And random-seed (It shouldn’t matter)

For boot-args I always add amfi=0xff To disable code-signing, but that’s pretty canonical too

Please note that other iBoot+kernel combos may require more changes – if you ever try something and it doesn’t work, I recommend looking into devicetree differences (both the initial template and how iBoot fills it in). boot_args The structure is passed to the iBoot kernel (not to be confused with boot-args stringThe boot_args structure This is a different matter)

kernel cache

The most complicated part. The iPod Touch 3 never officially got iOS 6, yes, but it was rumored that it was supposed to in the beginning, but Apple’s marketing team didn’t say so. Either way, almost every internal iOS 6 build gets both the standalone S5L8922X kernel and even standalone kexts (including those specific to iPod Touch 3).

The question is how to load them all at once. My initial idea was to do it the same way as the old Mac OS. Long story short, my strategy was the following:

  1. In iBoot context, load all kexts from file system – binary itself + Info.plist
  2. Keep them in memory and add related entries chosen/memory-map devicetree node
  3. Boot the standalone kernel which will then pick up and load them

Tragic consequences:

panic(cpu 0 caller 0x802e5223): "kern_return_t kxld_link_file(KXLDContext *, u_char *, u_long, const char *, void *, KXLDDependency *, u_int, u_char **, kxld_addr_t *) (com.apple.kec.corecrypto) called in kernel without kxld support"

The kernel has all the code to pick them up, but not to actually link…

Pasting previously linked kernel cache

So ultimately the only way is to create a valid kernel cache. I was already imagining all the horrors of writing software to analyze and implement LINKEDIT And etc., but then this thought came to my mind! Mac OS What if we use that logic to build our iOS kernel cache?

kcgen \
    -c output.bin \
    $(cat n18.10A403.kextlist | sed 's/^/--bundle-id /') \
    -kernel kernels_kexts_10A63970m/mach.development.s5l8922x \
    -arch armv7 \
    -all-personalities \
    -strip-symbols \
    -uncompressed \
    -- \
    kernels_kexts_10A63970m/Extensions

i used /usr/local/bin/kcgen From an internal Sierra build (can be found online as “Phoenix A1708.dmg”), but it seems that the latest macOS also kextcache It can (included by default)

Here are details of the options:

  • -c output.bin – output file to write to the resulting kernel cache
  • $(cat n18.10A403.kextlist | sed 's/^/--bundle-id /') – This strange expression connects --bundle-id on each line from the file n18.10A403.kextlistThis is to specify what texts we would like to include, How I created such a list is described below
  • -arch armv7 – obviously create only Armv7 slices
  • -all-personalities – very important flag that prevents irrelevant IOKit personalities will be stripped. “Irrelevant” like “irrelevant to the current machine”, meaning everything Suitable iPod touch 3 is being retired
  • -strip-symbols – Removes unnecessary symbols. This flag can theoretically be omitted, but I recommend keeping it to make the resulting kernel cache smaller.
  • -uncompressed – Do not apply compression. Since we’ll have to change a small thing later, the compression will have to be reapplied anyway.
  • -- This means that the rest args will point to directories to hold the kexts.
  • kernels_kexts_10A63970m/Extensions is the path to the folder containing the kexts

The smallest thing to do is to remove the fat header. For some reason, this makes a thicker Mac-O with one slice. iBoot doesn’t like it, so let’s remove it:

lipo -thin armv7 output.bin -o output.thin.bin

Kernel cache is now ready! Just need to compress it and pack it into Image3 container

About Kext Lists

Once again I compared iOS 5.1.1 vs 6.0 of iPhone 3GS – some kexts were added, some were removed, some changed their bundle ID, some were irrelevant for iPod Touch 3

Don’t forget to include camouflage extensions too!

Samples can be found here SundanceInH2A treasury

About IOKit Personalities

In this specific case I had to patch up the Wi-Fi Kext’s Info.plist. As usual there is a sample in the repo

restore ramdisk file system

Very canonical here. i patched asr Like always I had to walk further options.n88.plist To options.n18.plist so that it can partition properly

However, I also need to install the iBoot exploit. To do this I re-implement rc.boot Binary:

  1. Remount and set up the ramdisk umask just like the original does
  2. call out restored_externalbut with this -server Logic, so it doesn’t reboot after finishing the restore
  3. If the restore completed properly, I add a third partition, write the exploit there and set boot-partition To 2
  4. reboot the device

Where is my implementation estimate available? yes, in stock

root file system

This required a lot of changes:

  1. Add the matching hardware feature list to Springboard (/System/Library/CoreServices/SpringBoard.app/N18AP.plist in this case)

    • I took the iOS 5.1.1 variant as a basis and added iOS 6 specific capabilities
    • i tried to keep Original Sufficient Home Screen Icon Order by merger iPod Touch 3 iOS 5.1.1 and iPod Touch 4 6.x layout
  2. Add Multitouch and Wi-Fi Firmware

    • I use versions from 5.1.1
  3. Add Bluetooth Firmware and Script

    • It’s more complicated, because these are all hardcoded /usr/sbin/BlueTool
    • Fortunately, they can also be overridden by files /etc/bluetool – As always check my code for reference
    • I extracted both firmware and script from 5.1.1 BlueTool
  4. fair play limited to daemon N88AP (iPhone 3GS)

    • it is LimitLoadToHardware Key in its ‘launchdaemon plist’
    • But if we just remove the key, it also works on iPod Touch 3
    • This is important, because otherwise we cannot activate the device through Apple’s servers
    • This trick will be hard to implement on iOS 6.1+ because they load launchdaemons from a signed cache. Yet this can be circumvented in a number of ways – for example, by patching launchd or force loading another plist launchctl
  5. DYLD shared cash patch

    1. product id map patch

      • iOS 6 brings the concept of “product ID” as a long byte sequence
      • It is filled by iBoot product Node of devicetree (which didn’t even exist before)
      • I hardcode the value of iPhone 3GS directly into the devicetree (8784AE8D7066B0F0136BE91DCFE632A436FFD6FB,
      • There is also a short form of this identifier – 16-bit integer – which existed before iOS 6.
      • iPhone is 3GS 0x2714 and ipod 0x2715
      • mobilegestalt The framework has a table that matches the long form with the short form – I swap 0x2714 with 0x2715 There
      • I believe this is better for iTunes etc.
    2. getDeviceVariant() patch

      • mobilegestalt Once again our business is ruined
      • device variant is one letter – usually “A” or “B”
      • It seems this depends on the Wi-Fi transceiver vendor used in the exact device (?).
      • iOS 6 fails miserably to determine this value for iPod Touch 3
      • For example, it crashes the activation process
      • To fix this, I patched the function to always return “a” (as CFString,
    3. fix code signing

      • It’s easier than most people think
      • The signature format in shared cache files is the same as in normal Mac-OS.
      • And since this is only ad-hoc, all you have to do is recalculate the SHA-1 hash for the pages you modified and update the signature.
      • So easy, it can be done with just a hex-editor

iboot exploit

There was a bug in iOS 5 iBoot HFS+ File system driver. I did an exploit several years ago but that was Badlike really BadI reimplemented it for this project, making it deterministic (hopefully,,,),

This topic probably deserves a separate article

Conclusion and future plans

It was not easy to do, and yet it was much easier than I expected in the beginning

After releasing the tool many people asked me about jailbreaking. Older tools aren’t going to work, but it should be easy to patch the kernel and drop the Cydia tarball on the file system. I guess I’ll try it later

There was another device that year that Apple discontinued support for – the iPad 1. I’ll be trying that out soon too

I hope the information in this article will help you create other amazing combinations, like iOS 4 on iPhone 4S or iOS 5 on iPad Mini 1



<a href

1 thought on “Running unsupported iOS on deprecated devices”

  1. Witamy w polskim legalnym internetowym kasynie Slottica – miejscu stworzonym dla hazardzistów online w Polsce! Casino Slotica w polskiej wersji językowej otwiera przed Tobą świat pełen emocjonujących gier za darmo i atrakcyjnych nagród bez rejestracji. Strona przyciąga nowych użytkowników między innymi premią powitalną 25 zł bez konieczności wpłaty depozytu. W badaniu przeprowadzonym wśród ponad 8500 graczy wielu z nich wskazało Slottica jako jedną z najciekawszych stron z automatami online dostępnych w Polsce. W katalogu gier znajdują się znane produkcje, w tym Chicken Road 2, Fire Joker, Funky Time, Dog House 1000 czy Energy Joker: Hold and Win. Od momentu startu projektu w 2019 roku platformie zaufało już ponad 350 000 użytkowników. Serwis działa na podstawie licencji wydanej przez regulatora z Curaçao, co zapewnia zgodność z europejskimi standardami branży iGaming. Ambicją marki jest oferowanie jakości usług, która może skutecznie konkurować z popularnymi serwisami pokroju TotalCasino.

    Reply

Leave a Comment