Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
287 lines (241 sloc) 9.17 KB

Some notes on the mfg version to start with:

  • In the mfg version of the CB_A, the cpukey is set to null and XeCryptHmacSha is given the null buffer to generate the next key
  • Other than that there is no other change in the mfg version, boot process seems to continue as normal unless I'm overlooking a flag being set
  • It *seems* the mfg version is signed, which means we should be able to flash it to consoles without an issue and then we wouldn't have to worry about cpukeys for the cb at least. xebuild doesn't do that, I wonder why

Dump CB

// BLKey = 1BL Key
// Hvx methods are meant to be proxies to read HV memory from user mode.
#define SPACE_NAND 0x80000200C8000000ULL

void getCB_AKey(PBYTE Keybuf)
{
    QWORD cbAddy = SPACE_NAND + Hvx::HvPeekDWORD(SPACE_NAND + 8);
    BYTE cbSalt[0x10];
    Hvx::HvPeekBytes(cbAddy+0x10, cbSalt, 0x10);
    XeCryptHmacSha(BLKey, 0x10, cbSalt, 0x10, 0, 0, 0, 0, Keybuf, 0x10);
}

void getCB_BKey(PBYTE Keybuf)
{
    DWORD cbOffs = Hvx::HvPeekDWORD(SPACE_NAND + 8);
    DWORD cbbOffs = cbOffs + (Hvx::HvPeekDWORD(SPACE_NAND + cbOffs + 0xC) + 0xF) & 0xFFFFFFF0;
    QWORD cbbAddy = SPACE_NAND + cbbOffs;

    BYTE cbbSalt[0x10];
    BYTE cbKey[0x10];
    BYTE CPUKey[0x10];
    getCB_AKey(cbKey);
    getCPUKey(CPUKey);
    Hvx::HvPeekBytes(cbbAddy+0x10, cbbSalt, 0x10);
    XeCryptHmacSha(cbKey, 0x10, cbbSalt, 0x10, CPUKey, 0x10, 0, 0, Keybuf, 0x10);
}

void DumpCB_A()
{
    DbgOut("Dumping CB_A....\n");
    QWORD cbAddy = SPACE_NAND + Hvx::HvPeekDWORD(SPACE_NAND + 8);
    DWORD size = Hvx::HvPeekDWORD(cbAddy+0xC);
    printf("cbAddy: %016llX\nSize: %X\n", cbAddy, size);
    PBYTE cb = (PBYTE)XPhysicalAlloc(size, MAXULONG_PTR, NULL, PAGE_READWRITE);
    Hvx::HvPeekBytes(cbAddy, cb, size);
    CWriteFile("Hdd:\\cb_enc.bin", cb, size);

    BYTE rc4key[0x10];
    getCB_AKey(rc4key);
    XECRYPT_RC4_STATE rc4;
    XeCryptRc4Key(&rc4, rc4key, 0x10);
    XeCryptRc4Ecb(&rc4, cb + 0x20, size - 0x20);
    CWriteFile("Hdd:\\cb_dec.bin", cb, size);
    XPhysicalFree(cb);
}

void DumpCB_B()
{
    DbgOut("Dumping CB_B....\n");
    DWORD cbOffs = Hvx::HvPeekDWORD(SPACE_NAND + 8);
    DWORD cbbOffs = cbOffs + (Hvx::HvPeekDWORD(SPACE_NAND + cbOffs+0xC) + 0xF) & 0xFFFFFFF0;
    QWORD cbbAddy = SPACE_NAND + cbbOffs;
    DWORD size = Hvx::HvPeekDWORD(cbbAddy + 0xC);
    printf("cbbOffs: 0x%08X\ncbbAddy: 0x%016llX\nSize: 0x%X\n", cbbOffs, cbbAddy, size);
    PBYTE cbb = (PBYTE)XPhysicalAlloc(size, MAXULONG_PTR, NULL, PAGE_READWRITE);
    Hvx::HvPeekBytes(cbbAddy, cbb, size);
    CWriteFile("Hdd:\\cbb_enc.bin", cbb, size);

    BYTE cbbKey[0x10];
    getCB_BKey(cbbKey);
    XECRYPT_RC4_STATE rc4;
    XeCryptRc4Key(&rc4, cbbKey, 0x10);
    XeCryptRc4Ecb(&rc4, cbb + 0x20, size - 0x20);
    CWriteFile("Hdd:\\cbb_dec.bin", cbb, size);
    XPhysicalFree(cbb);
}

CB_A Pseudocode

Version: 9188 retail

// version: 0x23E4
// entry: 0x3C0
// size: 0x1AC0

// registers preset by the 1bl
// r27: not used
// r28: not used
// r29: not used
// r30: not used
// r31: next bl (cbb nand offset)
//          CB_A nand offset + CB_A size aligned to the upper 0x10 byte (ex: size = (size+0xF) & 0xFFFFFFF0)

#define STACK   0x800002000001F700 // r1
#define TOC     0x800002000001C000 // r2
#define SRAM    0x8000020000010000
#define POSTo   0x8000020000061010
#define NAND    0x80000200C8000000
#define SOC     0x8000020000020000

// an attempt to make this easier to read
#define read64(addy) *(QWORD*)addy
#define read32(addy) *(DWORD*)addy
#define read16(addy) *(WORD*)addy
#define write64(addy, data) *(QWORD*)addy = data
#define write32(addy, data) *(DWORD*)addy = data
#define write16(addy, data) *(WORD*)addy = data

typedef struct _BLHeader
{
    WORD Magic;         // 0 : 2
    WORD Version;       // 2 : 2
    DWORD Flags;        // 4 : 4
    DWORD EntryPoint;   // 8 : 4
    DWORD Size;         // 0xC : 4
    BYTE key[0x10];     // 0x10 : 0x10
    QWORD Pad[4];       // 0x20 : 0x20
    // not used here? XECRYPT_SIG Sig;  // 0x40 : 0x100
    // Header: 0x140
}BLHeader, *PBLHeader;

void POST(BYTE postCode)
{
    write64(POSTo, (postCode << 56));
}

void PanicGen()
{
    while(1)
        continue;
}

void Panic(QWORD postCode)
{
    POST(postCode);
    PanicGen();
}

bool VerifyOffset(DWORD offset, DWORD arg2)
{
    if(offset != (offset + 0xF) & 0xFFFFFFF0)
        return false;
    if(offset - 0x80 > 0x7FFFF7F)
        return false;
    if(arg2 & 0xFFFFFFF0 >= offset - 0x8000000)
        return false;
    return true;
}

// Copies by 0x10 byte blocks
// cBlocks: how many 0x10 byte blocks to copy
void CopyBy128(QWORD dest, QWORD src, DWORD cBlocks)
{
    for(int i = 0; i < cBlocks; i++)
    {
        write64(dest+(i*0x10), read64(src+(i*0x10)));
        write64(dest+(i*0x10)+8, read64(src+(i*0x10)+8))
    }
}

void ZeroBy128(QWORD addy, QWORD count)
{
    for(int i = 0; i < count; i++)
    {
        write64(addy+(i*0x10), 0ULL);
        write64(addy+(i*0x10)+8, 0ULL);
    }

}

QWORD getFuseline(DWORD fuse)
{
    if ((fuse * 0x40) < 0x300)
        return read64(SOC + ((fuse * 0x40) << 3));
    return 0;
}

void CBB_Jump(QWORD EntryPoint, QWORD NextBL)
{
    // presets for the next bootloader
    QWORD r27 = read64(SRAM + 0x20);
    QWORD r28 = read64(SRAM + 0x28);
    QWORD r29 = read64(SRAM + 0x30);
    QWORD r30 = read64(SRAM + 0x38);
    QWORD r31 = NextBL; // nand offset of the next bl

    // null the beginning of the cbb
    ZeroBy128(SRAM + 0x20, 0x12);

    DWORD tSize = (read32(SRAM + 0xC) + 0xF) & 0xFFFFFFF0;
    if(tSize > 0xC000)
        Panic(0xF3);

    // null the area after the cbb
    ZeroBy128(SRAM + tSize, (0xC000 - tSize) >> 4);

    // Sets r0-r26 and the CTR to 0

    // jump to cbb
    EntryPoint = (EntryPoint & 0xFFFF) + 0x2000000;
    goto EntryPoint;
}

void CBB_Load(const QWORD offCBB, QWORD destCBB)
{
    // first null the stack
    for(int i = 0; i < 0xB; i++)
        write64(stack-0x1A8+(i*8), 0ULL);

    POST(0xD1); // copy fuses for CBB decryption
    QWORD fuses[12] = { 0 };
    BYTE CPUKey[0x10] = { 0 };

    // if this is the mfg cb_a or a dev cb_a then the cpukey is set to 0
#ifdef RETAIL
    for(int i = 0; i < 12; i++)
        getFuseline(i);
    QWORD fuse = fuses[3] | fuses[4]; // first CPUKey fuses
    write64(CPUKey, fuse);
    fuse = fuses[5] | fuses[6]; // second CPUKey fuses
    write64(CPUKey+8, fuse);
#endif

    POST(0xD2); // verify CBB offset
    DWORD cbbOffset = offCBB & 0xFFFFFFFF; // r28
    if(!VerifyOffset(cbbOffset, 0x10))
        Panic(0xF0);

    POST(0xD3); // copy cbb header to sram
    QWORD cbbAddy = NAND + cbbOffset;
    CopyBy128(destCBB, cbbAddy, 1);

    POST(0xD4); // verify header
    PBLHeader cbbHeader = (PBLHeader)destCBB;
    if((cbbHeader->Size - 0x3C0) > 0xBC40 // size check
        || cbbHeader->Magic != read64(TOC) & 0xFFFF // magic check
        || cbbHeader->EntryPoint & 0x3 // alignment check
        || cbbHeader->EntryPoint < 0x3C0 // EntryPoint check
        || cbbHeader->EntryPoint >= cbbHeader->Size & 0xFFFFFFFC // entrypoint/size relation check
        || !VerifyOffset(cbbOffset, cbbHeader->Size))
        Panic(0xF1);

    POST(0xD5); // copy cbb to SRAM
    QWORD tSize = (cbbHeader->Size + 0xF) & 0xFFFFFFF0;
    CopyBy128(destCBB + 0x10, cbbAddy + 0x10, ((tSize - 0x10) >> 4) & 0xFFFFFFFF);

    POST(0xD6); // Gen cbb key
    // cbb key = hmacsha of the cb_a key, cb_b salt, and CPUKey
    XeCryptHmacSha(TOC+0x10, 0x10, &cbbHeader->key, 0x10, CPUKey, 0x10, 0, 0, &cbbHeader->key, 0x10);

    POST(0xD7); // set key
    XECRYPT_RC4_STATE rc4;
    XeCryptRc4Key(&rc4, &cbbHeader->key, 0x10);

    POST(0xD8); // decrypt cbb
    XeCryptRc4Ecb(&rc4, SRAM+0x20, tSize-0x20);

    POST(0xD9); // generate hash
    BYTE Hash[0x14] = { 0 };
    XeCryptRotSumSha(SRAM, 0x10, SRAM+0x140, tSize-0x140, Hash, 0x14);

    POST(0xDA); // verify integrity
    if(memcmp(Hash, TOC+0x39C, 0x14))
        Panic(0xF2);

    POST(0xDB); // jump to cbb
    write16(SRAM+6, read16(SRAM+6)); // copy flags
    CBB_Jump(cbbHeader->EntryPoint, tSize+offCBB);
    return;
}

void CBA_Main()
{
    // registers 27-31 are preset by the 1bl opon entry
    POST(0xD0); // CB_A entry, copy self to 0x800002000001C000 and continue from there
    DWORD size = *(QWORD*)SRAM+0xC;
    size = (size+0xF) >> 3;
    for(int i = 0; i < size; i++)
        write64(TOC+(i*8), read64(SRAM+(i*8)));

    // from now on we're executing from 0x800002000001C000
    CBB_Load(r31, SRAM);
}