Browse Source

Merge pull request #12915 from german77/cheat

dmnt: cheats: Update cheat vm to latest version
liamwhite 2 years ago
parent
commit
74cc8721c7

+ 30 - 16
src/core/memory/cheat_engine.cpp

@@ -9,6 +9,7 @@
 #include "core/core_timing.h"
 #include "core/core_timing.h"
 #include "core/hle/kernel/k_page_table.h"
 #include "core/hle/kernel/k_page_table.h"
 #include "core/hle/kernel/k_process.h"
 #include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_process_page_table.h"
 #include "core/hle/service/hid/hid_server.h"
 #include "core/hle/service/hid/hid_server.h"
 #include "core/hle/service/sm/sm.h"
 #include "core/hle/service/sm/sm.h"
 #include "core/memory.h"
 #include "core/memory.h"
@@ -46,12 +47,23 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta
 
 
 StandardVmCallbacks::~StandardVmCallbacks() = default;
 StandardVmCallbacks::~StandardVmCallbacks() = default;
 
 
-void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
-    system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size);
+void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
+    // Return zero on invalid address
+    if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
+        std::memset(data, 0, size);
+        return;
+    }
+
+    system.ApplicationMemory().ReadBlock(address, data, size);
 }
 }
 
 
-void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
-    system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size);
+void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
+    // Skip invalid memory write address
+    if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
+        return;
+    }
+
+    system.ApplicationMemory().WriteBlock(address, data, size);
 }
 }
 
 
 u64 StandardVmCallbacks::HidKeysDown() {
 u64 StandardVmCallbacks::HidKeysDown() {
@@ -81,21 +93,25 @@ void StandardVmCallbacks::CommandLog(std::string_view data) {
               data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
               data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
 }
 }
 
 
-VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const {
+bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
     if ((in < metadata.main_nso_extents.base ||
     if ((in < metadata.main_nso_extents.base ||
          in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
          in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
         (in < metadata.heap_extents.base ||
         (in < metadata.heap_extents.base ||
-         in >= metadata.heap_extents.base + metadata.heap_extents.size)) {
-        LOG_ERROR(CheatEngine,
+         in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
+        (in < metadata.alias_extents.base ||
+         in >= metadata.heap_extents.base + metadata.alias_extents.size) &&
+        (in < metadata.aslr_extents.base ||
+         in >= metadata.heap_extents.base + metadata.aslr_extents.size)) {
+        LOG_DEBUG(CheatEngine,
                   "Cheat attempting to access memory at invalid address={:016X}, if this "
                   "Cheat attempting to access memory at invalid address={:016X}, if this "
                   "persists, "
                   "persists, "
                   "the cheat may be incorrect. However, this may be normal early in execution if "
                   "the cheat may be incorrect. However, this may be normal early in execution if "
                   "the game has not properly set up yet.",
                   "the game has not properly set up yet.",
                   in);
                   in);
-        return 0; ///< Invalid addresses will hard crash
+        return false; ///< Invalid addresses will hard crash
     }
     }
 
 
-    return in;
+    return true;
 }
 }
 
 
 CheatParser::~CheatParser() = default;
 CheatParser::~CheatParser() = default;
@@ -211,16 +227,14 @@ void CheatEngine::Initialize() {
         .base = GetInteger(page_table.GetHeapRegionStart()),
         .base = GetInteger(page_table.GetHeapRegionStart()),
         .size = page_table.GetHeapRegionSize(),
         .size = page_table.GetHeapRegionSize(),
     };
     };
-
-    metadata.address_space_extents = {
-        .base = GetInteger(page_table.GetAddressSpaceStart()),
-        .size = page_table.GetAddressSpaceSize(),
-    };
-
-    metadata.alias_extents = {
+    metadata.aslr_extents = {
         .base = GetInteger(page_table.GetAliasCodeRegionStart()),
         .base = GetInteger(page_table.GetAliasCodeRegionStart()),
         .size = page_table.GetAliasCodeRegionSize(),
         .size = page_table.GetAliasCodeRegionSize(),
     };
     };
+    metadata.alias_extents = {
+        .base = GetInteger(page_table.GetAliasRegionStart()),
+        .size = page_table.GetAliasRegionSize(),
+    };
 
 
     is_pending_reload.exchange(true);
     is_pending_reload.exchange(true);
 }
 }

+ 4 - 4
src/core/memory/cheat_engine.h

@@ -27,17 +27,17 @@ public:
     StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
     StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
     ~StandardVmCallbacks() override;
     ~StandardVmCallbacks() override;
 
 
-    void MemoryRead(VAddr address, void* data, u64 size) override;
-    void MemoryWrite(VAddr address, const void* data, u64 size) override;
+    void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
+    void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
     u64 HidKeysDown() override;
     u64 HidKeysDown() override;
     void DebugLog(u8 id, u64 value) override;
     void DebugLog(u8 id, u64 value) override;
     void CommandLog(std::string_view data) override;
     void CommandLog(std::string_view data) override;
 
 
 private:
 private:
-    VAddr SanitizeAddress(VAddr address) const;
+    bool IsAddressInRange(VAddr address) const;
 
 
     const CheatProcessMetadata& metadata;
     const CheatProcessMetadata& metadata;
-    System& system;
+    Core::System& system;
 };
 };
 
 
 // Intermediary class that parses a text file or other disk format for storing cheats into a
 // Intermediary class that parses a text file or other disk format for storing cheats into a

+ 1 - 1
src/core/memory/dmnt_cheat_types.h

@@ -18,7 +18,7 @@ struct CheatProcessMetadata {
     MemoryRegionExtents main_nso_extents{};
     MemoryRegionExtents main_nso_extents{};
     MemoryRegionExtents heap_extents{};
     MemoryRegionExtents heap_extents{};
     MemoryRegionExtents alias_extents{};
     MemoryRegionExtents alias_extents{};
-    MemoryRegionExtents address_space_extents{};
+    MemoryRegionExtents aslr_extents{};
     std::array<u8, 0x20> main_nso_build_id{};
     std::array<u8, 0x20> main_nso_build_id{};
 };
 };
 
 

+ 52 - 22
src/core/memory/dmnt_cheat_vm.cpp

@@ -322,8 +322,9 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
     } break;
     } break;
     case CheatVmOpcodeType::EndConditionalBlock: {
     case CheatVmOpcodeType::EndConditionalBlock: {
         // 20000000
         // 20000000
-        // There's actually nothing left to process here!
-        opcode.opcode = EndConditionalOpcode{};
+        opcode.opcode = EndConditionalOpcode{
+            .is_else = ((first_dword >> 24) & 0xf) == 1,
+        };
     } break;
     } break;
     case CheatVmOpcodeType::ControlLoop: {
     case CheatVmOpcodeType::ControlLoop: {
         // 300R0000 VVVVVVVV
         // 300R0000 VVVVVVVV
@@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
             .idx = first_dword & 0xF,
             .idx = first_dword & 0xF,
         };
         };
     } break;
     } break;
+    case CheatVmOpcodeType::PauseProcess: {
+        /* FF0????? */
+        /* FF0 = opcode 0xFF0 */
+        /* Pauses the current process. */
+        opcode.opcode = PauseProcessOpcode{};
+    } break;
+    case CheatVmOpcodeType::ResumeProcess: {
+        /* FF0????? */
+        /* FF0 = opcode 0xFF0 */
+        /* Pauses the current process. */
+        opcode.opcode = ResumeProcessOpcode{};
+    } break;
     case CheatVmOpcodeType::DebugLog: {
     case CheatVmOpcodeType::DebugLog: {
         // FFFTIX##
         // FFFTIX##
         // FFFTI0Ma aaaaaaaa
         // FFFTI0Ma aaaaaaaa
@@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
     return valid;
     return valid;
 }
 }
 
 
-void DmntCheatVm::SkipConditionalBlock() {
+void DmntCheatVm::SkipConditionalBlock(bool is_if) {
     if (condition_depth > 0) {
     if (condition_depth > 0) {
         // We want to continue until we're out of the current block.
         // We want to continue until we're out of the current block.
         const std::size_t desired_depth = condition_depth - 1;
         const std::size_t desired_depth = condition_depth - 1;
@@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() {
             // We also support nesting of conditional blocks, and Gateway does not.
             // We also support nesting of conditional blocks, and Gateway does not.
             if (skip_opcode.begin_conditional_block) {
             if (skip_opcode.begin_conditional_block) {
                 condition_depth++;
                 condition_depth++;
-            } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) {
-                condition_depth--;
+            } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) {
+                if (!end_cond->is_else) {
+                    condition_depth--;
+                } else if (is_if && condition_depth - 1 == desired_depth) {
+                    break;
+                }
             }
             }
         }
         }
     } else {
     } else {
@@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
         return metadata.main_nso_extents.base + rel_address;
         return metadata.main_nso_extents.base + rel_address;
     case MemoryAccessType::Heap:
     case MemoryAccessType::Heap:
         return metadata.heap_extents.base + rel_address;
         return metadata.heap_extents.base + rel_address;
+    case MemoryAccessType::Alias:
+        return metadata.alias_extents.base + rel_address;
+    case MemoryAccessType::Aslr:
+        return metadata.aslr_extents.base + rel_address;
     }
     }
 }
 }
 
 
@@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() {
     registers.fill(0);
     registers.fill(0);
     saved_values.fill(0);
     saved_values.fill(0);
     loop_tops.fill(0);
     loop_tops.fill(0);
-    static_registers.fill(0);
     instruction_ptr = 0;
     instruction_ptr = 0;
     condition_depth = 0;
     condition_depth = 0;
     decode_success = true;
     decode_success = true;
@@ -753,7 +773,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
             case 2:
             case 2:
             case 4:
             case 4:
             case 8:
             case 8:
-                callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width);
+                callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width);
                 break;
                 break;
             }
             }
         } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
         } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
@@ -766,7 +786,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
             case 2:
             case 2:
             case 4:
             case 4:
             case 8:
             case 8:
-                callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width);
+                callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width);
                 break;
                 break;
             }
             }
             // Check against condition.
             // Check against condition.
@@ -794,13 +814,18 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
             }
             }
             // Skip conditional block if condition not met.
             // Skip conditional block if condition not met.
             if (!cond_met) {
             if (!cond_met) {
-                SkipConditionalBlock();
+                SkipConditionalBlock(true);
             }
             }
-        } else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) {
-            // Decrement the condition depth.
-            // We will assume, graciously, that mismatched conditional block ends are a nop.
-            if (condition_depth > 0) {
-                condition_depth--;
+        } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
+            if (end_cond->is_else) {
+                /* Skip to the end of the conditional block. */
+                this->SkipConditionalBlock(false);
+            } else {
+                /* Decrement the condition depth. */
+                /* We will assume, graciously, that mismatched conditional block ends are a nop. */
+                if (condition_depth > 0) {
+                    condition_depth--;
+                }
             }
             }
         } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
         } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
             if (ctrl_loop->start_loop) {
             if (ctrl_loop->start_loop) {
@@ -832,8 +857,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
             case 2:
             case 2:
             case 4:
             case 4:
             case 8:
             case 8:
-                callbacks->MemoryRead(src_address, &registers[ldr_memory->reg_index],
-                                      ldr_memory->bit_width);
+                callbacks->MemoryReadUnsafe(src_address, &registers[ldr_memory->reg_index],
+                                            ldr_memory->bit_width);
                 break;
                 break;
             }
             }
         } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
         } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
@@ -849,7 +874,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
             case 2:
             case 2:
             case 4:
             case 4:
             case 8:
             case 8:
-                callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width);
+                callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width);
                 break;
                 break;
             }
             }
             // Increment register if relevant.
             // Increment register if relevant.
@@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
             // Check for keypress.
             // Check for keypress.
             if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
             if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
                 // Keys not pressed. Skip conditional block.
                 // Keys not pressed. Skip conditional block.
-                SkipConditionalBlock();
+                SkipConditionalBlock(true);
             }
             }
         } else if (auto perform_math_reg =
         } else if (auto perform_math_reg =
                        std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
                        std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
@@ -1007,7 +1032,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
             case 2:
             case 2:
             case 4:
             case 4:
             case 8:
             case 8:
-                callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width);
+                callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width);
                 break;
                 break;
             }
             }
 
 
@@ -1086,7 +1111,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
                 case 2:
                 case 2:
                 case 4:
                 case 4:
                 case 8:
                 case 8:
-                    callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width);
+                    callbacks->MemoryReadUnsafe(cond_address, &cond_value,
+                                                begin_reg_cond->bit_width);
                     break;
                     break;
                 }
                 }
             }
             }
@@ -1116,7 +1142,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
 
 
             // Skip conditional block if condition not met.
             // Skip conditional block if condition not met.
             if (!cond_met) {
             if (!cond_met) {
-                SkipConditionalBlock();
+                SkipConditionalBlock(true);
             }
             }
         } else if (auto save_restore_reg =
         } else if (auto save_restore_reg =
                        std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
                        std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
@@ -1178,6 +1204,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
                 // Store a register to a static register.
                 // Store a register to a static register.
                 static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
                 static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
             }
             }
+        } else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
+            // TODO: Pause cheat process
+        } else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
+            // TODO: Resume cheat process
         } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
         } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
             // Read value from memory.
             // Read value from memory.
             u64 log_value = 0;
             u64 log_value = 0;
@@ -1224,7 +1254,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
                 case 2:
                 case 2:
                 case 4:
                 case 4:
                 case 8:
                 case 8:
-                    callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width);
+                    callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width);
                     break;
                     break;
                 }
                 }
             }
             }

+ 16 - 6
src/core/memory/dmnt_cheat_vm.h

@@ -42,12 +42,16 @@ enum class CheatVmOpcodeType : u32 {
     DoubleExtendedWidth = 0xF0,
     DoubleExtendedWidth = 0xF0,
 
 
     // Double-extended width opcodes.
     // Double-extended width opcodes.
+    PauseProcess = 0xFF0,
+    ResumeProcess = 0xFF1,
     DebugLog = 0xFFF,
     DebugLog = 0xFFF,
 };
 };
 
 
 enum class MemoryAccessType : u32 {
 enum class MemoryAccessType : u32 {
     MainNso = 0,
     MainNso = 0,
     Heap = 1,
     Heap = 1,
+    Alias = 2,
+    Aslr = 3,
 };
 };
 
 
 enum class ConditionalComparisonType : u32 {
 enum class ConditionalComparisonType : u32 {
@@ -131,7 +135,9 @@ struct BeginConditionalOpcode {
     VmInt value{};
     VmInt value{};
 };
 };
 
 
-struct EndConditionalOpcode {};
+struct EndConditionalOpcode {
+    bool is_else;
+};
 
 
 struct ControlLoopOpcode {
 struct ControlLoopOpcode {
     bool start_loop{};
     bool start_loop{};
@@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode {
     u32 idx{};
     u32 idx{};
 };
 };
 
 
+struct PauseProcessOpcode {};
+
+struct ResumeProcessOpcode {};
+
 struct DebugLogOpcode {
 struct DebugLogOpcode {
     u32 bit_width{};
     u32 bit_width{};
     u32 log_id{};
     u32 log_id{};
@@ -244,8 +254,8 @@ struct CheatVmOpcode {
                  PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
                  PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
                  PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
                  PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
                  BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
                  BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
-                 SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode,
-                 UnrecognizedInstruction>
+                 SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
+                 ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
         opcode{};
         opcode{};
 };
 };
 
 
@@ -256,8 +266,8 @@ public:
     public:
     public:
         virtual ~Callbacks();
         virtual ~Callbacks();
 
 
-        virtual void MemoryRead(VAddr address, void* data, u64 size) = 0;
-        virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0;
+        virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0;
+        virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0;
 
 
         virtual u64 HidKeysDown() = 0;
         virtual u64 HidKeysDown() = 0;
 
 
@@ -296,7 +306,7 @@ private:
     std::array<std::size_t, NumRegisters> loop_tops{};
     std::array<std::size_t, NumRegisters> loop_tops{};
 
 
     bool DecodeNextOpcode(CheatVmOpcode& out);
     bool DecodeNextOpcode(CheatVmOpcode& out);
-    void SkipConditionalBlock();
+    void SkipConditionalBlock(bool is_if);
     void ResetState();
     void ResetState();
 
 
     // For implementing the DebugLog opcode.
     // For implementing the DebugLog opcode.