Dmnt: Add break/continue commands, add static register api. (#899)

* dmnt: implement break/continue, static reg commands

* dmnt: revise per WerWolv's feedback.
This commit is contained in:
SciresM 2020-04-24 17:00:43 -07:00 committed by GitHub
parent b7c4dae899
commit be07035954
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 242 additions and 8 deletions

View file

@ -66,6 +66,14 @@ namespace ams::dmnt::cheat {
return dmnt::cheat::impl::QueryCheatProcessMemory(mapping.GetPointer(), address);
}
Result CheatService::BreakCheatProcess() {
return dmnt::cheat::impl::BreakCheatProcess();
}
Result CheatService::ContinueCheatProcess() {
return dmnt::cheat::impl::ContinueCheatProcess();
}
/* ========================================================================================= */
/* =================================== Cheat Commands ==================================== */
/* ========================================================================================= */
@ -95,6 +103,18 @@ namespace ams::dmnt::cheat {
return dmnt::cheat::impl::RemoveCheat(cheat_id);
}
Result CheatService::ReadStaticRegister(sf::Out<u64> out, u8 which) {
return dmnt::cheat::impl::ReadStaticRegister(out.GetPointer(), which);
}
Result CheatService::WriteStaticRegister(u8 which, u64 value) {
return dmnt::cheat::impl::WriteStaticRegister(which, value);
}
Result CheatService::ResetStaticRegisters() {
return dmnt::cheat::impl::ResetStaticRegisters();
}
/* ========================================================================================= */
/* =================================== Address Commands ================================== */
/* ========================================================================================= */

View file

@ -33,14 +33,19 @@ namespace ams::dmnt::cheat {
ReadCheatProcessMemory = 65102,
WriteCheatProcessMemory = 65103,
QueryCheatProcessMemory = 65104,
BreakCheatProcess = 65105,
ContinueCheatProcess = 65106,
/* Interact with Cheats */
GetCheatCount = 65200,
GetCheats = 65201,
GetCheatById = 65202,
ToggleCheat = 65203,
AddCheat = 65204,
RemoveCheat = 65205,
GetCheatCount = 65200,
GetCheats = 65201,
GetCheatById = 65202,
ToggleCheat = 65203,
AddCheat = 65204,
RemoveCheat = 65205,
ReadStaticRegister = 65206,
WriteStaticRegister = 65207,
ResetStaticRegisters = 65208,
/* Interact with Frozen Addresses */
GetFrozenAddressCount = 65300,
@ -60,6 +65,8 @@ namespace ams::dmnt::cheat {
Result ReadCheatProcessMemory(const sf::OutBuffer &buffer, u64 address, u64 out_size);
Result WriteCheatProcessMemory(const sf::InBuffer &buffer, u64 address, u64 in_size);
Result QueryCheatProcessMemory(sf::Out<MemoryInfo> mapping, u64 address);
Result BreakCheatProcess();
Result ContinueCheatProcess();
Result GetCheatCount(sf::Out<u64> out_count);
Result GetCheats(const sf::OutArray<CheatEntry> &cheats, sf::Out<u64> out_count, u64 offset);
@ -67,6 +74,9 @@ namespace ams::dmnt::cheat {
Result ToggleCheat(u32 cheat_id);
Result AddCheat(const CheatDefinition &cheat, sf::Out<u32> out_cheat_id, bool enabled);
Result RemoveCheat(u32 cheat_id);
Result ReadStaticRegister(sf::Out<u64> out, u8 which);
Result WriteStaticRegister(u8 which, u64 value);
Result ResetStaticRegisters();
Result GetFrozenAddressCount(sf::Out<u64> out_count);
Result GetFrozenAddresses(const sf::OutArray<FrozenAddressEntry> &addresses, sf::Out<u64> out_count, u64 offset);
@ -86,6 +96,8 @@ namespace ams::dmnt::cheat {
MAKE_SERVICE_COMMAND_META(ReadCheatProcessMemory),
MAKE_SERVICE_COMMAND_META(WriteCheatProcessMemory),
MAKE_SERVICE_COMMAND_META(QueryCheatProcessMemory),
MAKE_SERVICE_COMMAND_META(BreakCheatProcess),
MAKE_SERVICE_COMMAND_META(ContinueCheatProcess),
MAKE_SERVICE_COMMAND_META(GetCheatCount),
MAKE_SERVICE_COMMAND_META(GetCheats),
@ -93,6 +105,9 @@ namespace ams::dmnt::cheat {
MAKE_SERVICE_COMMAND_META(ToggleCheat),
MAKE_SERVICE_COMMAND_META(AddCheat),
MAKE_SERVICE_COMMAND_META(RemoveCheat),
MAKE_SERVICE_COMMAND_META(ReadStaticRegister),
MAKE_SERVICE_COMMAND_META(WriteStaticRegister),
MAKE_SERVICE_COMMAND_META(ResetStaticRegisters),
MAKE_SERVICE_COMMAND_META(GetFrozenAddressCount),
MAKE_SERVICE_COMMAND_META(GetFrozenAddresses),

View file

@ -31,6 +31,7 @@ namespace ams::dmnt::cheat::impl {
static constexpr size_t ThreadStackSize = 0x4000;
private:
os::Mutex cheat_lock;
os::Event unsafe_break_event;
os::Event debug_events_event; /* Autoclear. */
os::ThreadType detect_thread, debug_events_thread;
os::SystemEvent cheat_process_event;
@ -38,6 +39,7 @@ namespace ams::dmnt::cheat::impl {
CheatProcessMetadata cheat_process_metadata = {};
os::ThreadType vm_thread;
bool broken_unsafe = false;
bool needs_reload_vm = false;
CheatVirtualMachine cheat_vm;
@ -85,6 +87,8 @@ namespace ams::dmnt::cheat::impl {
for (size_t i = 0; i < MaxCheatCount; i++) {
this->ResetCheatEntry(i);
}
this->cheat_vm.ResetStaticRegisters();
}
CheatEntry *GetCheatEntryById(size_t i) {
@ -119,6 +123,10 @@ namespace ams::dmnt::cheat::impl {
void CloseActiveCheatProcess() {
if (this->cheat_process_debug_handle != svc::InvalidHandle) {
/* We don't need to do any unsafe brekaing. */
this->broken_unsafe = false;
this->unsafe_break_event.Signal();
/* Knock out the debug events thread. */
os::CancelThreadSynchronization(std::addressof(this->debug_events_thread));
@ -182,7 +190,7 @@ namespace ams::dmnt::cheat::impl {
}
public:
CheatProcessManager() : cheat_lock(false), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
CheatProcessManager() : cheat_lock(false), unsafe_break_event(os::EventClearMode_ManualClear), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
/* Learn whether we should enable cheats by default. */
{
u8 en = 0;
@ -257,6 +265,19 @@ namespace ams::dmnt::cheat::impl {
return ResultSuccess();
}
Result BreakCheatProcessUnsafe() {
this->broken_unsafe = true;
this->unsafe_break_event.Clear();
return svcBreakDebugProcess(this->GetCheatProcessHandle());
}
Result ContinueCheatProcessUnsafe() {
this->broken_unsafe = false;
this->unsafe_break_event.Signal();
dmnt::cheat::impl::ContinueCheatProcess(this->GetCheatProcessHandle());
return ResultSuccess();
}
Result GetCheatProcessMappingCount(u64 *out_count) {
std::scoped_lock lk(this->cheat_lock);
@ -335,6 +356,22 @@ namespace ams::dmnt::cheat::impl {
return svcQueryDebugProcessMemory(mapping, &tmp, this->GetCheatProcessHandle(), address);
}
Result BreakCheatProcess() {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
return this->BreakCheatProcessUnsafe();
}
Result ContinueCheatProcess() {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
return this->ContinueCheatProcessUnsafe();
}
Result GetCheatCount(u64 *out_count) {
std::scoped_lock lk(this->cheat_lock);
@ -436,6 +473,35 @@ namespace ams::dmnt::cheat::impl {
return ResultSuccess();
}
Result ReadStaticRegister(u64 *out, size_t which) {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
*out = this->cheat_vm.GetStaticRegister(which);
return ResultSuccess();
}
Result WriteStaticRegister(size_t which, u64 value) {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
this->cheat_vm.SetStaticRegister(which, value);
return ResultSuccess();
}
Result ResetStaticRegisters() {
std::scoped_lock lk(this->cheat_lock);
R_TRY(this->EnsureCheatProcess());
this->cheat_vm.ResetStaticRegisters();
return ResultSuccess();
}
Result GetFrozenAddressCount(u64 *out_count) {
std::scoped_lock lk(this->cheat_lock);
@ -533,7 +599,20 @@ namespace ams::dmnt::cheat::impl {
this_ptr->debug_events_event.Wait();
while (true) {
while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), std::numeric_limits<u64>::max()))) {
std::scoped_lock lk(this_ptr->cheat_lock);
this_ptr->cheat_lock.Lock();
ON_SCOPE_EXIT { this_ptr->cheat_lock.Unlock(); };
/* If we did an unsafe break, wait until we're not broken. */
if (this_ptr->broken_unsafe) {
this_ptr->cheat_lock.Unlock();
this_ptr->unsafe_break_event.Wait();
this_ptr->cheat_lock.Lock();
if (this_ptr->GetCheatProcessHandle() != svc::InvalidHandle) {
continue;
} else {
break;
}
}
/* Handle any pending debug events. */
if (this_ptr->HasActiveCheatProcess()) {
@ -680,6 +759,10 @@ namespace ams::dmnt::cheat::impl {
/* Cancel process guard. */
proc_guard.Cancel();
/* Reset broken state. */
this->broken_unsafe = false;
this->unsafe_break_event.Signal();
/* If new process, start the process. */
if (on_process_launch) {
this->StartProcess(this->cheat_process_metadata.process_id);
@ -1021,6 +1104,14 @@ namespace ams::dmnt::cheat::impl {
return GetReference(g_cheat_process_manager).WriteCheatProcessMemoryUnsafe(process_addr, data, size);
}
Result BreakCheatProcessUnsafe() {
return GetReference(g_cheat_process_manager).BreakCheatProcessUnsafe();
}
Result ContinueCheatProcessUnsafe() {
return GetReference(g_cheat_process_manager).ContinueCheatProcessUnsafe();
}
Result GetCheatProcessMappingCount(u64 *out_count) {
return GetReference(g_cheat_process_manager).GetCheatProcessMappingCount(out_count);
}
@ -1041,6 +1132,14 @@ namespace ams::dmnt::cheat::impl {
return GetReference(g_cheat_process_manager).QueryCheatProcessMemory(mapping, address);
}
Result BreakCheatProcess() {
return GetReference(g_cheat_process_manager).BreakCheatProcess();
}
Result ContinueCheatProcess() {
return GetReference(g_cheat_process_manager).ContinueCheatProcess();
}
Result GetCheatCount(u64 *out_count) {
return GetReference(g_cheat_process_manager).GetCheatCount(out_count);
}
@ -1065,6 +1164,18 @@ namespace ams::dmnt::cheat::impl {
return GetReference(g_cheat_process_manager).RemoveCheat(cheat_id);
}
Result ReadStaticRegister(u64 *out, size_t which) {
return GetReference(g_cheat_process_manager).ReadStaticRegister(out, which);
}
Result WriteStaticRegister(size_t which, u64 value) {
return GetReference(g_cheat_process_manager).WriteStaticRegister(which, value);
}
Result ResetStaticRegisters() {
return GetReference(g_cheat_process_manager).ResetStaticRegisters();
}
Result GetFrozenAddressCount(u64 *out_count) {
return GetReference(g_cheat_process_manager).GetFrozenAddressCount(out_count);
}

View file

@ -28,11 +28,16 @@ namespace ams::dmnt::cheat::impl {
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size);
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size);
Result BreakCheatProcessUnsafe();
Result ContinueCheatProcessUnsafe();
Result GetCheatProcessMappingCount(u64 *out_count);
Result GetCheatProcessMappings(MemoryInfo *mappings, size_t max_count, u64 *out_count, u64 offset);
Result ReadCheatProcessMemory(u64 proc_addr, void *out_data, size_t size);
Result WriteCheatProcessMemory(u64 proc_addr, const void *data, size_t size);
Result QueryCheatProcessMemory(MemoryInfo *mapping, u64 address);
Result BreakCheatProcess();
Result ContinueCheatProcess();
Result GetCheatCount(u64 *out_count);
Result GetCheats(CheatEntry *cheats, size_t max_count, u64 *out_count, u64 offset);
@ -40,6 +45,9 @@ namespace ams::dmnt::cheat::impl {
Result ToggleCheat(u32 cheat_id);
Result AddCheat(u32 *out_id, const CheatDefinition &def, bool enabled);
Result RemoveCheat(u32 cheat_id);
Result ReadStaticRegister(u64 *out, size_t which);
Result WriteStaticRegister(size_t which, u64 value);
Result ResetStaticRegisters();
Result GetFrozenAddressCount(u64 *out_count);
Result GetFrozenAddresses(FrozenAddressEntry *frz_addrs, size_t max_count, u64 *out_count, u64 offset);

View file

@ -237,6 +237,22 @@ namespace ams::dmnt::cheat::impl {
this->LogToDebugFile("Act[%02x]: %d\n", i, opcode->save_restore_regmask.should_operate[i]);
}
break;
case CheatVmOpcodeType_ReadWriteStaticRegister:
this->LogToDebugFile("Opcode: Read/Write Static Register\n");
if (opcode->rw_static_reg.static_idx < NumReadableStaticRegisters) {
this->LogToDebugFile("Op Type: ReadStaticRegister\n");
} else {
this->LogToDebugFile("Op Type: WriteStaticRegister\n");
}
this->LogToDebugFile("Reg Idx: %x\n", opcode->rw_static_reg.idx);
this->LogToDebugFile("Stc Idx: %x\n", opcode->rw_static_reg.static_idx);
break;
case CheatVmOpcodeType_BreakProcess:
this->LogToDebugFile("Opcode: Break Cheat Process\n");
break;
case CheatVmOpcodeType_ContinueProcess:
this->LogToDebugFile("Opcode: Continue Cheat Process\n");
break;
case CheatVmOpcodeType_DebugLog:
this->LogToDebugFile("Opcode: Debug Log\n");
this->LogToDebugFile("Bit Width: %x\n", opcode->debug_log.bit_width);
@ -570,6 +586,30 @@ namespace ams::dmnt::cheat::impl {
}
}
break;
case CheatVmOpcodeType_ReadWriteStaticRegister:
{
/* C3000XXx */
/* C3 = opcode 0xC3. */
/* XX = static register index. */
/* x = register index. */
opcode.rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF);
opcode.rw_static_reg.idx = (first_dword & 0xF);
}
break;
case CheatVmOpcodeType_BreakProcess:
{
/* FF0????? */
/* FF0 = opcode 0xFF0 */
/* Breaks the current process. */
}
break;
case CheatVmOpcodeType_ContinueProcess:
{
/* FF1????? */
/* FF1 = opcode 0xFF1 */
/* Continues the current process. */
}
break;
case CheatVmOpcodeType_DebugLog:
{
/* FFFTIX## */
@ -1174,6 +1214,21 @@ namespace ams::dmnt::cheat::impl {
}
}
break;
case CheatVmOpcodeType_ReadWriteStaticRegister:
if (cur_opcode.rw_static_reg.static_idx < NumReadableStaticRegisters) {
/* Load a register with a static register. */
this->registers[cur_opcode.rw_static_reg.idx] = this->static_registers[cur_opcode.rw_static_reg.static_idx];
} else {
/* Store a register to a static register. */
this->static_registers[cur_opcode.rw_static_reg.static_idx] = this->registers[cur_opcode.rw_static_reg.idx];
}
break;
case CheatVmOpcodeType_BreakProcess:
dmnt::cheat::impl::BreakCheatProcessUnsafe();
break;
case CheatVmOpcodeType_ContinueProcess:
dmnt::cheat::impl::ContinueCheatProcessUnsafe();
break;
case CheatVmOpcodeType_DebugLog:
{
/* Read value from memory. */

View file

@ -42,12 +42,15 @@ namespace ams::dmnt::cheat::impl {
CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
CheatVmOpcodeType_ReadWriteStaticRegister = 0xC3,
/* This is a meta entry, and not a real opcode. */
/* This is to facilitate multi-nybble instruction decoding. */
CheatVmOpcodeType_DoubleExtendedWidth = 0xF0,
/* Double-extended width opcodes. */
CheatVmOpcodeType_BreakProcess = 0xFF0,
CheatVmOpcodeType_ContinueProcess = 0xFF1,
CheatVmOpcodeType_DebugLog = 0xFFF,
};
@ -223,6 +226,11 @@ namespace ams::dmnt::cheat::impl {
bool should_operate[0x10];
};
struct ReadWriteStaticRegisterOpcode {
u32 static_idx;
u32 idx;
};
struct DebugLogOpcode {
u32 bit_width;
u32 log_id;
@ -252,6 +260,7 @@ namespace ams::dmnt::cheat::impl {
BeginRegisterConditionalOpcode begin_reg_cond;
SaveRestoreRegisterOpcode save_restore_reg;
SaveRestoreRegisterMaskOpcode save_restore_regmask;
ReadWriteStaticRegisterOpcode rw_static_reg;
DebugLogOpcode debug_log;
};
};
@ -260,6 +269,9 @@ namespace ams::dmnt::cheat::impl {
public:
constexpr static size_t MaximumProgramOpcodeCount = 0x400;
constexpr static size_t NumRegisters = 0x10;
constexpr static size_t NumReadableStaticRegisters = 0x80;
constexpr static size_t NumWritableStaticRegisters = 0x80;
constexpr static size_t NumStaticRegisters = NumReadableStaticRegisters + NumWritableStaticRegisters;
private:
size_t num_opcodes = 0;
size_t instruction_ptr = 0;
@ -268,6 +280,7 @@ namespace ams::dmnt::cheat::impl {
u32 program[MaximumProgramOpcodeCount] = {0};
u64 registers[NumRegisters] = {0};
u64 saved_values[NumRegisters] = {0};
u64 static_registers[NumStaticRegisters] = {0};
size_t loop_tops[NumRegisters] = {0};
private:
bool DecodeNextOpcode(CheatVmOpcode *out);
@ -294,6 +307,18 @@ namespace ams::dmnt::cheat::impl {
bool LoadProgram(const CheatEntry *cheats, size_t num_cheats);
void Execute(const CheatProcessMetadata *metadata);
u64 GetStaticRegister(size_t which) const {
return this->static_registers[which];
}
void SetStaticRegister(size_t which, u64 value) {
this->static_registers[which] = value;
}
void ResetStaticRegisters() {
std::memset(this->static_registers, 0, sizeof(this->static_registers));
}
#ifdef DMNT_CHEAT_VM_DEBUG_LOG
private:
fs::FileHandle debug_log_file;