[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.
This commit is contained in:
@@ -52,8 +52,24 @@ class XexModule : public xe::cpu::Module {
|
||||
return reinterpret_cast<const xex2_header*>(xex_header_mem_.data());
|
||||
}
|
||||
const xex2_security_info* xex_security_info() const {
|
||||
return reinterpret_cast<const xex2_security_info*>(
|
||||
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<ImportLibrary>* 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<void**>(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<Function> 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<uint8_t> xex_header_mem_; // Holds the xex header
|
||||
std::vector<uint8_t> xexp_data_mem_; // Holds XEXP patch data
|
||||
|
||||
// various optional headers
|
||||
std::vector<ImportLibrary>
|
||||
import_libs_; // pre-loaded import libraries for ease of use
|
||||
|
||||
std::vector<PESection> 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
|
||||
|
||||
Reference in New Issue
Block a user