From 5d79952bddf0786c354ddfcb7174652702c8e8cd Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 15 Mar 2019 13:45:35 -0700 Subject: [PATCH] dmnt-cheat: Add register conditional vm instruction --- stratosphere/dmnt/source/dmnt_cheat_vm.cpp | 158 +++++++++++++++++++++ stratosphere/dmnt/source/dmnt_cheat_vm.hpp | 27 +++- 2 files changed, 184 insertions(+), 1 deletion(-) diff --git a/stratosphere/dmnt/source/dmnt_cheat_vm.cpp b/stratosphere/dmnt/source/dmnt_cheat_vm.cpp index 82c4d95cc..7eb36296d 100644 --- a/stratosphere/dmnt/source/dmnt_cheat_vm.cpp +++ b/stratosphere/dmnt/source/dmnt_cheat_vm.cpp @@ -144,6 +144,37 @@ void DmntCheatVm::LogOpcode(const CheatVmOpcode *opcode) { break; } break; + case CheatVmOpcodeType_BeginRegisterConditionalBlock: + this->LogToDebugFile("Opcode: Begin Register Conditional\n"); + this->LogToDebugFile("Bit Width: %x\n", opcode->begin_reg_cond.bit_width); + this->LogToDebugFile("V Reg Idx: %x\n", opcode->begin_reg_cond.val_reg_index); + switch (opcode->begin_reg_cond.comp_type) { + case CompareRegisterValueType_StaticValue: + this->LogToDebugFile("Comp Type: Static Value\n"); + this->LogToDebugFile("Value: %lx\n", opcode->begin_reg_cond.value.bit64); + break; + case CompareRegisterValueType_MemoryRelAddr: + this->LogToDebugFile("Comp Type: Memory Relative Address\n"); + this->LogToDebugFile("Mem Type: %x\n", opcode->begin_reg_cond.mem_type); + this->LogToDebugFile("Rel Addr: %lx\n", opcode->begin_reg_cond.rel_address); + break; + case CompareRegisterValueType_MemoryOfsReg: + this->LogToDebugFile("Comp Type: Memory Offset Register\n"); + this->LogToDebugFile("Mem Type: %x\n", opcode->begin_reg_cond.mem_type); + this->LogToDebugFile("O Reg Idx: %x\n", opcode->begin_reg_cond.ofs_reg_index); + break; + case CompareRegisterValueType_RegisterRelAddr: + this->LogToDebugFile("Comp Type: Register Relative Address\n"); + this->LogToDebugFile("A Reg Idx: %x\n", opcode->begin_reg_cond.addr_reg_index); + this->LogToDebugFile("Rel Addr: %lx\n", opcode->begin_reg_cond.rel_address); + break; + case CompareRegisterValueType_RegisterOfsReg: + this->LogToDebugFile("Comp Type: Register Offset Register\n"); + this->LogToDebugFile("A Reg Idx: %x\n", opcode->begin_reg_cond.addr_reg_index); + this->LogToDebugFile("O Reg Idx: %x\n", opcode->begin_reg_cond.ofs_reg_index); + break; + } + break; default: this->LogToDebugFile("Unknown opcode: %x\n", opcode->opcode); break; @@ -208,6 +239,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode *out) { switch (opcode.opcode) { case CheatVmOpcodeType_BeginConditionalBlock: case CheatVmOpcodeType_BeginKeypressConditionalBlock: + case CheatVmOpcodeType_BeginRegisterConditionalBlock: opcode.begin_conditional_block = true; break; default: @@ -356,6 +388,52 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode *out) { } } break; + case CheatVmOpcodeType_BeginRegisterConditionalBlock: + { + /* C0TcSX## */ + /* C0TcS0Ma aaaaaaaa */ + /* C0TcS1Mr */ + /* C0TcS2Ra aaaaaaaa */ + /* C0TcS3Rr */ + /* C0TcS400 VVVVVVVV (VVVVVVVV) */ + /* C0 = opcode 0xC0 */ + /* T = bit width */ + /* c = condition type. */ + /* S = source register. */ + /* X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset register, */ + /* 1 = register with relative offset, 2 = register with offset register, 3 = static value. */ + /* M = memory type. */ + /* a = relative address. */ + /* r = offset register. */ + /* V = value */ + opcode.begin_reg_cond.bit_width = (first_dword >> 20) & 0xF; + opcode.begin_reg_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF); + opcode.begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF); + opcode.begin_reg_cond.comp_type = (CompareRegisterValueType)((first_dword >> 8) & 0xF); + + switch (opcode.begin_reg_cond.comp_type) { + case CompareRegisterValueType_StaticValue: + opcode.begin_reg_cond.value = GetNextVmInt(opcode.begin_reg_cond.bit_width); + break; + case CompareRegisterValueType_MemoryRelAddr: + opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF); + opcode.begin_reg_cond.rel_address = (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword())); + break; + case CompareRegisterValueType_MemoryOfsReg: + opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF); + opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF); + break; + case CompareRegisterValueType_RegisterRelAddr: + opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); + opcode.begin_reg_cond.rel_address = (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword())); + break; + case CompareRegisterValueType_RegisterOfsReg: + opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); + opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF); + break; + } + } + break; case CheatVmOpcodeType_ExtendedWidth: default: /* Unrecognized instruction cannot be decoded. */ @@ -754,6 +832,86 @@ void DmntCheatVm::Execute(const CheatProcessMetadata *metadata) { } } break; + case CheatVmOpcodeType_BeginRegisterConditionalBlock: + { + /* Get value from register. */ + u64 src_value = 0; + switch (cur_opcode.begin_reg_cond.bit_width) { + case 1: + src_value = static_cast(this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFul); + break; + case 2: + src_value = static_cast(this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFul); + break; + case 4: + src_value = static_cast(this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFFFFFul); + break; + case 8: + src_value = static_cast(this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFFFFFFFFFFFFFul); + break; + } + + /* Read value from memory. */ + u64 cond_value = 0; + if (cur_opcode.begin_reg_cond.comp_type == CompareRegisterValueType_StaticValue) { + cond_value = GetVmInt(cur_opcode.begin_reg_cond.value, cur_opcode.begin_reg_cond.bit_width); + } else { + u64 cond_address = 0; + switch (cur_opcode.begin_reg_cond.comp_type) { + case CompareRegisterValueType_MemoryRelAddr: + cond_address = GetCheatProcessAddress(metadata, cur_opcode.begin_reg_cond.mem_type, cur_opcode.begin_reg_cond.rel_address); + break; + case CompareRegisterValueType_MemoryOfsReg: + cond_address = GetCheatProcessAddress(metadata, cur_opcode.begin_reg_cond.mem_type, this->registers[cur_opcode.begin_reg_cond.ofs_reg_index]); + break; + case CompareRegisterValueType_RegisterRelAddr: + cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] + cur_opcode.begin_reg_cond.rel_address; + break; + case CompareRegisterValueType_RegisterOfsReg: + cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] + this->registers[cur_opcode.begin_reg_cond.ofs_reg_index]; + break; + default: + break; + } + switch (cur_opcode.begin_reg_cond.bit_width) { + case 1: + case 2: + case 4: + case 8: + DmntCheatManager::ReadCheatProcessMemoryForVm(cond_address, &cond_value, cur_opcode.begin_reg_cond.bit_width); + break; + } + } + + /* Check against condition. */ + bool cond_met = false; + switch (cur_opcode.begin_reg_cond.cond_type) { + case ConditionalComparisonType_GT: + cond_met = src_value > cond_value; + break; + case ConditionalComparisonType_GE: + cond_met = src_value >= cond_value; + break; + case ConditionalComparisonType_LT: + cond_met = src_value < cond_value; + break; + case ConditionalComparisonType_LE: + cond_met = src_value <= cond_value; + break; + case ConditionalComparisonType_EQ: + cond_met = src_value == cond_value; + break; + case ConditionalComparisonType_NE: + cond_met = src_value != cond_value; + break; + } + + /* Skip conditional block if condition not met. */ + if (!cond_met) { + this->SkipConditionalBlock(); + } + } + break; default: /* By default, we do a no-op. */ break; diff --git a/stratosphere/dmnt/source/dmnt_cheat_vm.hpp b/stratosphere/dmnt/source/dmnt_cheat_vm.hpp index 469e36699..a7303afe2 100644 --- a/stratosphere/dmnt/source/dmnt_cheat_vm.hpp +++ b/stratosphere/dmnt/source/dmnt_cheat_vm.hpp @@ -35,10 +35,14 @@ enum CheatVmOpcodeType : u32 { /* These are not implemented by Gateway's VM. */ CheatVmOpcodeType_PerformArithmeticRegister = 9, CheatVmOpcodeType_StoreRegisterToAddress = 10, + CheatVmOpcodeType_Reserved11 = 11, /* This is a meta entry, and not a real opcode. */ - /* This is to facilitate multi-nybble instruction decoding in the future. */ + /* This is to facilitate multi-nybble instruction decoding. */ CheatVmOpcodeType_ExtendedWidth = 12, + + /* Extended width opcodes. */ + CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0, }; enum MemoryAccessType : u32 { @@ -77,6 +81,14 @@ enum StoreRegisterOffsetType : u32 { StoreRegisterOffsetType_Imm = 2, }; +enum CompareRegisterValueType : u32 { + CompareRegisterValueType_MemoryRelAddr = 0, + CompareRegisterValueType_MemoryOfsReg = 1, + CompareRegisterValueType_RegisterRelAddr = 2, + CompareRegisterValueType_RegisterOfsReg = 3, + CompareRegisterValueType_StaticValue = 4, +}; + union VmInt { u8 bit8; u16 bit16; @@ -161,6 +173,18 @@ struct StoreRegisterToAddressOpcode { u64 rel_address; }; +struct BeginRegisterConditionalOpcode { + u32 bit_width; + ConditionalComparisonType cond_type; + u32 val_reg_index; + CompareRegisterValueType comp_type; + MemoryAccessType mem_type; + u32 addr_reg_index; + u32 ofs_reg_index; + u64 rel_address; + VmInt value; +}; + struct CheatVmOpcode { CheatVmOpcodeType opcode; @@ -177,6 +201,7 @@ struct CheatVmOpcode { BeginKeypressConditionalOpcode begin_keypress_cond; PerformArithmeticRegisterOpcode perform_math_reg; StoreRegisterToAddressOpcode str_register; + BeginRegisterConditionalOpcode begin_reg_cond; }; };