iBoot Patch Analysis: iBSS / iBEC / LLB
Analysis of iBoot patches for vresearch101 from PCC-CloudOS 26.3 (23D128).
Source Files
All six vresearch101 iBoot variants share just two unique payload binaries (after IM4P decode/decompress):
Key finding: This "identical" claim is strictly about the decoded payload bytes. At the IM4P container level, iBSS/iBEC/LLB are still different files (different fourcc and full-file hashes). Within each build variant (RELEASE or RESEARCH), the decoded payload bytes are identical.
Mode/stage identity is therefore not encoded as different payload binaries in these pristine IPSW extracts; it comes from how the boot chain loads and treats each image.
fw_patch.py targets the RELEASE variants, matching the BuildManifest identity
(PCC RELEASE for LLB/iBSS/iBEC). The dynamic patcher works on both variants.
Note: if you compare files under
vm/...after running patch scripts, RELEASE payloads will no longer be identical (expected), because mode-specific patches are applied to iBEC/LLB.
Binary Layout
Single flat ROM segment (no Mach-O, no sections):
File offset = VA − 0x7006C000.
Patch Summary
Base Patches (fw_patch.py via IBootPatcher)
JB Extension Patch (implemented)
Status: implemented in IBootJBPatcher.patch_skip_generate_nonce() and applied
by fw_patch_jb.py (JB flow). This follows the current pipeline split where
base boot patching stays minimal and nonce control is handled in JB/research flow.
Patch Details (RELEASE variant, 26.3)
Patch 1: Serial Labels
Purpose: Replace two ===...=== banner strings with descriptive labels for
serial log identification.
Anchoring: Find runs of ≥20 = characters in the binary. There are exactly
4 such runs, but only the first 2 are the banners (the other 2 are
"Start of %s serial output" / "End of %s serial output" format strings).
Label text changes per mode: Loaded iBSS / Loaded iBEC / Loaded LLB.
Containing function: sub_7006F71C (main boot function, ~0x9B4 bytes).
Patch 2: image4_validate_property_callback
Purpose: Force the image4 property validation callback to always return 0 (success), bypassing signature/property verification for all image4 objects.
Function: sub_70075350 (~0xA98 bytes) — the image4 property callback handler.
Dispatches on 4-char property tags (BORD, CHIP, CEPO, CSEC, DICE, BNCH, etc.)
and validates each against expected values.
Anchoring pattern:
B.NEfollowed immediately byMOV X0, X22CMPwithin 8 instructions before theB.NEMOVN W22, #0orMOV W22, #-1(setting error return = -1) within 64 instructions before
The B.NE is the stack canary check at the function epilogue. X22 holds the
computed return value (0 = success, -1 = failure). The patch forces return 0
regardless of validation results.
Context (function epilogue):
Patch 3: Boot-args (iBEC / LLB only)
Purpose: Replace the default boot-args format string "%s" with
"serial=3 -v debug=0x2014e %s" to enable serial output, verbose boot,
and debug flags.
This is the only source of the guest's boot-args. iBoot formats the kernel
command line with this string, and the trailing %s takes the boot-args
from iBoot's own NVRAM banks in nvram.bin, which are empty after a restore.
The host cannot add to them: VZMacAuxiliaryStorage's
_setDataValue:forNVRAMVariableNamed: writes a different store, which this
iBoot never reads. vphone-vm wrote serial=3 debug=0x104c04 there before every
boot until 2026-10; the guest never saw it, and the write was removed. A custom
variable written the same way does not reach the guest's
IODeviceTree:/options either. To change the guest's boot-args, change
IBootPatcher.bootArgs or extraBootArgs and run cfw install again.
Anchoring:
- Find
"rd=md0"string → search nearby for standalone"%s"(NUL-terminated) - Find
ADRP+ADD X2pair referencing that"%s"offset - Write new string to a NUL-padded area, redirect ADRP+ADD to it
Patch 4: Rootfs Bypass (LLB only)
Purpose: 5 patches that bypass root filesystem signature verification, allowing modified rootfs to boot.
Anchoring techniques:
- 4a, 4c, 4e: Find unique
MOV W8, #<error>instruction, theCBZis 4 bytes before. Convert conditional branch to unconditionalB(same target). - 4b: Find unique
CMP X8, #0x400, NOP theB.HSthat follows. - 4d: Scan backwards from error
0x110forLDR X8, [xN, #0x78]+CBZ X8, NOP theCBZ.
Patch 5: Panic Bypass (LLB only)
Purpose: Prevent panic when a specific boot check fails.
Anchoring: Find MOV W8, #0x328 followed by MOVK W8, #0x40, LSL #16
(forming constant 0x400328), walk forward to BL; CBNZ W0, NOP the CBNZ.
Patch 6: Skip generate_nonce (iBSS only, JB flow)
Purpose: Skip nonce generation to preserve the existing AP nonce. Required for deterministic DFU restore — without this, iBSS generates a random nonce on each boot, which can interfere with the restore process.
Function: sub_70077064 (~0x1C00 bytes) — iBSS platform initialization.
Anchoring: Find "boot-nonce" string reference via ADRP+ADD, then scan forward
for: TBZ/TBNZ W0, #0 + MOV W0, #0 + BL pattern. Convert TBZ/TBNZ to unconditional B.
Disassembly context:
The generate_nonce function (sub_70087414) calls a random number generator
(sub_70083FA4) to create a new 64-bit nonce and stores it in the platform state.
The patch makes the TBZ unconditional so the nonce generation block is always
skipped, preserving whatever nonce was already set (or leaving it empty).
Current placement (rewrite/JB path):
This patch is intentionally kept in the JB extension path (fw_patch_jb.py +
IBootJBPatcher) so the base flow remains unchanged. Use JB flow when you need
deterministic nonce behavior for restore/research scenarios.
RELEASE vs RESEARCH_RELEASE Variants
Both variants work with all dynamic patches. Offsets differ but the patcher finds them by pattern matching:
fw_patch.py targets RELEASE, matching the BuildManifest identity
(PCC RELEASE for LLB/iBSS/iBEC). The reference script used RESEARCH_RELEASE.
Both work — the dynamic patcher is variant-agnostic.
Cross-Version Comparison (26.1 → 26.3)
Reference hardcoded offsets (26.1 RESEARCH_RELEASE) vs dynamic patcher results (26.3 RELEASE):
Offsets shift significantly between versions and variants, confirming that hardcoded offsets would break. The dynamic patcher handles all combinations.
Appendix: IDA Pseudocode / Disassembly
A. Serial Label Banners (ibss_main @ 0x7006F71C)
Patcher writes "Loaded iBSS" at banner+1 (offset into the ===...=== run).
B. image4_validate_property_callback (0x70075350)
Pseudocode:
Epilogue disassembly (patch site):
Effect: function always returns 0 (success) regardless of property validation.
C. generate_nonce (0x70087414)
Pseudocode:
D. Skip generate_nonce — platform_init (0x70077064)
Disassembly (boot-nonce handling region):
Patch effect: TBZ W0, #0, 0x700777F0 → B 0x700777F0
Unconditionally skips the generate_nonce(0) call and all nonce storage logic,
jumping directly to the "dram-vendor" init. Preserves any existing AP nonce from
a previous boot or NVRAM.
Appendix E: Nonce Skip — IDA Pseudocode Before/After
generate_nonce (sub_70087414)
platform_init — boot-nonce region: BEFORE patch
platform_init — boot-nonce region: AFTER patch
Patch effect in decompiler: The entire if block containing generate_nonce()
is removed. The decompiler recognizes the unconditional B creates dead code and
eliminates it entirely — execution flows straight from env_check_property() to
the "dram-vendor" env_get.
Byte Comparison
Reference: patch(0x1b544, 0x1400000e) (26.1 RESEARCH, hardcoded)
All three produce byte-identical 0x1400000E — same branch delta +0x38 (14 words)
across all variants. Only the file offset differs between versions.
Status
patch_skip_generate_nonce() is active in the JB path via
IBootJBPatcher and fw_patch_jb.py (iBSS JB component enabled).