Skip to content

Research Notes

Angelpro09_Dev edited this page Aug 9, 2026 · 2 revisions

Research Notes

How this was actually found, wrong turns included, because the wrong turns are the useful part.

Getting a symbolicated kernel

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.lib is an import library, so parsing its COFF members gives ordinal → name for all 905 kernel exports.
  • On the console, XexGetProcedureAddress for 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 dump that looked fine and was garbage

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.

Dead ends worth recording

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.

The mistake that cost the most

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?".

A self-inflicted black screen

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.

Confirming the diagnosis

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.

Clone this wiki locally