private void LoadHeaders() { ImageData imageData = xexData; xexData.header = rdr.ReadStruct <XexHeader>(); XexHeader header = xexData.header; switch (header.magic) { case XEX2_MAGIC: case XEX1_MAGIC: break; default: throw new BadImageFormatException("Invalid XEX Magic"); } for (uint i = 0; i < header.header_count; i++) { bool add = true; XexOptionalHeader st_optionalHeader = rdr.ReadStruct <XexOptionalHeader>(); OptionalHeader optionalHeader = new OptionalHeader(st_optionalHeader); switch ((byte)optionalHeader.key) { // just the data case 0x00: case 0x01: optionalHeader.value = optionalHeader.offset; optionalHeader.offset = 0; break; case 0xFF: optionalHeader.length = rdr.ReadAt <UInt32>(optionalHeader.offset, (r) => { return(r.ReadUInt32()); }); optionalHeader.offset += 4; if (optionalHeader.length + optionalHeader.offset > rdr.Bytes.Length) { decompilerEventListener.Warn( new NullCodeLocation(""), $"Optional header {i} (0x{optionalHeader.key:X}) crosses file boundary. Will not be read" ); add = false; } break; default: optionalHeader.length = ((uint)(byte)optionalHeader.key) * 4; if (optionalHeader.length + optionalHeader.offset > rdr.Bytes.Length) { decompilerEventListener.Warn( new NullCodeLocation(""), $"Optional header {i} (0x{optionalHeader.key:X}) crosses file boundary. Will not be read" ); add = false; } break; } if (add) { optional_headers.Add(optionalHeader); } } for (int i = 0; i < optional_headers.Count; i++) { OptionalHeader opt = optional_headers[i]; // go to the header offset if (opt.length > 0 && opt.offset != 0) { rdr.Offset = opt.offset; } // process the optional headers switch (opt.key) { case XEXHeaderKeys.XEX_HEADER_SYSTEM_FLAGS: imageData.system_flags = (XEXSystemFlags)opt.value; break; case XEXHeaderKeys.XEX_HEADER_RESOURCE_INFO: uint count = (opt.length - 4) / 16; xexData.resources = new List <XexResourceInfo>((int)count); for (uint n = 0; n < count; n++) { xexData.resources.Insert(i, rdr.ReadStruct <XexResourceInfo>()); } break; case XEXHeaderKeys.XEX_HEADER_EXECUTION_INFO: imageData.execution_info = rdr.ReadStruct <XexExecutionInfo>(); break; case XEXHeaderKeys.XEX_HEADER_GAME_RATINGS: break; case XEXHeaderKeys.XEX_HEADER_TLS_INFO: imageData.tls_info = rdr.ReadStruct <XexTlsInfo>(); break; case XEXHeaderKeys.XEX_HEADER_IMAGE_BASE_ADDRESS: imageData.exe_address = opt.value; break; case XEXHeaderKeys.XEX_HEADER_ENTRY_POINT: imageData.exe_entry_point = opt.value; break; case XEXHeaderKeys.XEX_HEADER_DEFAULT_STACK_SIZE: imageData.exe_stack_size = opt.value; break; case XEXHeaderKeys.XEX_HEADER_DEFAULT_HEAP_SIZE: imageData.exe_heap_size = opt.value; break; case XEXHeaderKeys.XEX_HEADER_FILE_FORMAT_INFO: XexEncryptionHeader encHeader = rdr.ReadStruct <XexEncryptionHeader>(); imageData.file_format_info.encryption_type = encHeader.encryption_type; imageData.file_format_info.compression_type = encHeader.compression_type; switch (encHeader.compression_type) { case XEXCompressionType.XEX_COMPRESSION_NONE: break; case XEXCompressionType.XEX_COMPRESSION_DELTA: throw new NotImplementedException("XEX: image::Binary is using unsupported delta compression"); case XEXCompressionType.XEX_COMPRESSION_BASIC: uint block_count = (opt.length - 8) / 8; imageData.file_format_info.basic_blocks = new List <XexFileBasicCompressionBlock>((int)block_count); for (int ib = 0; ib < block_count; ib++) { imageData.file_format_info.basic_blocks.Insert(ib, rdr.ReadStruct <XexFileBasicCompressionBlock>()); } break; case XEXCompressionType.XEX_COMPRESSION_NORMAL: imageData.file_format_info.normal = rdr.ReadStruct <XexFileNormalCompressionInfo>(); break; } if (encHeader.encryption_type != XEXEncryptionType.XEX_ENCRYPTION_NONE) { // } break; case XEXHeaderKeys.XEX_HEADER_IMPORT_LIBRARIES: XexImportLibraryBlockHeader blockHeader = rdr.ReadStruct <XexImportLibraryBlockHeader>(); long string_table = rdr.Offset; for (int j = 0; j < blockHeader.count; j++) { string name = rdr.ReadCString(PrimitiveType.Char, Encoding.ASCII).ToString(); imageData.libNames.Add(name); } rdr.Offset = string_table + blockHeader.string_table_size; for (int m = 0; m < blockHeader.count; m++) { XexImportLibaryHeader imp_header = rdr.ReadStruct <XexImportLibaryHeader>(); string name = null; int name_index = (byte)imp_header.name_index; if (name_index < blockHeader.count) { name = imageData.libNames[name_index]; } for (uint ri = 0; ri < imp_header.record_count; ++ri) { UInt32 recordEntry = rdr.ReadUInt32(); xexData.import_records.Add(recordEntry); } } break; } } // load the loader info { rdr.Offset = header.security_offset; switch (header.magic) { case XEX1_MAGIC: Xex1LoaderInfo info1 = rdr.ReadStruct <Xex1LoaderInfo>(); xexData.loader_info.aes_key = info1.aes_key; break; case XEX2_MAGIC: Xex2LoaderInfo info2 = rdr.ReadStruct <Xex2LoaderInfo>(); xexData.loader_info.aes_key = info2.aes_key; break; } } // load the sections { rdr.Offset = header.security_offset + 0x180; UInt32 sectionCount = rdr.ReadUInt32(); xexData.sections = new List <XexSection>((int)sectionCount); for (int si = 0; si < sectionCount; si++) { xexData.sections.Insert(0, rdr.ReadStruct <XexSection>()); } } // decrypt the XEX key { byte[] keyToUse = xe_xex2_devkit_key; if (header.magic != XEX1_MAGIC && xexData.execution_info.title_id != 0) { keyToUse = xe_xex2_retail_key; } Rijndael aes = new RijndaelManaged() { BlockSize = 128, KeySize = 128, Mode = CipherMode.ECB, Key = keyToUse, Padding = PaddingMode.None }; xexData.session_key = aes .CreateDecryptor() .TransformFinalBlock(xexData.loader_info.aes_key, 0, 16); decompilerEventListener.Info( new NullCodeLocation(""), "XEX Session key: " + BitConverter.ToString(xexData.session_key).Replace("-", "") ); } }