Back in junior high school I was really into jailbreak. I spent hours on /r/jailbreak browsing tweaks, installed so many things that I broke my iOS more times than I can count. It was fun. You could actually run whatever code you wanted on your own phone.
These days? Good luck. Jailbreaking is almost impossible on modern iPhones. Apple locked everything down tight. That is why I started looking at iBoot I wanted to understand what exactly makes it so hard.
Turns out the answer is: a lot of things.
The Boot Chain
iPhone booting is like airport security. You cannot just walk to the gate. Each layer checks the next one before letting it through.
flowchart LR
A[SecureROM] -->|checks| B[iBoot]
B -->|checks| C[iOS Kernel]
C -->|checks| D[iOS]SecureROM is the first thing that runs when you press power. It is a tiny program burned into the CPU when the chip is made. Like a stamp in concrete Apple cannot update it, nobody can change it. Its only job is to check if iBoot is signed by Apple.
If iBoot passes, SecureROM loads it and gets out of the way.
Then iBoot takes over.
What Is iBoot Anyway?
A bootloader is a small program that runs before the main operating system. Think of it like the security guard at the entrance of a building. The guard checks everyone's ID before letting them in. iBoot does the same thing it checks every piece of software before letting it run on your iPhone.
iBoot is not the iOS kernel, by the way. The kernel (XNU) comes later. iBoot is the thing that loads and verifies the kernel. Think of it like airport security checking your passport before you even get to the boarding gate.
iBoot is 3.75 MB of code. That is bigger than some operating systems. It has 17,614 functions doing everything from setting up memory to decrypting firmware.
What Happens During Boot
Here is the full sequence I traced from the code:
flowchart TB
A[Power button] --> B[SecureROM checks iBoot]
B --> C[iBoot starts]
C --> D[Setup CPU, power, memory]
D --> E[Create heap, interrupts, timers]
E --> F[Init storage, display, USB]
F --> G[Read boot settings]
G --> H{Recovery mode?}
H -->|Yes| I[Show recovery screen]
H -->|No| J[Verify AP ticket]
J --> K[Verify hash chain]
K --> L[Draw Apple logo]
L --> M[Load iBoot update]
M --> N[Load SEP OS]
N --> O[Load device tree]
O --> P[Load ramdisk]
P --> Q[Load kernel]
Q --> R[Final check]
R --> S[Jump to iOS]22 steps before you see the progress bar. Let me walk through each hardware step so you actually understand what is happening.
The Hardware Init Steps Explained
Step 1: Power Management Unit (PMGR)
The PMGR is a separate chip (or a section of the main chip) that controls power to different parts of the phone. Think of it like the electrical panel in your house. When you flip a switch, power goes to that room. The PMGR does the same thing it turns on power to the CPU, the memory, the GPU, the camera, etc.
But here is the thing: when you first press the power button, only a tiny part of the phone has power. Just enough to run SecureROM. When iBoot starts, it needs to turn on everything else. The function iboot_pmgr_init at 0x405AC does this. It is like walking through the house flipping all the switches on.
Without this step, the phone would have power but nothing else would work. No CPU, no memory, nothing.
Step 2: CPU Init
The CPU (Central Processing Unit) is the brain of the phone. But it does not just wake up ready to go. It needs to be configured things like:
- What speed should it run at? (1 GHz? 2 GHz? 3 GHz?)
- What features should be enabled? (there are many optional features in modern ARM CPUs)
- How should it handle errors?
The function iboot_hardware_init_early at 0x6F8 handles this. It is like waking up and remembering how to think.
Step 3: Heap
A heap is a pool of memory that programs can borrow from. Imagine you are building something with LEGO. You have a big box of bricks (the heap). When you need bricks, you take some from the box. When you are done, you put them back.
iBoot creates a heap of 1 MB (that is iboot_heap_init at 0x17B1D8). This is where all the temporary data during boot will live things like file paths, parsed data from manifests, and so on.
Without a heap, iBoot would have nowhere to store things while it works. It would be like trying to build something with no table to put your parts on.
Step 4: Memory Controller
The memory controller is the part of the phone that manages access to RAM (the main memory). RAM is where all running programs and data live. But RAM is not just a simple box it is organized in banks, rows, and columns, like a huge spreadsheet. The memory controller knows how to read and write to it efficiently.
The function iboot_memory_map at 0x1A4FB4 initializes the memory controller and tells iBoot how much RAM is available and where it is located. This is like measuring your workspace before you start building.
Step 5: Interrupts
An interrupt is a signal that the hardware sends to the CPU to get its attention. Think of it like someone tapping your shoulder while you are working.
There are many types of interrupts:
- Timer interrupt: "One millisecond has passed!"
- Storage interrupt: "The data you asked for from the flash chip is now ready!"
- USB interrupt: "Someone plugged in a cable!"
Without interrupts, the CPU would have to constantly check if things are ready (polling), which wastes a lot of power. Interrupts let the CPU sleep or work on other things until something needs its attention.
The function iboot_interrupt_init at 0x134320 sets up the interrupt controller, which is like a receptionist that routes interrupt signals to the right place.
Step 6: Timers
A timer is exactly what it sounds like a hardware counter that counts up or down at a fixed rate. iBoot needs timers for things like:
- The boot delay countdown (wait 5 seconds before auto-boot)
- Timeouts when waiting for USB data
- Measuring how long things take
The function iboot_timer_init at 0x1527E0 starts the system timer. Like starting a stopwatch.
Step 7: Storage
Storage here means the flash memory inside your phone the same thing that stores your photos, apps, and iOS itself. iBoot needs to read files from storage to load firmware components.
The function at 0xC61B4 (I did not rename this one) initializes the NAND/NOR flash controller. This is like opening the file cabinet so you can start pulling out documents.
Step 8: Display
The display is the screen. iBoot initializes the display controller at iboot_display_init (0x179F58) so it can show the Apple logo later. Before this step, the screen is completely black because the controller is not even turned on.
Step 9: USB
USB is how your phone talks to computers over a cable. iBoot initializes USB at iboot_usb_init (0x7E5DC) so that engineers can communicate with the phone even before iOS boots. This is how recovery mode works the phone talks to iTunes over USB while iBoot is running.
NVRAM - The Phone's Notebook
Before we talk about recovery mode, you need to know what NVRAM is.
NVRAM stands for Non-Volatile Random Access Memory. Fancy words, simple meaning: it is memory that remembers things even after you turn off the phone. Like a notebook that does not lose its ink when you close it.
Regular RAM (the memory your phone uses to run apps) forgets everything when power is cut. That is why we call it "volatile" it evaporates like water. NVRAM is "non-volatile" it keeps its data like a permanent marker.
Your iPhone uses NVRAM to store boot settings. Things like "should I boot automatically?" or "how long should I wait before booting?" These settings survive reboots, battery drains, everything.
Recovery Mode - The Waiting Room
You know that screen with the cable pointing to a laptop? That is iBoot in recovery mode. It is waiting for someone to tell it what to do.
flowchart LR
A[Boot settings] --> B{recovery-boot-mode?}
B -->|Yes| C[Recovery screen]
B -->|No| D{auto-boot?}
D -->|Yes| E[Wait N seconds]
E --> F{Key pressed?}
F -->|Yes| G[Command prompt]
F -->|No| H[Boot]
D -->|No| GHere is how it works. iBoot reads a setting called recovery-boot-mode from NVRAM. If that is set to 1, it goes straight to recovery mode.
If not, it checks auto-boot. If auto-boot is 1, it waits for bootdelay seconds (usually 5). If you press a button during that time, it drops to a command prompt. If you do nothing, it boots normally.
The commands I found:
| Command | What It Does |
|---|---|
bootx | Boot the kernel |
go | Jump to a specific memory address |
reboot | Restart the phone |
reset | Hardware reset |
bgcolor | Change background color |
getenv | Read an environment variable |
setenv | Set an environment variable |
saveenv | Save to NVRAM |
ramdisk | Ramdisk stuff |
Notice there is no write_flash or dump_memory command. Apple stripped the dangerous stuff from this RELEASE build. Their internal debug builds probably have them. Imagine if they left those commands in anyone with a USB cable could dump the entire phone memory.
Right in the binary you can see the strings laid out one after another:
bootx, go, reboot, reset, bgcolor, devicetree, getenv, saveenv, setenv, setenvnp, ramdiskEach command is a null-terminated string stored in a table. The dispatcher at iboot_dispatch_command_table (0x4918) iterates through 56-byte entries, compares the command name with what the user typed, and calls the handler if it matches.
Hash - The Fingerprint
You will see this word a lot, so here is what it means.
A hash is like a fingerprint for data. You take a file, run it through a hash function, and get a short string of letters and numbers. The important thing is: if you change even one tiny bit of the file, the hash changes completely.
File: "Hello World" → Hash: a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
File: "Hello Wor1d" → Hash: 8ba7b1b3c7b3e5e8b5c5d5e5f5a5b5c5d5e5f5a5b5c5d5e5f5a5b5c5d5e5f5See how changing one letter ("l" to "1") made the second hash completely different from the first? That is the point. Hash functions are designed so that even the smallest change produces a completely different result.
This is useful for security because if someone modifies a firmware file, the hash will not match what it should be, and iBoot will know something is wrong. It is like a tamper-evident seal on a package if the seal is broken, you know someone opened it.
IMG4 - The Sealed Envelope
Every firmware component is wrapped in a format called Image4 (IMG4). Think of it like a sealed envelope with extra security features.
IMG4 Container
├── Magic: "MiST" (identifies it as IMG4)
├── Production/Dev flag
├── The actual firmware data
├── Manifest (instruction booklet):
│ ├── Hash of firmware (fingerprint)
│ ├── Certificate chain (who signed it)
│ ├── AES-GCM signature (tamper seal)
│ └── Component type (what is this for)A magic is a special value at the start of a file that identifies its format. Like how PDF files start with "%PDF" and ZIP files start with "PK". IMG4 files start with "MiST".
A manifest is like an instruction booklet that comes with the firmware. It contains the hash, the certificates, the signature everything iBoot needs to verify the firmware is genuine.
The strings IMG4 at 0x29F692 and IM4P at 0x29F697 confirm the format.
In the binary at address 0x29F692 you can see:
0x29F692: 49 4D 47 34 00 "IMG4"
0x29F697: 49 4D 34 50 00 "IM4P"And the code at img4_setup_header (0xC4524) writes the magic value directly:
MOV W8, #0x4D695354 ; Load "MiST" magic
STR W8, [X19, #8] ; Store it at offset +8 in the headerThe check happens in img4_check_magic at 0x2663C:
BL der_parse_internal ; Parse DER tag from image
CMP X0, #0 ; Did it succeed?
B.NE parse_failed ; If not, fail
LDR X7, [X2] ; Load parsed magic value
CMP X7, X1 ; Compare with expected (22)
B.EQ magic_match ; Match? Good
MOV X0, #-1 ; No match? Return error
B return
magic_match:
MOV X0, #0 ; Success
return:
RETThe magic constant 22 is the DER tag value that represents an IMG4 container. If the file does not start with the right tag, iBoot will not even try to parse it.
Six Security Checks
Every firmware component goes through six checks. If any fails, boot stops. Simple as that.
flowchart TB
A[Load image] --> B[Check format]
B --> C[Verify magic]
C --> D[Parse manifest]
D --> E[1. Hash check]
E --> F[2. Component type]
F --> G[3. Hash chain]
G --> H[4. Production cert]
H --> I[5. AES-GCM decrypt]
I --> J[6. Signature verify]
J --> K{All good?}
K -->|Yes| L[Load it]
K -->|No| M[Stop boot]The function img4_parse_manifest_full at 0x26F14 does all of this. About 1500 lines of code.
1. Hash Check
iBoot computes the hash of the firmware data and compares it to the hash stored in the manifest. Remember the fingerprint analogy? If the fingerprints match, the data has not been modified. If they do not match? Someone tampered with the file. Rejected.
2. Component Type
iBoot reads a 4-byte tag to know what it is dealing with. Is this a kernel? A device tree? A ramdisk? Each tag is like a label on the envelope that tells you what is inside.
| Tag | Text | Meaning |
|---|---|---|
4D695354 | MiST | IMG4 container |
494D4731 | IMG1 | Hash data |
6C6F6F43 | Cool | Dev mode |
6954734D | MiSt | Kernel |
Wrong tag for this boot stage? Rejected.
3. Hash Chain
Not one hash a chain of hashes, each 48 bytes, linked together. Think of it like multiple tamper seals on the same envelope. This prevents someone from taking a valid hash from one image and putting it into another. If any link in the chain is broken, the image is rejected.
4. Production Check
iBoot checks three values from the manifest:
certificate-production-statuswas this signed with a production cert?certificate-security-modewhat security level to enforce?trusted-boot-policy-measurementboot integrity flag
A certificate is like a digital ID card. It proves who signed the firmware. Apple has production certificates (for phones that ship to customers) and development certificates (for engineers testing software). If a development-signed image tries to boot on a production phone, iBoot rejects it. No way around it.
This check alone makes jailbreaking much harder. Even if you could sign your own firmware with a development certificate, it would not boot on a normal iPhone. You would need to either exploit iBoot to skip this check, or find a hardware vulnerability.
These strings are stored in the binary:
certificate-production-status
certificate-security-mode
trusted-boot-policy-measurementThe function that checks these values is part of img4_parse_manifest_full. It reads the manifest properties and compares the certificate flags. If a development-signed image tries to boot on a production device, the check fails and iBoot calls img4_report_error to stop the boot process.
5. AES-GCM - Not Just Encryption
Here is where it gets interesting. You probably know what encryption is it scrambles data so nobody can read it without the key. But there is a problem with regular encryption: it does not tell you if someone tampered with the data.
Let me explain with an example. Imagine you have a locked box. Regular encryption is like putting a lock on the box. Only someone with the key can open it. But what if someone breaks the lock, changes the contents, and puts a different lock on it? You would not know. The box is still locked, but the contents are not what you put in there.
AES-GCM is different. GCM stands for Galois/Counter Mode do not worry about the name. What matters is what it does. It does two things at once:
- Encrypts the data (scrambles it so nobody can read it)
- Authenticates the data (creates a tamper seal that proves nobody touched it)
This second part is the key. When iBoot decrypts the manifest, it also checks this authentication tag. If someone modified the encrypted data, the tag will not match. The decryption fails. The image is rejected.
So with regular encryption, you only know if the key is right or wrong. With AES-GCM, you know if the key is right AND if the data has been tampered with. It is like having a lock that also tells you if someone tried to pick it.
Most systems use RSA or ECDSA for signatures. Apple does something different. They use AES-GCM for everything. The key likely comes from the GID key a secret key fused into the CPU during manufacturing. You cannot read it. You cannot extract it. It is like a key that is melted into the lock itself.
The AES dispatch at 0x8798C handles this. It selects between GCM and a simpler mode called CBC (Cipher Block Chaining), depending on what the manifest needs.
AND W8, W0, #0xF ; Get algorithm type
AND W9, W0, #0xF0 ; Get direction (encrypt/decrypt)
CMP W8, #0 ; CBC or GCM?
B.EQ cbc_path
; GCM path
CMP W9, #0x10 ; Decrypt?
B.EQ gcm_decrypt
BL aes_gcm_encrypt
B return
gcm_decrypt:
BL aes_gcm_decrypt
return:
RETHere is the actual AES dispatch code at 0x8798C:
AND W8, W0, #0xF ; Get algorithm type from low 4 bits
AND W9, W0, #0xF0 ; Get direction from bits 4-7
CMP W8, #0 ; Is it CBC mode?
B.EQ loc_cbc ; If yes, go CBC
; GCM mode path
LDR X10, [X3] ; Load key size info
UBFX X11, X10, #28, #4 ; Extract key size (128/192/256 bit)
CMP W9, #0x10 ; Is it decrypt?
B.EQ loc_gcm_decrypt
BL aes_gcm_get_context_enc
BL aes_gcm_encrypt_impl
B loc_return
loc_gcm_decrypt:
BL aes_gcm_get_context_dec
BL aes_gcm_encrypt_impl ; Same engine, different context
B loc_return
loc_cbc:
BL aes_cbc_encrypt
loc_return:
RETThe AND instruction masks off bits we don't need. AND W8, W0, #0xF keeps only the lowest 4 bits of the parameter register - that tells us what algorithm variant to use. UBFX is a bitfield extract instruction that pulls the key size out of bits 28-31 of the key info word. ARM64 has a lot of these bit manipulation instructions because security code constantly needs to pack and unpack flags.
6. DER Parsing
The manifest also contains certificates. These are like digital ID cards that prove who signed the firmware. They are encoded in a format called DER (Distinguished Encoding Rules). It is a standard way to organize certificate data like how every ID card has a photo on the left and name on the right.
The parser at der_parse_internal (0x1DBA10) reads these certificates. It extracts the tag (what type of data), the length (how many bytes), and the value (the actual data).
What Gets Loaded
Components are loaded one at a time. Each is fully checked before the next is read.
flowchart LR
A[AP Ticket] --> B[Hash Chain]
B --> C[Apple Logo]
C --> D[iBoot Update]
D --> E[SEP OS]
E --> F[Device Tree]
F --> G[Ramdisk]
G --> H[iBoot Data]
H --> I[KERNEL]
I --> J[Final Check]
J --> K[HANDOFF]A few things you might not know:
SEP stands for Secure Enclave Processor. It is a separate tiny computer inside your iPhone that handles sensitive things like your fingerprint data and encryption keys. It runs its own operating system (SEP OS) that iBoot loads separately. The SEP is so isolated that even iOS itself cannot directly access its memory.
A device tree is a file that describes what hardware is connected to the phone. Things like "there is a camera on this bus" or "the screen is at this address". It is like a map of the phone's hardware. Instead of hardcoding hardware information in the kernel, Apple puts it in a device tree file that can be updated separately.
A ramdisk is a small file system that lives in memory. It contains temporary files needed during boot, like drivers and configuration data. Think of it like a suitcase you bring on a trip it has everything you need for the first few days.
The kernel is the core of iOS. It manages memory, processes, drivers, and security. Everything else runs on top of it. If iBoot is the security guard at the entrance, the kernel is the building manager inside.
Kernel is loaded last. Everything else comes first.
The Final Step
iboot_final_handoff at 0x1024 is the last function before iOS starts.
flowchart TB
A[Final handoff] --> B[Check permissions]
B -->|Fail| C[STOP]
B -->|Pass| D[Load trust cache]
D --> E[Prepare kernel memory]
E --> F[Validate memory]
F --> G[Check SEP]
G --> H[Policy check]
H -->|Fail| I[STOP]
H -->|Pass| J[Clean caches]
J --> K[JUMP TO KERNEL]The function iboot_cache_flush_and_jump at 0x39DFC is the literal last thing iBoot does.
Think of CPU cache as a scratch pad. When the CPU reads or writes data, it keeps a copy on the scratch pad for speed. It is faster to read from the scratch pad than from main memory. But when iBoot is about to hand off to iOS, the scratch pad might have old or wrong data. So iBoot cleans everything.
TLB stands for Translation Lookaside Buffer. It is a cache that stores the CPU's map of virtual addresses to physical addresses. Think of it like a mini map of the city. When iBoot hands off to iOS, the old map is useless because iOS has its own memory layout. So iBoot throws away the old map.
DC CIVAC, X0 ; Clean data cache - write back scratch pad to main memory
DSB SY ; Wait for all memory operations to finish
ISB SY ; Flush instruction pipeline - discard old instructions
TLBI VMALLE1IS ; Clear address translation cache - throw away old map
BR X1 ; Jump to kernel entry pointEach instruction matters:
- Skip the cache clean? The kernel might read stale data from the scratch pad instead of the real memory.
- Skip the memory barrier? A write might not have actually reached memory yet.
- Skip the instruction flush? The CPU might execute old instructions it was holding.
- Skip the TLB flush? The kernel would use iBoot's memory map instead of its own.
- Skip the jump? Nothing happens. The phone stays on forever.
Here is the actual code at 0x39DFC:
DC CIVAC, X0 ; Clean data cache by virtual address
DSB SY ; Data sync barrier - wait for memory
ISB SY ; Instruction sync barrier - flush pipeline
TLBI VMALLE1IS ; Invalidate TLB at EL1 inner shareable
BR X1 ; Branch to kernel entry pointDC CIVAC is a data cache maintenance instruction. It writes back any dirty data at address X0 to main memory and marks the cache line as invalid. DSB SY stalls the CPU until every single memory operation before it has completed. ISB SY flushes the CPU pipeline so it doesn't execute stale instructions. TLBI VMALLE1IS clears the TLB which caches virtual-to-physical address mappings. Without this, the kernel would inherit iBoot's memory map. BR X1 jumps to the address stored in register X1, which is the kernel's entry point.
Why You Cannot Modify Your iPhone
flowchart TB
A[Try to modify firmware] --> B[SecureROM hardware blocks it]
A --> C[IMG4 hash check fails]
A --> D[AES-GCM auth fails]
A --> E[Production cert check fails]
A --> F[Boot stops immediately]Five layers, every one will stop you.
SecureROM is hardware. Cannot be changed. Only runs Apple-signed code. IMG4 means modify the firmware? Hash fails. Modify the hash? Chain breaks. Modify the manifest? AES-GCM tag fails. Modify the cert? Production check fails. AES-GCM key is fused into the CPU, cannot be read or extracted, so you cannot create a valid encrypted manifest without it. Production check means you need Apple's signing certificate, which is not public. And even if you somehow get past all that, fail-stop means any failure = no boot. No fallback. No skip this component.
This is why jailbreaking is not like it was back in the iPhone 4 days. Back then, you could just boot unsigned code if you had a bootrom exploit. Now? You have to break through all five layers. And each layer was designed by some of the best security engineers in the world.
Error Messages
| Message | What Happened |
|---|---|
Permission Denied | Security check at handoff failed |
Kernelcache image not valid | Kernel IMG4 check failed |
verify boot manifest hash | Hash verification failed |
failed to load | File not found or corrupt |
corrupt | Image damaged |
unsupported magic | Wrong IMG4 format |
All these error strings are stored in the binary:
Permission Denied
failed to load
failed to boot
Kernelcache image not valid
verify boot manifest hash
DERFLOWEach one is referenced by code that checks a specific condition. When iBoot hits a verify boot manifest hash failure, it means the hash comparison at hash_compare_manifest (0x1D8A8C) returned non-zero. The error is not just a log message - it causes iBoot to stop loading and enter an error loop.
What I Still Dont Know
- Where does the AES key come from? I traced it to
aes_decrypt_imagewhich allocates memory for key + IV + tag, but the actual key load is not visible. Probably the GID key fused into the CPU. - What hash algorithm? SHA-256 or SHA-384. The 448-byte work area suggests SHA-384, but I could not confirm from the binary alone.
- RSA or ECDSA? Found zero RSA/ECDSA code. Maybe it all happens in hardware that iBoot just talks to.
- "isp-horizon"? Shows up in a memory validation function. No idea what it is.
- "cebilefciladmplarmmhtreptlhptmbr"? Found at
0x237Cin a memory validation function. Looks obfuscated. If you know what this is, let me know.
I started this because I missed the old jailbreak days. I thought maybe I could find something interesting, maybe understand why it is so much harder now. And honestly? This was fun. Just sitting with IDA, tracing through 17,614 functions, watching how every single piece of this bootloader is designed to stop unsigned code from running.
iBoot alone is not the whole story though. There is still SecureROM, the SEP, the kernel itself. So many layers to explore. I am still learning, still reading through the code, still finding new things every time I open IDA.
If you are also into iOS reversing or just curious about how this stuff works, hit me up. Always happy to talk about this.
iBoot.d94.RELEASE.bin from iPhone 15 Pro Max. Reversed with IDA Pro