CB Code: Difference between revisions

From Xenon Wiki
Jump to navigation Jump to search
imported>TEIR1plus2
imported>TEIR1plus2
No edit summary
Line 67: Line 67:
CWriteFile("Hdd:\\cbb_dec.bin", cbb, size);
CWriteFile("Hdd:\\cbb_dec.bin", cbb, size);
XPhysicalFree(cbb);
XPhysicalFree(cbb);
}
</pre>
=CB_A Pseudocode=
Version: 9188 retail
<pre>
// 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);
}
}
</pre>
</pre>

Revision as of 06:50, 21 October 2017

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);
}