From 0b7f7e1657a759905b405272d8fe48b1c8f179d9 Mon Sep 17 00:00:00 2001 From: emoose Date: Sat, 20 Oct 2018 04:18:18 +0100 Subject: [PATCH 1/5] [CPU] Move XEX2 code into XexModule class, autodetect XEX key Code is mainly just copy/pasted from kernel/util/xex2.cc, I've tried fixing it up to work better in a class, but there's probably some things I missed. Also includes some minor improvements to the XEX loader, like being able to try both XEX keys (retail/devkit) automatically, and some fixes to how the base address is determined. (Previously there was code that would get base address from optional header, code that'd get it from xex_security_info, code that'd use a stored base address value... Now everything reads it from a single stored value instead, which is set either from the xex_security_info, or if it exists from the optional header instead. Maybe this can help improve compatibility with any weird XEX's that don't have a base address optional header?) Compressed XEX loader also has some extra checks to make sure the compressed data hash matches what's expected. Might increase loading times by a fraction, but could save reports from people unknowingly using corrupt XEXs. (still no checks for non-compressed data though, maybe need to compare data with xex_security_info->ImageHash?) --- src/xenia/base/byte_order.h | 2 + src/xenia/cpu/xex_module.cc | 680 +++++++++- src/xenia/cpu/xex_module.h | 84 +- src/xenia/kernel/user_module.cc | 105 +- src/xenia/kernel/user_module.h | 9 +- src/xenia/kernel/util/xex2.cc | 1095 ----------------- src/xenia/kernel/util/xex2.h | 72 -- src/xenia/kernel/util/xex2_info.h | 464 +++---- src/xenia/kernel/xam/xam_info.cc | 1 - src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc | 1 - src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc | 1 - src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc | 3 +- src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc | 1 - 13 files changed, 939 insertions(+), 1579 deletions(-) delete mode 100644 src/xenia/kernel/util/xex2.cc delete mode 100644 src/xenia/kernel/util/xex2.h diff --git a/src/xenia/base/byte_order.h b/src/xenia/base/byte_order.h index c1b44c6e6..90fbdc75d 100644 --- a/src/xenia/base/byte_order.h +++ b/src/xenia/base/byte_order.h @@ -69,6 +69,8 @@ template inline T byte_swap(T value) { if (sizeof(T) == 4) { return static_cast(byte_swap(static_cast(value))); + } else if (sizeof(T) == 2) { + return static_cast(byte_swap(static_cast(value))); } else { assert_always("not handled"); } diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index bd138f690..2c27628c8 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -21,7 +21,13 @@ #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/xmodule.h" +#include "third_party/crypto/TinySHA1.hpp" +#include "third_party/crypto/rijndael-alg-fst.c" #include "third_party/crypto/rijndael-alg-fst.h" +#include "third_party/mspack/lzx.h" +#include "third_party/mspack/lzxd.c" +#include "third_party/mspack/mspack.h" +#include "third_party/pe/pe_image.h" namespace xe { namespace cpu { @@ -35,9 +41,9 @@ void UndefinedImport(ppc::PPCContext* ppc_context, KernelState* kernel_state) { XexModule::XexModule(Processor* processor, KernelState* kernel_state) : Module(processor), processor_(processor), kernel_state_(kernel_state) {} -XexModule::~XexModule() { xe_xex2_dealloc(xex_); } +XexModule::~XexModule() {} -bool XexModule::GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, +bool XexModule::GetOptHeader(const xex2_header* header, xex2_header_keys key, void** out_ptr) { assert_not_null(header); assert_not_null(out_ptr); @@ -72,14 +78,103 @@ bool XexModule::GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, return false; } -bool XexModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const { +bool XexModule::GetOptHeader(xex2_header_keys key, void** out_ptr) const { return XexModule::GetOptHeader(xex_header(), key, out_ptr); } -const xex2_security_info* XexModule::GetSecurityInfo( - const xex2_header* header) { - return reinterpret_cast(uintptr_t(header) + - header->security_offset); +const PESection* XexModule::GetPESection(const char* name) { + for (std::vector::iterator it = pe_sections_.begin(); + it != pe_sections_.end(); ++it) { + if (!strcmp(it->name, name)) { + return &(*it); + } + } + return nullptr; +} + +typedef struct mspack_memory_file_t { + struct mspack_system sys; + void* buffer; + off_t buffer_size; + off_t offset; +} mspack_memory_file; +mspack_memory_file* mspack_memory_open(struct mspack_system* sys, void* buffer, + const size_t buffer_size) { + assert_true(buffer_size < INT_MAX); + if (buffer_size >= INT_MAX) { + return NULL; + } + mspack_memory_file* memfile = + (mspack_memory_file*)calloc(1, sizeof(mspack_memory_file)); + if (!memfile) { + return NULL; + } + memfile->buffer = buffer; + memfile->buffer_size = (off_t)buffer_size; + memfile->offset = 0; + return memfile; +} +void mspack_memory_close(mspack_memory_file* file) { + mspack_memory_file* memfile = (mspack_memory_file*)file; + free(memfile); +} +int mspack_memory_read(struct mspack_file* file, void* buffer, int chars) { + mspack_memory_file* memfile = (mspack_memory_file*)file; + const off_t remaining = memfile->buffer_size - memfile->offset; + const off_t total = std::min(static_cast(chars), remaining); + memcpy(buffer, (uint8_t*)memfile->buffer + memfile->offset, total); + memfile->offset += total; + return (int)total; +} +int mspack_memory_write(struct mspack_file* file, void* buffer, int chars) { + mspack_memory_file* memfile = (mspack_memory_file*)file; + const off_t remaining = memfile->buffer_size - memfile->offset; + const off_t total = std::min(static_cast(chars), remaining); + memcpy((uint8_t*)memfile->buffer + memfile->offset, buffer, total); + memfile->offset += total; + return (int)total; +} +void* mspack_memory_alloc(struct mspack_system* sys, size_t chars) { + return calloc(chars, 1); +} +void mspack_memory_free(void* ptr) { free(ptr); } +void mspack_memory_copy(void* src, void* dest, size_t chars) { + memcpy(dest, src, chars); +} +struct mspack_system* mspack_memory_sys_create() { + struct mspack_system* sys = + (struct mspack_system*)calloc(1, sizeof(struct mspack_system)); + if (!sys) { + return NULL; + } + sys->read = mspack_memory_read; + sys->write = mspack_memory_write; + sys->alloc = mspack_memory_alloc; + sys->free = mspack_memory_free; + sys->copy = mspack_memory_copy; + return sys; +} +void mspack_memory_sys_destroy(struct mspack_system* sys) { free(sys); } + +void XexModule::DecryptBuffer(const uint8_t* session_key, + const uint8_t* input_buffer, + const size_t input_size, uint8_t* output_buffer, + const size_t output_size) { + uint32_t rk[4 * (MAXNR + 1)]; + uint8_t ivec[16] = {0}; + int32_t Nr = rijndaelKeySetupDec(rk, session_key, 128); + const uint8_t* ct = input_buffer; + uint8_t* pt = output_buffer; + for (size_t n = 0; n < input_size; n += 16, ct += 16, pt += 16) { + // Decrypt 16 uint8_ts from input -> output. + rijndaelDecrypt(rk, Nr, ct, pt); + for (size_t i = 0; i < 16; i++) { + // XOR with previous. + pt[i] ^= ivec[i]; + // Set previous. + ivec[i] = ct[i]; + } + } } uint32_t XexModule::GetProcAddress(uint16_t ordinal) const { @@ -101,21 +196,19 @@ uint32_t XexModule::GetProcAddress(uint16_t ordinal) const { } // Second: Check the PE exports. - xe::be* exe_address = nullptr; - GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address); - assert_not_null(exe_address); + assert_not_zero(base_address_); xex2_opt_data_directory* pe_export_directory = 0; if (GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) { auto e = memory()->TranslateVirtual( - *exe_address + pe_export_directory->offset); + base_address_ + pe_export_directory->offset); assert_not_null(e); uint32_t* function_table = reinterpret_cast(uintptr_t(e) + e->AddressOfFunctions); if (ordinal < e->NumberOfFunctions) { - return xex_security_info()->load_address + function_table[ordinal]; + return base_address_ + function_table[ordinal]; } } @@ -123,18 +216,16 @@ uint32_t XexModule::GetProcAddress(uint16_t ordinal) const { } uint32_t XexModule::GetProcAddress(const char* name) const { - xe::be* exe_address = nullptr; - GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address); - assert_not_null(exe_address); - xex2_opt_data_directory* pe_export_directory = 0; if (!GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) { // No exports by name. return 0; } + assert_not_zero(base_address_); + auto e = memory()->TranslateVirtual( - *exe_address + pe_export_directory->offset); + base_address_ + pe_export_directory->offset); assert_not_null(e); // e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY! @@ -152,7 +243,7 @@ uint32_t XexModule::GetProcAddress(const char* name) const { for (uint32_t i = 0; i < e->NumberOfNames; i++) { auto fn_name = reinterpret_cast(uintptr_t(e) + name_table[i]); uint16_t ordinal = ordinal_table[i]; - uint32_t addr = *exe_address + function_table[ordinal]; + uint32_t addr = base_address_ + function_table[ordinal]; if (!std::strcmp(name, fn_name)) { // We have a match! return addr; @@ -183,52 +274,507 @@ bool XexModule::ApplyPatch(XexModule* module) { return true; } -bool XexModule::Load(const std::string& name, const std::string& path, - const void* xex_addr, size_t xex_length) { - // TODO(DrChat): Move loading code here. - xex_ = xe_xex2_load(memory(), xex_addr, xex_length, {0}); - if (!xex_) { - return false; +void XexModule::DecryptSessionKey(bool useDevkit) { + static const uint8_t xe_xex2_retail_key[16] = { + 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, + 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91}; + static const uint8_t xe_xex2_devkit_key[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + const uint8_t* xexkey = useDevkit ? xe_xex2_devkit_key : xe_xex2_retail_key; + + // Decrypt the header key. + uint32_t rk[4 * (MAXNR + 1)]; + int32_t Nr = rijndaelKeySetupDec(rk, xexkey, 128); + rijndaelDecrypt(rk, Nr, + reinterpret_cast(xex_security_info()->aes_key), + session_key_); +} + +int XexModule::ReadImage(const void* xex_addr, size_t xex_length) { + if (!opt_file_format_info()) { + return 1; } - // Make a copy of the xex header. - auto src_header = reinterpret_cast(xex_addr); - xex_header_mem_.resize(src_header->header_size); + auto* ff = opt_file_format_info(); - std::memcpy(xex_header_mem_.data(), src_header, src_header->header_size); + switch (opt_file_format_info()->compression_type) { + case XEX_COMPRESSION_NONE: + return ReadImageUncompressed(xex_addr, xex_length); + case XEX_COMPRESSION_BASIC: + return ReadImageBasicCompressed(xex_addr, xex_length); + case XEX_COMPRESSION_NORMAL: + case XEX_COMPRESSION_DELTA: + return ReadImageCompressed(xex_addr, xex_length); + default: + assert_always(); + return 1; + } +} - return Load(name, path, xex_); +int XexModule::ReadImageUncompressed(const void* xex_addr, size_t xex_length) { + // Allocate in-place the XEX memory. + const uint32_t exe_length = + static_cast(xex_length - xex_header()->header_size); + + uint32_t uncompressed_size = exe_length; + bool alloc_result = + memory() + ->LookupHeap(base_address_) + ->AllocFixed( + base_address_, uncompressed_size, 4096, + xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit, + xe::kMemoryProtectRead | xe::kMemoryProtectWrite); + if (!alloc_result) { + XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", base_address_, + uncompressed_size); + return 2; + } + uint8_t* buffer = memory()->TranslateVirtual(base_address_); + std::memset(buffer, 0, uncompressed_size); + + const uint8_t* p = (const uint8_t*)xex_addr + xex_header()->header_size; + + switch (opt_file_format_info()->encryption_type) { + case XEX_ENCRYPTION_NONE: + if (exe_length > uncompressed_size) { + return 1; + } + memcpy(buffer, p, exe_length); + return 0; + case XEX_ENCRYPTION_NORMAL: + DecryptBuffer(session_key_, p, exe_length, buffer, uncompressed_size); + return 0; + default: + assert_always(); + return 1; + } + + return 0; +} + +int XexModule::ReadImageBasicCompressed(const void* xex_addr, + size_t xex_length) { + const uint32_t exe_length = + static_cast(xex_length - xex_header()->header_size); + const uint8_t* source_buffer = + (const uint8_t*)xex_addr + xex_header()->header_size; + const uint8_t* p = source_buffer; + + auto heap = memory()->LookupHeap(base_address_); + + // Calculate uncompressed length. + uint32_t uncompressed_size = 0; + + auto* file_info = opt_file_format_info(); + auto& comp_info = file_info->compression_info.basic; + + uint32_t block_count = (file_info->info_size - 8) / 8; + for (uint32_t n = 0; n < block_count; n++) { + const uint32_t data_size = comp_info.blocks[n].data_size; + const uint32_t zero_size = comp_info.blocks[n].zero_size; + uncompressed_size += data_size + zero_size; + } + + // Calculate the total size of the XEX image from its headers. + uint32_t total_size = 0; + for (uint32_t i = 0; i < xex_security_info()->page_descriptor_count; i++) { + // Byteswap the bitfield manually. + xex2_page_descriptor desc; + desc.value = xe::byte_swap(xex_security_info()->page_descriptors[i].value); + + total_size += desc.size * heap->page_size(); + } + + // Allocate in-place the XEX memory. + bool alloc_result = heap->AllocFixed( + base_address_, total_size, 4096, + xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit, + xe::kMemoryProtectRead | xe::kMemoryProtectWrite); + if (!alloc_result) { + XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", base_address_, + uncompressed_size); + return 1; + } + + uint8_t* buffer = memory()->TranslateVirtual(base_address_); + std::memset(buffer, 0, total_size); // Quickly zero the contents. + uint8_t* d = buffer; + + uint32_t rk[4 * (MAXNR + 1)]; + uint8_t ivec[16] = {0}; + int32_t Nr = rijndaelKeySetupDec(rk, session_key_, 128); + + for (size_t n = 0; n < block_count; n++) { + const uint32_t data_size = comp_info.blocks[n].data_size; + const uint32_t zero_size = comp_info.blocks[n].zero_size; + + switch (opt_file_format_info()->encryption_type) { + case XEX_ENCRYPTION_NONE: + if (data_size > uncompressed_size - (d - buffer)) { + // Overflow. + return 1; + } + memcpy(d, p, data_size); + break; + case XEX_ENCRYPTION_NORMAL: { + const uint8_t* ct = p; + uint8_t* pt = d; + for (size_t m = 0; m < data_size; m += 16, ct += 16, pt += 16) { + // Decrypt 16 uint8_ts from input -> output. + rijndaelDecrypt(rk, Nr, ct, pt); + for (size_t i = 0; i < 16; i++) { + // XOR with previous. + pt[i] ^= ivec[i]; + // Set previous. + ivec[i] = ct[i]; + } + } + } break; + default: + assert_always(); + return 1; + } + + p += data_size; + d += data_size + zero_size; + } + + return 0; +} + +int XexModule::ReadImageCompressed(const void* xex_addr, size_t xex_length) { + const uint32_t exe_length = + static_cast(xex_length - xex_header()->header_size); + const uint8_t* exe_buffer = + (const uint8_t*)xex_addr + xex_header()->header_size; + + // src -> dest: + // - decrypt (if encrypted) + // - de-block: + // 4b total size of next block in uint8_ts + // 20b hash of entire next block (including size/hash) + // Nb block uint8_ts + // - decompress block contents + + uint8_t* compress_buffer = NULL; + const uint8_t* p = NULL; + uint8_t* d = NULL; + uint8_t* deblock_buffer = NULL; + // size_t block_size = 0; + uint32_t uncompressed_size = 0; + struct mspack_system* sys = NULL; + mspack_memory_file* lzxsrc = NULL; + mspack_memory_file* lzxdst = NULL; + struct lzxd_stream* lzxd = NULL; + sha1::SHA1 s; + + // Decrypt (if needed). + bool free_input = false; + const uint8_t* input_buffer = exe_buffer; + size_t input_size = exe_length; + + switch (opt_file_format_info()->encryption_type) { + case XEX_ENCRYPTION_NONE: + // No-op. + break; + case XEX_ENCRYPTION_NORMAL: + // TODO: a way to do without a copy/alloc? + free_input = true; + input_buffer = (const uint8_t*)calloc(1, exe_length); + DecryptBuffer(session_key_, exe_buffer, exe_length, + (uint8_t*)input_buffer, exe_length); + break; + default: + assert_always(); + return 1; + } + + const auto* compression_info = &opt_file_format_info()->compression_info; + const xex2_compressed_block_info* cur_block = + &compression_info->normal.first_block; + + compress_buffer = (uint8_t*)calloc(1, exe_length); + + p = input_buffer; + d = compress_buffer; + + // De-block. + deblock_buffer = (uint8_t*)calloc(1, input_size); + + int result_code = 0; + + uint8_t block_calced_digest[0x14]; + while (cur_block->block_size) { + const uint8_t* pnext = p + cur_block->block_size; + const auto* next_block = (const xex2_compressed_block_info*)p; + + // Compare block hash, if no match we probably used wrong decrypt key + s.reset(); + s.processBytes(p, cur_block->block_size); + s.finalize(block_calced_digest); + if (memcmp(block_calced_digest, cur_block->block_hash, 0x14) != 0) { + result_code = 2; + break; + } + + p += 4; + p += 20; // skip 20b hash + + while (true) { + const size_t chunk_size = (p[0] << 8) | p[1]; + p += 2; + if (!chunk_size) { + break; + } + + memcpy(d, p, chunk_size); + p += chunk_size; + d += chunk_size; + + uncompressed_size += 0x8000; + } + + p = pnext; + cur_block = next_block; + } + + if (!result_code) { + // Allocate in-place the XEX memory. + bool alloc_result = + memory() + ->LookupHeap(base_address_) + ->AllocFixed( + base_address_, uncompressed_size, 4096, + xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit, + xe::kMemoryProtectRead | xe::kMemoryProtectWrite); + + if (alloc_result) { + uint8_t* buffer = memory()->TranslateVirtual(base_address_); + + // Reset buffer if this isn't a patch + std::memset(buffer, 0, uncompressed_size); + + // Setup decompressor and decompress. + uint32_t window_size = compression_info->normal.window_size; + uint32_t window_bits = 0; + for (size_t m = 0; m < 32; m++, window_bits++) { + window_size >>= 1; + if (window_size == 0x00000000) { + break; + } + } + + sys = mspack_memory_sys_create(); + lzxsrc = + mspack_memory_open(sys, (void*)compress_buffer, d - compress_buffer); + lzxdst = mspack_memory_open(sys, buffer, uncompressed_size); + lzxd = lzxd_init(sys, (struct mspack_file*)lzxsrc, + (struct mspack_file*)lzxdst, window_bits, 0, 32768, + (off_t)xex_security_info()->image_size); + result_code = + lzxd_decompress(lzxd, (off_t)xex_security_info()->image_size); + } else { + XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", base_address_, + uncompressed_size); + result_code = 3; + } + } + + if (lzxd) { + lzxd_free(lzxd); + lzxd = NULL; + } + if (lzxsrc) { + mspack_memory_close(lzxsrc); + lzxsrc = NULL; + } + if (lzxdst) { + mspack_memory_close(lzxdst); + lzxdst = NULL; + } + if (sys) { + mspack_memory_sys_destroy(sys); + sys = NULL; + } + if (compress_buffer) { + free((void*)compress_buffer); + } + if (deblock_buffer) { + free((void*)deblock_buffer); + } + if (free_input) { + free((void*)input_buffer); + } + return result_code; +} + +int XexModule::ReadPEHeaders() { + const uint8_t* p = memory()->TranslateVirtual(base_address_); + + // Verify DOS signature (MZ). + const IMAGE_DOS_HEADER* doshdr = (const IMAGE_DOS_HEADER*)p; + if (doshdr->e_magic != IMAGE_DOS_SIGNATURE) { + XELOGE("PE signature mismatch; likely bad decryption/decompression"); + return 1; + } + + // Move to the NT header offset from the DOS header. + p += doshdr->e_lfanew; + + // Verify NT signature (PE\0\0). + const IMAGE_NT_HEADERS32* nthdr = (const IMAGE_NT_HEADERS32*)(p); + if (nthdr->Signature != IMAGE_NT_SIGNATURE) { + return 1; + } + + // Verify matches an Xbox PE. + const IMAGE_FILE_HEADER* filehdr = &nthdr->FileHeader; + if ((filehdr->Machine != IMAGE_FILE_MACHINE_POWERPCBE) || + !(filehdr->Characteristics & IMAGE_FILE_32BIT_MACHINE)) { + return 1; + } + // Verify the expected size. + if (filehdr->SizeOfOptionalHeader != IMAGE_SIZEOF_NT_OPTIONAL_HEADER) { + return 1; + } + + // Verify optional header is 32bit. + const IMAGE_OPTIONAL_HEADER32* opthdr = &nthdr->OptionalHeader; + if (opthdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + return 1; + } + // Verify subsystem. + if (opthdr->Subsystem != IMAGE_SUBSYSTEM_XBOX) { + return 1; + } + +// Linker version - likely 8+ +// Could be useful for recognizing certain patterns +// opthdr->MajorLinkerVersion; opthdr->MinorLinkerVersion; + +// Data directories of interest: +// EXPORT IMAGE_EXPORT_DIRECTORY +// IMPORT IMAGE_IMPORT_DESCRIPTOR[] +// EXCEPTION IMAGE_CE_RUNTIME_FUNCTION_ENTRY[] +// BASERELOC +// DEBUG IMAGE_DEBUG_DIRECTORY[] +// ARCHITECTURE /IMAGE_ARCHITECTURE_HEADER/ ----- import thunks! +// TLS IMAGE_TLS_DIRECTORY +// IAT Import Address Table ptr +// opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_X].VirtualAddress / .Size + +// The macros in pe_image.h don't work with clang, for some reason. +// offsetof seems to be unable to find OptionalHeader. +#define offsetof1(type, member) ((std::size_t) & (((type*)0)->member)) +#define IMAGE_FIRST_SECTION1(ntheader) \ + ((PIMAGE_SECTION_HEADER)( \ + (uint8_t*)ntheader + offsetof1(IMAGE_NT_HEADERS, OptionalHeader) + \ + ((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader)) + + // Quick scan to determine bounds of sections. + size_t upper_address = 0; + const IMAGE_SECTION_HEADER* sechdr = IMAGE_FIRST_SECTION1(nthdr); + for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) { + const size_t physical_address = opthdr->ImageBase + sechdr->VirtualAddress; + upper_address = + std::max(upper_address, physical_address + sechdr->Misc.VirtualSize); + } + + // Setup/load sections. + sechdr = IMAGE_FIRST_SECTION1(nthdr); + for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) { + PESection section; + memcpy(section.name, sechdr->Name, sizeof(sechdr->Name)); + section.name[8] = 0; + section.raw_address = sechdr->PointerToRawData; + section.raw_size = sechdr->SizeOfRawData; + section.address = base_address_ + sechdr->VirtualAddress; + section.size = sechdr->Misc.VirtualSize; + section.flags = sechdr->Characteristics; + pe_sections_.push_back(section); + // pe_sections_.push_back(section); + } + + // DumpTLSDirectory(pImageBase, pNTHeader, (PIMAGE_TLS_DIRECTORY32)0); + // DumpExportsSection(pImageBase, pNTHeader); + return 0; } bool XexModule::Load(const std::string& name, const std::string& path, - xe_xex2_ref xex) { + const void* xex_addr, size_t xex_length) { + auto src_header = reinterpret_cast(xex_addr); + + if (src_header->magic != 'XEX2') { + return false; + } + assert_false(loaded_); loaded_ = true; - xex_ = xex; - auto old_header = xe_xex2_get_header(xex_); + // Read in XEX headers + xex_header_mem_.resize(src_header->header_size); + std::memcpy(xex_header_mem_.data(), src_header, src_header->header_size); + + auto sec_header = xex_security_info(); + + // Try setting our base_address based on XEX_HEADER_IMAGE_BASE_ADDRESS, fall + // back to xex_security_info otherwise + base_address_ = sec_header->load_address; + xe::be* base_addr_opt = nullptr; + if (GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &base_addr_opt)) + base_address_ = *base_addr_opt; // Setup debug info. name_ = std::string(name); path_ = std::string(path); - // TODO(benvanik): debug info + + uint8_t* data = memory()->TranslateVirtual(base_address_); + + // Load in the XEX basefile + // We'll try using both XEX2 keys to see if any give a valid PE + while (true) { + memory()->LookupHeap(base_address_)->Reset(); + + DecryptSessionKey(is_dev_kit_); + + if (!ReadImage(xex_addr, xex_length) && !ReadPEHeaders()) { + break; + } + + is_dev_kit_ = !is_dev_kit_; + + // is_dev_kit starts as false, then flips to true if load failed, if it's + // back to false again this must be invalid + if (!is_dev_kit_) { + return false; + } + + XELOGW("XEX load failed, trying with devkit encryption key..."); + } // Scan and find the low/high addresses. // All code sections are continuous, so this should be easy. - // TODO(DrChat): Use the new xex header to do this. + auto heap = memory()->LookupHeap(base_address_); + auto page_size = heap->page_size(); + low_address_ = UINT_MAX; high_address_ = 0; - for (uint32_t n = 0, i = 0; n < old_header->section_count; n++) { - const xe_xex2_section_t* section = &old_header->sections[n]; - const uint32_t start_address = - old_header->exe_address + (i * section->page_size); - const uint32_t end_address = - start_address + (section->info.page_count * section->page_size); - if (section->info.type == XEX_SECTION_CODE) { + + for (uint32_t i = 0, page = 0; i < sec_header->page_descriptor_count; i++) { + // Byteswap the bitfield manually. + xex2_page_descriptor desc; + desc.value = xe::byte_swap(sec_header->page_descriptors[i].value); + + const auto start_address = base_address_ + (page * page_size); + const auto end_address = start_address + (desc.size * page_size); + if (desc.info == XEX_SECTION_CODE) { low_address_ = std::min(low_address_, start_address); high_address_ = std::max(high_address_, end_address); } - i += section->info.page_count; + + page += desc.size; } // Notify backend that we have an executable range. @@ -288,15 +834,12 @@ bool XexModule::Load(const std::string& name, const std::string& path, } // Setup memory protection. - auto sec_header = xex_security_info(); - auto heap = memory()->LookupHeap(sec_header->load_address); - auto page_size = heap->page_size(); for (uint32_t i = 0, page = 0; i < sec_header->page_descriptor_count; i++) { // Byteswap the bitfield manually. xex2_page_descriptor desc; desc.value = xe::byte_swap(sec_header->page_descriptors[i].value); - auto address = sec_header->load_address + (page * page_size); + auto address = base_address_ + (page * page_size); auto size = desc.size * page_size; switch (desc.info) { case XEX_SECTION_CODE: @@ -321,11 +864,9 @@ bool XexModule::Unload() { loaded_ = false; // Just deallocate the memory occupied by the exe - xe::be* exe_address = 0; - GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address); - assert_not_zero(exe_address); + assert_not_zero(base_address_); - memory()->LookupHeap(*exe_address)->Release(*exe_address); + memory()->LookupHeap(base_address_)->Release(base_address_); xex_header_mem_.resize(0); return true; @@ -346,6 +887,12 @@ bool XexModule::SetupLibraryImports(const char* name, libbasename = libbasename.substr(0, dot); } + ImportLibrary library_info; + library_info.Name = libbasename; + library_info.ID = library->id; + library_info.Version.value = library->version.value; + library_info.MinVersion.value = library->version_min.value; + // Imports are stored as {import descriptor, thunk addr, import desc, ...} // Even thunks have an import descriptor (albeit unused/useless) for (uint32_t i = 0; i < library->count; i++) { @@ -379,6 +926,12 @@ bool XexModule::SetupLibraryImports(const char* name, StringBuffer import_name; if (record_type == 0) { // Variable. + + ImportLibraryFn import_info; + import_info.Ordinal = ordinal; + import_info.ValueAddress = record_addr; + library_info.Imports.push_back(import_info); + import_name.AppendFormat("__imp__"); if (kernel_export) { import_name.AppendFormat("%s", kernel_export->name); @@ -418,6 +971,11 @@ bool XexModule::SetupLibraryImports(const char* name, var_info->set_status(Symbol::Status::kDefined); } else if (record_type == 1) { // Thunk. + assert_true(library_info.Imports.size() > 0); + auto& prev_import = library_info.Imports[library_info.Imports.size() - 1]; + assert_true(prev_import.Ordinal == ordinal); + prev_import.ThunkAddress = record_addr; + if (kernel_export) { import_name.AppendFormat("%s", kernel_export->name); } else { @@ -488,6 +1046,8 @@ bool XexModule::SetupLibraryImports(const char* name, } } + import_libs_.push_back(library_info); + return true; } @@ -669,14 +1229,15 @@ bool XexModule::FindSaveRest() { uint32_t gplr_start = 0; uint32_t fpr_start = 0; uint32_t vmx_start = 0; - const xe_xex2_header_t* header = xe_xex2_get_header(xex_); - for (uint32_t n = 0, i = 0; n < header->section_count; n++) { - const xe_xex2_section_t* section = &header->sections[n]; - const uint32_t start_address = - header->exe_address + (i * section->page_size); - const uint32_t end_address = - start_address + (section->info.page_count * section->page_size); - if (section->info.type == XEX_SECTION_CODE) { + + auto page_size = base_address_ <= 0x90000000 ? 64 * 1024 : 4 * 1024; + auto sec_header = xex_security_info(); + for (uint32_t i = 0, page = 0; i < sec_header->page_descriptor_count; i++) { + const xex2_page_descriptor* section = &sec_header->page_descriptors[i]; + const auto start_address = base_address_ + (page * page_size); + const auto end_address = start_address + (section->size * page_size); + + if (section->info == XEX_SECTION_CODE) { if (!gplr_start) { gplr_start = memory_->SearchAligned(start_address, end_address, gprlr_code_values, @@ -696,7 +1257,8 @@ bool XexModule::FindSaveRest() { break; } } - i += section->info.page_count; + + page += section->size; } // Add function stubs. diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index 483945e08..5dee7dbf3 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -14,7 +14,6 @@ #include #include "xenia/cpu/module.h" -#include "xenia/kernel/util/xex2.h" #include "xenia/kernel/util/xex2_info.h" namespace xe { @@ -30,40 +29,73 @@ class Runtime; class XexModule : public xe::cpu::Module { public: + struct ImportLibraryFn { + public: + uint32_t Ordinal; + uint32_t ValueAddress; + uint32_t ThunkAddress; + }; + struct ImportLibrary { + public: + std::string Name; + uint32_t ID; + xe_xex2_version_t Version; + xe_xex2_version_t MinVersion; + std::vector Imports; + }; + XexModule(Processor* processor, kernel::KernelState* kernel_state); virtual ~XexModule(); - xe_xex2_ref xex() const { return xex_; } bool loaded() const { return loaded_; } const xex2_header* xex_header() const { return reinterpret_cast(xex_header_mem_.data()); } const xex2_security_info* xex_security_info() const { - return GetSecurityInfo(xex_header()); + return reinterpret_cast( + uintptr_t(xex_header()) + xex_header()->security_offset); } + const std::vector* import_libraries() const { + return &import_libs_; + } + + const xex2_opt_execution_info* opt_execution_info() const { + xex2_opt_execution_info* retval = nullptr; + GetOptHeader(XEX_HEADER_EXECUTION_INFO, &retval); + return retval; + } + + const xex2_opt_file_format_info* opt_file_format_info() const { + xex2_opt_file_format_info* retval = nullptr; + GetOptHeader(XEX_HEADER_FILE_FORMAT_INFO, &retval); + return retval; + } + + const uint32_t base_address() const { return base_address_; } + // Gets an optional header. Returns NULL if not found. // Special case: if key & 0xFF == 0x00, this function will return the value, // not a pointer! This assumes out_ptr points to uint32_t. - static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, + static bool GetOptHeader(const xex2_header* header, xex2_header_keys key, void** out_ptr); - bool GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const; + bool GetOptHeader(xex2_header_keys key, void** out_ptr) const; // Ultra-cool templated version // Special case: if key & 0xFF == 0x00, this function will return the value, // not a pointer! This assumes out_ptr points to uint32_t. template - static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key, + static bool GetOptHeader(const xex2_header* header, xex2_header_keys key, T* out_ptr) { return GetOptHeader(header, key, reinterpret_cast(out_ptr)); } template - bool GetOptHeader(xe_xex2_header_keys key, T* out_ptr) const { + bool GetOptHeader(xex2_header_keys key, T* out_ptr) const { return GetOptHeader(key, reinterpret_cast(out_ptr)); } - static const xex2_security_info* GetSecurityInfo(const xex2_header* header); + const PESection* GetPESection(const char* name); uint32_t GetProcAddress(uint16_t ordinal) const; uint32_t GetProcAddress(const char* name) const; @@ -71,7 +103,6 @@ class XexModule : public xe::cpu::Module { bool ApplyPatch(XexModule* module); bool Load(const std::string& name, const std::string& path, const void* xex_addr, size_t xex_length); - bool Load(const std::string& name, const std::string& path, xe_xex2_ref xex); bool Unload(); const std::string& name() const override { return name_; } @@ -81,10 +112,31 @@ class XexModule : public xe::cpu::Module { bool ContainsAddress(uint32_t address) override; + static void DecryptBuffer(const uint8_t* session_key, + const uint8_t* input_buffer, + const size_t input_size, uint8_t* output_buffer, + const size_t output_size); + + uint8_t* HostData() { + if (base_address_) + return memory()->TranslateVirtual(base_address_); + else + return nullptr; + } + protected: std::unique_ptr CreateFunction(uint32_t address) override; private: + void DecryptSessionKey(bool useDevkit = false); + + int ReadImage(const void* xex_addr, size_t xex_length); + int ReadImageUncompressed(const void* xex_addr, size_t xex_length); + int ReadImageBasicCompressed(const void* xex_addr, size_t xex_length); + int ReadImageCompressed(const void* xex_addr, size_t xex_length); + + int ReadPEHeaders(); + bool SetupLibraryImports(const char* name, const xex2_import_library* library); bool FindSaveRest(); @@ -93,13 +145,23 @@ class XexModule : public xe::cpu::Module { kernel::KernelState* kernel_state_ = nullptr; std::string name_; std::string path_; - xe_xex2_ref xex_ = nullptr; std::vector xex_header_mem_; // Holds the xex header - bool loaded_ = false; // Loaded into memory? + + // various optional headers + std::vector + import_libs_; // pre-loaded import libraries for ease of use + + std::vector pe_sections_; + + uint8_t session_key_[0x10]; + + bool loaded_ = false; // Loaded into memory? uint32_t base_address_ = 0; uint32_t low_address_ = 0; uint32_t high_address_ = 0; + + bool is_dev_kit_ = false; }; } // namespace cpu diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index b9831fd93..f4c42caa2 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -138,6 +138,12 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) { uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_); std::memcpy(xex_header_ptr, header, header->header_size); + // Cache some commonly used headers... + this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_); + this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, + &stack_size_); + is_dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE); + // Setup the loader data entry auto ldr_data = memory()->TranslateVirtual(hmodule_ptr_); @@ -145,20 +151,8 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) { ldr_data->dll_base = 0; // GetProcAddress will read this. ldr_data->xex_header_base = guest_xex_header_; ldr_data->full_image_size = security_header->image_size; - this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, - &ldr_data->entry_point); - - xe::be* image_base_ptr = nullptr; - if (this->xex_module()->GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, - &image_base_ptr)) { - ldr_data->image_base = *image_base_ptr; - } - - // Cache some commonly used headers... - this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_); - this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, - &stack_size_); - is_dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE); + ldr_data->image_base = this->xex_module()->base_address(); + ldr_data->entry_point = entry_point_; } else if (module_format_ == kModuleFormatElf) { auto elf_module = std::make_unique(processor, kernel_state()); @@ -229,7 +223,7 @@ X_STATUS UserModule::GetSection(const char* name, uint32_t* out_section_data, return X_STATUS_NOT_FOUND; } -X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) { +X_STATUS UserModule::GetOptHeader(xex2_header_keys key, void** out_ptr) { assert_not_null(out_ptr); if (module_format_ == kModuleFormatElf) { @@ -245,7 +239,7 @@ X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) { return X_STATUS_SUCCESS; } -X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key, +X_STATUS UserModule::GetOptHeader(xex2_header_keys key, uint32_t* out_header_guest_ptr) { if (module_format_ == kModuleFormatElf) { // Quick die. @@ -262,7 +256,7 @@ X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key, } X_STATUS UserModule::GetOptHeader(uint8_t* membase, const xex2_header* header, - xe_xex2_header_keys key, + xex2_header_keys key, uint32_t* out_header_guest_ptr) { assert_not_null(out_header_guest_ptr); uint32_t field_value = 0; @@ -575,7 +569,7 @@ void UserModule::Dump() { auto dir = reinterpret_cast(opt_header_ptr); - auto exe_address = xex_module()->xex_security_info()->load_address; + auto exe_address = xex_module()->base_address(); auto e = memory()->TranslateVirtual( exe_address + dir->offset); auto e_base = reinterpret_cast(e); @@ -624,8 +618,8 @@ void UserModule::Dump() { } const uint32_t page_size = - security_info->load_address < 0x90000000 ? 64 * 1024 : 4 * 1024; - uint32_t start_address = security_info->load_address + (page * page_size); + xex_module()->base_address() < 0x90000000 ? 64 * 1024 : 4 * 1024; + uint32_t start_address = xex_module()->base_address() + (page * page_size); uint32_t end_address = start_address + (page_descriptor.size * page_size); sb.AppendFormat(" %3u %s %3u pages %.8X - %.8X (%d bytes)\n", page, @@ -635,24 +629,22 @@ void UserModule::Dump() { } // Print out imports. - // TODO(benvanik): figure out a way to remove dependency on old xex header. - auto old_header = xe_xex2_get_header(xex_module()->xex()); + + auto import_libs = xex_module()->import_libraries(); sb.AppendFormat("Imports:\n"); - for (size_t n = 0; n < old_header->import_library_count; n++) { - const xe_xex2_import_library_t* library = &old_header->import_libraries[n]; - - xe_xex2_import_info_t* import_infos; - size_t import_info_count; - if (!xe_xex2_get_import_infos(xex_module()->xex(), library, &import_infos, - &import_info_count)) { - sb.AppendFormat(" %s - %lld imports\n", library->name, import_info_count); - sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->version.major, - library->version.minor, library->version.build, - library->version.qfe); + for (std::vector::const_iterator library = + import_libs->begin(); + library != import_libs->end(); ++library) { + if (library->Imports.size() > 0) { + sb.AppendFormat(" %s - %lld imports\n", library->Name.c_str(), + library->Imports.size()); + sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->Version.major, + library->Version.minor, library->Version.build, + library->Version.qfe); sb.AppendFormat(" Min Version: %d.%d.%d.%d\n", - library->min_version.major, library->min_version.minor, - library->min_version.build, library->min_version.qfe); + library->MinVersion.major, library->MinVersion.minor, + library->MinVersion.build, library->MinVersion.qfe); sb.AppendFormat("\n"); // Counts. @@ -660,12 +652,13 @@ void UserModule::Dump() { int unknown_count = 0; int impl_count = 0; int unimpl_count = 0; - for (size_t m = 0; m < import_info_count; m++) { - const xe_xex2_import_info_t* info = &import_infos[m]; - if (kernel_state_->IsKernelModule(library->name)) { - auto kernel_export = - export_resolver->GetExportByOrdinal(library->name, info->ordinal); + for (std::vector::const_iterator info = + library->Imports.begin(); + info != library->Imports.end(); ++info) { + if (kernel_state_->IsKernelModule(library->Name.c_str())) { + auto kernel_export = export_resolver->GetExportByOrdinal( + library->Name.c_str(), info->Ordinal); if (kernel_export) { known_count++; if (kernel_export->is_implemented()) { @@ -678,10 +671,10 @@ void UserModule::Dump() { unimpl_count++; } } else { - auto module = kernel_state_->GetModule(library->name); + auto module = kernel_state_->GetModule(library->Name.c_str()); if (module) { uint32_t export_addr = - module->GetProcAddressByOrdinal(info->ordinal); + module->GetProcAddressByOrdinal(info->Ordinal); if (export_addr) { impl_count++; known_count++; @@ -695,8 +688,8 @@ void UserModule::Dump() { } } } - float total_count = static_cast(import_info_count) / 100.0f; - sb.AppendFormat(" Total: %4llu\n", import_info_count); + float total_count = static_cast(library->Imports.size()) / 100.0f; + sb.AppendFormat(" Total: %4llu\n", library->Imports.size()); sb.AppendFormat(" Known: %3d%% (%d known, %d unknown)\n", static_cast(known_count / total_count), known_count, unknown_count); @@ -706,22 +699,23 @@ void UserModule::Dump() { sb.AppendFormat("\n"); // Listing. - for (size_t m = 0; m < import_info_count; m++) { - const xe_xex2_import_info_t* info = &import_infos[m]; + for (std::vector::const_iterator info = + library->Imports.begin(); + info != library->Imports.end(); ++info) { const char* name = "UNKNOWN"; bool implemented = false; cpu::Export* kernel_export = nullptr; - if (kernel_state_->IsKernelModule(library->name)) { - kernel_export = - export_resolver->GetExportByOrdinal(library->name, info->ordinal); + if (kernel_state_->IsKernelModule(library->Name.c_str())) { + kernel_export = export_resolver->GetExportByOrdinal( + library->Name.c_str(), info->Ordinal); if (kernel_export) { name = kernel_export->name; implemented = kernel_export->is_implemented(); } } else { - auto module = kernel_state_->GetModule(library->name); - if (module && module->GetProcAddressByOrdinal(info->ordinal)) { + auto module = kernel_state_->GetModule(library->Name.c_str()); + if (module && module->GetProcAddressByOrdinal(info->Ordinal)) { // TODO(benvanik): name lookup. implemented = true; } @@ -729,13 +723,12 @@ void UserModule::Dump() { if (kernel_export && kernel_export->type == cpu::Export::Type::kVariable) { sb.AppendFormat(" V %.8X %.3X (%3d) %s %s\n", - info->value_address, info->ordinal, info->ordinal, + info->ValueAddress, info->Ordinal, info->Ordinal, implemented ? " " : "!!", name); - } else if (info->thunk_address) { + } else if (info->ThunkAddress) { sb.AppendFormat(" F %.8X %.8X %.3X (%3d) %s %s\n", - info->value_address, info->thunk_address, - info->ordinal, info->ordinal, - implemented ? " " : "!!", name); + info->ValueAddress, info->ThunkAddress, info->Ordinal, + info->Ordinal, implemented ? " " : "!!", name); } } } diff --git a/src/xenia/kernel/user_module.h b/src/xenia/kernel/user_module.h index 36c88309b..bd03853da 100644 --- a/src/xenia/kernel/user_module.h +++ b/src/xenia/kernel/user_module.h @@ -71,19 +71,18 @@ class UserModule : public XModule { uint32_t* out_section_size) override; // Get optional header - FOR HOST USE ONLY! - X_STATUS GetOptHeader(xe_xex2_header_keys key, void** out_ptr); + X_STATUS GetOptHeader(xex2_header_keys key, void** out_ptr); // Get optional header - FOR HOST USE ONLY! template - X_STATUS GetOptHeader(xe_xex2_header_keys key, T* out_ptr) { + X_STATUS GetOptHeader(xex2_header_keys key, T* out_ptr) { return GetOptHeader(key, reinterpret_cast(out_ptr)); } // Get optional header that can safely be returned to guest code. - X_STATUS GetOptHeader(xe_xex2_header_keys key, - uint32_t* out_header_guest_ptr); + X_STATUS GetOptHeader(xex2_header_keys key, uint32_t* out_header_guest_ptr); static X_STATUS GetOptHeader(uint8_t* membase, const xex2_header* header, - xe_xex2_header_keys key, + xex2_header_keys key, uint32_t* out_header_guest_ptr); void Dump(); diff --git a/src/xenia/kernel/util/xex2.cc b/src/xenia/kernel/util/xex2.cc deleted file mode 100644 index e2de74cf0..000000000 --- a/src/xenia/kernel/util/xex2.cc +++ /dev/null @@ -1,1095 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/kernel/util/xex2.h" - -#include - -#include -#include -#include - -#include "third_party/crypto/rijndael-alg-fst.c" -#include "third_party/mspack/lzx.h" -#include "third_party/mspack/lzxd.c" -#include "third_party/mspack/mspack.h" -#include "third_party/pe/pe_image.h" - -#include "xenia/base/logging.h" -#include "xenia/base/math.h" -#include "xenia/base/memory.h" -#include "xenia/base/platform.h" - -namespace xe {} // namespace xe - -DEFINE_bool(xex_dev_key, false, "Use the devkit key."); - -typedef struct xe_xex2 { - xe::Memory* memory; - - xe_xex2_header_t header; - - std::vector* sections; - - struct { - size_t count; - xe_xex2_import_info_t* infos; - } library_imports[16]; -} xe_xex2_t; - -int xe_xex2_read_header(const uint8_t* addr, const size_t length, - xe_xex2_header_t* header); -int xe_xex2_decrypt_key(xe_xex2_header_t* header); -int xe_xex2_read_image(xe_xex2_ref xex, const uint8_t* xex_addr, - const uint32_t xex_length, xe::Memory* memory); -int xe_xex2_load_pe(xe_xex2_ref xex); -int xe_xex2_find_import_infos(xe_xex2_ref xex, - const xe_xex2_import_library_t* library); - -xe_xex2_ref xe_xex2_load(xe::Memory* memory, const void* addr, - const size_t length, xe_xex2_options_t options) { - xe_xex2_ref xex = (xe_xex2_ref)calloc(1, sizeof(xe_xex2)); - - xex->memory = memory; - xex->sections = new std::vector(); - - if (xe_xex2_read_header((const uint8_t*)addr, length, &xex->header)) { - xe_xex2_dealloc(xex); - return nullptr; - } - - if (xe_xex2_decrypt_key(&xex->header)) { - xe_xex2_dealloc(xex); - return nullptr; - } - - if (xe_xex2_read_image(xex, (const uint8_t*)addr, uint32_t(length), memory)) { - xe_xex2_dealloc(xex); - return nullptr; - } - - if (xe_xex2_load_pe(xex)) { - xe_xex2_dealloc(xex); - return nullptr; - } - - for (size_t n = 0; n < xex->header.import_library_count; n++) { - auto library = &xex->header.import_libraries[n]; - if (xe_xex2_find_import_infos(xex, library)) { - xe_xex2_dealloc(xex); - return nullptr; - } - } - - return xex; -} - -void xe_xex2_dealloc(xe_xex2_ref xex) { - if (!xex) { - return; - } - - for (std::vector::iterator it = xex->sections->begin(); - it != xex->sections->end(); ++it) { - delete *it; - } - - xe_xex2_header_t* header = &xex->header; - free(header->sections); - free(header->resource_infos); - if (header->file_format_info.compression_type == XEX_COMPRESSION_BASIC) { - free(header->file_format_info.compression_info.basic.blocks); - } - for (size_t n = 0; n < header->import_library_count; n++) { - xe_xex2_import_library_t* library = &header->import_libraries[n]; - free(library->records); - } - - xex->memory = NULL; -} - -const xe_xex2_header_t* xe_xex2_get_header(xe_xex2_ref xex) { - return &xex->header; -} - -int xe_xex2_read_header(const uint8_t* addr, const size_t length, - xe_xex2_header_t* header) { - const uint8_t* p = addr; - const uint8_t* pc; - const uint8_t* ps; - xe_xex2_loader_info_t* ldr; - - header->xex2 = xe::load_and_swap(p + 0x00); - if (header->xex2 != 'XEX2') { - return 1; - } - - header->module_flags = - (xe_xex2_module_flags)xe::load_and_swap(p + 0x04); - header->exe_offset = xe::load_and_swap(p + 0x08); - header->unknown0 = xe::load_and_swap(p + 0x0C); - header->certificate_offset = xe::load_and_swap(p + 0x10); - header->header_count = xe::load_and_swap(p + 0x14); - - for (size_t n = 0; n < header->header_count; n++) { - const uint8_t* ph = p + 0x18 + (n * 8); - const uint32_t key = xe::load_and_swap(ph + 0x00); - const uint32_t data_offset = xe::load_and_swap(ph + 0x04); - - xe_xex2_opt_header_t* opt_header = &header->headers[n]; - opt_header->key = key; - switch (key & 0xFF) { - case 0x01: - // dataOffset = data - opt_header->length = 0; - opt_header->value = data_offset; - break; - case 0xFF: - // dataOffset = offset (first dword in data is size) - opt_header->length = xe::load_and_swap(p + data_offset); - opt_header->offset = data_offset; - break; - default: - // dataOffset = size in dwords - opt_header->length = (key & 0xFF) * 4; - opt_header->offset = data_offset; - break; - } - - const uint8_t* pp = p + opt_header->offset; - switch (opt_header->key) { - case XEX_HEADER_SYSTEM_FLAGS: - header->system_flags = (xe_xex2_system_flags)data_offset; - break; - case XEX_HEADER_RESOURCE_INFO: { - header->resource_info_count = (opt_header->length - 4) / 16; - header->resource_infos = (xe_xex2_resource_info_t*)calloc( - header->resource_info_count, sizeof(xe_xex2_resource_info_t)); - const uint8_t* phi = pp + 0x04; - for (size_t m = 0; m < header->resource_info_count; m++) { - auto& res = header->resource_infos[m]; - memcpy(res.name, phi + 0x00, 8); - res.address = xe::load_and_swap(phi + 0x08); - res.size = xe::load_and_swap(phi + 0x0C); - phi += 16; - } - } break; - case XEX_HEADER_EXECUTION_INFO: { - xe_xex2_execution_info_t* ex = &header->execution_info; - ex->media_id = xe::load_and_swap(pp + 0x00); - ex->version.value = xe::load_and_swap(pp + 0x04); - ex->base_version.value = xe::load_and_swap(pp + 0x08); - ex->title_id = xe::load_and_swap(pp + 0x0C); - ex->platform = xe::load_and_swap(pp + 0x10); - ex->executable_table = xe::load_and_swap(pp + 0x11); - ex->disc_number = xe::load_and_swap(pp + 0x12); - ex->disc_count = xe::load_and_swap(pp + 0x13); - ex->savegame_id = xe::load_and_swap(pp + 0x14); - } break; - case XEX_HEADER_GAME_RATINGS: { - xe_xex2_game_ratings_t* ratings = &header->game_ratings; - ratings->esrb = - (xe_xex2_rating_esrb_value)xe::load_and_swap(pp + 0x00); - ratings->pegi = - (xe_xex2_rating_pegi_value)xe::load_and_swap(pp + 0x01); - ratings->pegifi = - (xe_xex2_rating_pegi_fi_value)xe::load_and_swap(pp + 0x02); - ratings->pegipt = - (xe_xex2_rating_pegi_pt_value)xe::load_and_swap(pp + 0x03); - ratings->bbfc = - (xe_xex2_rating_bbfc_value)xe::load_and_swap(pp + 0x04); - ratings->cero = - (xe_xex2_rating_cero_value)xe::load_and_swap(pp + 0x05); - ratings->usk = - (xe_xex2_rating_usk_value)xe::load_and_swap(pp + 0x06); - ratings->oflcau = - (xe_xex2_rating_oflc_au_value)xe::load_and_swap(pp + 0x07); - ratings->oflcnz = - (xe_xex2_rating_oflc_nz_value)xe::load_and_swap(pp + 0x08); - ratings->kmrb = - (xe_xex2_rating_kmrb_value)xe::load_and_swap(pp + 0x09); - ratings->brazil = - (xe_xex2_rating_brazil_value)xe::load_and_swap(pp + 0x0A); - ratings->fpb = - (xe_xex2_rating_fpb_value)xe::load_and_swap(pp + 0x0B); - } break; - case XEX_HEADER_TLS_INFO: { - xe_xex2_tls_info_t* tls = &header->tls_info; - tls->slot_count = xe::load_and_swap(pp + 0x00); - tls->raw_data_address = xe::load_and_swap(pp + 0x04); - tls->data_size = xe::load_and_swap(pp + 0x08); - tls->raw_data_size = xe::load_and_swap(pp + 0x0C); - } break; - case XEX_HEADER_IMAGE_BASE_ADDRESS: - header->exe_address = opt_header->value; - break; - case XEX_HEADER_ENTRY_POINT: - header->exe_entry_point = opt_header->value; - break; - case XEX_HEADER_DEFAULT_STACK_SIZE: - header->exe_stack_size = opt_header->value; - break; - case XEX_HEADER_DEFAULT_HEAP_SIZE: - header->exe_heap_size = opt_header->value; - break; - case XEX_HEADER_EXPORTS_BY_NAME: { - // IMAGE_DATA_DIRECTORY (w/ offset from PE file base) - header->pe_export_table_offset = xe::load_and_swap(pp); - // size = xe::load_and_swap(pp + 0x04); - } break; - case XEX_HEADER_IMPORT_LIBRARIES: { - auto import_libraries = - reinterpret_cast(pp); - - const uint32_t max_count = - (uint32_t)xe::countof(header->import_libraries); - uint32_t count = import_libraries->library_count; - assert_true(count <= max_count); - if (count > max_count) { - XELOGW("ignoring %zu extra entries in XEX_HEADER_IMPORT_LIBRARIES", - (max_count - import_libraries->library_count)); - count = max_count; - } - header->import_library_count = count; - - uint32_t string_table_size = import_libraries->string_table_size; - const char* string_table[32]; // Pretend 32 is max_count - std::memset(string_table, 0, sizeof(string_table)); - - // Parse the string table - for (size_t i = 0, j = 0; i < string_table_size; j++) { - const char* str = import_libraries->string_table + i; - - string_table[j] = str; - i += std::strlen(str) + 1; - - // Padding - if ((i % 4) != 0) { - i += 4 - (i % 4); - } - } - - pp += 12 + import_libraries->string_table_size; - for (size_t m = 0; m < count; m++) { - xe_xex2_import_library_t* library = &header->import_libraries[m]; - auto src_library = (xe::xex2_import_library*)pp; - - memcpy(library->digest, pp + 0x04, 20); - library->import_id = src_library->id; - library->version.value = src_library->version.value; - library->min_version.value = src_library->version_min.value; - - std::strncpy(library->name, - string_table[src_library->name_index & 0xFF], - xe::countof(library->name)); - - library->record_count = src_library->count; - library->records = - (uint32_t*)calloc(library->record_count, sizeof(uint32_t)); - if (!library->records) { - return 1; - } - for (size_t i = 0; i < library->record_count; i++) { - library->records[i] = src_library->import_table[i]; - } - - pp += src_library->size; - } - } break; - case XEX_HEADER_STATIC_LIBRARIES: { - const size_t max_count = xe::countof(header->static_libraries); - size_t count = (opt_header->length - 4) / 16; - assert_true(count <= max_count); - if (count > max_count) { - XELOGW("ignoring %zu extra entries in XEX_HEADER_STATIC_LIBRARIES", - (max_count - count)); - count = max_count; - } - header->static_library_count = count; - pp += 4; - for (size_t m = 0; m < count; m++) { - xe_xex2_static_library_t* library = &header->static_libraries[m]; - memcpy(library->name, pp + 0x00, 8); - library->name[8] = 0; - library->major = xe::load_and_swap(pp + 0x08); - library->minor = xe::load_and_swap(pp + 0x0A); - library->build = xe::load_and_swap(pp + 0x0C); - uint16_t qfeapproval = xe::load_and_swap(pp + 0x0E); - library->approval = (xe_xex2_approval_type)(qfeapproval & 0x8000); - library->qfe = qfeapproval & ~0x8000; - pp += 16; - } - } break; - case XEX_HEADER_FILE_FORMAT_INFO: { - xe_xex2_file_format_info_t* fmt = &header->file_format_info; - fmt->encryption_type = - (xe_xex2_encryption_type)xe::load_and_swap(pp + 0x04); - fmt->compression_type = - (xe_xex2_compression_type)xe::load_and_swap(pp + 0x06); - switch (fmt->compression_type) { - case XEX_COMPRESSION_NONE: - // TODO: XEX_COMPRESSION_NONE - assert_always(); - break; - case XEX_COMPRESSION_BASIC: { - xe_xex2_file_basic_compression_info_t* comp_info = - &fmt->compression_info.basic; - uint32_t info_size = xe::load_and_swap(pp + 0x00); - comp_info->block_count = (info_size - 8) / 8; - comp_info->blocks = (xe_xex2_file_basic_compression_block_t*)calloc( - comp_info->block_count, - sizeof(xe_xex2_file_basic_compression_block_t)); - if (!comp_info->blocks) { - return 1; - } - for (size_t m = 0; m < comp_info->block_count; m++) { - xe_xex2_file_basic_compression_block_t* block = - &comp_info->blocks[m]; - block->data_size = - xe::load_and_swap(pp + 0x08 + (m * 8)); - block->zero_size = - xe::load_and_swap(pp + 0x0C + (m * 8)); - } - } break; - case XEX_COMPRESSION_NORMAL: { - xe_xex2_file_normal_compression_info_t* comp_info = - &fmt->compression_info.normal; - uint32_t window_size = xe::load_and_swap(pp + 0x08); - uint32_t window_bits = 0; - for (size_t m = 0; m < 32; m++, window_bits++) { - window_size >>= 1; - if (window_size == 0x00000000) { - break; - } - } - comp_info->window_size = xe::load_and_swap(pp + 0x08); - comp_info->window_bits = window_bits; - comp_info->block_size = xe::load_and_swap(pp + 0x0C); - memcpy(comp_info->block_hash, pp + 0x10, 20); - } break; - case XEX_COMPRESSION_DELTA: - // TODO: XEX_COMPRESSION_DELTA - assert_always(); - break; - } - } break; - } - } - - // Loader info. - pc = p + header->certificate_offset; - ldr = &header->loader_info; - ldr->header_size = xe::load_and_swap(pc + 0x000); - ldr->image_size = xe::load_and_swap(pc + 0x004); - memcpy(ldr->rsa_signature, pc + 0x008, 256); - ldr->unklength = xe::load_and_swap(pc + 0x108); - ldr->image_flags = - (xe_xex2_image_flags)xe::load_and_swap(pc + 0x10C); - ldr->load_address = xe::load_and_swap(pc + 0x110); - memcpy(ldr->section_digest, pc + 0x114, 20); - ldr->import_table_count = xe::load_and_swap(pc + 0x128); - memcpy(ldr->import_table_digest, pc + 0x12C, 20); - memcpy(ldr->media_id, pc + 0x140, 16); - memcpy(ldr->file_key, pc + 0x150, 16); - ldr->export_table = xe::load_and_swap(pc + 0x160); - memcpy(ldr->header_digest, pc + 0x164, 20); - ldr->game_regions = - (xe_xex2_region_flags)xe::load_and_swap(pc + 0x178); - ldr->media_flags = - (xe_xex2_media_flags)xe::load_and_swap(pc + 0x17C); - - // Section info follows loader info. - ps = p + header->certificate_offset + 0x180; - header->section_count = xe::load_and_swap(ps + 0x000); - ps += 4; - header->sections = (xe_xex2_section_t*)calloc(header->section_count, - sizeof(xe_xex2_section_t)); - if (!header->sections) { - return 1; - } - for (size_t n = 0; n < header->section_count; n++) { - xe_xex2_section_t* section = &header->sections[n]; - section->page_size = - header->exe_address <= 0x90000000 ? 64 * 1024 : 4 * 1024; - section->info.value = xe::load_and_swap(ps); - ps += 4; - memcpy(section->digest, ps, sizeof(section->digest)); - ps += sizeof(section->digest); - } - - return 0; -} - -int xe_xex2_decrypt_key(xe_xex2_header_t* header) { - static const uint8_t xe_xex2_retail_key[16] = { - 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, - 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91}; - static const uint8_t xe_xex2_devkit_key[16] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - // Guess key based on file info. - // TODO: better way to finding out which key to use? - const uint8_t* xexkey; - if (header->execution_info.title_id && !FLAGS_xex_dev_key) { - xexkey = xe_xex2_retail_key; - } else { - xexkey = xe_xex2_devkit_key; - } - - // Decrypt the header key. - uint32_t rk[4 * (MAXNR + 1)]; - int32_t Nr = rijndaelKeySetupDec(rk, xexkey, 128); - rijndaelDecrypt(rk, Nr, header->loader_info.file_key, header->session_key); - - return 0; -} - -typedef struct mspack_memory_file_t { - struct mspack_system sys; - void* buffer; - off_t buffer_size; - off_t offset; -} mspack_memory_file; -mspack_memory_file* mspack_memory_open(struct mspack_system* sys, void* buffer, - const size_t buffer_size) { - assert_true(buffer_size < INT_MAX); - if (buffer_size >= INT_MAX) { - return NULL; - } - mspack_memory_file* memfile = - (mspack_memory_file*)calloc(1, sizeof(mspack_memory_file)); - if (!memfile) { - return NULL; - } - memfile->buffer = buffer; - memfile->buffer_size = (off_t)buffer_size; - memfile->offset = 0; - return memfile; -} -void mspack_memory_close(mspack_memory_file* file) { - mspack_memory_file* memfile = (mspack_memory_file*)file; - free(memfile); -} -int mspack_memory_read(struct mspack_file* file, void* buffer, int chars) { - mspack_memory_file* memfile = (mspack_memory_file*)file; - const off_t remaining = memfile->buffer_size - memfile->offset; - const off_t total = std::min(static_cast(chars), remaining); - memcpy(buffer, (uint8_t*)memfile->buffer + memfile->offset, total); - memfile->offset += total; - return (int)total; -} -int mspack_memory_write(struct mspack_file* file, void* buffer, int chars) { - mspack_memory_file* memfile = (mspack_memory_file*)file; - const off_t remaining = memfile->buffer_size - memfile->offset; - const off_t total = std::min(static_cast(chars), remaining); - memcpy((uint8_t*)memfile->buffer + memfile->offset, buffer, total); - memfile->offset += total; - return (int)total; -} -void* mspack_memory_alloc(struct mspack_system* sys, size_t chars) { - return calloc(chars, 1); -} -void mspack_memory_free(void* ptr) { free(ptr); } -void mspack_memory_copy(void* src, void* dest, size_t chars) { - memcpy(dest, src, chars); -} -struct mspack_system* mspack_memory_sys_create() { - struct mspack_system* sys = - (struct mspack_system*)calloc(1, sizeof(struct mspack_system)); - if (!sys) { - return NULL; - } - sys->read = mspack_memory_read; - sys->write = mspack_memory_write; - sys->alloc = mspack_memory_alloc; - sys->free = mspack_memory_free; - sys->copy = mspack_memory_copy; - return sys; -} -void mspack_memory_sys_destroy(struct mspack_system* sys) { free(sys); } - -void xe_xex2_decrypt_buffer(const uint8_t* session_key, - const uint8_t* input_buffer, - const size_t input_size, uint8_t* output_buffer, - const size_t output_size) { - uint32_t rk[4 * (MAXNR + 1)]; - uint8_t ivec[16] = {0}; - int32_t Nr = rijndaelKeySetupDec(rk, session_key, 128); - const uint8_t* ct = input_buffer; - uint8_t* pt = output_buffer; - for (size_t n = 0; n < input_size; n += 16, ct += 16, pt += 16) { - // Decrypt 16 uint8_ts from input -> output. - rijndaelDecrypt(rk, Nr, ct, pt); - for (size_t i = 0; i < 16; i++) { - // XOR with previous. - pt[i] ^= ivec[i]; - // Set previous. - ivec[i] = ct[i]; - } - } -} - -int xe_xex2_read_image_uncompressed(const xe_xex2_header_t* header, - const uint8_t* xex_addr, - const uint32_t xex_length, - xe::Memory* memory) { - // Allocate in-place the XEX memory. - const uint32_t exe_length = xex_length - header->exe_offset; - uint32_t uncompressed_size = exe_length; - bool alloc_result = - memory->LookupHeap(header->exe_address) - ->AllocFixed( - header->exe_address, uncompressed_size, 4096, - xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit, - xe::kMemoryProtectRead | xe::kMemoryProtectWrite); - if (!alloc_result) { - XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", header->exe_address, - uncompressed_size); - return 2; - } - uint8_t* buffer = memory->TranslateVirtual(header->exe_address); - std::memset(buffer, 0, uncompressed_size); - - const uint8_t* p = (const uint8_t*)xex_addr + header->exe_offset; - - switch (header->file_format_info.encryption_type) { - case XEX_ENCRYPTION_NONE: - if (exe_length > uncompressed_size) { - return 1; - } - memcpy(buffer, p, exe_length); - return 0; - case XEX_ENCRYPTION_NORMAL: - xe_xex2_decrypt_buffer(header->session_key, p, exe_length, buffer, - uncompressed_size); - return 0; - default: - assert_always(); - return 1; - } - - return 0; -} - -int xe_xex2_read_image_basic_compressed(const xe_xex2_header_t* header, - const uint8_t* xex_addr, - const uint32_t xex_length, - xe::Memory* memory) { - const uint32_t exe_length = xex_length - header->exe_offset; - const uint8_t* source_buffer = (const uint8_t*)xex_addr + header->exe_offset; - const uint8_t* p = source_buffer; - - // Calculate uncompressed length. - uint32_t uncompressed_size = 0; - const xe_xex2_file_basic_compression_info_t* comp_info = - &header->file_format_info.compression_info.basic; - for (uint32_t n = 0; n < comp_info->block_count; n++) { - const uint32_t data_size = comp_info->blocks[n].data_size; - const uint32_t zero_size = comp_info->blocks[n].zero_size; - uncompressed_size += data_size + zero_size; - } - - // Calculate the total size of the XEX image from its headers. - uint32_t total_size = 0; - for (uint32_t i = 0; i < header->section_count; i++) { - xe_xex2_section_t& section = header->sections[i]; - - total_size += section.info.page_count * section.page_size; - } - - // Allocate in-place the XEX memory. - bool alloc_result = - memory->LookupHeap(header->exe_address) - ->AllocFixed( - header->exe_address, total_size, 4096, - xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit, - xe::kMemoryProtectRead | xe::kMemoryProtectWrite); - if (!alloc_result) { - XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", header->exe_address, - uncompressed_size); - return 1; - } - uint8_t* buffer = memory->TranslateVirtual(header->exe_address); - std::memset(buffer, 0, total_size); // Quickly zero the contents. - uint8_t* d = buffer; - - uint32_t rk[4 * (MAXNR + 1)]; - uint8_t ivec[16] = {0}; - int32_t Nr = rijndaelKeySetupDec(rk, header->session_key, 128); - - for (size_t n = 0; n < comp_info->block_count; n++) { - const uint32_t data_size = comp_info->blocks[n].data_size; - const uint32_t zero_size = comp_info->blocks[n].zero_size; - - switch (header->file_format_info.encryption_type) { - case XEX_ENCRYPTION_NONE: - if (data_size > uncompressed_size - (d - buffer)) { - // Overflow. - return 1; - } - memcpy(d, p, data_size); - break; - case XEX_ENCRYPTION_NORMAL: { - const uint8_t* ct = p; - uint8_t* pt = d; - for (size_t m = 0; m < data_size; m += 16, ct += 16, pt += 16) { - // Decrypt 16 uint8_ts from input -> output. - rijndaelDecrypt(rk, Nr, ct, pt); - for (size_t i = 0; i < 16; i++) { - // XOR with previous. - pt[i] ^= ivec[i]; - // Set previous. - ivec[i] = ct[i]; - } - } - } break; - default: - assert_always(); - return 1; - } - - p += data_size; - d += data_size + zero_size; - } - - return 0; -} - -int xe_xex2_read_image_compressed(const xe_xex2_header_t* header, - const uint8_t* xex_addr, - const uint32_t xex_length, - xe::Memory* memory) { - const uint32_t exe_length = xex_length - header->exe_offset; - const uint8_t* exe_buffer = (const uint8_t*)xex_addr + header->exe_offset; - - // src -> dest: - // - decrypt (if encrypted) - // - de-block: - // 4b total size of next block in uint8_ts - // 20b hash of entire next block (including size/hash) - // Nb block uint8_ts - // - decompress block contents - - int result_code = 1; - - uint8_t* compress_buffer = NULL; - const uint8_t* p = NULL; - uint8_t* d = NULL; - uint8_t* deblock_buffer = NULL; - size_t block_size = 0; - uint32_t uncompressed_size = 0; - struct mspack_system* sys = NULL; - mspack_memory_file* lzxsrc = NULL; - mspack_memory_file* lzxdst = NULL; - struct lzxd_stream* lzxd = NULL; - - // Decrypt (if needed). - bool free_input = false; - const uint8_t* input_buffer = exe_buffer; - const size_t input_size = exe_length; - switch (header->file_format_info.encryption_type) { - case XEX_ENCRYPTION_NONE: - // No-op. - break; - case XEX_ENCRYPTION_NORMAL: - // TODO: a way to do without a copy/alloc? - free_input = true; - input_buffer = (const uint8_t*)calloc(1, input_size); - xe_xex2_decrypt_buffer(header->session_key, exe_buffer, exe_length, - (uint8_t*)input_buffer, input_size); - break; - default: - assert_always(); - return false; - } - - compress_buffer = (uint8_t*)calloc(1, exe_length); - - p = input_buffer; - d = compress_buffer; - - // De-block. - deblock_buffer = (uint8_t*)calloc(1, input_size); - block_size = header->file_format_info.compression_info.normal.block_size; - while (block_size) { - const uint8_t* pnext = p + block_size; - const size_t next_size = xe::load_and_swap(p); - p += 4; - p += 20; // skip 20b hash - - while (true) { - const size_t chunk_size = (p[0] << 8) | p[1]; - p += 2; - if (!chunk_size) { - break; - } - memcpy(d, p, chunk_size); - p += chunk_size; - d += chunk_size; - - uncompressed_size += 0x8000; - } - - p = pnext; - block_size = next_size; - } - - // Allocate in-place the XEX memory. - bool alloc_result = - memory->LookupHeap(header->exe_address) - ->AllocFixed( - header->exe_address, uncompressed_size, 4096, - xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit, - xe::kMemoryProtectRead | xe::kMemoryProtectWrite); - if (!alloc_result) { - XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", header->exe_address, - uncompressed_size); - result_code = 2; - // Not doing any cleanup here; - // TODO(benvanik): rewrite this entire file using RAII. - assert_always(); - return 1; - } - uint8_t* buffer = memory->TranslateVirtual(header->exe_address); - std::memset(buffer, 0, uncompressed_size); - - // Setup decompressor and decompress. - sys = mspack_memory_sys_create(); - lzxsrc = mspack_memory_open(sys, (void*)compress_buffer, d - compress_buffer); - lzxdst = mspack_memory_open(sys, buffer, uncompressed_size); - lzxd = - lzxd_init(sys, (struct mspack_file*)lzxsrc, (struct mspack_file*)lzxdst, - header->file_format_info.compression_info.normal.window_bits, 0, - 32768, (off_t)header->loader_info.image_size); - result_code = lzxd_decompress(lzxd, (off_t)header->loader_info.image_size); - - if (lzxd) { - lzxd_free(lzxd); - lzxd = NULL; - } - if (lzxsrc) { - mspack_memory_close(lzxsrc); - lzxsrc = NULL; - } - if (lzxdst) { - mspack_memory_close(lzxdst); - lzxdst = NULL; - } - if (sys) { - mspack_memory_sys_destroy(sys); - sys = NULL; - } - free(compress_buffer); - free(deblock_buffer); - if (free_input) { - free((void*)input_buffer); - } - return result_code; -} - -int xe_xex2_read_image(xe_xex2_ref xex, const uint8_t* xex_addr, - const uint32_t xex_length, xe::Memory* memory) { - const xe_xex2_header_t* header = &xex->header; - switch (header->file_format_info.compression_type) { - case XEX_COMPRESSION_NONE: - return xe_xex2_read_image_uncompressed(header, xex_addr, xex_length, - memory); - case XEX_COMPRESSION_BASIC: - return xe_xex2_read_image_basic_compressed(header, xex_addr, xex_length, - memory); - case XEX_COMPRESSION_NORMAL: - return xe_xex2_read_image_compressed(header, xex_addr, xex_length, - memory); - default: - assert_always(); - return 1; - } -} - -int xe_xex2_load_pe(xe_xex2_ref xex) { - const xe_xex2_header_t* header = &xex->header; - const uint8_t* p = xex->memory->TranslateVirtual(header->exe_address); - - // Verify DOS signature (MZ). - const IMAGE_DOS_HEADER* doshdr = (const IMAGE_DOS_HEADER*)p; - if (doshdr->e_magic != IMAGE_DOS_SIGNATURE) { - XELOGE("PE signature mismatch; likely bad decryption/decompression"); - return 1; - } - - // Move to the NT header offset from the DOS header. - p += doshdr->e_lfanew; - - // Verify NT signature (PE\0\0). - const IMAGE_NT_HEADERS32* nthdr = (const IMAGE_NT_HEADERS32*)(p); - if (nthdr->Signature != IMAGE_NT_SIGNATURE) { - return 1; - } - - // Verify matches an Xbox PE. - const IMAGE_FILE_HEADER* filehdr = &nthdr->FileHeader; - if ((filehdr->Machine != IMAGE_FILE_MACHINE_POWERPCBE) || - !(filehdr->Characteristics & IMAGE_FILE_32BIT_MACHINE)) { - return 1; - } - // Verify the expected size. - if (filehdr->SizeOfOptionalHeader != IMAGE_SIZEOF_NT_OPTIONAL_HEADER) { - return 1; - } - - // Verify optional header is 32bit. - const IMAGE_OPTIONAL_HEADER32* opthdr = &nthdr->OptionalHeader; - if (opthdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) { - return 1; - } - // Verify subsystem. - if (opthdr->Subsystem != IMAGE_SUBSYSTEM_XBOX) { - return 1; - } - -// Linker version - likely 8+ -// Could be useful for recognizing certain patterns -// opthdr->MajorLinkerVersion; opthdr->MinorLinkerVersion; - -// Data directories of interest: -// EXPORT IMAGE_EXPORT_DIRECTORY -// IMPORT IMAGE_IMPORT_DESCRIPTOR[] -// EXCEPTION IMAGE_CE_RUNTIME_FUNCTION_ENTRY[] -// BASERELOC -// DEBUG IMAGE_DEBUG_DIRECTORY[] -// ARCHITECTURE /IMAGE_ARCHITECTURE_HEADER/ ----- import thunks! -// TLS IMAGE_TLS_DIRECTORY -// IAT Import Address Table ptr -// opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_X].VirtualAddress / .Size - -// The macros in pe_image.h don't work with clang, for some reason. -// offsetof seems to be unable to find OptionalHeader. -#define offsetof1(type, member) ((std::size_t) & (((type*)0)->member)) -#define IMAGE_FIRST_SECTION1(ntheader) \ - ((PIMAGE_SECTION_HEADER)( \ - (uint8_t*)ntheader + offsetof1(IMAGE_NT_HEADERS, OptionalHeader) + \ - ((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader)) - - // Quick scan to determine bounds of sections. - size_t upper_address = 0; - const IMAGE_SECTION_HEADER* sechdr = IMAGE_FIRST_SECTION1(nthdr); - for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) { - const size_t physical_address = opthdr->ImageBase + sechdr->VirtualAddress; - upper_address = - std::max(upper_address, physical_address + sechdr->Misc.VirtualSize); - } - - // Setup/load sections. - sechdr = IMAGE_FIRST_SECTION1(nthdr); - for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) { - PESection* section = (PESection*)calloc(1, sizeof(PESection)); - memcpy(section->name, sechdr->Name, sizeof(sechdr->Name)); - section->name[8] = 0; - section->raw_address = sechdr->PointerToRawData; - section->raw_size = sechdr->SizeOfRawData; - section->address = header->exe_address + sechdr->VirtualAddress; - section->size = sechdr->Misc.VirtualSize; - section->flags = sechdr->Characteristics; - xex->sections->push_back(section); - } - - // DumpTLSDirectory(pImageBase, pNTHeader, (PIMAGE_TLS_DIRECTORY32)0); - // DumpExportsSection(pImageBase, pNTHeader); - return 0; -} - -const PESection* xe_xex2_get_pe_section(xe_xex2_ref xex, const char* name) { - for (std::vector::iterator it = xex->sections->begin(); - it != xex->sections->end(); ++it) { - if (!strcmp((*it)->name, name)) { - return *it; - } - } - return NULL; -} - -int xe_xex2_find_import_infos(xe_xex2_ref xex, - const xe_xex2_import_library_t* library) { - auto header = xe_xex2_get_header(xex); - - // Find library index for verification. - size_t library_index = ~0ull; - for (size_t n = 0; n < header->import_library_count; n++) { - if (&header->import_libraries[n] == library) { - library_index = n; - break; - } - } - assert_true(library_index != (size_t)-1); - - // Records: - // The number of records does not correspond to the number of imports! - // Each record points at either a location in text or data - dereferencing the - // pointer will yield a value that & 0xFFFF = the import ordinal, - // >> 16 & 0xFF = import library index, and >> 24 & 0xFF = 0 if a variable - // (just get address) or 1 if a thunk (needs rewrite). - - // Calculate real count. - size_t info_count = 0; - for (size_t n = 0; n < library->record_count; n++) { - const uint32_t record = library->records[n]; - const uint32_t value = - xe::load_and_swap(xex->memory->TranslateVirtual(record)); - if (value & 0xFF000000) { - // Thunk for previous record - ignore. - } else { - // Variable/thunk. - info_count++; - } - } - - // Allocate storage. - xe_xex2_import_info_t* infos = - (xe_xex2_import_info_t*)calloc(info_count, sizeof(xe_xex2_import_info_t)); - assert_not_null(infos); - - assert_not_zero(info_count); - - // Construct infos. - for (size_t n = 0, i = 0; n < library->record_count; n++) { - const uint32_t record = library->records[n]; - const uint32_t value = - xe::load_and_swap(xex->memory->TranslateVirtual(record)); - const uint32_t type = (value & 0xFF000000) >> 24; - - // Verify library index matches given library. - // assert_true(library_index == ((value >> 16) & 0xFF)); - - switch (type) { - case 0x00: { - xe_xex2_import_info_t* info = &infos[i++]; - info->ordinal = value & 0xFFFF; - info->value_address = record; - } break; - case 0x01: { - // Thunk for previous record. - assert_true(i > 0); - xe_xex2_import_info_t* info = &infos[i - 1]; - assert_true(info->ordinal == (value & 0xFFFF)); - info->thunk_address = record; - } break; - default: - assert_always(); - break; - } - } - - xex->library_imports[library_index].count = info_count; - xex->library_imports[library_index].infos = infos; - return 0; -} - -int xe_xex2_get_import_infos(xe_xex2_ref xex, - const xe_xex2_import_library_t* library, - xe_xex2_import_info_t** out_import_infos, - size_t* out_import_info_count) { - auto header = xe_xex2_get_header(xex); - - // Find library index for verification. - size_t library_index = ~0ull; - for (size_t n = 0; n < header->import_library_count; n++) { - if (&header->import_libraries[n] == library) { - library_index = n; - break; - } - } - if (library_index == (size_t)-1) { - return 1; - } - - *out_import_info_count = xex->library_imports[library_index].count; - *out_import_infos = xex->library_imports[library_index].infos; - return 0; -} - -uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, const char* name) { - auto header = xe_xex2_get_header(xex); - - // No exports. - if (!header->pe_export_table_offset) { - XELOGE("xe_xex2_lookup_export(%s) failed: no PE export table", name); - return 0; - } - - auto e = reinterpret_cast( - xex->memory->TranslateVirtual(header->exe_address + - header->pe_export_table_offset)); - - // e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY! - uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions); - - // Names relative to directory. - uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames); - - // Table of ordinals (by name). - uint16_t* ordinal_table = (uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals); - - // Module name (sometimes). - // const char* mod_name = (const char*)((uint64_t)e + e->Name); - - for (uint32_t i = 0; i < e->NumberOfNames; i++) { - const char* fn_name = (const char*)((uint64_t)e + name_table[i]); - uint16_t ordinal = ordinal_table[i]; - uint32_t addr = header->exe_address + function_table[ordinal]; - - if (!strcmp(name, fn_name)) { - // We have a match! - return addr; - } - } - - // No match. - return 0; -} - -uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal) { - auto header = xe_xex2_get_header(xex); - - // XEX-style export table. - if (header->loader_info.export_table) { - auto export_table = reinterpret_cast( - xex->memory->TranslateVirtual(header->loader_info.export_table)); - uint32_t ordinal_count = export_table->count; - uint32_t ordinal_base = export_table->base; - if (ordinal > ordinal_count) { - XELOGE("xe_xex2_lookup_export: ordinal out of bounds"); - return 0; - } - uint32_t i = ordinal - ordinal_base; - uint32_t ordinal_offset = export_table->ordOffset[i]; - ordinal_offset += export_table->imagebaseaddr << 16; - return ordinal_offset; - } - - // Check for PE-style export table. - if (!header->pe_export_table_offset) { - XELOGE("xe_xex2_lookup_export(%.4X) failed: no XEX or PE export table"); - return 0; - } - - auto e = reinterpret_cast( - xex->memory->TranslateVirtual(header->exe_address + - header->pe_export_table_offset)); - - // e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY! - // Functions relative to base. - uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions); - if (ordinal < e->NumberOfFunctions) { - return header->exe_address + function_table[ordinal]; - } - - // No match. - return 0; -} diff --git a/src/xenia/kernel/util/xex2.h b/src/xenia/kernel/util/xex2.h deleted file mode 100644 index 64f2ab0de..000000000 --- a/src/xenia/kernel/util/xex2.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_KERNEL_UTIL_XEX2_H_ -#define XENIA_KERNEL_UTIL_XEX2_H_ - -#include "xenia/kernel/util/xex2_info.h" -#include "xenia/memory.h" - -namespace xe {} // namespace xe - -typedef struct { - int reserved; -} xe_xex2_options_t; - -struct xe_xex2; -typedef struct xe_xex2* xe_xex2_ref; - -typedef struct { - uint32_t ordinal; - uint32_t value_address; // address to place value - uint32_t thunk_address; // NULL or address of thunk -} xe_xex2_import_info_t; - -enum xe_pe_section_flags_e : uint32_t { - kXEPESectionContainsCode = 0x00000020, - kXEPESectionContainsDataInit = 0x00000040, - kXEPESectionContainsDataUninit = 0x00000080, - kXEPESectionMemoryExecute = 0x20000000, - kXEPESectionMemoryRead = 0x40000000, - kXEPESectionMemoryWrite = 0x80000000, -}; - -class PESection { - public: - char name[9]; // 8 + 1 for \0 - uint32_t raw_address; - uint32_t raw_size; - uint32_t address; - uint32_t size; - uint32_t flags; // kXEPESection* -}; - -struct PEExport { - const char* name; - uint32_t ordinal; - - uint64_t addr; // Function address -}; - -xe_xex2_ref xe_xex2_load(xe::Memory* memory, const void* addr, - const size_t length, xe_xex2_options_t options); -void xe_xex2_dealloc(xe_xex2_ref xex); - -const xe_xex2_header_t* xe_xex2_get_header(xe_xex2_ref xex); -const PESection* xe_xex2_get_pe_section(xe_xex2_ref xex, const char* name); - -int xe_xex2_get_import_infos(xe_xex2_ref xex, - const xe_xex2_import_library_t* library, - xe_xex2_import_info_t** out_import_infos, - size_t* out_import_info_count); - -uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, const char* name); -uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal); - -#endif // XENIA_KERNEL_UTIL_XEX2_H_ diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index 1a6ed8644..02163fb34 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -14,40 +14,103 @@ #include "xenia/base/byte_order.h" -enum xe_xex2_header_keys : uint32_t { - XEX_HEADER_RESOURCE_INFO = 0x000002FF, - XEX_HEADER_FILE_FORMAT_INFO = 0x000003FF, - XEX_HEADER_DELTA_PATCH_DESCRIPTOR = 0x000005FF, - XEX_HEADER_BASE_REFERENCE = 0x00000405, - XEX_HEADER_BOUNDING_PATH = 0x000080FF, - XEX_HEADER_DEVICE_ID = 0x00008105, - XEX_HEADER_ORIGINAL_BASE_ADDRESS = 0x00010001, - XEX_HEADER_ENTRY_POINT = 0x00010100, - XEX_HEADER_IMAGE_BASE_ADDRESS = 0x00010201, - XEX_HEADER_IMPORT_LIBRARIES = 0x000103FF, - XEX_HEADER_CHECKSUM_TIMESTAMP = 0x00018002, - XEX_HEADER_ENABLED_FOR_CALLCAP = 0x00018102, - XEX_HEADER_ENABLED_FOR_FASTCAP = 0x00018200, - XEX_HEADER_ORIGINAL_PE_NAME = 0x000183FF, - XEX_HEADER_STATIC_LIBRARIES = 0x000200FF, - XEX_HEADER_TLS_INFO = 0x00020104, - XEX_HEADER_DEFAULT_STACK_SIZE = 0x00020200, - XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE = 0x00020301, - XEX_HEADER_DEFAULT_HEAP_SIZE = 0x00020401, - XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS = 0x00028002, - XEX_HEADER_SYSTEM_FLAGS = 0x00030000, - XEX_HEADER_EXECUTION_INFO = 0x00040006, - XEX_HEADER_TITLE_WORKSPACE_SIZE = 0x00040201, - XEX_HEADER_GAME_RATINGS = 0x00040310, - XEX_HEADER_LAN_KEY = 0x00040404, - XEX_HEADER_XBOX360_LOGO = 0x000405FF, - XEX_HEADER_MULTIDISC_MEDIA_IDS = 0x000406FF, - XEX_HEADER_ALTERNATE_TITLE_IDS = 0x000407FF, - XEX_HEADER_ADDITIONAL_TITLE_MEMORY = 0x00040801, - XEX_HEADER_EXPORTS_BY_NAME = 0x00E10402, +union xe_xex2_version_t { + uint32_t value; + struct { + uint32_t major : 4; + uint32_t minor : 4; + uint32_t build : 16; + uint32_t qfe : 8; + }; }; -enum xe_xex2_module_flags : uint32_t { +enum xe_pe_section_flags_e : uint32_t { + kXEPESectionContainsCode = 0x00000020, + kXEPESectionContainsDataInit = 0x00000040, + kXEPESectionContainsDataUninit = 0x00000080, + kXEPESectionMemoryExecute = 0x20000000, + kXEPESectionMemoryRead = 0x40000000, + kXEPESectionMemoryWrite = 0x80000000, +}; + +class PESection { + public: + char name[9]; // 8 + 1 for \0 + uint32_t raw_address; + uint32_t raw_size; + uint32_t address; + uint32_t size; + uint32_t flags; // kXEPESection* +}; + +struct PEExport { + const char* name; + uint32_t ordinal; + + uint64_t addr; // Function address +}; + +namespace xe { + +enum xex2_section_type { + XEX_SECTION_CODE = 1, + XEX_SECTION_DATA = 2, + XEX_SECTION_READONLY_DATA = 3, +}; + +enum xex2_image_flags : uint32_t { + XEX_IMAGE_MANUFACTURING_UTILITY = 0x00000002, + XEX_IMAGE_MANUFACTURING_SUPPORT_TOOLS = 0x00000004, + XEX_IMAGE_XGD2_MEDIA_ONLY = 0x00000008, + XEX_IMAGE_CARDEA_KEY = 0x00000100, + XEX_IMAGE_XEIKA_KEY = 0x00000200, + XEX_IMAGE_USERMODE_TITLE = 0x00000400, + XEX_IMAGE_USERMODE_SYSTEM = 0x00000800, + XEX_IMAGE_ORANGE0 = 0x00001000, + XEX_IMAGE_ORANGE1 = 0x00002000, + XEX_IMAGE_ORANGE2 = 0x00004000, + XEX_IMAGE_IPTV_SIGNUP_APPLICATION = 0x00010000, + XEX_IMAGE_IPTV_TITLE_APPLICATION = 0x00020000, + XEX_IMAGE_KEYVAULT_PRIVILEGES_REQUIRED = 0x04000000, + XEX_IMAGE_ONLINE_ACTIVATION_REQUIRED = 0x08000000, + XEX_IMAGE_PAGE_SIZE_4KB = 0x10000000, // else 64KB + XEX_IMAGE_REGION_FREE = 0x20000000, + XEX_IMAGE_REVOCATION_CHECK_OPTIONAL = 0x40000000, + XEX_IMAGE_REVOCATION_CHECK_REQUIRED = 0x80000000, +}; + +enum xex2_media_flags : uint32_t { + XEX_MEDIA_HARDDISK = 0x00000001, + XEX_MEDIA_DVD_X2 = 0x00000002, + XEX_MEDIA_DVD_CD = 0x00000004, + XEX_MEDIA_DVD_5 = 0x00000008, + XEX_MEDIA_DVD_9 = 0x00000010, + XEX_MEDIA_SYSTEM_FLASH = 0x00000020, + XEX_MEDIA_MEMORY_UNIT = 0x00000080, + XEX_MEDIA_USB_MASS_STORAGE_DEVICE = 0x00000100, + XEX_MEDIA_NETWORK = 0x00000200, + XEX_MEDIA_DIRECT_FROM_MEMORY = 0x00000400, + XEX_MEDIA_RAM_DRIVE = 0x00000800, + XEX_MEDIA_SVOD = 0x00001000, + XEX_MEDIA_INSECURE_PACKAGE = 0x01000000, + XEX_MEDIA_SAVEGAME_PACKAGE = 0x02000000, + XEX_MEDIA_LOCALLY_SIGNED_PACKAGE = 0x04000000, + XEX_MEDIA_LIVE_SIGNED_PACKAGE = 0x08000000, + XEX_MEDIA_XBOX_PACKAGE = 0x10000000, +}; + +enum xex2_region_flags : uint32_t { + XEX_REGION_NTSCU = 0x000000FF, + XEX_REGION_NTSCJ = 0x0000FF00, + XEX_REGION_NTSCJ_JAPAN = 0x00000100, + XEX_REGION_NTSCJ_CHINA = 0x00000200, + XEX_REGION_PAL = 0x00FF0000, + XEX_REGION_PAL_AU_NZ = 0x00010000, + XEX_REGION_OTHER = 0xFF000000, + XEX_REGION_ALL = 0xFFFFFFFF, +}; + +enum xex2_module_flags : uint32_t { XEX_MODULE_TITLE = 0x00000001, XEX_MODULE_EXPORTS_TO_TITLE = 0x00000002, XEX_MODULE_SYSTEM_DEBUGGER = 0x00000004, @@ -58,7 +121,7 @@ enum xe_xex2_module_flags : uint32_t { XEX_MODULE_USER_MODE = 0x00000080, }; -enum xe_xex2_system_flags : uint32_t { +enum xex2_system_flags : uint32_t { XEX_SYSTEM_NO_FORCED_REBOOT = 0x00000001, XEX_SYSTEM_FOREGROUND_TASKS = 0x00000002, XEX_SYSTEM_NO_ODD_MAPPING = 0x00000004, @@ -102,7 +165,7 @@ enum xe_xex2_system_flags : uint32_t { }; // ESRB (Entertainment Software Rating Board) -enum xe_xex2_rating_esrb_value : uint32_t { +enum xex2_rating_esrb_value : uint32_t { XEX_RATING_ESRB_eC = 0x00, XEX_RATING_ESRB_E = 0x02, XEX_RATING_ESRB_E10 = 0x04, @@ -112,7 +175,7 @@ enum xe_xex2_rating_esrb_value : uint32_t { XEX_RATING_ESRB_UNRATED = 0xFF, }; // PEGI (Pan European Game Information) -enum xe_xex2_rating_pegi_value : uint32_t { +enum xex2_rating_pegi_value : uint32_t { XEX_RATING_PEGI_3_PLUS = 0, XEX_RATING_PEGI_7_PLUS = 4, XEX_RATING_PEGI_12_PLUS = 9, @@ -121,7 +184,7 @@ enum xe_xex2_rating_pegi_value : uint32_t { XEX_RATING_PEGI_UNRATED = 0xFF, }; // PEGI (Pan European Game Information) - Finland -enum xe_xex2_rating_pegi_fi_value : uint32_t { +enum xex2_rating_pegi_fi_value : uint32_t { XEX_RATING_PEGI_FI_3_PLUS = 0, XEX_RATING_PEGI_FI_7_PLUS = 4, XEX_RATING_PEGI_FI_11_PLUS = 8, @@ -130,7 +193,7 @@ enum xe_xex2_rating_pegi_fi_value : uint32_t { XEX_RATING_PEGI_FI_UNRATED = 0xFF, }; // PEGI (Pan European Game Information) - Portugal -enum xe_xex2_rating_pegi_pt_value : uint32_t { +enum xex2_rating_pegi_pt_value : uint32_t { XEX_RATING_PEGI_PT_4_PLUS = 1, XEX_RATING_PEGI_PT_6_PLUS = 3, XEX_RATING_PEGI_PT_12_PLUS = 9, @@ -139,7 +202,7 @@ enum xe_xex2_rating_pegi_pt_value : uint32_t { XEX_RATING_PEGI_PT_UNRATED = 0xFF, }; // BBFC (British Board of Film Classification) - UK/Ireland -enum xe_xex2_rating_bbfc_value : uint32_t { +enum xex2_rating_bbfc_value : uint32_t { XEX_RATING_BBFC_UNIVERSAL = 1, XEX_RATING_BBFC_PG = 5, XEX_RATING_BBFC_3_PLUS = 0, @@ -151,7 +214,7 @@ enum xe_xex2_rating_bbfc_value : uint32_t { XEX_RATING_BBFC_UNRATED = 0xFF, }; // CERO (Computer Entertainment Rating Organization) -enum xe_xex2_rating_cero_value : uint32_t { +enum xex2_rating_cero_value : uint32_t { XEX_RATING_CERO_A = 0, XEX_RATING_CERO_B = 2, XEX_RATING_CERO_C = 4, @@ -160,7 +223,7 @@ enum xe_xex2_rating_cero_value : uint32_t { XEX_RATING_CERO_UNRATED = 0xFF, }; // USK (Unterhaltungssoftware SelbstKontrolle) -enum xe_xex2_rating_usk_value : uint32_t { +enum xex2_rating_usk_value : uint32_t { XEX_RATING_USK_ALL = 0, XEX_RATING_USK_6_PLUS = 2, XEX_RATING_USK_12_PLUS = 4, @@ -169,7 +232,7 @@ enum xe_xex2_rating_usk_value : uint32_t { XEX_RATING_USK_UNRATED = 0xFF, }; // OFLC (Office of Film and Literature Classification) - Australia -enum xe_xex2_rating_oflc_au_value : uint32_t { +enum xex2_rating_oflc_au_value : uint32_t { XEX_RATING_OFLC_AU_G = 0, XEX_RATING_OFLC_AU_PG = 2, XEX_RATING_OFLC_AU_M = 4, @@ -177,7 +240,7 @@ enum xe_xex2_rating_oflc_au_value : uint32_t { XEX_RATING_OFLC_AU_UNRATED = 0xFF, }; // OFLC (Office of Film and Literature Classification) - New Zealand -enum xe_xex2_rating_oflc_nz_value : uint32_t { +enum xex2_rating_oflc_nz_value : uint32_t { XEX_RATING_OFLC_NZ_G = 0, XEX_RATING_OFLC_NZ_PG = 2, XEX_RATING_OFLC_NZ_M = 4, @@ -185,7 +248,7 @@ enum xe_xex2_rating_oflc_nz_value : uint32_t { XEX_RATING_OFLC_NZ_UNRATED = 0xFF, }; // KMRB (Korea Media Rating Board) -enum xe_xex2_rating_kmrb_value : uint32_t { +enum xex2_rating_kmrb_value : uint32_t { XEX_RATING_KMRB_ALL = 0, XEX_RATING_KMRB_12_PLUS = 2, XEX_RATING_KMRB_15_PLUS = 4, @@ -193,7 +256,7 @@ enum xe_xex2_rating_kmrb_value : uint32_t { XEX_RATING_KMRB_UNRATED = 0xFF, }; // Brazil -enum xe_xex2_rating_brazil_value : uint32_t { +enum xex2_rating_brazil_value : uint32_t { XEX_RATING_BRAZIL_ALL = 0, XEX_RATING_BRAZIL_12_PLUS = 2, XEX_RATING_BRAZIL_14_PLUS = 4, @@ -202,7 +265,7 @@ enum xe_xex2_rating_brazil_value : uint32_t { XEX_RATING_BRAZIL_UNRATED = 0xFF, }; // FPB (Film and Publication Board) -enum xe_xex2_rating_fpb_value : uint32_t { +enum xex2_rating_fpb_value : uint32_t { XEX_RATING_FPB_ALL = 0, XEX_RATING_FPB_PG = 6, XEX_RATING_FPB_10_PLUS = 7, @@ -212,252 +275,102 @@ enum xe_xex2_rating_fpb_value : uint32_t { XEX_RATING_FPB_UNRATED = 0xFF, }; -struct xe_xex2_game_ratings_t { - xe_xex2_rating_esrb_value esrb; - xe_xex2_rating_pegi_value pegi; - xe_xex2_rating_pegi_fi_value pegifi; - xe_xex2_rating_pegi_pt_value pegipt; - xe_xex2_rating_bbfc_value bbfc; - xe_xex2_rating_cero_value cero; - xe_xex2_rating_usk_value usk; - xe_xex2_rating_oflc_au_value oflcau; - xe_xex2_rating_oflc_nz_value oflcnz; - xe_xex2_rating_kmrb_value kmrb; - xe_xex2_rating_brazil_value brazil; - xe_xex2_rating_fpb_value fpb; +enum xex2_header_keys : uint32_t { + XEX_HEADER_RESOURCE_INFO = 0x000002FF, + XEX_HEADER_FILE_FORMAT_INFO = 0x000003FF, + XEX_HEADER_DELTA_PATCH_DESCRIPTOR = 0x000005FF, + XEX_HEADER_BASE_REFERENCE = 0x00000405, + XEX_HEADER_BOUNDING_PATH = 0x000080FF, + XEX_HEADER_DEVICE_ID = 0x00008105, + XEX_HEADER_ORIGINAL_BASE_ADDRESS = 0x00010001, + XEX_HEADER_ENTRY_POINT = 0x00010100, + XEX_HEADER_IMAGE_BASE_ADDRESS = 0x00010201, + XEX_HEADER_IMPORT_LIBRARIES = 0x000103FF, + XEX_HEADER_CHECKSUM_TIMESTAMP = 0x00018002, + XEX_HEADER_ENABLED_FOR_CALLCAP = 0x00018102, + XEX_HEADER_ENABLED_FOR_FASTCAP = 0x00018200, + XEX_HEADER_ORIGINAL_PE_NAME = 0x000183FF, + XEX_HEADER_STATIC_LIBRARIES = 0x000200FF, + XEX_HEADER_TLS_INFO = 0x00020104, + XEX_HEADER_DEFAULT_STACK_SIZE = 0x00020200, + XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE = 0x00020301, + XEX_HEADER_DEFAULT_HEAP_SIZE = 0x00020401, + XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS = 0x00028002, + XEX_HEADER_SYSTEM_FLAGS = 0x00030000, + XEX_HEADER_EXECUTION_INFO = 0x00040006, + XEX_HEADER_TITLE_WORKSPACE_SIZE = 0x00040201, + XEX_HEADER_GAME_RATINGS = 0x00040310, + XEX_HEADER_LAN_KEY = 0x00040404, + XEX_HEADER_XBOX360_LOGO = 0x000405FF, + XEX_HEADER_MULTIDISC_MEDIA_IDS = 0x000406FF, + XEX_HEADER_ALTERNATE_TITLE_IDS = 0x000407FF, + XEX_HEADER_ADDITIONAL_TITLE_MEMORY = 0x00040801, + XEX_HEADER_EXPORTS_BY_NAME = 0x00E10402, }; -union xe_xex2_version_t { - uint32_t value; - struct { - uint32_t major : 4; - uint32_t minor : 4; - uint32_t build : 16; - uint32_t qfe : 8; - }; -}; - -struct xe_xex2_opt_header_t { - uint32_t key; - uint32_t length; - union { - uint32_t value; - uint32_t offset; - }; -}; - -struct xe_xex2_resource_info_t { - char name[9]; - uint32_t address; - uint32_t size; -}; - -struct xe_xex2_execution_info_t { - uint32_t media_id; - xe_xex2_version_t version; - xe_xex2_version_t base_version; - uint32_t title_id; - uint8_t platform; - uint8_t executable_table; - uint8_t disc_number; - uint8_t disc_count; - uint32_t savegame_id; -}; - -struct xe_xex2_tls_info_t { - uint32_t slot_count; - uint32_t raw_data_address; - uint32_t data_size; - uint32_t raw_data_size; -}; - -struct xe_xex2_import_library_t { - char name[32]; - uint8_t digest[20]; - uint32_t import_id; - xe_xex2_version_t version; - xe_xex2_version_t min_version; - size_t record_count; - uint32_t* records; -}; - -enum xe_xex2_approval_type : uint32_t { - XEX_APPROVAL_UNAPPROVED = 0, - XEX_APPROVAL_POSSIBLE = 1, - XEX_APPROVAL_APPROVED = 2, - XEX_APPROVAL_EXPIRED = 3, -}; - -struct xe_xex2_static_library_t { - char name[9]; // 8 + 1 for \0 - uint16_t major; - uint16_t minor; - uint16_t build; - uint16_t qfe; - xe_xex2_approval_type approval; -}; - -enum xe_xex2_encryption_type : uint32_t { +enum xex2_encryption_type : uint16_t { XEX_ENCRYPTION_NONE = 0, XEX_ENCRYPTION_NORMAL = 1, }; -enum xe_xex2_compression_type : uint32_t { +enum xex2_compression_type : uint16_t { XEX_COMPRESSION_NONE = 0, XEX_COMPRESSION_BASIC = 1, XEX_COMPRESSION_NORMAL = 2, XEX_COMPRESSION_DELTA = 3, }; -struct xe_xex2_file_basic_compression_block_t { - uint32_t data_size; - uint32_t zero_size; +enum xex2_approval_type : uint32_t { + XEX_APPROVAL_UNAPPROVED = 0, + XEX_APPROVAL_POSSIBLE = 1, + XEX_APPROVAL_APPROVED = 2, + XEX_APPROVAL_EXPIRED = 3, }; -struct xe_xex2_file_basic_compression_info_t { - uint32_t block_count; - xe_xex2_file_basic_compression_block_t* blocks; +struct xex2_game_ratings_t { + xe::be esrb; + xe::be pegi; + xe::be pegifi; + xe::be pegipt; + xe::be bbfc; + xe::be cero; + xe::be usk; + xe::be oflcau; + xe::be oflcnz; + xe::be kmrb; + xe::be brazil; + xe::be fpb; }; -struct xe_xex2_file_normal_compression_info_t { - uint32_t window_size; - uint32_t window_bits; - uint32_t block_size; +struct xex2_file_basic_compression_block { + xe::be data_size; + xe::be zero_size; +}; + +struct xex2_file_basic_compression_info { + xex2_file_basic_compression_block blocks[1]; +}; + +struct xex2_compressed_block_info { + xe::be block_size; uint8_t block_hash[20]; }; -struct xe_xex2_file_format_info_t { - xe_xex2_encryption_type encryption_type; - xe_xex2_compression_type compression_type; +struct xex2_file_normal_compression_info { + xe::be window_size; + xex2_compressed_block_info first_block; +}; + +struct xex2_opt_file_format_info { + xe::be info_size; + xe::be encryption_type; + xe::be compression_type; union { - xe_xex2_file_basic_compression_info_t basic; - xe_xex2_file_normal_compression_info_t normal; + xex2_file_basic_compression_info basic; + xex2_file_normal_compression_info normal; } compression_info; }; -enum xe_xex2_image_flags : uint32_t { - XEX_IMAGE_MANUFACTURING_UTILITY = 0x00000002, - XEX_IMAGE_MANUFACTURING_SUPPORT_TOOLS = 0x00000004, - XEX_IMAGE_XGD2_MEDIA_ONLY = 0x00000008, - XEX_IMAGE_CARDEA_KEY = 0x00000100, - XEX_IMAGE_XEIKA_KEY = 0x00000200, - XEX_IMAGE_USERMODE_TITLE = 0x00000400, - XEX_IMAGE_USERMODE_SYSTEM = 0x00000800, - XEX_IMAGE_ORANGE0 = 0x00001000, - XEX_IMAGE_ORANGE1 = 0x00002000, - XEX_IMAGE_ORANGE2 = 0x00004000, - XEX_IMAGE_IPTV_SIGNUP_APPLICATION = 0x00010000, - XEX_IMAGE_IPTV_TITLE_APPLICATION = 0x00020000, - XEX_IMAGE_KEYVAULT_PRIVILEGES_REQUIRED = 0x04000000, - XEX_IMAGE_ONLINE_ACTIVATION_REQUIRED = 0x08000000, - XEX_IMAGE_PAGE_SIZE_4KB = 0x10000000, // else 64KB - XEX_IMAGE_REGION_FREE = 0x20000000, - XEX_IMAGE_REVOCATION_CHECK_OPTIONAL = 0x40000000, - XEX_IMAGE_REVOCATION_CHECK_REQUIRED = 0x80000000, -}; - -enum xe_xex2_media_flags : uint32_t { - XEX_MEDIA_HARDDISK = 0x00000001, - XEX_MEDIA_DVD_X2 = 0x00000002, - XEX_MEDIA_DVD_CD = 0x00000004, - XEX_MEDIA_DVD_5 = 0x00000008, - XEX_MEDIA_DVD_9 = 0x00000010, - XEX_MEDIA_SYSTEM_FLASH = 0x00000020, - XEX_MEDIA_MEMORY_UNIT = 0x00000080, - XEX_MEDIA_USB_MASS_STORAGE_DEVICE = 0x00000100, - XEX_MEDIA_NETWORK = 0x00000200, - XEX_MEDIA_DIRECT_FROM_MEMORY = 0x00000400, - XEX_MEDIA_RAM_DRIVE = 0x00000800, - XEX_MEDIA_SVOD = 0x00001000, - XEX_MEDIA_INSECURE_PACKAGE = 0x01000000, - XEX_MEDIA_SAVEGAME_PACKAGE = 0x02000000, - XEX_MEDIA_LOCALLY_SIGNED_PACKAGE = 0x04000000, - XEX_MEDIA_LIVE_SIGNED_PACKAGE = 0x08000000, - XEX_MEDIA_XBOX_PACKAGE = 0x10000000, -}; - -enum xe_xex2_region_flags : uint32_t { - XEX_REGION_NTSCU = 0x000000FF, - XEX_REGION_NTSCJ = 0x0000FF00, - XEX_REGION_NTSCJ_JAPAN = 0x00000100, - XEX_REGION_NTSCJ_CHINA = 0x00000200, - XEX_REGION_PAL = 0x00FF0000, - XEX_REGION_PAL_AU_NZ = 0x00010000, - XEX_REGION_OTHER = 0xFF000000, - XEX_REGION_ALL = 0xFFFFFFFF, -}; - -struct xe_xex2_loader_info_t { - uint32_t header_size; - uint32_t image_size; - uint8_t rsa_signature[256]; - uint32_t unklength; - xe_xex2_image_flags image_flags; - uint32_t load_address; - uint8_t section_digest[20]; - uint32_t import_table_count; - uint8_t import_table_digest[20]; - uint8_t media_id[16]; - uint8_t file_key[16]; - uint32_t export_table; // address of the export table - uint8_t header_digest[20]; - xe_xex2_region_flags game_regions; - xe_xex2_media_flags media_flags; -}; - -enum xe_xex2_section_type { - XEX_SECTION_CODE = 1, - XEX_SECTION_DATA = 2, - XEX_SECTION_READONLY_DATA = 3, -}; - -struct xe_xex2_section_t { - uint32_t page_size; - union { - struct { - xe_xex2_section_type type : 4; - uint32_t page_count : 28; // # of pages - }; - uint32_t value; // To make uint8_t swapping easier - } info; - uint8_t digest[20]; -}; - -struct xe_xex2_header_t { - uint32_t xex2; - xe_xex2_module_flags module_flags; - uint32_t exe_offset; - uint32_t unknown0; - uint32_t certificate_offset; - - xe_xex2_system_flags system_flags; - xe_xex2_execution_info_t execution_info; - xe_xex2_game_ratings_t game_ratings; - xe_xex2_tls_info_t tls_info; - size_t import_library_count; - xe_xex2_import_library_t import_libraries[32]; - uint32_t pe_export_table_offset; // PE Export Directory - size_t static_library_count; - xe_xex2_static_library_t static_libraries[32]; - xe_xex2_file_format_info_t file_format_info; - xe_xex2_loader_info_t loader_info; - uint8_t session_key[16]; - - uint32_t exe_address; - uint32_t exe_entry_point; - uint32_t exe_stack_size; - uint32_t exe_heap_size; - - size_t header_count; - xe_xex2_opt_header_t headers[64]; - - size_t resource_info_count; - xe_xex2_resource_info_t* resource_infos; - size_t section_count; - xe_xex2_section_t* sections; -}; - -namespace xe { union xex2_version { xe::be value; struct { @@ -536,8 +449,9 @@ struct xex2_opt_delta_patch_descriptor { xe::be delta_image_source_offset; // 0x40 xe::be delta_image_source_size; // 0x44 xe::be delta_image_target_offset; // 0x48 + char delta_header_patch_data[1]; }; -static_assert_size(xex2_opt_delta_patch_descriptor, 0x4C); +// static_assert_size(xex2_opt_delta_patch_descriptor, 0x4D); struct xex2_opt_execution_info { xe::be media_id; // 0x0 @@ -580,12 +494,12 @@ struct xex2_opt_header { }; struct xex2_header { - xe::be magic; // 0x0 'XEX2' - xe::be module_flags; // 0x4 - xe::be header_size; // 0x8 - xe::be reserved; // 0xC - xe::be security_offset; // 0x10 - xe::be header_count; // 0x14 + xe::be magic; // 0x0 'XEX2' + xe::be module_flags; // 0x4 + xe::be header_size; // 0x8 + xe::be reserved; // 0xC + xe::be security_offset; // 0x10 + xe::be header_count; // 0x14 xex2_opt_header headers[1]; // 0x18 }; diff --git a/src/xenia/kernel/xam/xam_info.cc b/src/xenia/kernel/xam/xam_info.cc index 2c14e4763..b4afb14ba 100644 --- a/src/xenia/kernel/xam/xam_info.cc +++ b/src/xenia/kernel/xam/xam_info.cc @@ -11,7 +11,6 @@ #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" -#include "xenia/kernel/util/xex2.h" #include "xenia/kernel/xam/xam_module.h" #include "xenia/kernel/xam/xam_private.h" #include "xenia/kernel/xenumerator.h" diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc index 4e6360e09..6718b130b 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_error.cc @@ -15,7 +15,6 @@ #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" -#include "xenia/kernel/util/xex2.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_error.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/kernel/xthread.h" diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc index d647e2f8c..1a6031422 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc @@ -12,7 +12,6 @@ #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" -#include "xenia/kernel/util/xex2.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/xbox.h" diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc index c659fa216..7ac9cd05c 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc @@ -19,7 +19,6 @@ #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" -#include "xenia/kernel/util/xex2.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h" #include "xenia/kernel/xevent.h" @@ -285,7 +284,7 @@ pointer_result_t RtlImageXexHeaderField(pointer_t xex_header, uint32_t field = field_dword; // VS acts weird going from dword_t -> enum UserModule::GetOptHeader(kernel_memory()->virtual_membase(), xex_header, - xe_xex2_header_keys(field), &field_value); + xex2_header_keys(field), &field_value); return field_value; } diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc index d5ef2d661..a26e62c20 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_strings.cc @@ -15,7 +15,6 @@ #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" -#include "xenia/kernel/util/xex2.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/kernel/xthread.h" #include "xenia/xbox.h" From 265903fe6678d8c020c19e29b45e93fe04c9b8e5 Mon Sep 17 00:00:00 2001 From: emoose Date: Sat, 20 Oct 2018 04:36:21 +0100 Subject: [PATCH 2/5] [CPU] Add XEXP support to XexModule, if XEXP is in same folder as XEX This was a headache to work out, big thanks to the lack of documentation on .xexp files... a ton of guesswork was involved here but luckily it turned out well. I did have to make some pretty major changes to the way XEX files are loaded though. Previously it'd just load everything in one go: XEX headers -> decrypt/decompress data -> load imports/symbols -> set loader data table entries, etc... Now it's changed to something like this: - Load base XEX headers + decrypted/decompressed image data, return X_STATUS_PENDING - In the LoadFromFile call used to load the XEX, search for XEXP patch file (only .xexp in same folder atm) - If patch exists: load XEXP, decrypt headers/data, apply patch to base XEX, dispose of XEXP - Finish XEX load via LoadXexContinue() (handles imports/symbols/loader data...) This saves us from needing to reset the imports/function/symbol stuff after patching (since all the XEX code will be a lot different), but I'm not really sure if I went about it the best way. --- src/xenia/cpu/xex_module.cc | 622 +++++++++++++++++++++--------- src/xenia/cpu/xex_module.h | 68 ++-- src/xenia/kernel/user_module.cc | 105 +++-- src/xenia/kernel/user_module.h | 2 + src/xenia/kernel/util/xex2_info.h | 15 +- 5 files changed, 589 insertions(+), 223 deletions(-) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 2c27628c8..ccc6b81f5 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -29,68 +29,12 @@ #include "third_party/mspack/mspack.h" #include "third_party/pe/pe_image.h" -namespace xe { -namespace cpu { - -using xe::kernel::KernelState; - -void UndefinedImport(ppc::PPCContext* ppc_context, KernelState* kernel_state) { - XELOGE("call to undefined import"); -} - -XexModule::XexModule(Processor* processor, KernelState* kernel_state) - : Module(processor), processor_(processor), kernel_state_(kernel_state) {} - -XexModule::~XexModule() {} - -bool XexModule::GetOptHeader(const xex2_header* header, xex2_header_keys key, - void** out_ptr) { - assert_not_null(header); - assert_not_null(out_ptr); - - for (uint32_t i = 0; i < header->header_count; i++) { - const xex2_opt_header& opt_header = header->headers[i]; - if (opt_header.key == key) { - // Match! - switch (key & 0xFF) { - case 0x00: { - // We just return the value of the optional header. - // Assume that the output pointer points to a uint32_t. - *reinterpret_cast(out_ptr) = - static_cast(opt_header.value); - } break; - case 0x01: { - // Pointer to the value on the optional header. - *out_ptr = const_cast( - reinterpret_cast(&opt_header.value)); - } break; - default: { - // Pointer to the header. - *out_ptr = - reinterpret_cast(uintptr_t(header) + opt_header.offset); - } break; - } - - return true; - } - } - - return false; -} - -bool XexModule::GetOptHeader(xex2_header_keys key, void** out_ptr) const { - return XexModule::GetOptHeader(xex_header(), key, out_ptr); -} - -const PESection* XexModule::GetPESection(const char* name) { - for (std::vector::iterator it = pe_sections_.begin(); - it != pe_sections_.end(); ++it) { - if (!strcmp(it->name, name)) { - return &(*it); - } - } - return nullptr; -} +static const uint8_t xe_xex2_retail_key[16] = { + 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, + 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91}; +static const uint8_t xe_xex2_devkit_key[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; typedef struct mspack_memory_file_t { struct mspack_system sys; @@ -156,10 +100,104 @@ struct mspack_system* mspack_memory_sys_create() { } void mspack_memory_sys_destroy(struct mspack_system* sys) { free(sys); } -void XexModule::DecryptBuffer(const uint8_t* session_key, - const uint8_t* input_buffer, - const size_t input_size, uint8_t* output_buffer, - const size_t output_size) { +int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest, + size_t dest_len, int window_size, void* window_data, + size_t window_data_len) { + uint32_t window_bits = 0; + uint32_t temp_sz = window_size; + for (size_t m = 0; m < 32; m++, window_bits++) { + temp_sz >>= 1; + if (temp_sz == 0x00000000) { + break; + } + } + + int result_code = 1; + + mspack_system* sys = mspack_memory_sys_create(); + mspack_memory_file* lzxsrc = + mspack_memory_open(sys, (void*)lzx_data, lzx_len); + mspack_memory_file* lzxdst = mspack_memory_open(sys, dest, dest_len); + lzxd_stream* lzxd = + lzxd_init(sys, (struct mspack_file*)lzxsrc, (struct mspack_file*)lzxdst, + window_bits, 0, 0x8000, (off_t)dest_len); + + if (lzxd) { + if (window_data) { + // zero the window and then copy window_data to the end of it + memset(lzxd->window, 0, window_data_len); + memcpy(lzxd->window + (window_size - window_data_len), window_data, + window_data_len); + } + + result_code = lzxd_decompress(lzxd, (off_t)dest_len); + + lzxd_free(lzxd); + lzxd = NULL; + } + if (lzxsrc) { + mspack_memory_close(lzxsrc); + lzxsrc = NULL; + } + if (lzxdst) { + mspack_memory_close(lzxdst); + lzxdst = NULL; + } + if (sys) { + mspack_memory_sys_destroy(sys); + sys = NULL; + } + + return result_code; +} + +int lzxdelta_apply_patch(xe::xex2_delta_patch* patch, size_t patch_len, + void* dest) { + void* patch_end = (char*)patch + patch_len; + auto* cur_patch = patch; + + while (patch_end > cur_patch) { + int patch_sz = -4; // 0 byte patches need us to remove 4 byte from next + // patch addr because of patch_data field + if (cur_patch->compressed_len == 0 && cur_patch->uncompressed_len == 0 && + cur_patch->new_addr == 0 && cur_patch->old_addr == 0) + break; + switch (cur_patch->compressed_len) { + case 0: // fill with 0 + memset((char*)dest + cur_patch->new_addr, 0, + cur_patch->uncompressed_len); + break; + case 1: // copy from old -> new + memcpy((char*)dest + cur_patch->new_addr, + (char*)dest + cur_patch->old_addr, cur_patch->uncompressed_len); + break; + default: // delta patch + patch_sz = + cur_patch->compressed_len - 4; // -4 because of patch_data field + + int result = lzx_decompress( + cur_patch->patch_data, cur_patch->compressed_len, + (char*)dest + cur_patch->new_addr, cur_patch->uncompressed_len, + 0x8000, (char*)dest + cur_patch->old_addr, + cur_patch->uncompressed_len); + + if (result) { + return result; + } + break; + } + + cur_patch++; + cur_patch = (xe::xex2_delta_patch*)((char*)cur_patch + + patch_sz); // TODO: make this less ugly + } + + return 0; +} + +void aes_decrypt_buffer(const uint8_t* session_key, const uint8_t* input_buffer, + const size_t input_size, uint8_t* output_buffer, + const size_t output_size) { uint32_t rk[4 * (MAXNR + 1)]; uint8_t ivec[16] = {0}; int32_t Nr = rijndaelKeySetupDec(rk, session_key, 128); @@ -177,6 +215,71 @@ void XexModule::DecryptBuffer(const uint8_t* session_key, } } +namespace xe { +namespace cpu { + +using xe::kernel::KernelState; + +XexModule::XexModule(Processor* processor, KernelState* kernel_state) + : Module(processor), processor_(processor), kernel_state_(kernel_state) {} + +XexModule::~XexModule() {} + +bool XexModule::GetOptHeader(const xex2_header* header, xex2_header_keys key, + void** out_ptr) { + assert_not_null(header); + assert_not_null(out_ptr); + + for (uint32_t i = 0; i < header->header_count; i++) { + const xex2_opt_header& opt_header = header->headers[i]; + if (opt_header.key == key) { + // Match! + switch (key & 0xFF) { + case 0x00: { + // We just return the value of the optional header. + // Assume that the output pointer points to a uint32_t. + *reinterpret_cast(out_ptr) = + static_cast(opt_header.value); + } break; + case 0x01: { + // Pointer to the value on the optional header. + *out_ptr = const_cast( + reinterpret_cast(&opt_header.value)); + } break; + default: { + // Pointer to the header. + *out_ptr = + reinterpret_cast(uintptr_t(header) + opt_header.offset); + } break; + } + + return true; + } + } + + return false; +} + +bool XexModule::GetOptHeader(xex2_header_keys key, void** out_ptr) const { + return XexModule::GetOptHeader(xex_header(), key, out_ptr); +} + +const xex2_security_info* XexModule::GetSecurityInfo( + const xex2_header* header) { + return reinterpret_cast(uintptr_t(header) + + header->security_offset); +} + +const PESection* XexModule::GetPESection(const char* name) { + for (std::vector::iterator it = pe_sections_.begin(); + it != pe_sections_.end(); ++it) { + if (!strcmp(it->name, name)) { + return &(*it); + } + } + return nullptr; +} + uint32_t XexModule::GetProcAddress(uint16_t ordinal) const { // First: Check the xex2 export table. if (xex_security_info()->export_table) { @@ -216,14 +319,14 @@ uint32_t XexModule::GetProcAddress(uint16_t ordinal) const { } uint32_t XexModule::GetProcAddress(const char* name) const { + assert_not_zero(base_address_); + xex2_opt_data_directory* pe_export_directory = 0; if (!GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) { // No exports by name. return 0; } - assert_not_zero(base_address_); - auto e = memory()->TranslateVirtual( base_address_ + pe_export_directory->offset); assert_not_null(e); @@ -254,63 +357,259 @@ uint32_t XexModule::GetProcAddress(const char* name) const { return 0; } -bool XexModule::ApplyPatch(XexModule* module) { - auto header = reinterpret_cast(module->xex_header()); - if (!(header->module_flags & - (XEX_MODULE_MODULE_PATCH | XEX_MODULE_PATCH_DELTA | - XEX_MODULE_PATCH_FULL))) { +int XexModule::ApplyPatch(XexModule* module) { + if (!is_patch()) { // This isn't a XEX2 patch. - return false; + return 1; } // Grab the delta descriptor and get to work. xex2_opt_delta_patch_descriptor* patch_header = nullptr; - GetOptHeader(header, XEX_HEADER_DELTA_PATCH_DESCRIPTOR, + GetOptHeader(XEX_HEADER_DELTA_PATCH_DESCRIPTOR, reinterpret_cast(&patch_header)); assert_not_null(patch_header); - // TODO(benvanik): patching code! + // Compare hash inside delta descriptor to our loaded base XEX + uint8_t digest[0x14]; + sha1::SHA1 s; + s.processBytes(module->xex_security_info()->rsa_signature, 0x100); + s.finalize(digest); - return true; + if (memcmp(digest, patch_header->digest_source, 0x14) != 0) { + XELOGW( + "XEX patch signature hash doesn't match base XEX signature hash, patch " + "will likely fail!"); + } + + uint32_t size = module->xex_header()->header_size; + if (patch_header->delta_headers_source_offset > size) { + XELOGE("XEX header patch source is outside base XEX header area"); + return 2; + } + + uint32_t header_size_available = + size - patch_header->delta_headers_source_offset; + if (patch_header->delta_headers_source_size > header_size_available) { + XELOGE("XEX header patch source is too large"); + return 3; + } + + if (patch_header->delta_headers_target_offset > + patch_header->size_of_target_headers) { + XELOGE("XEX header patch target is outside base XEX header area"); + return 4; + } + + uint32_t delta_target_size = patch_header->size_of_target_headers - + patch_header->delta_headers_target_offset; + if (patch_header->delta_headers_source_size > delta_target_size) { + return 5; // ? unsure what the point of this test is, kernel checks for it + // though + } + + // TODO: actually use delta_*_offset / delta_*_size etc + assert_zero(patch_header->delta_headers_source_offset); + assert_zero(patch_header->delta_headers_target_offset); + + // Patch base XEX's header + module->xex_header_mem_.resize(patch_header->size_of_target_headers); + + uint32_t original_image_size = module->image_size(); + uint32_t headerpatch_size = patch_header->info.compressed_len; + + auto dest_header = module->xex_header(); + int result_code = lzxdelta_apply_patch( + &patch_header->info, headerpatch_size + 0xC, (void*)dest_header); + if (result_code) { + XELOGE("XEX header patch application failed, error code %d", result_code); + return result_code; + } + + // Check if we need to alloc new memory for the patched xex + uint32_t new_image_size = module->image_size(); + if (new_image_size > original_image_size) { + uint32_t size_delta = new_image_size - original_image_size; + uint32_t addr_new_mem = module->base_address_ + original_image_size; + + bool alloc_result = + memory() + ->LookupHeap(addr_new_mem) + ->AllocFixed( + addr_new_mem, size_delta, 4096, + xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit, + xe::kMemoryProtectRead | xe::kMemoryProtectWrite); + + if (!alloc_result) { + XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", addr_new_mem, + size_delta); + assert_always(); + return 6; + } + } + + uint8_t orig_session_key[0x10]; + memcpy(orig_session_key, module->session_key_, 0x10); + + // Header patch updated the base XEX key, need to redecrypt it + aes_decrypt_buffer( + module->is_dev_kit_ ? xe_xex2_devkit_key : xe_xex2_retail_key, + reinterpret_cast(module->xex_security_info()->aes_key), + 16, module->session_key_, 16); + + // Decrypt the patch XEX's key using base XEX key + aes_decrypt_buffer( + module->session_key_, + reinterpret_cast(xex_security_info()->aes_key), 16, + session_key_, 16); + + // Test delta key against our decrypted keys + // (kernel doesn't seem to check this, but it's the one use for the + // image_key_source field I can think of...) + uint8_t test_delta_key[0x10]; + aes_decrypt_buffer(module->session_key_, patch_header->image_key_source, 0x10, + test_delta_key, 0x10); + + if (memcmp(test_delta_key, orig_session_key, 0x10) != 0) { + XELOGE("XEX patch image key doesn't match original XEX!"); + return 7; + } + + // Decrypt (if needed). + bool free_input = false; + const uint8_t* exe_buffer = xexp_data_mem_.data(); + const size_t exe_length = xexp_data_mem_.size(); + + const uint8_t* input_buffer = exe_buffer; + + switch (opt_file_format_info()->encryption_type) { + case XEX_ENCRYPTION_NONE: + // No-op. + break; + case XEX_ENCRYPTION_NORMAL: + // TODO: a way to do without a copy/alloc? + free_input = true; + input_buffer = (const uint8_t*)calloc(1, exe_length); + aes_decrypt_buffer(session_key_, exe_buffer, exe_length, + (uint8_t*)input_buffer, exe_length); + break; + default: + assert_always(); + return 8; + } + + // Now loop through each block and apply the delta patches inside + + const xex2_compressed_block_info* cur_block = + &opt_file_format_info()->compression_info.normal.first_block; + + const uint8_t* p = input_buffer; + uint8_t* base_exe = memory()->TranslateVirtual(module->base_address_); + + while (cur_block->block_size) { + const auto* next_block = (const xex2_compressed_block_info*)p; + + // Compare block hash, if no match we probably used wrong decrypt key + s.reset(); + s.processBytes(p, cur_block->block_size); + s.finalize(digest); + + if (memcmp(digest, cur_block->block_hash, 0x14) != 0) { + result_code = 9; + XELOGE("XEX patch block hash doesn't match hash inside block info!"); + break; + } + + // skip block info + p += 20; + p += 4; + + uint32_t block_data_size = cur_block->block_size - 20 - 4; + + result_code = + lzxdelta_apply_patch((xex2_delta_patch*)p, block_data_size, base_exe); + if (result_code) { + break; + } + + p += block_data_size; + cur_block = next_block; + } + + if (!result_code) { + // byteswap versions because of bitfields... + xex2_version source_ver, target_ver; + source_ver.value = + xe::byte_swap(patch_header->source_version.value); + + target_ver.value = + xe::byte_swap(patch_header->target_version.value); + + XELOGI( + "XEX patch applied successfully: base version: %d.%d.%d.%d, new " + "version: %d.%d.%d.%d", + source_ver.major, source_ver.minor, source_ver.build, source_ver.qfe, + target_ver.major, target_ver.minor, target_ver.build, target_ver.qfe); + } else { + XELOGE("XEX patch application failed, error code %d", result_code); + } + + if (free_input) { + free((void*)input_buffer); + } + return result_code; } -void XexModule::DecryptSessionKey(bool useDevkit) { - static const uint8_t xe_xex2_retail_key[16] = { - 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, - 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91}; - static const uint8_t xe_xex2_devkit_key[16] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - const uint8_t* xexkey = useDevkit ? xe_xex2_devkit_key : xe_xex2_retail_key; - - // Decrypt the header key. - uint32_t rk[4 * (MAXNR + 1)]; - int32_t Nr = rijndaelKeySetupDec(rk, xexkey, 128); - rijndaelDecrypt(rk, Nr, - reinterpret_cast(xex_security_info()->aes_key), - session_key_); -} - -int XexModule::ReadImage(const void* xex_addr, size_t xex_length) { +int XexModule::ReadImage(const void* xex_addr, size_t xex_length, + bool use_dev_key) { if (!opt_file_format_info()) { return 1; } - auto* ff = opt_file_format_info(); + is_dev_kit_ = use_dev_key; + if (is_patch()) { + // Make a copy of patch data for other XEX's to use with ApplyPatch() + const uint32_t data_len = + static_cast(xex_length - xex_header()->header_size); + xexp_data_mem_.resize(data_len); + std::memcpy(xexp_data_mem_.data(), + (uint8_t*)xex_addr + xex_header()->header_size, data_len); + return 0; + } + + memory()->LookupHeap(base_address_)->Reset(); + + aes_decrypt_buffer( + use_dev_key ? xe_xex2_devkit_key : xe_xex2_retail_key, + reinterpret_cast(xex_security_info()->aes_key), 16, + session_key_, 16); + + int result_code = 0; switch (opt_file_format_info()->compression_type) { case XEX_COMPRESSION_NONE: - return ReadImageUncompressed(xex_addr, xex_length); + result_code = ReadImageUncompressed(xex_addr, xex_length); + break; case XEX_COMPRESSION_BASIC: - return ReadImageBasicCompressed(xex_addr, xex_length); + result_code = ReadImageBasicCompressed(xex_addr, xex_length); + break; case XEX_COMPRESSION_NORMAL: - case XEX_COMPRESSION_DELTA: - return ReadImageCompressed(xex_addr, xex_length); + result_code = ReadImageCompressed(xex_addr, xex_length); + break; default: assert_always(); - return 1; + return 2; } + + if (result_code) { + return result_code; + } + + if (is_patch() || is_valid_executable()) { + return 0; + } + + // Not a patch and image doesn't have proper PE header, return 3 + return 3; } int XexModule::ReadImageUncompressed(const void* xex_addr, size_t xex_length) { @@ -344,7 +643,8 @@ int XexModule::ReadImageUncompressed(const void* xex_addr, size_t xex_length) { memcpy(buffer, p, exe_length); return 0; case XEX_ENCRYPTION_NORMAL: - DecryptBuffer(session_key_, p, exe_length, buffer, uncompressed_size); + aes_decrypt_buffer(session_key_, p, exe_length, buffer, + uncompressed_size); return 0; default: assert_always(); @@ -461,13 +761,7 @@ int XexModule::ReadImageCompressed(const void* xex_addr, size_t xex_length) { uint8_t* compress_buffer = NULL; const uint8_t* p = NULL; uint8_t* d = NULL; - uint8_t* deblock_buffer = NULL; - // size_t block_size = 0; uint32_t uncompressed_size = 0; - struct mspack_system* sys = NULL; - mspack_memory_file* lzxsrc = NULL; - mspack_memory_file* lzxdst = NULL; - struct lzxd_stream* lzxd = NULL; sha1::SHA1 s; // Decrypt (if needed). @@ -483,8 +777,8 @@ int XexModule::ReadImageCompressed(const void* xex_addr, size_t xex_length) { // TODO: a way to do without a copy/alloc? free_input = true; input_buffer = (const uint8_t*)calloc(1, exe_length); - DecryptBuffer(session_key_, exe_buffer, exe_length, - (uint8_t*)input_buffer, exe_length); + aes_decrypt_buffer(session_key_, exe_buffer, exe_length, + (uint8_t*)input_buffer, exe_length); break; default: assert_always(); @@ -501,8 +795,6 @@ int XexModule::ReadImageCompressed(const void* xex_addr, size_t xex_length) { d = compress_buffer; // De-block. - deblock_buffer = (uint8_t*)calloc(1, input_size); - int result_code = 0; uint8_t block_calced_digest[0x14]; @@ -519,8 +811,9 @@ int XexModule::ReadImageCompressed(const void* xex_addr, size_t xex_length) { break; } + // skip block info p += 4; - p += 20; // skip 20b hash + p += 20; while (true) { const size_t chunk_size = (p[0] << 8) | p[1]; @@ -552,29 +845,12 @@ int XexModule::ReadImageCompressed(const void* xex_addr, size_t xex_length) { if (alloc_result) { uint8_t* buffer = memory()->TranslateVirtual(base_address_); - - // Reset buffer if this isn't a patch std::memset(buffer, 0, uncompressed_size); - // Setup decompressor and decompress. - uint32_t window_size = compression_info->normal.window_size; - uint32_t window_bits = 0; - for (size_t m = 0; m < 32; m++, window_bits++) { - window_size >>= 1; - if (window_size == 0x00000000) { - break; - } - } - - sys = mspack_memory_sys_create(); - lzxsrc = - mspack_memory_open(sys, (void*)compress_buffer, d - compress_buffer); - lzxdst = mspack_memory_open(sys, buffer, uncompressed_size); - lzxd = lzxd_init(sys, (struct mspack_file*)lzxsrc, - (struct mspack_file*)lzxdst, window_bits, 0, 32768, - (off_t)xex_security_info()->image_size); - result_code = - lzxd_decompress(lzxd, (off_t)xex_security_info()->image_size); + // Decompress into XEX base + result_code = lzx_decompress( + compress_buffer, d - compress_buffer, buffer, uncompressed_size, + compression_info->normal.window_size, nullptr, 0); } else { XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", base_address_, uncompressed_size); @@ -582,28 +858,9 @@ int XexModule::ReadImageCompressed(const void* xex_addr, size_t xex_length) { } } - if (lzxd) { - lzxd_free(lzxd); - lzxd = NULL; - } - if (lzxsrc) { - mspack_memory_close(lzxsrc); - lzxsrc = NULL; - } - if (lzxdst) { - mspack_memory_close(lzxdst); - lzxdst = NULL; - } - if (sys) { - mspack_memory_sys_destroy(sys); - sys = NULL; - } if (compress_buffer) { free((void*)compress_buffer); } - if (deblock_buffer) { - free((void*)deblock_buffer); - } if (free_input) { free((void*)input_buffer); } @@ -694,7 +951,6 @@ int XexModule::ReadPEHeaders() { section.size = sechdr->Misc.VirtualSize; section.flags = sechdr->Characteristics; pe_sections_.push_back(section); - // pe_sections_.push_back(section); } // DumpTLSDirectory(pImageBase, pNTHeader, (PIMAGE_TLS_DIRECTORY32)0); @@ -721,7 +977,7 @@ bool XexModule::Load(const std::string& name, const std::string& path, // Try setting our base_address based on XEX_HEADER_IMAGE_BASE_ADDRESS, fall // back to xex_security_info otherwise - base_address_ = sec_header->load_address; + base_address_ = xex_security_info()->load_address; xe::be* base_addr_opt = nullptr; if (GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &base_addr_opt)) base_address_ = *base_addr_opt; @@ -734,24 +990,37 @@ bool XexModule::Load(const std::string& name, const std::string& path, // Load in the XEX basefile // We'll try using both XEX2 keys to see if any give a valid PE - while (true) { - memory()->LookupHeap(base_address_)->Reset(); + int result_code = ReadImage(xex_addr, xex_length, false); + if (result_code) { + XELOGW("XEX load failed with code %d, trying with devkit encryption key...", + result_code); - DecryptSessionKey(is_dev_kit_); - - if (!ReadImage(xex_addr, xex_length) && !ReadPEHeaders()) { - break; - } - - is_dev_kit_ = !is_dev_kit_; - - // is_dev_kit starts as false, then flips to true if load failed, if it's - // back to false again this must be invalid - if (!is_dev_kit_) { + result_code = ReadImage(xex_addr, xex_length, true); + if (result_code) { + XELOGE("XEX load failed with code %d, tried both encryption keys", + result_code); return false; } + } - XELOGW("XEX load failed, trying with devkit encryption key..."); + // Note: caller will have to call LoadContinue once it's determined whether a + // patch file exists or not! + return true; +} + +bool XexModule::LoadContinue() { + // Second part of image load + // Split from Load() so that we can patch the XEX before loading this data + assert_false(finished_load_); + if (finished_load_) { + return true; + } + + finished_load_ = true; + + if (ReadPEHeaders()) { + XELOGE("Failed to load XEX PE headers!"); + return false; } // Scan and find the low/high addresses. @@ -762,6 +1031,7 @@ bool XexModule::Load(const std::string& name, const std::string& path, low_address_ = UINT_MAX; high_address_ = 0; + auto sec_header = xex_security_info(); for (uint32_t i = 0, page = 0; i < sec_header->page_descriptor_count; i++) { // Byteswap the bitfield manually. xex2_page_descriptor desc; @@ -863,10 +1133,13 @@ bool XexModule::Unload() { } loaded_ = false; - // Just deallocate the memory occupied by the exe - assert_not_zero(base_address_); + // If this isn't a patch, just deallocate the memory occupied by the exe + if (!is_patch()) { + assert_not_zero(base_address_); + + memory()->LookupHeap(base_address_)->Release(base_address_); + } - memory()->LookupHeap(base_address_)->Release(base_address_); xex_header_mem_.resize(0); return true; @@ -1233,7 +1506,8 @@ bool XexModule::FindSaveRest() { auto page_size = base_address_ <= 0x90000000 ? 64 * 1024 : 4 * 1024; auto sec_header = xex_security_info(); for (uint32_t i = 0, page = 0; i < sec_header->page_descriptor_count; i++) { - const xex2_page_descriptor* section = &sec_header->page_descriptors[i]; + const xex2_page_descriptor* section = + &xex_security_info()->page_descriptors[i]; const auto start_address = base_address_ + (page * page_size); const auto end_address = start_address + (section->size * page_size); diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index 5dee7dbf3..3bc5bc732 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -52,8 +52,24 @@ class XexModule : public xe::cpu::Module { return reinterpret_cast(xex_header_mem_.data()); } const xex2_security_info* xex_security_info() const { - return reinterpret_cast( - uintptr_t(xex_header()) + xex_header()->security_offset); + return GetSecurityInfo(xex_header()); + } + + uint32_t image_size() const { + assert_not_zero(base_address_); + + // Calculate the new total size of the XEX image from its headers. + auto heap = memory()->LookupHeap(base_address_); + uint32_t total_size = 0; + for (uint32_t i = 0; i < xex_security_info()->page_descriptor_count; i++) { + // Byteswap the bitfield manually. + xex2_page_descriptor desc; + desc.value = + xe::byte_swap(xex_security_info()->page_descriptors[i].value); + + total_size += desc.size * heap->page_size(); + } + return total_size; } const std::vector* import_libraries() const { @@ -73,6 +89,7 @@ class XexModule : public xe::cpu::Module { } const uint32_t base_address() const { return base_address_; } + const bool is_dev_kit() const { return is_dev_kit_; } // Gets an optional header. Returns NULL if not found. // Special case: if key & 0xFF == 0x00, this function will return the value, @@ -95,42 +112,50 @@ class XexModule : public xe::cpu::Module { return GetOptHeader(key, reinterpret_cast(out_ptr)); } + static const xex2_security_info* GetSecurityInfo(const xex2_header* header); + const PESection* GetPESection(const char* name); uint32_t GetProcAddress(uint16_t ordinal) const; uint32_t GetProcAddress(const char* name) const; - bool ApplyPatch(XexModule* module); + int ApplyPatch(XexModule* module); bool Load(const std::string& name, const std::string& path, const void* xex_addr, size_t xex_length); + bool LoadContinue(); bool Unload(); + bool ContainsAddress(uint32_t address) override; + const std::string& name() const override { return name_; } bool is_executable() const override { return (xex_header()->module_flags & XEX_MODULE_TITLE) != 0; } - bool ContainsAddress(uint32_t address) override; + bool is_valid_executable() const { + assert_not_zero(base_address_); + if (!base_address_) { + return false; + } + uint8_t* buffer = memory()->TranslateVirtual(base_address_); + return *(uint32_t*)buffer == 0x905A4D; + } - static void DecryptBuffer(const uint8_t* session_key, - const uint8_t* input_buffer, - const size_t input_size, uint8_t* output_buffer, - const size_t output_size); - - uint8_t* HostData() { - if (base_address_) - return memory()->TranslateVirtual(base_address_); - else - return nullptr; + bool is_patch() const { + assert_not_null(xex_header()); + if (!xex_header()) { + return false; + } + return (xex_header()->module_flags & + (XEX_MODULE_MODULE_PATCH | XEX_MODULE_PATCH_DELTA | + XEX_MODULE_PATCH_FULL)); } protected: std::unique_ptr CreateFunction(uint32_t address) override; private: - void DecryptSessionKey(bool useDevkit = false); - - int ReadImage(const void* xex_addr, size_t xex_length); + int ReadImage(const void* xex_addr, size_t xex_length, bool use_dev_key); int ReadImageUncompressed(const void* xex_addr, size_t xex_length); int ReadImageBasicCompressed(const void* xex_addr, size_t xex_length); int ReadImageCompressed(const void* xex_addr, size_t xex_length); @@ -146,22 +171,21 @@ class XexModule : public xe::cpu::Module { std::string name_; std::string path_; std::vector xex_header_mem_; // Holds the xex header + std::vector xexp_data_mem_; // Holds XEXP patch data - // various optional headers std::vector import_libs_; // pre-loaded import libraries for ease of use - std::vector pe_sections_; uint8_t session_key_[0x10]; + bool is_dev_kit_ = false; - bool loaded_ = false; // Loaded into memory? + bool loaded_ = false; // Loaded into memory? + bool finished_load_ = false; // PE/imports/symbols/etc all loaded? uint32_t base_address_ = 0; uint32_t low_address_ = 0; uint32_t high_address_ = 0; - - bool is_dev_kit_ = false; }; } // namespace cpu diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index f4c42caa2..60fcb71fe 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -95,7 +95,36 @@ X_STATUS UserModule::LoadFromFile(std::string path) { file->Destroy(); } - return result; + // Only XEX returns X_STATUS_PENDING + if (result != X_STATUS_PENDING) { + return result; + } + + // Search for xexp patch file + auto patch_entry = kernel_state()->file_system()->ResolvePath(path_ + "p"); + + if (patch_entry) { + auto patch_path = patch_entry->absolute_path(); + + XELOGI("Loading XEX patch from %s", patch_path.c_str()); + + auto patch_module = object_ref(new UserModule(kernel_state_)); + result = patch_module->LoadFromFile(patch_path); + if (!result) { + result = patch_module->xex_module()->ApplyPatch(xex_module()); + if (result) { + XELOGE("Failed to apply XEX patch, code: %d", result); + } + } else { + XELOGE("Failed to load XEX patch, code: %d", result); + } + + if (result) { + return X_STATUS_UNSUCCESSFUL; + } + } + + return LoadXexContinue(); } X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) { @@ -130,29 +159,13 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) { return X_STATUS_UNSUCCESSFUL; } - // Copy the xex2 header into guest memory. - auto header = this->xex_module()->xex_header(); - auto security_header = this->xex_module()->xex_security_info(); - guest_xex_header_ = memory()->SystemHeapAlloc(header->header_size); + // Only XEX headers + image are loaded right now + // Caller will have to call LoadXexContinue after they've loaded in a patch + // (or after patch isn't found anywhere) + // or if this is an XEXP being loaded return success since there's nothing + // else to load + return this->xex_module()->is_patch() ? X_STATUS_SUCCESS : X_STATUS_PENDING; - uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_); - std::memcpy(xex_header_ptr, header, header->header_size); - - // Cache some commonly used headers... - this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_); - this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, - &stack_size_); - is_dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE); - - // Setup the loader data entry - auto ldr_data = - memory()->TranslateVirtual(hmodule_ptr_); - - ldr_data->dll_base = 0; // GetProcAddress will read this. - ldr_data->xex_header_base = guest_xex_header_; - ldr_data->full_image_size = security_header->image_size; - ldr_data->image_base = this->xex_module()->base_address(); - ldr_data->entry_point = entry_point_; } else if (module_format_ == kModuleFormatElf) { auto elf_module = std::make_unique(processor, kernel_state()); @@ -175,6 +188,52 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) { return X_STATUS_SUCCESS; } +X_STATUS UserModule::LoadXexContinue() { + // LoadXexContinue: finishes loading XEX after a patch has been applied (or + // patch wasn't found) + + if (!this->xex_module()) { + return X_STATUS_UNSUCCESSFUL; + } + + // If guest_xex_header is set we must have already loaded the XEX + if (guest_xex_header_) { + return X_STATUS_SUCCESS; + } + + // Finish XexModule load (PE sections/imports/symbols...) + if (!xex_module()->LoadContinue()) { + return X_STATUS_UNSUCCESSFUL; + } + + // Copy the xex2 header into guest memory. + auto header = this->xex_module()->xex_header(); + auto security_header = this->xex_module()->xex_security_info(); + guest_xex_header_ = memory()->SystemHeapAlloc(header->header_size); + + uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_); + std::memcpy(xex_header_ptr, header, header->header_size); + + // Cache some commonly used headers... + this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_); + this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, &stack_size_); + is_dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE); + + // Setup the loader data entry + auto ldr_data = + memory()->TranslateVirtual(hmodule_ptr_); + + ldr_data->dll_base = 0; // GetProcAddress will read this. + ldr_data->xex_header_base = guest_xex_header_; + ldr_data->full_image_size = security_header->image_size; + ldr_data->image_base = this->xex_module()->base_address(); + ldr_data->entry_point = entry_point_; + + OnLoad(); + + return X_STATUS_SUCCESS; +} + X_STATUS UserModule::Unload() { if (module_format_ == kModuleFormatXex && (!processor_module_ || !xex_module()->loaded())) { diff --git a/src/xenia/kernel/user_module.h b/src/xenia/kernel/user_module.h index bd03853da..bb00255d6 100644 --- a/src/xenia/kernel/user_module.h +++ b/src/xenia/kernel/user_module.h @@ -92,6 +92,8 @@ class UserModule : public XModule { ByteStream* stream, std::string path); private: + X_STATUS LoadXexContinue(); + uint32_t guest_xex_header_ = 0; ModuleFormat module_format_ = kModuleFormatUndefined; diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index 02163fb34..4a7d85d2f 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -436,12 +436,20 @@ struct xex2_opt_resource_info { xex2_resource resources[1]; // 0x4 }; +struct xex2_delta_patch { + xe::be old_addr; + xe::be new_addr; + xe::be uncompressed_len; + xe::be compressed_len; + char patch_data[1]; +}; + struct xex2_opt_delta_patch_descriptor { xe::be size; // 0x0 xex2_version target_version; // 0x4 xex2_version source_version; // 0x8 - char digest_source[0x14]; // 0xC - char image_key_source[0x10]; // 0x20 + uint8_t digest_source[0x14]; // 0xC + uint8_t image_key_source[0x10]; // 0x20 xe::be size_of_target_headers; // 0x30 xe::be delta_headers_source_offset; // 0x34 xe::be delta_headers_source_size; // 0x38 @@ -449,9 +457,8 @@ struct xex2_opt_delta_patch_descriptor { xe::be delta_image_source_offset; // 0x40 xe::be delta_image_source_size; // 0x44 xe::be delta_image_target_offset; // 0x48 - char delta_header_patch_data[1]; + xex2_delta_patch info; // 0x4C }; -// static_assert_size(xex2_opt_delta_patch_descriptor, 0x4D); struct xex2_opt_execution_info { xe::be media_id; // 0x0 From d2fd109af3011742f343ebeb787d53f1a7083b0f Mon Sep 17 00:00:00 2001 From: emoose Date: Sat, 20 Oct 2018 05:08:14 +0100 Subject: [PATCH 3/5] [CPU] Add more asserts for XEXP descriptor (+ log a warning) --- src/xenia/cpu/xex_module.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index ccc6b81f5..f62d255bf 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -410,6 +410,17 @@ int XexModule::ApplyPatch(XexModule* module) { // TODO: actually use delta_*_offset / delta_*_size etc assert_zero(patch_header->delta_headers_source_offset); assert_zero(patch_header->delta_headers_target_offset); + assert_zero(patch_header->delta_image_source_offset); + assert_zero(patch_header->delta_image_target_offset); + + if (patch_header->delta_headers_source_offset || + patch_header->delta_headers_target_offset || + patch_header->delta_image_source_offset || + patch_header->delta_image_target_offset) { + XELOGW( + "XEX patch descriptor has a non-zero delta_*_offset field, patch might " + "not get applied properly!"); + } // Patch base XEX's header module->xex_header_mem_.resize(patch_header->size_of_target_headers); From 6e74ba93d69e53015e195b6fbae3a98be0f5f4b5 Mon Sep 17 00:00:00 2001 From: emoose Date: Mon, 22 Oct 2018 12:26:14 +0100 Subject: [PATCH 4/5] [CPU] Add support for delta patches using *_offset fields --- src/xenia/cpu/xex_module.cc | 129 ++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 35 deletions(-) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index f62d255bf..852d49087 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -101,7 +101,7 @@ struct mspack_system* mspack_memory_sys_create() { void mspack_memory_sys_destroy(struct mspack_system* sys) { free(sys); } int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest, - size_t dest_len, int window_size, void* window_data, + size_t dest_len, uint32_t window_size, void* window_data, size_t window_data_len) { uint32_t window_bits = 0; uint32_t temp_sz = window_size; @@ -152,7 +152,7 @@ int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest, } int lzxdelta_apply_patch(xe::xex2_delta_patch* patch, size_t patch_len, - void* dest) { + uint32_t window_size, void* dest) { void* patch_end = (char*)patch + patch_len; auto* cur_patch = patch; @@ -178,7 +178,7 @@ int lzxdelta_apply_patch(xe::xex2_delta_patch* patch, size_t patch_len, int result = lzx_decompress( cur_patch->patch_data, cur_patch->compressed_len, (char*)dest + cur_patch->new_addr, cur_patch->uncompressed_len, - 0x8000, (char*)dest + cur_patch->old_addr, + window_size, (char*)dest + cur_patch->old_addr, cur_patch->uncompressed_len); if (result) { @@ -369,7 +369,7 @@ int XexModule::ApplyPatch(XexModule* module) { reinterpret_cast(&patch_header)); assert_not_null(patch_header); - // Compare hash inside delta descriptor to our loaded base XEX + // Compare hash inside delta descriptor to base XEX signature uint8_t digest[0x14]; sha1::SHA1 s; s.processBytes(module->xex_security_info()->rsa_signature, 0x100); @@ -407,37 +407,61 @@ int XexModule::ApplyPatch(XexModule* module) { // though } - // TODO: actually use delta_*_offset / delta_*_size etc - assert_zero(patch_header->delta_headers_source_offset); - assert_zero(patch_header->delta_headers_target_offset); - assert_zero(patch_header->delta_image_source_offset); - assert_zero(patch_header->delta_image_target_offset); + // Patch base XEX header + uint32_t original_image_size = module->image_size(); + uint32_t header_target_size = patch_header->delta_headers_target_offset + + patch_header->delta_headers_source_size; - if (patch_header->delta_headers_source_offset || - patch_header->delta_headers_target_offset || - patch_header->delta_image_source_offset || - patch_header->delta_image_target_offset) { - XELOGW( - "XEX patch descriptor has a non-zero delta_*_offset field, patch might " - "not get applied properly!"); + if (!header_target_size) { + header_target_size = + patch_header->size_of_target_headers; // unsure which is more correct.. } - // Patch base XEX's header - module->xex_header_mem_.resize(patch_header->size_of_target_headers); + size_t mem_size = module->xex_header_mem_.size(); - uint32_t original_image_size = module->image_size(); - uint32_t headerpatch_size = patch_header->info.compressed_len; + // Increase xex header buffer length if needed + if (header_target_size > module->xex_header_mem_.size()) { + module->xex_header_mem_.resize(header_target_size); + } + + auto header_ptr = (uint8_t*)module->xex_header(); + + // If headers_source_offset is set, copy [source_offset:source_size] to + // target_offset + if (patch_header->delta_headers_source_offset) { + memcpy(header_ptr + patch_header->delta_headers_target_offset, + header_ptr + patch_header->delta_headers_source_offset, + patch_header->delta_headers_source_size); + } + + // If new size is smaller than original, null out the difference + if (header_target_size < module->xex_header_mem_.size()) { + memset(header_ptr + header_target_size, 0, + module->xex_header_mem_.size() - header_target_size); + } + + auto file_format_header = opt_file_format_info(); + assert_not_null(file_format_header); + + // Apply header patch... + uint32_t headerpatch_size = patch_header->info.compressed_len + 0xC; - auto dest_header = module->xex_header(); int result_code = lzxdelta_apply_patch( - &patch_header->info, headerpatch_size + 0xC, (void*)dest_header); + &patch_header->info, headerpatch_size, + file_format_header->compression_info.normal.window_size, header_ptr); if (result_code) { XELOGE("XEX header patch application failed, error code %d", result_code); return result_code; } - // Check if we need to alloc new memory for the patched xex + // Decrease xex header buffer length if needed (but only after patching) + if (module->xex_header_mem_.size() > header_target_size) { + module->xex_header_mem_.resize(header_target_size); + } + uint32_t new_image_size = module->image_size(); + + // Check if we need to alloc new memory for the patched xex if (new_image_size > original_image_size) { uint32_t size_delta = new_image_size - original_image_size; uint32_t addr_new_mem = module->base_address_ + original_image_size; @@ -487,35 +511,52 @@ int XexModule::ApplyPatch(XexModule* module) { // Decrypt (if needed). bool free_input = false; - const uint8_t* exe_buffer = xexp_data_mem_.data(); - const size_t exe_length = xexp_data_mem_.size(); + const uint8_t* patch_buffer = xexp_data_mem_.data(); + const size_t patch_length = xexp_data_mem_.size(); - const uint8_t* input_buffer = exe_buffer; + const uint8_t* input_buffer = patch_buffer; - switch (opt_file_format_info()->encryption_type) { + switch (file_format_header->encryption_type) { case XEX_ENCRYPTION_NONE: // No-op. break; case XEX_ENCRYPTION_NORMAL: // TODO: a way to do without a copy/alloc? free_input = true; - input_buffer = (const uint8_t*)calloc(1, exe_length); - aes_decrypt_buffer(session_key_, exe_buffer, exe_length, - (uint8_t*)input_buffer, exe_length); + input_buffer = (const uint8_t*)calloc(1, patch_length); + aes_decrypt_buffer(session_key_, patch_buffer, patch_length, + (uint8_t*)input_buffer, patch_length); break; default: assert_always(); return 8; } - // Now loop through each block and apply the delta patches inside - const xex2_compressed_block_info* cur_block = - &opt_file_format_info()->compression_info.normal.first_block; + &file_format_header->compression_info.normal.first_block; const uint8_t* p = input_buffer; uint8_t* base_exe = memory()->TranslateVirtual(module->base_address_); + // If image_source_offset is set, copy [source_offset:source_size] to + // target_offset + if (patch_header->delta_image_source_offset) { + memcpy(base_exe + patch_header->delta_image_target_offset, + base_exe + patch_header->delta_image_source_offset, + patch_header->delta_image_source_size); + } + + // TODO: should we use new_image_size here instead? + uint32_t image_target_size = patch_header->delta_image_target_offset + + patch_header->delta_image_source_size; + + // If new size is smaller than original, null out the difference + if (image_target_size < original_image_size) { + memset(base_exe + image_target_size, 0, + original_image_size - image_target_size); + } + + // Now loop through each block and apply the delta patches inside while (cur_block->block_size) { const auto* next_block = (const xex2_compressed_block_info*)p; @@ -536,8 +577,10 @@ int XexModule::ApplyPatch(XexModule* module) { uint32_t block_data_size = cur_block->block_size - 20 - 4; - result_code = - lzxdelta_apply_patch((xex2_delta_patch*)p, block_data_size, base_exe); + // Apply delta patch + result_code = lzxdelta_apply_patch( + (xex2_delta_patch*)p, block_data_size, + file_format_header->compression_info.normal.window_size, base_exe); if (result_code) { break; } @@ -547,6 +590,22 @@ int XexModule::ApplyPatch(XexModule* module) { } if (!result_code) { + // Decommit unused pages if new image size is smaller than original + if (original_image_size > new_image_size) { + uint32_t size_delta = original_image_size - new_image_size; + uint32_t addr_free_mem = module->base_address_ + new_image_size; + + bool free_result = memory() + ->LookupHeap(addr_free_mem) + ->Decommit(addr_free_mem, size_delta); + + if (!free_result) { + XELOGE("Unable to decommit XEX memory at %.8X-%.8X.", addr_free_mem, + size_delta); + assert_always(); + } + } + // byteswap versions because of bitfields... xex2_version source_ver, target_ver; source_ver.value = From 7443b7e61fadfd16701c65367e4a33d7e62ddffd Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 20 Oct 2018 11:13:42 -0500 Subject: [PATCH 5/5] [CPU] Rename ImportLibrary fields to follow naming conventions --- src/xenia/cpu/xex_module.cc | 22 +++++++------- src/xenia/cpu/xex_module.h | 16 +++++----- src/xenia/kernel/user_module.cc | 52 ++++++++++++++++----------------- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 852d49087..88a61b964 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -1231,10 +1231,10 @@ bool XexModule::SetupLibraryImports(const char* name, } ImportLibrary library_info; - library_info.Name = libbasename; - library_info.ID = library->id; - library_info.Version.value = library->version.value; - library_info.MinVersion.value = library->version_min.value; + library_info.name = libbasename; + library_info.id = library->id; + library_info.version.value = library->version.value; + library_info.min_version.value = library->version_min.value; // Imports are stored as {import descriptor, thunk addr, import desc, ...} // Even thunks have an import descriptor (albeit unused/useless) @@ -1271,9 +1271,9 @@ bool XexModule::SetupLibraryImports(const char* name, // Variable. ImportLibraryFn import_info; - import_info.Ordinal = ordinal; - import_info.ValueAddress = record_addr; - library_info.Imports.push_back(import_info); + import_info.ordinal = ordinal; + import_info.value_address = record_addr; + library_info.imports.push_back(import_info); import_name.AppendFormat("__imp__"); if (kernel_export) { @@ -1314,10 +1314,10 @@ bool XexModule::SetupLibraryImports(const char* name, var_info->set_status(Symbol::Status::kDefined); } else if (record_type == 1) { // Thunk. - assert_true(library_info.Imports.size() > 0); - auto& prev_import = library_info.Imports[library_info.Imports.size() - 1]; - assert_true(prev_import.Ordinal == ordinal); - prev_import.ThunkAddress = record_addr; + assert_true(library_info.imports.size() > 0); + auto& prev_import = library_info.imports[library_info.imports.size() - 1]; + assert_true(prev_import.ordinal == ordinal); + prev_import.thunk_address = record_addr; if (kernel_export) { import_name.AppendFormat("%s", kernel_export->name); diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index 3bc5bc732..d9d1996de 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -31,17 +31,17 @@ class XexModule : public xe::cpu::Module { public: struct ImportLibraryFn { public: - uint32_t Ordinal; - uint32_t ValueAddress; - uint32_t ThunkAddress; + uint32_t ordinal; + uint32_t value_address; + uint32_t thunk_address; }; struct ImportLibrary { public: - std::string Name; - uint32_t ID; - xe_xex2_version_t Version; - xe_xex2_version_t MinVersion; - std::vector Imports; + std::string name; + uint32_t id; + xe_xex2_version_t version; + xe_xex2_version_t min_version; + std::vector imports; }; XexModule(Processor* processor, kernel::KernelState* kernel_state); diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index 60fcb71fe..254863510 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -695,15 +695,15 @@ void UserModule::Dump() { for (std::vector::const_iterator library = import_libs->begin(); library != import_libs->end(); ++library) { - if (library->Imports.size() > 0) { - sb.AppendFormat(" %s - %lld imports\n", library->Name.c_str(), - library->Imports.size()); - sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->Version.major, - library->Version.minor, library->Version.build, - library->Version.qfe); + if (library->imports.size() > 0) { + sb.AppendFormat(" %s - %lld imports\n", library->name.c_str(), + library->imports.size()); + sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->version.major, + library->version.minor, library->version.build, + library->version.qfe); sb.AppendFormat(" Min Version: %d.%d.%d.%d\n", - library->MinVersion.major, library->MinVersion.minor, - library->MinVersion.build, library->MinVersion.qfe); + library->min_version.major, library->min_version.minor, + library->min_version.build, library->min_version.qfe); sb.AppendFormat("\n"); // Counts. @@ -713,11 +713,11 @@ void UserModule::Dump() { int unimpl_count = 0; for (std::vector::const_iterator info = - library->Imports.begin(); - info != library->Imports.end(); ++info) { - if (kernel_state_->IsKernelModule(library->Name.c_str())) { + library->imports.begin(); + info != library->imports.end(); ++info) { + if (kernel_state_->IsKernelModule(library->name.c_str())) { auto kernel_export = export_resolver->GetExportByOrdinal( - library->Name.c_str(), info->Ordinal); + library->name.c_str(), info->ordinal); if (kernel_export) { known_count++; if (kernel_export->is_implemented()) { @@ -730,10 +730,10 @@ void UserModule::Dump() { unimpl_count++; } } else { - auto module = kernel_state_->GetModule(library->Name.c_str()); + auto module = kernel_state_->GetModule(library->name.c_str()); if (module) { uint32_t export_addr = - module->GetProcAddressByOrdinal(info->Ordinal); + module->GetProcAddressByOrdinal(info->ordinal); if (export_addr) { impl_count++; known_count++; @@ -747,8 +747,8 @@ void UserModule::Dump() { } } } - float total_count = static_cast(library->Imports.size()) / 100.0f; - sb.AppendFormat(" Total: %4llu\n", library->Imports.size()); + float total_count = static_cast(library->imports.size()) / 100.0f; + sb.AppendFormat(" Total: %4llu\n", library->imports.size()); sb.AppendFormat(" Known: %3d%% (%d known, %d unknown)\n", static_cast(known_count / total_count), known_count, unknown_count); @@ -759,22 +759,22 @@ void UserModule::Dump() { // Listing. for (std::vector::const_iterator info = - library->Imports.begin(); - info != library->Imports.end(); ++info) { + library->imports.begin(); + info != library->imports.end(); ++info) { const char* name = "UNKNOWN"; bool implemented = false; cpu::Export* kernel_export = nullptr; - if (kernel_state_->IsKernelModule(library->Name.c_str())) { + if (kernel_state_->IsKernelModule(library->name.c_str())) { kernel_export = export_resolver->GetExportByOrdinal( - library->Name.c_str(), info->Ordinal); + library->name.c_str(), info->ordinal); if (kernel_export) { name = kernel_export->name; implemented = kernel_export->is_implemented(); } } else { - auto module = kernel_state_->GetModule(library->Name.c_str()); - if (module && module->GetProcAddressByOrdinal(info->Ordinal)) { + auto module = kernel_state_->GetModule(library->name.c_str()); + if (module && module->GetProcAddressByOrdinal(info->ordinal)) { // TODO(benvanik): name lookup. implemented = true; } @@ -782,12 +782,12 @@ void UserModule::Dump() { if (kernel_export && kernel_export->type == cpu::Export::Type::kVariable) { sb.AppendFormat(" V %.8X %.3X (%3d) %s %s\n", - info->ValueAddress, info->Ordinal, info->Ordinal, + info->value_address, info->ordinal, info->ordinal, implemented ? " " : "!!", name); - } else if (info->ThunkAddress) { + } else if (info->thunk_address) { sb.AppendFormat(" F %.8X %.8X %.3X (%3d) %s %s\n", - info->ValueAddress, info->ThunkAddress, info->Ordinal, - info->Ordinal, implemented ? " " : "!!", name); + info->value_address, info->thunk_address, info->ordinal, + info->ordinal, implemented ? " " : "!!", name); } } }