TEIR1plus2
VIP
VIP
Hidden Devils
I spent a day looking into how XeKeysExecute works and what it's purpose is other than blocking xbox live from modded consoles. This is the result
EDIT: I rewrote some of it to be more versatile and easier to use. Simply provide it with the payload to be executed and it will build the header and encrypt the data
UPDATE 12/12/2017: After going back and reversing the entirety of HvxKeysExecute, I rewrote this to be more inline to how the syscall handles it, and removed some unneeded checks that the HV takes care of. Also fixed a potential memory leak (although it wouldn't trigger unless you were executing hundreds of payloads per session).
Also: Added support for a variable entrypoint, that way if you wanted to store some data or subroutines before the actual entry for some reason, you can.
If anyone wants to see the reversed syscall, I put it on free60: http://free60.org/wiki/HvxKeysExecute
EDIT: I rewrote some of it to be more versatile and easier to use. Simply provide it with the payload to be executed and it will build the header and encrypt the data
UPDATE 12/12/2017: After going back and reversing the entirety of HvxKeysExecute, I rewrote this to be more inline to how the syscall handles it, and removed some unneeded checks that the HV takes care of. Also fixed a potential memory leak (although it wouldn't trigger unless you were executing hundreds of payloads per session).
Also: Added support for a variable entrypoint, that way if you wanted to store some data or subroutines before the actual entry for some reason, you can.
If anyone wants to see the reversed syscall, I put it on free60: http://free60.org/wiki/HvxKeysExecute
Code:
const WORD cMagic = 0x4D4D;
const WORD cVersion = 0x4099;
const DWORD cFlags = NULL;
const DWORD cHeaderSize = 0x120;
const BYTE cKey[0x10] = { 0x1b, 0x9d, 0xc0, 0x98, 0xd8, 0x09, 0x81, 0x50, 0xe5, 0x57, 0xc2, 0xc2, 0xd8, 0xfe, 0xf9, 0xf7 }; // https://www.random.org/bytes/
const BYTE cSig[0x100] = { 0 };
const DWORD cBufferSize = 0x3000;
// similar to a bootloader header, missing the presets field.
typedef struct _KeysExecute
{
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
BYTE Sig[0x100]; // 0x20 : 0x100
// Header: 0x120
}KeysExecutes, *PKeysExecute;
BYTE BLKey[0x10] = { <redacted> };
LPCSTR KeysExecutePayloadFile = "usb:\\XeKeys\\KeysExecutePayload.bin";
LPCSTR KeysExecuteRespFile = "usb:\\XeKeys\\KeysExecuteResp.bin";
// XeKeysExecute seems to be ms's official way of executing a privileged ppc block.
// Similar to expansions, except this will free the memory after being used instead of it being taken for the entirety of the session.
// Each(KeysExecute and Expansions) has their advantages/disadvantages.
// If your console freezes after calling HvxKeysExecute check the following:
// - Allocate the code block in using something like XPhysicalAlloc or MmAllocatePhysicalMemory
// The address must be within a certain range to be considered valid
// - Check your PPC, I can't protect you from bad PPC code that causes a read/write exception
// Parameters
// Payload: Payload buffer
// - Must be 0x80 byte aligned
// - Must be > 0x120 and < 0x10000
// - Must be allocated with XPhysicalAlloc or MmAllocatePhysicalMemory
// BufferSize: Return buffer size
// - Must be > 0x120 and < 0x10000
// - Must be 0x80 byte aligned
// Header Info
// WORD Magic: Must be 0x4D4D
// WORD Version: Doesn't really matter
// DWORD Flags: Doesn't really matter, leave null
// DWORD EntryPoint:
// - Must be after >= 0x120 and < Size
// - Must be 4 byte aligned
// DWORD Size:
// - Must be > 0x120 and < BufferSize
// BYTE Key[0x10]: key used for Rc4 encryption/decryption. rc4Key = HmacSha(1BLKey, 0x10, header->Key, 0x10)
// BYTE Sig[0x100]: Rsa signature, we patch the RSA check so this can be null without an issue
// PBYTE CodeBlock: Payload to be executed
// 7s censors this word for some reason, but its needed here so its: _declspec(n_a_k_e_d) remove the underscores. pm me if there is an issue with this
static QWORD _declspec(n***d) HvxKeysExecute(PVOID pvPayload, DWORD cbPayload, QWORD Arg1, QWORD Arg2, QWORD Arg3, QWORD Arg4)
{
__asm
{
li r0, 0x40
sc
blr
}
}
QWORD ExecutePayload(PBYTE pbPayload, DWORD cbPayload, DWORD EntryPoint, QWORD Arg1, QWORD Arg2, QWORD Arg3, QWORD Arg4)
{
if (EntryPoint & 3 // entrypoint must be 4 byte aligned, as always
|| EntryPoint >= cbPayload // sanity check
|| cbPayload + cHeaderSize > cBufferSize) // sanity check
return -1;
BYTE* bPayload = (PBYTE)XPhysicalAlloc(cBufferSize, MAXULONG_PTR, 0x10000, PAGE_READWRITE);
memset(bPayload, 0, cBufferSize);
QWORD physPayload = 0x8000000000000000ULL + (DWORD)MmGetPhysicalAddress(bPayload);
// we have memory allocated, we can't just return when something goes wrong.
QWORD ret = 0;
// check for an internal system
// As long as the buffer does not extend over multiple 0x10000 byte pages, this will not trip
QWORD SOCCheck = (((physPayload + cBufferSize) - 1) ^ physPayload) & 0xFFFF0000;
if (SOCCheck == 0)
{
if ((physPayload & 0xFFFFFFFF) <= 0x1FFFFFFF) // address validation (HV hangs if it catches this)
{
// build the header
PKeysExecute cHeader = (PKeysExecute)bPayload;
cHeader->Magic = cMagic;
cHeader->Version = cVersion;
cHeader->Flags = cFlags;
cHeader->Size = cHeaderSize + cbPayload;
cHeader->EntryPoint = cHeaderSize + EntryPoint;
memcpy(bPayload + cHeaderSize, pbPayload, cbPayload);
memcpy(cHeader->key, cKey, 0x10);
memcpy(cHeader->Sig, cSig, 0x100);
// data needs to be sent encrypted with the key in the header! rc4Key = HmacSha(header+0x10, 0x10)
// you could just patch the call to XeCryptRc4Ecb as well and send it unencrypted data. but im not doing that - address: 0x6148
XECRYPT_RC4_STATE rc4;
BYTE rc4Key[0x10];
XeCryptHmacSha(Keys::BLKey, 0x10, cHeader->key, 0x10, 0, 0, 0, 0, rc4Key, 0x10);
printf("Encrypting data...\n");
XeCryptRc4Key(&rc4, rc4Key, 0x10);
XeCryptRc4Ecb(&rc4, bPayload + 0x20, cHeader->Size - 0x20);
printf("Done!\n");
// patch the rsa check in HvxKeysExecute - allows a custom payload to be run.
HvxPokeDWORD(0x617C, 0x38600001); // li r3, 1
// attempt to execute payload by syscall - HvxKeysExecute = 0x40
printf("Executing Payload...\n");
if (Arg1)
printf("Arg1: 0x%016llX\n", Arg1);
if (Arg2)
printf("Arg2: 0x%016llX\n", Arg2);
if (Arg3)
printf("Arg3: 0x%016llX\n", Arg3);
if (Arg4)
printf("Arg4: 0x%016llX\n", Arg4);
ret = HvxKeysExecute(MmGetPhysicalAddress(bPayload), cBufferSize, Arg1, Arg2, Arg3, Arg4);
// restore the rsa check
HvxPokeDWORD(0x617C, 0x48005245); // bl XeCryptBnQwBeSigVerify
}
else
{
printf("Address out of range!\n");
printf("physPayload: 0x%016llX\n", physPayload);
ret = -1;
}
}
else
{
printf("SOCCheck Failed\n");
printf("[SOCCheck: 0x%016llX][cBufferSize: 0x%X]\n", SOCCheck, cBufferSize);
ret = -1;
}
if (ret != 0)
{
if (ret == 0xC8000030)
printf("[%08X] Parameter Fail!\n\tPayload must be 0x80 byte aligned!\n\tSize must be greater than 0x120 and less than 0x10000 AND must be 0x80 byte aligned!\n", ret);
else if (ret == 0xC8000032)
printf("[%08X] Magic or Address Fail!\n\tMagic must be 0x4D4D\n", ret);
else if (ret == 0xC8000033)
printf("[%08X] HV Magic Fail!\n\tHV magic must be 0x4E4E\n", ret);
else if (ret == 0xC8000034)
printf("[%08X] Header Size Fail!\n\tSize in header must be > 0x120 AND aligned to 0x10 AND < the buffer size!\n", ret);
else if (ret == 0xC8000035)
printf("[%08X] EntryPoint Fail!\n\tEntrypoint must be > 0x120 AND 4 byte aligned AND < code size\n", ret);
else if (ret == 0xC8000036)
printf("[%08X] Crypt/Signature Fail!\n\tPatch the call to XeCryptBnQwBeSigVerify!\n", ret);
else
printf("ret: %016llX\n", ret);
}
// dump return buffer (incase it was used)
if (!CWriteFile("Hdd:\\KeysExecuteRetBuf.bin", bPayload, cBufferSize))
printf("Return buffer not dumped!\n");
// done, whether it failed or not, free the memory
XPhysicalFree(bPayload);
return ret;
}
Last edited: