-
Notifications
You must be signed in to change notification settings - Fork 2
Research Notes
How this was actually found, wrong turns included, because the wrong turns are the useful part.
The NAND extraction route (CB_A → CB_B → CD → CE → base kernel 1888, then the CF/CG patch chain up to 17559) stalled on a data-integrity bug in the LZX delta.
The route that worked was simpler: dump the live kernel straight off the console and symbolicate it from two sources that meet in the middle:
- The XDK's
xboxkrnl.libis an import library, so parsing its COFF members gives ordinal → name for all 905 kernel exports. - On the console,
XexGetProcedureAddressfor each ordinal gives ordinal → live address.
Joining them yields name → address for the whole exported surface — 887 symbols. That is enough
to symbolicate every call target in the disassembly, including calls made by the internal,
non-exported SataDisk* functions we cared about.
Capstone (CS_ARCH_PPC, CS_MODE_32 | CS_MODE_BIG_ENDIAN) handled the disassembly, driven in a
loop that resumes four bytes past anything it cannot decode — the kernel image starts with a PE
header and has data words scattered through .text, and Capstone otherwise stops at the first one.
The first dump reported complete success and was pure noise. All 256 byte values appeared roughly equally often; 0.4% zeros.
The kernel region is in the Xenon's encrypted memory. The CPU decrypts on cache-line fill, but
the DMA path WriteFile uses for a large transfer sees raw ciphertext. Reading the same addresses
through a volatile DWORD* and logging the hex gave perfectly valid PowerPC — the difference was
purely CPU read versus DMA read.
Fix: copy word by word with the CPU into an ordinary title buffer, then write that.
Patching SataDiskAuthenticateDevice. Several rounds went into this. It cannot work: the gate
is evaluated once at boot. Reading the bytes before writing showed XeUnshackle had already applied
that exact patch, and the disk still did not mount — which was the evidence that finally killed the
idea.
Re-running the disk initialisation. No warm reboot preserves patches; every reset reloads clean from NAND. Hot-swapping the drive triggers the boot animation and drops the exploit.
Installing the disk key by hand. The one piece of authenticated state that cannot be
reconstructed from the disk in front of you, and the reason avatars stay out of reach. A sector
saved from a drive that does authenticate was replayed through
XeKeysSetKey(0x1001, sector, 0x200) — the same call SataDiskAuthenticateDevice makes — and it
returned 0xC8000002.
The control that settled it: the same call, with the same sector, on the drive that had just
authenticated with it during boot. Also 0xC8000002. So the slot is write-once per boot and the
validity of the sector never enters into it. The scaffolding written for this experiment is not in
the fork, since it only ever produced a negative result.
Blaming Bad Storage for deleting the partitions. A reasonable theory, and worth checking. It is
loaded on every boot — XeUnshackle does it with LoadLibrary("GAME:\\BadStorage.xex.dll"), outside
the launch.ini plugin list, which is easy to miss. But with XboxHardwareInfo->Flags & 0x20 clear
it returns at the "Disk not genuine" check before touching any device object, and there is no
IoDeleteDevice anywhere in its source or in the kernel's entire SATA region. It fails because
the partitions are unusable, not the other way round.
For several rounds the working assumption was that the partition device objects did not exist,
because ObReferenceObjectByName failed for every one of them.
The status code said otherwise the whole time. 0xC000000E is STATUS_NO_SUCH_DEVICE — the name
resolved, the device was refused. STATUS_OBJECT_NAME_NOT_FOUND is 0xC0000034, and that is not
what came back.
Meanwhile the disassembly plainly showed PhysicalDisk and Partition0 being created six
instructions apart with no branch between them, so "one exists and the other does not" was never
consistent. Trusting the disassembly over the assumption sooner would have saved a lot of time.
What broke the deadlock was enumerating the directory instead of guessing names — the moment
SystemAuxPartition showed up in a listing, the objects were proven to exist and the question
changed from "why were they never created?" to "why are they unreachable?".
The first directory enumeration hung the console at a black screen.
Cause: assuming NtQueryDirectoryObject had the Windows signature. The Xbox version has six
parameters, not seven — there is no RestartScan — so a BOOLEAN landed where the kernel expects
PULONG Context. It writes through that pointer without a NULL check, so the value 1 became an
unaligned store to address 1.
The same bug also explained an earlier symptom that had looked unrelated: every call reading its
context from address 0 (which is mapped on Xbox 360, so no immediate fault) and therefore
returning the same first entry over and over.
Lesson: disassemble the API before calling it. The kernel dump was already sitting there; five minutes of reading the prologue would have avoided the hang.
Once the device objects were reachable by walking the directory, the flags settled it:
PhysicalDisk flags=0x0D ready len=240057409536
Partition0 flags=0x1D INITIALIZING len=0
Partition1 flags=0x1C INITIALIZING len=0
(nine more) flags=0x1C/0x1D INITIALIZING len=0
0x1D and 0x1C are exactly what IoCreateDevice (0x18) plus SataDiskCreateDevice's
flags | 4 predicts for flags arguments of 1 and 0. The 0x10 bit was clear only on
PhysicalDisk — the one device completed before the gate.