8#include <crypto/rijndael-alg-fst.h>
9#include <memory/memory.h>
11#include <loader/lzx.h>
12#include <kernel/kernel.h>
13#include <kernel/modules/xboxkrnl.h>
15static uint32_t handle = 0x10000001;
19uint32_t mainXexBase, mainXexSize;
23static const uint8_t xe_xex2_retail_key[16] = {
24 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3,
25 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91};
27void aes_decrypt_buffer(
const uint8_t* session_key,
const uint8_t* input_buffer,
28 const size_t input_size, uint8_t* output_buffer,
29 const size_t output_size)
31 uint32_t rk[4 * (MAXNR + 1)];
32 uint8_t ivec[16] = {0};
33 int32_t Nr = rijndaelKeySetupDec(rk, session_key, 128);
34 const uint8_t* ct = input_buffer;
35 uint8_t* pt = output_buffer;
36 for (
size_t n = 0; n < input_size; n += 16, ct += 16, pt += 16)
38 rijndaelDecrypt(rk, Nr, ct, pt);
39 for (
size_t i = 0; i < 16; i++)
48: IModule(path.substr(path.find_last_of(
'/')+1).c_str())
50 this->buffer = buffer;
51 this->path = path.substr(0, path.find_last_of(
'/'));
52 this->xexHandle = handle++;
55 header.module_flags = bswap32(header.module_flags);
56 header.header_size = bswap32(header.header_size);
57 header.sec_info_offset = bswap32(header.sec_info_offset);
58 header.optional_header_count = bswap32(header.optional_header_count);
60 if (memcmp(header.magic,
"XEX2", 4) != 0)
63 strncpy(magic, header.magic, 4);
65 printf(
"ERROR: Invalid magic: Expected \"XEX2\", got \"%s\"\n", magic);
69 printf(
"%d optional headers found\n", header.optional_header_count);
70 printf(
"PE data is at offset 0x%08x\n", header.header_size);
72 std::vector<optionalHeader_t> optHeaders;
73 size_t optHeaderSize = header.optional_header_count*
sizeof(
optionalHeader_t);
78 opt.id = bswap32(opt.id);
79 opt.offset = bswap32(opt.offset);
80 optHeaders.push_back(opt);
84 mainXexSize = image_size();
87 uint8_t* aes_key = (buffer+header.sec_info_offset+336);
88 aes_decrypt_buffer(xe_xex2_retail_key, aes_key, 16, session_key, 16);
90 printf(
"AES key is 0x%02x", session_key[0]);
91 for (
int i = 0; i < 15; i++)
93 printf(
"%02x", session_key[i+1]);
97 for (
auto& hdr : optHeaders)
105 fileInfoOffset = hdr.offset;
106 ParseFileInfo(hdr.offset);
110 entryPoint = hdr.value;
111 printf(
"Image entry point is 0x%08x\n", entryPoint);
114 baseAddress = hdr.value;
116 mainXexBase = baseAddress;
117 printf(
"Image base is 0x%08x\n", baseAddress);
120 importBaseAddr = hdr.offset;
123 stackSize = hdr.value;
124 printf(
"Stack size is 0x%08x\n", hdr.value);
127 printf(
"Unknown optional header ID: 0x%08x\n", hdr.id);
132 printf(
"%d, %d\n", encryptionFormat, compressionFormat);
134 uint32_t uncompressedSize;
135 switch (compressionFormat)
138 uncompressedSize = ReadImageBasicCompressed(buffer, len, &outBuffer);
141 uncompressedSize = ReadImageCompressed(buffer, len, &outBuffer);
144 printf(
"Unknown compression format %d\n", compressionFormat);
148 std::ofstream out(
"out.pe");
149 out.write(outBuffer, uncompressedSize);
153 if (*(uint32_t*)outBuffer != 0x00905a4d )
155 printf(
"Invalid PE magic\n");
158 printf(
"Found valid PE header file\n");
160 void* base = Memory::AllocMemory(baseAddress, uncompressedSize);
161 memcpy(base, outBuffer, uncompressedSize);
164 exportBaseAddr = bswap32(*(uint32_t*)&buffer[header.sec_info_offset+0x160]);
167 exportTable.magic[0] = Memory::Read32(exportBaseAddr+0x00);
168 exportTable.magic[1] = Memory::Read32(exportBaseAddr+0x04);
169 exportTable.magic[2] = Memory::Read32(exportBaseAddr+0x08);
170 exportTable.modulenumber[0] = Memory::Read32(exportBaseAddr+0x0C);
171 exportTable.modulenumber[1] = Memory::Read32(exportBaseAddr+0x10);
172 exportTable.version[0] = Memory::Read32(exportBaseAddr+0x14);
173 exportTable.version[1] = Memory::Read32(exportBaseAddr+0x18);
174 exportTable.version[2] = Memory::Read32(exportBaseAddr+0x1C);
175 exportTable.imagebaseaddr = Memory::Read32(exportBaseAddr+0x20);
176 exportTable.count = Memory::Read32(exportBaseAddr+0x24);
177 exportTable.base = Memory::Read32(exportBaseAddr+0x28);
183 importHdr.size = bswap32(importHdr.size);
184 importHdr.stringTable.count = bswap32(importHdr.stringTable.count);
185 importHdr.stringTable.size = bswap32(importHdr.stringTable.size);
187 std::vector<std::string> importNames;
188 for (
size_t i = 0, j = 0; i < importHdr.stringTable.size && j < importHdr.stringTable.count; j++)
190 const char*
string = (
const char*)&buffer[importBaseAddr+
sizeof(
importHeader_t)+i];
191 importNames.push_back(std::string(
string));
193 i += strlen(
string) + 1;
197 printf(
"Found import \"%s\"\n",
string);
200 uint32_t libraryoffs = importHdr.stringTable.size + 12;
201 while (libraryoffs < importHdr.size)
205 libHdr.count = bswap16(libHdr.count);
206 libHdr.id = bswap32(libHdr.id);
207 libHdr.name_index = bswap16(libHdr.name_index);
208 libHdr.size = bswap32(libHdr.size);
209 libHdr.version_min_value = bswap32(libHdr.version_min_value);
210 libHdr.version_value = bswap32(libHdr.version_value);
214 lib.name = importNames[libHdr.name_index];
216 printf(
"Parsing imports for \"%s\"\n", lib.name.c_str());
218 ParseLibraryInfo(importBaseAddr+libraryoffs+
sizeof(
libraryHeader_t), lib, libraries.size(), lib.name);
220 libraries.push_back(lib);
222 libraryoffs += libHdr.size;
225 Kernel::RegisterModuleForName(GetName().c_str(),
this);
228uint32_t XexLoader::GetEntryPoint()
const
233uint32_t XexLoader::GetStackSize()
const
238size_t XexLoader::GetLibraryIndexByName(
const char *name)
const
240 for (
int i = 0; i < libraries.size(); i++)
242 auto& lib = libraries[i];
243 if (lib.name == name)
249uint32_t XexLoader::LookupOrdinal(uint32_t ordinal)
251 ordinal -= exportTable.base;
252 if (ordinal >= exportTable.count)
254 printf(
"ERROR: Imported unknown function 0x%08x\n", ordinal);
258 uint32_t num = ordinal;
259 uint32_t ordinal_offset = Memory::Read32(exportBaseAddr+
sizeof(
xexExport_t)+(num*4));
260 ordinal_offset += exportTable.imagebaseaddr << 16;
261 return ordinal_offset;
264void XexLoader::ParseFileInfo(uint32_t offset)
267 fileInfo.info_size = bswap32(fileInfo.info_size);
268 fileInfo.compression_type = bswap16(fileInfo.compression_type);
269 fileInfo.encryption_type = bswap16(fileInfo.encryption_type);
271 printf(
"Found file info optional header: %d bytes, compression of type %d, encryption of type %d\n", fileInfo.info_size, fileInfo.compression_type, fileInfo.encryption_type);
273 compressionFormat = fileInfo.compression_type;
274 encryptionFormat = fileInfo.encryption_type;
278void XexLoader::ParseLibraryInfo(uint32_t offset,
xexLibrary_t &lib,
int index, std::string& name)
280 for (uint32_t i = 0; i < lib.header.count; i++)
282 uint32_t recordAddr = bswap32(*(uint32_t*)&buffer[offset]);
285 uint32_t record = Memory::Read32(recordAddr);
292 if ((record >> 24) == 1 && name !=
"xam.xex")
294 Memory::Write32(recordAddr+0x00, 0x39600000 | (index << 12) | (record & 0xFFFF));
295 Memory::Write32(recordAddr+0x04, 0x44000042);
296 Memory::Write32(recordAddr+0x08, 0x4e800020);
297 Memory::Write32(recordAddr+0x0C, 0x60000000);
299 else if ((record >> 24) == 1 && name ==
"xam.xex")
305 uint32_t addr = xam->LookupOrdinal(record & 0xFFFF);
306 Memory::Write32(recordAddr+0x00, 0x3D600000 | addr >> 16);
307 Memory::Write32(recordAddr+0x04, 0x616B0000 | (addr & 0xFFFF));
308 Memory::Write32(recordAddr+0x08, 0x7D6903A6);
309 Memory::Write32(recordAddr+0x0C, 0x4E800420);
313 if (name ==
"xboxkrnl.exe" && krnlModule.IsExportVariable(record & 0xFFFF))
315 printf(
"TODO: Variable import 0x%08x\n", record);
321int XexLoader::ReadImageBasicCompressed(uint8_t *buffer,
size_t xex_len,
char** outBuffer)
323 const uint8_t* p = buffer+header.header_size;
324 std::vector<basicCompression_t> blocks((info.info_size - 8) / 8);
325 uint32_t uncompressedSize = 0;
326 for (
size_t i = 0; i < (info.info_size - 8) / 8; i++)
328 uint32_t offset = fileInfoOffset + 8 + (i * 8);
330 comp.data_size = bswap32(comp.data_size);
331 comp.zero_size = bswap32(comp.zero_size);
332 blocks.push_back(comp);
333 uncompressedSize += comp.data_size + comp.zero_size;
336 printf(
"Image is %d bytes uncompressed\n", uncompressedSize);
338 char* out =
new char[uncompressedSize];
340 uint8_t* d = (uint8_t*)out;
342 uint32_t rk[4 * (MAXNR + 1)];
343 uint8_t ivec[16] = {0};
344 int32_t Nr = rijndaelKeySetupDec(rk, session_key, 128);
346 for (
size_t n = 0; n < blocks.size(); n++)
348 const uint32_t dataSize = blocks[n].data_size;
349 const uint32_t zeroSize = blocks[n].zero_size;
351 const uint8_t* ct = p;
353 for (
size_t m = 0; m < dataSize; m += 16, ct += 16, pt += 16)
355 rijndaelDecrypt(rk, Nr, ct, pt);
356 for (
size_t i = 0; i < 16; i++)
364 d += dataSize + zeroSize;
367 return uncompressedSize;
370int XexLoader::ReadImageCompressed(uint8_t *buffer,
size_t xex_len,
char **outBuffer)
372 const uint32_t exe_length = (uint32_t)(xex_len - header.header_size);
373 const uint8_t* exe_buffer = (
const uint8_t*)(buffer + header.header_size);
375 uint8_t* compress_buffer = NULL;
376 const uint8_t* p = NULL;
379 bool free_input =
false;
380 const uint8_t* input_buffer = exe_buffer;
381 size_t input_size = exe_length;
383 switch (encryptionFormat)
389 input_buffer = (
const uint8_t*)calloc(1, exe_length);
390 aes_decrypt_buffer(session_key, exe_buffer, exe_length, (uint8_t*)input_buffer, exe_length);
395 hdr.windowSize = bswap32(hdr.windowSize);
396 hdr.firstBlock.blockSize = bswap32(hdr.firstBlock.blockSize);
400 compress_buffer = (uint8_t*)calloc(1, exe_length);
408 while (curBlock.blockSize)
410 const uint8_t* pnext = p + curBlock.blockSize;
414 next_block.blockSize = bswap32(next_block.blockSize);
421 const size_t chunk_size = (p[0] << 8) | p[1];
426 memcpy(d, p, chunk_size);
432 curBlock = next_block;
435 uint32_t uncompressed_size = image_size();
436 char* out =
new char[uncompressed_size];
442 std::memset(out, 0, uncompressed_size);
444 result_code = lzx_decompress(compress_buffer, d - compress_buffer, out, uncompressed_size,
445 hdr.windowSize,
nullptr, 0);
449 free((
void*)compress_buffer);
451 free((
void*)input_buffer);
452 return uncompressed_size;
455uint32_t XexLoader::image_size()
457 uint32_t pageDescriptorCount = bswap32(*(uint32_t*)&buffer[header.sec_info_offset + 0x180]);
459 uint32_t totalSize = 0;
461 for (
int i = 0; i < pageDescriptorCount; i++)
463 uint32_t offs = header.sec_info_offset + 0x184 + (i * 0x18);
465 page.value = bswap32(page.value);
467 totalSize += page.value * 4096;
XexLoader(uint8_t *buffer, size_t len, std::string path)
Loads a .xex file from a buffer into memory.