From 60a067969989026e7d69354ff68518dda1b91657 Mon Sep 17 00:00:00 2001 From: Maschell Date: Tue, 26 Jul 2022 19:29:41 +0200 Subject: [PATCH] WIP --- main.py | 22 +++---- src/debugger.cpp | 160 ++++++++++++++++++++++++++++++++++------------- src/debugger.h | 9 ++- src/main.cpp | 55 +++++++++++++--- src/patches.cpp | 36 ++++++++--- src/screen.cpp | 23 +++++-- src/screen.h | 2 + 7 files changed, 232 insertions(+), 75 deletions(-) diff --git a/main.py b/main.py index 6407610..add729f 100644 --- a/main.py +++ b/main.py @@ -116,6 +116,7 @@ COMMAND_TOGGLE_BREAKPOINT = 8 COMMAND_POKE_REGISTERS = 9 COMMAND_RECEIVE_MESSAGES = 10 COMMAND_SEND_MESSAGE = 11 +COMMAND_DISASM = 12 class Debugger: def __init__(self): @@ -157,6 +158,12 @@ class Debugger: self.sendall(struct.pack(">II", addr, num)) data = self.recvall(num) return data + + def disasm(self, addr, num): + self.sendbyte(COMMAND_DISASM) + self.sendall(struct.pack(">II", addr, num)) + length = struct.unpack(">I", self.recvall(4))[0] + return self.recvall(length).decode("ascii") def write(self, addr, data): self.sendbyte(COMMAND_WRITE) @@ -503,18 +510,11 @@ class DisassemblyWidget(QTextEdit): self.updateText() self.updateHighlight() - def updateText(self): - if debugger.connected: - blob = debugger.read(self.base, 0x60) - else: - blob = b"\x00" * 0x60 - + def updateText(self): text = "" - for i in range(24): - address = self.base + i * 4 - value = struct.unpack_from(">I", blob, i * 4)[0] - instr = disassemble.disassemble(value, address) - text += "%08X: %08X %s\n" %(address, value, instr) + if debugger.connected: + text = debugger.disasm(self.base, 0x60) + self.setPlainText(text) def updateHighlight(self): diff --git a/src/debugger.cpp b/src/debugger.cpp index 95b730d..9fbfe8d 100644 --- a/src/debugger.cpp +++ b/src/debugger.cpp @@ -7,20 +7,27 @@ #include #include #include +#include #include +#include #include #include #include +#include #include +#include #include +#include +#include + Debugger *debugger; +bool initDebugState = false; bool BreakPoint::isRange(uint32_t addr, uint32_t length) const { return address >= addr && address <= addr + length - 1; } - BreakPoint *BreakPointMgr::find(uint32_t addr, bool includeSpecial) { BreakPoint *bp = breakpoints.find(addr); if (!bp && includeSpecial) { @@ -280,7 +287,6 @@ bool ExceptionState::isBreakpoint() { } void ExceptionState::resume() { - DEBUG_FUNCTION_LINE("OSLoadContext"); OSLoadContext(&context); } @@ -540,30 +546,26 @@ void Debugger::handleBreakPoint(ExceptionState *state) { if (firstTrap) { firstTrap = false; - Screen screen; - screen.init(); + auto *screen = new Screen; Screen::drawText( 0, 0, "Waiting for debugger connection.\n" "Press the home button to continue without debugger.\n" "You can still connect while the game is running."); Screen::flip(); + delete screen; while (!connected) { uint32_t buttons = GetInput(VPAD_BUTTON_HOME); if (buttons) { - DEBUG_FUNCTION_LINE("Pressed home"); state->context.srr0 += 4; state->resume(); } } } - DEBUG_FUNCTION_LINE("stepper.handleBreakPoint(state);"); - stepper.handleBreakPoint(state); if (!connected) { - DEBUG_FUNCTION_LINE("if (!connected) {"); handleFatalCrash(&state->context, state->type); } @@ -671,10 +673,6 @@ void Debugger::cleanup() { ; } -extern "C" void OSRestoreInterrupts(int state); -extern "C" void __OSLockScheduler(void *); -extern "C" void __OSUnlockScheduler(void *); -extern "C" int OSDisableInterrupts(); static const char **commandNames = (const char *[]){ "COMMAND_CLOSE", @@ -688,15 +686,49 @@ static const char **commandNames = (const char *[]){ "COMMAND_TOGGLE_BREAKPOINT", "COMMAND_POKE_REGISTERS", "COMMAND_RECEIVE_MESSAGES", - "COMMAND_SEND_MESSAGE"}; + "COMMAND_SEND_MESSAGE", + "COMMAND_DISASM"}; + + +#define LOG_DISASSEMBLY_SIZE (4096) + +static char + sDisassemblyBuffer[LOG_DISASSEMBLY_SIZE]; + +static uint32_t + sDisassemblyLength = 0; + + +static void +disassemblyPrintCallback(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + sDisassemblyLength += vsprintf(sDisassemblyBuffer + sDisassemblyLength, + fmt, args); + sDisassemblyBuffer[sDisassemblyLength] = 0; + va_end(args); +} + +void eraseAllSubStr(std::string & mainStr, const std::string & toErase) +{ + size_t pos = std::string::npos; + // Search for the substring in string in a loop untill nothing is found + while ((pos = mainStr.find(toErase) )!= std::string::npos) + { + // If found then erase it from string + mainStr.erase(pos, toErase.length()); + } +} + +//#define OSIopShell_Command_Disassemble ((void (*)(void*, uint32_t))(0x101C400 + 0x173e0)) +#define __os_printf ((void (*)(char*, ...))(0x101C400 + 0x012e88)) void Debugger::mainLoop(Client *client) { - DEBUG_FUNCTION_LINE("About to enter mainLoop while"); while (!stopRunning) { uint8_t cmd; if (!client->recvall(&cmd, 1)) return; - if (cmd <= 11) { + if (cmd <= 12 && cmd != 10) { DEBUG_FUNCTION_LINE("Recieved command %s %d", commandNames[cmd], cmd); } if (cmd == COMMAND_CLOSE) { @@ -713,6 +745,50 @@ void Debugger::mainLoop(Client *client) { return; } delete[] buffer; + } else if (cmd == COMMAND_DISASM) { + uint32_t addr, length; + if (!client->recvall(&addr, 4)) return; + if (!client->recvall(&length, 4)) return; + + + char *buffer = new char[0x40]; + + auto addrAsPtr = (uint32_t *) (addr & 0xfffffffc); + + + + + DisassemblePPCRange(reinterpret_cast(addr + 0x20), reinterpret_cast(addr + length), reinterpret_cast(__os_printf),OSGetSymbolName, + static_cast(0x121)); + + + for (int i = 0; i < length / 4; i++) { + DisassemblePPCOpcode(&addrAsPtr[i], + buffer, + 0x40, + OSGetSymbolName, + static_cast(0x121)); + disassemblyPrintCallback("0x%08x 0x%08x %s\n",&addrAsPtr[i],addrAsPtr[i],buffer); + } + delete[] buffer; + + + std::string shit(sDisassemblyBuffer); + eraseAllSubStr(shit, "(null)"); + + + DEBUG_FUNCTION_LINE("done"); + + auto length_ = shit.length() + 1; + if (!client->sendall(&length_, 4)) { + sDisassemblyLength = 0; + return; + } + if (!client->sendall(shit.c_str(), length_)) { + sDisassemblyLength = 0; + return; + } + sDisassemblyLength = 0; } else if (cmd == COMMAND_WRITE) { uint32_t addr, length; if (!client->recvall(&addr, 4)) return; @@ -733,15 +809,18 @@ void Debugger::mainLoop(Client *client) { } else if (cmd == COMMAND_GET_MODULE_NAME) { char name[0x40]; int length = 0x40; - OSDynLoad_GetModuleName(reinterpret_cast(-1), name, &length); - + if(OSDynLoad_GetModuleName(reinterpret_cast(-1), name, &length) != OS_DYNLOAD_OK){ + strncat(name, "ERROR", sizeof(name) -1); + } length = strlen(name); + if (!client->sendall(&length, 4)) return; if (!client->sendall(name, length)) return; } else if (cmd == COMMAND_GET_MODULE_LIST) { + int num_rpls = OSDynLoad_GetNumberOfRPLs(); if (num_rpls == 0) { - return; + continue; } std::vector rpls; @@ -749,7 +828,7 @@ void Debugger::mainLoop(Client *client) { bool ret = OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data()); if (!ret) { - return; + continue; } char buffer[0x1000]; //This should be enough @@ -784,6 +863,7 @@ void Debugger::mainLoop(Client *client) { OSThread *current = ThreadList; while (current) { const char *name = OSGetThreadName(current); + OSReport("name %s", name); uint32_t namelen = 0; if (name) { @@ -795,7 +875,7 @@ void Debugger::mainLoop(Client *client) { } int priority = current->basePriority; - int type = *(uint32_t *) (current->__unk11); + int type = *((uint32_t *) current->__unk11); if (type == 1) { priority -= 0x20; } else if (type == 2) { @@ -905,24 +985,20 @@ void Debugger::mainLoop(Client *client) { void Debugger::threadFunc() { DEBUG_FUNCTION_LINE("Hello from debugger thread :)!"); - OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, dsiHandler); - OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, isiHandler); - OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, programHandler); - DEBUG_FUNCTION_LINE("Callback init done.!"); + prevDsiHandler = OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, dsiHandler); + prevIsiHandler = OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, isiHandler); + prevProgramHandler = OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, programHandler); Server server; Client client; - DEBUG_FUNCTION_LINE("Set initialized = true"); initialized = true; while (!stopRunning) { if (!server.init(Socket::TCP)) continue; if (!server.bind(1560)) continue; if (!server.accept(&client)) continue; - DEBUG_FUNCTION_LINE("Accepted a connection"); connected = true; mainLoop(&client); - DEBUG_FUNCTION_LINE("Lets do some cleanup"); cleanup(); connected = false; client.close(); @@ -931,55 +1007,55 @@ void Debugger::threadFunc() { } int Debugger::threadEntry(int argc, const char **argv) { - DEBUG_FUNCTION_LINE("threadEntry"); debugger->threadFunc(); return 0; } + void Debugger::start() { initialized = false; connected = false; firstTrap = true; - DEBUG_FUNCTION_LINE("OSInitMessageQueue"); OSInitMessageQueue(&eventQueue, eventMessages, MESSAGE_COUNT); - DEBUG_FUNCTION_LINE("init breakpoints"); breakpoints.init(); - DEBUG_FUNCTION_LINE("init exceptions"); exceptions.init(); - DEBUG_FUNCTION_LINE("init stepper"); stepper.init(); - - DEBUG_FUNCTION_LINE("Alloc thread"); serverThread = (OSThread *) memalign(0x20, sizeof(OSThread)); - DEBUG_FUNCTION_LINE("Alloc stack"); - serverStack = (char *) memalign(0x20, STACK_SIZE); + serverStack = (char *) memalign(0x20, STACK_SIZE); - - DEBUG_FUNCTION_LINE("Create thread"); OSCreateThread( serverThread, threadEntry, 0, 0, serverStack + STACK_SIZE, STACK_SIZE, - 0, 12); - DEBUG_FUNCTION_LINE("Set thread name"); + 0, OS_THREAD_ATTRIB_AFFINITY_CPU2 | OS_THREAD_ATTRIB_DETACHED); OSSetThreadName(serverThread, "Debug Server"); - DEBUG_FUNCTION_LINE("Resume thread"); OSResumeThread(serverThread); while (!initialized) { - DEBUG_FUNCTION_LINE("Wait for thread init"); OSSleepTicks(OSMillisecondsToTicks(20)); } + initDebugState = true; + DCFlushRange(&initDebugState, sizeof(initDebugState)); DEBUG_FUNCTION_LINE("Thread init done! Exit start()"); } Debugger::~Debugger() { stopRunning = true; + DCFlushRange(&stopRunning, sizeof(stopRunning)); OSJoinThread(serverThread, nullptr); free(serverStack); serverStack = nullptr; free(serverThread); serverThread = nullptr; + + initialized = false; + connected = false; + firstTrap = true; + + // Restore exceptions. + OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, prevDsiHandler); + OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, prevIsiHandler); + OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, prevProgramHandler); } diff --git a/src/debugger.h b/src/debugger.h index 74f3e29..b33d577 100644 --- a/src/debugger.h +++ b/src/debugger.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -233,7 +234,8 @@ private: COMMAND_TOGGLE_BREAKPOINT, COMMAND_POKE_REGISTERS, COMMAND_RECEIVE_MESSAGES, - COMMAND_SEND_MESSAGE + COMMAND_SEND_MESSAGE, + COMMAND_DISASM }; static int threadEntry(int argc, const char **argv); @@ -269,6 +271,11 @@ private: bool initialized{}; bool connected{}; bool firstTrap{}; + + OSExceptionCallbackFn prevDsiHandler = nullptr; + OSExceptionCallbackFn prevIsiHandler = nullptr; + OSExceptionCallbackFn prevProgramHandler = nullptr; }; extern "C" Debugger *debugger; +extern bool initDebugState; diff --git a/src/main.cpp b/src/main.cpp index 6f108b4..584973f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,28 +1,65 @@ #include "debugger.h" #include "exceptions.h" #include "logger.h" +#include #include +#include +#include +#include #include OSThread **pThreadList; - WUPS_PLUGIN_NAME("Debugger"); -WUPS_PLUGIN_DESCRIPTION("FTP Server"); +WUPS_PLUGIN_DESCRIPTION("Wii U Debugger"); WUPS_PLUGIN_VERSION("0.1"); -WUPS_PLUGIN_AUTHOR("Kinnay"); +WUPS_PLUGIN_AUTHOR("Kinnay, Maschell"); WUPS_PLUGIN_LICENSE("GPL"); WUPS_USE_WUT_DEVOPTAB(); -INITIALIZE_PLUGIN() { - InstallExceptionHandlers(); +/* https://github.com/QuarkTheAwesome/CafeBinPatch/blob/main/src/runtime-patcher.cpp#L58 */ +bool PatchInstruction(void *instr, uint32_t original, uint32_t replacement) { + uint32_t current = *(uint32_t *) instr; + if (current != original) return current == replacement; + + KernelCopyData(OSEffectiveToPhysical((uint32_t) instr), OSEffectiveToPhysical((uint32_t) &replacement), sizeof(replacement)); + //Only works on AROMA! WUPS 0.1's KernelCopyData is uncached, needs DCInvalidate here instead + DCFlushRange(instr, 4); + ICInvalidateRange(instr, 4); + + current = *(uint32_t *) instr; + + return true; } + +/* https://github.com/QuarkTheAwesome/CafeBinPatch/blob/main/src/runtime-patcher.cpp#L74 */ +bool PatchDynLoadFunctions() { + uint32_t *patch1 = ((uint32_t *) &OSDynLoad_GetNumberOfRPLs) + 6; + uint32_t *patch2 = ((uint32_t *) &OSDynLoad_GetRPLInfo) + 22; + + if (!PatchInstruction(patch1, 0x41820038 /* beq +38 */, 0x60000000 /*nop*/)) { + return false; + } + if (!PatchInstruction(patch2, 0x41820100 /* beq +100 */, 0x60000000 /*nop*/)) { + return false; + } + + return true; +} + + +INITIALIZE_PLUGIN() { + PatchDynLoadFunctions(); +} + ON_APPLICATION_START() { initLogging(); - DEBUG_FUNCTION_LINE("Started Debugger plugin"); - pThreadList = (OSThread **) 0x100567F8; + DEBUG_FUNCTION_LINE("Hello from Debugger plugin"); + pThreadList = (OSThread **) 0x100567F8; // 100567f8 debugger = new Debugger(); + DCFlushRange(&debugger, 4); + DCFlushRange(debugger, sizeof(Debugger)); DEBUG_FUNCTION_LINE("Created Debugger"); debugger->start(); DEBUG_FUNCTION_LINE("Started Debugger thread"); @@ -34,3 +71,7 @@ ON_APPLICATION_REQUESTS_EXIT() { DEBUG_FUNCTION_LINE("Deleted Debugger thread"); deinitLogging(); } +ON_APPLICATION_ENDS() { + initDebugState = false; + DCFlushRange(&initDebugState, sizeof(initDebugState)); +} diff --git a/src/patches.cpp b/src/patches.cpp index 07d76b9..a389948 100644 --- a/src/patches.cpp +++ b/src/patches.cpp @@ -1,17 +1,35 @@ +#include "logger.h" +#include #include #include -DECL_FUNCTION(int, OSSetExceptionCallback) { - return 0; +extern bool initDebugState; + +DECL_FUNCTION(OSExceptionCallbackFn, OSSetExceptionCallback, + OSExceptionType exceptionType, + OSExceptionCallbackFn callback) { + if (initDebugState) { + return nullptr; + } else { + return real_OSSetExceptionCallback(exceptionType, callback); + } } -DECL_FUNCTION(int, OSSetExceptionCallbackEx) { - return 0; +DECL_FUNCTION(OSExceptionCallbackFn, OSSetExceptionCallbackEx, + OSExceptionMode mode, + OSExceptionType exceptionType, + OSExceptionCallbackFn callback) { + if (initDebugState) { + return nullptr; + } else { + return real_OSSetExceptionCallbackEx(mode, exceptionType, callback); + } } DECL_FUNCTION(int, OSIsDebuggerInitialized) { - return true; + return initDebugState; } - -WUPS_MUST_REPLACE(OSSetExceptionCallback, WUPS_LOADER_LIBRARY_COREINIT, OSSetExceptionCallback); -WUPS_MUST_REPLACE(OSSetExceptionCallbackEx, WUPS_LOADER_LIBRARY_COREINIT, OSSetExceptionCallbackEx); -WUPS_MUST_REPLACE(OSIsDebuggerInitialized, WUPS_LOADER_LIBRARY_COREINIT, OSIsDebuggerInitialized); \ No newline at end of file +// OSSetExceptionCallbackEx is just a branch to another function. +// Instead of "fixing" the FunctionPatcher, we use this hacky solution :) +WUPS_MUST_REPLACE_PHYSICAL_FOR_PROCESS(OSSetExceptionCallbackEx, (0x3201C400 + 0x286b0), (0x101C400 + 0x286b0), WUPS_FP_TARGET_PROCESS_GAME_AND_MENU); +WUPS_MUST_REPLACE_FOR_PROCESS(OSSetExceptionCallback, WUPS_LOADER_LIBRARY_COREINIT, OSSetExceptionCallback, WUPS_FP_TARGET_PROCESS_GAME_AND_MENU); +WUPS_MUST_REPLACE_FOR_PROCESS(OSIsDebuggerInitialized, WUPS_LOADER_LIBRARY_COREINIT, OSIsDebuggerInitialized, WUPS_FP_TARGET_PROCESS_GAME_AND_MENU); diff --git a/src/screen.cpp b/src/screen.cpp index 61a0adc..26af14e 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -1,14 +1,16 @@ #include "screen.h" +#include "logger.h" +#include +#include #include -Screen::Screen() : screenBuffer(nullptr) {} +Screen::Screen() : screenBuffer(nullptr) { + init(); +} Screen::~Screen() { - if (screenBuffer) { - MEMFreeToMappedMemory(screenBuffer); - screenBuffer = nullptr; - } + destroyBuffer(); } void Screen::init() { @@ -17,6 +19,9 @@ void Screen::init() { uint32_t bufferSize0 = OSScreenGetBufferSizeEx(SCREEN_TV); uint32_t bufferSize1 = OSScreenGetBufferSizeEx(SCREEN_DRC); screenBuffer = MEMAllocFromMappedMemoryForGX2Ex(bufferSize0 + bufferSize1, 0x100); + if (screenBuffer == nullptr) { + OSFatal("Failed to allocate screenbuffer"); + } OSScreenSetBufferEx(SCREEN_TV, screenBuffer); OSScreenSetBufferEx(SCREEN_DRC, (char *) screenBuffer + bufferSize0); @@ -86,3 +91,11 @@ void Screen::flip() { flip(SCREEN_TV); flip(SCREEN_DRC); } +void Screen::destroyBuffer() { + if (screenBuffer) { + MEMFreeToMappedMemory(screenBuffer); + screenBuffer = nullptr; + } + + GX2Init(nullptr); +} diff --git a/src/screen.h b/src/screen.h index b01d508..4853a64 100644 --- a/src/screen.h +++ b/src/screen.h @@ -22,6 +22,8 @@ public: static void drawText(OSScreenID screen, int x, int y, const char *text); static void flip(OSScreenID screen); + void destroyBuffer(); + private: void *screenBuffer;