diff --git a/.idea/markdown-exported-files.xml b/.idea/markdown-exported-files.xml new file mode 100644 index 0000000..5d1f129 --- /dev/null +++ b/.idea/markdown-exported-files.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/meta/meta.xml b/meta/meta.xml index dd25833..ead026f 100644 --- a/meta/meta.xml +++ b/meta/meta.xml @@ -1,13 +1,18 @@ - - TCPgecko - wj44, BullyWiiPlaza - 2.2 - WiiU RAM Hacking - A memory editor that does magical things to your games. In order to apply Cafe Codes (real-time cheats) use JGecko U. -Special Thanks to: -Chadderz, Marionumber1 - TCPGecko codehandler -pwsincd - icon and xml -CosmoCortney - codehandler for cheat codes, xml - - + + + TCP Gecko + BullyWiiPlaza, wj44, dimok, Chadderz, Marionumber1 + 2.3 + WiiU RAM Hacking + A memory editor that does magical things to your games. In order to develop and apply real-time + cheats use JGecko U. + + Special thanks to: + Chadderz, Marionumber1 - Original TCP Gecko Installer + dimok - Homebrew Launcher + kinnay - Diibugger + pwsincd - Icon and XML + CosmoCortney - Cheat code handler + + \ No newline at end of file diff --git a/src/address.c b/src/address.c new file mode 100644 index 0000000..2f8a7b9 --- /dev/null +++ b/src/address.c @@ -0,0 +1,16 @@ +#include "address.h" +#include "dynamic_libs/os_functions.h" + +int validateAddressRange(int starting_address, int ending_address) { + return __OSValidateAddressSpaceRange(1, (void *) starting_address, ending_address - starting_address + 1); +} + +bool isValidDataAddress(int address) { + return OSIsAddressValid((const void *) address) + && address >= 0x10000000 + && address < 0x50000000; +} + +int roundUpToAligned(int number) { + return (number + 3) & ~0x03; +} \ No newline at end of file diff --git a/src/address.h b/src/address.h new file mode 100644 index 0000000..7a38497 --- /dev/null +++ b/src/address.h @@ -0,0 +1,12 @@ +#ifndef TCPGECKO_ADDRESS_H +#define TCPGECKO_ADDRESS_H + +#include + +int validateAddressRange(int starting_address, int ending_address); + +bool isValidDataAddress(int address); + +int roundUpToAligned(int number); + +#endif \ No newline at end of file diff --git a/src/assertions.h b/src/assertions.h new file mode 100644 index 0000000..d79fa26 --- /dev/null +++ b/src/assertions.h @@ -0,0 +1,53 @@ +#ifndef TCPGECKO_ASSERTIONS_H +#define TCPGECKO_ASSERTIONS_H + +#define ASSERT_MINIMUM_HOLDS(actual, minimum, variableName) \ +if(actual < minimum) { \ + char buffer[100] = {0}; \ + __os_snprintf(buffer, 100, "%s: Limit exceeded (minimum: %i, actual: %i)", variableName, minimum, actual); \ + OSFatal(buffer); \ +} \ + +#define ASSERT_MAXIMUM_HOLDS(maximum, actual, variableName) \ +if(actual > maximum) { \ + char buffer[100] = {0}; \ + __os_snprintf(buffer, 100, "%s: Limit exceeded (maximum: %i, actual: %i)", variableName, maximum, actual); \ + OSFatal(buffer); \ +} \ + +#define ASSERT_FUNCTION_SUCCEEDED(returnValue, functionName) \ + if (returnValue < 0) { \ + char buffer[100] = {0}; \ + __os_snprintf(buffer, 100, "%s failed with return value: %i", functionName, returnValue); \ + OSFatal(buffer); \ + } \ + +#define ASSERT_VALID_EFFECTIVE_ADDRESS(effectiveAddress, message) \ + if(!OSIsAddressValid((void *) effectiveAddress)) { \ + char buffer[100] = {0}; \ + __os_snprintf(buffer, 100, "Address %04x invalid: %s", effectiveAddress, message); \ + OSFatal(buffer); \ + } + +#define ASSERT_INTEGER(actual, expected, name) \ + if(actual != expected) { \ + char buffer[50] = {0}; \ + __os_snprintf(buffer, 50, "%s assertion failed: %i == %i", name, actual, expected); \ + OSFatal(buffer); \ + } + +#define ASSERT_STRING(actual, expected) \ + if(strcmp(actual, expected) != 0) { \ + char buffer[50] = {0}; \ + __os_snprintf(buffer, 50, "String assertion failed: \"%s\" == \"%s\"", actual, expected); \ + OSFatal(buffer); \ + } + +#define ASSERT_ALLOCATED(variable, name) \ + if(variable == 0) { \ + char buffer[50] = {0}; \ + __os_snprintf(buffer, 50, "%s allocation failed", name); \ + OSFatal(buffer); \ + } + +#endif \ No newline at end of file diff --git a/src/disassembler.c b/src/disassembler.c new file mode 100644 index 0000000..a7725f1 --- /dev/null +++ b/src/disassembler.c @@ -0,0 +1,37 @@ +#include "disassembler.h" +#include "assertions.h" +#include "dynamic_libs/os_functions.h" +#include +#include +#include +#include + +char *disassemblerBuffer; +void *disassemblerBufferPointer; + +#define DISASSEMBLER_BUFFER_SIZE 0x1024 + +void formatDisassembled(char *format, ...) { + if (!disassemblerBuffer) { + disassemblerBuffer = malloc(DISASSEMBLER_BUFFER_SIZE); + ASSERT_ALLOCATED(disassemblerBuffer, "Disassembler buffer") + disassemblerBufferPointer = disassemblerBuffer; + } + + va_list variableArguments; + va_start(variableArguments, format); + char *temporaryBuffer; + int printedBytesCount = vasprintf(&temporaryBuffer, format, variableArguments); + ASSERT_ALLOCATED(temporaryBuffer, "Temporary buffer") + ASSERT_MINIMUM_HOLDS(printedBytesCount, 1, "Printed bytes count") + va_end(variableArguments); + + // Do not smash the buffer + long projectedSize = (void *) disassemblerBuffer - disassemblerBufferPointer + printedBytesCount; + if (projectedSize < DISASSEMBLER_BUFFER_SIZE) { + memcpy(disassemblerBuffer, temporaryBuffer, printedBytesCount); + disassemblerBuffer += printedBytesCount; + } + + free(temporaryBuffer); +} \ No newline at end of file diff --git a/src/disassembler.h b/src/disassembler.h new file mode 100644 index 0000000..f0efed7 --- /dev/null +++ b/src/disassembler.h @@ -0,0 +1,6 @@ +#ifndef TCPGECKO_DISASSEMBLER_H +#define TCPGECKO_DISASSEMBLER_H + +void formatDisassembled(char *format, ...); + +#endif \ No newline at end of file diff --git a/src/entry.c b/src/entry.c index 683742c..7712e90 100644 --- a/src/entry.c +++ b/src/entry.c @@ -2,7 +2,7 @@ #include "dynamic_libs/gx2_functions.h" #include "dynamic_libs/socket_functions.h" #include "common/common.h" -#include "pygecko.h" +#include "tcp_gecko.h" #include "main.h" #include "utils/logger.h" #include "title.h" diff --git a/src/kernel.h b/src/kernel.h new file mode 100644 index 0000000..ef8e112 --- /dev/null +++ b/src/kernel.h @@ -0,0 +1,113 @@ +#pragma once + +#include "kernel/syscalls.h" +#include "assertions.h" +#include "dynamic_libs/os_functions.h" +#include "tcp_gecko.h" +#include "utils/logger.h" + +unsigned char *kernelCopyBuffer[sizeof(int)]; + +// TODO Variable size, not hard-coded +unsigned char *kernelCopyBufferOld[DATA_BUFFER_SIZE]; + +void kernelCopyData(unsigned char *destinationBuffer, unsigned char *sourceBuffer, unsigned int length) { + if (length > DATA_BUFFER_SIZE) { + OSFatal("Kernel copy buffer size exceeded"); + } + + memcpy(kernelCopyBufferOld, sourceBuffer, length); + SC0x25_KernelCopyData((unsigned int) OSEffectiveToPhysical(destinationBuffer), (unsigned int) &kernelCopyBufferOld, length); + DCFlushRange(destinationBuffer, (u32) length); +} + +void kernelCopyInt(unsigned char *destinationBuffer, unsigned char *sourceBuffer, unsigned int length) { + memcpy(kernelCopyBuffer, sourceBuffer, length); + unsigned int destinationAddress = (unsigned int) OSEffectiveToPhysical(destinationBuffer); + SC0x25_KernelCopyData(destinationAddress, (unsigned int) &kernelCopyBuffer, length); + DCFlushRange(destinationBuffer, (u32) length); +} + +void writeKernelMemory(const void *address, uint32_t value) { + ((int *) kernelCopyBuffer)[0] = value; + kernelCopyInt((unsigned char *) address, (unsigned char *) kernelCopyBuffer, sizeof(int)); +} + +int readKernelMemory(const void *address) { + // For addresses in that range use Chadderz' function to avoid crashing + if (address > (const void *) 0xF0000000) { + log_print("Using Chadderz' kern_read()...\n"); + return kern_read(address); + } + + log_print("Using dimok's kernelCopy()...\n"); + unsigned char *readBuffer[sizeof(int)]; + kernelCopyInt((unsigned char *) readBuffer, (unsigned char *) address, sizeof(int)); + + return ((int *) readBuffer)[0]; +} + +#define KERNEL_COPY_SOURCE_ADDRESS 0x10100000 + +int kernelCopyService(int argc, void *argv) { + while (true) { + // Read the destination address from the source address + int destinationAddress = *(int *) KERNEL_COPY_SOURCE_ADDRESS; + + // Avoid crashing + if (OSIsAddressValid((const void *) destinationAddress)) { + // Perform memory copy + unsigned char *valueBuffer = (unsigned char *) (KERNEL_COPY_SOURCE_ADDRESS + 4); + kernelCopyInt((unsigned char *) destinationAddress, valueBuffer, 4); + + // "Consume" address and value for synchronization with the code handler for instance + *(int *) KERNEL_COPY_SOURCE_ADDRESS = 0; + *(((int *) KERNEL_COPY_SOURCE_ADDRESS) + 1) = 0; + } + } +} + +void startKernelCopyService() { + unsigned int stack = (unsigned int) memalign(0x40, 0x100); + ASSERT_ALLOCATED(stack, "Kernel copy thread stack") + stack += 0x100; + void *thread = memalign(0x40, 0x1000); + ASSERT_ALLOCATED(thread, "Kernel copy thread") + + int status = OSCreateThread(thread, kernelCopyService, 1, NULL, (u32) stack + sizeof(stack), sizeof(stack), 31, + OS_THREAD_ATTR_AFFINITY_CORE1 | OS_THREAD_ATTR_PINNED_AFFINITY | OS_THREAD_ATTR_DETACH); + ASSERT_INTEGER(status, 1, "Creating kernel copy thread") + // OSSetThreadName(thread, "Kernel Copier"); + OSResumeThread(thread); +} + +#define MINIMUM_KERNEL_COMPARE_LENGTH 4 +#define KERNEL_MEMORY_COMPARE_STEP_SIZE 1 + +int kernelMemoryCompare(const void *sourceBuffer, + const void *destinationBuffer, + int length) { + if (length < MINIMUM_KERNEL_COMPARE_LENGTH) { + ASSERT_MINIMUM_HOLDS(length, MINIMUM_KERNEL_COMPARE_LENGTH, "length"); + } + + bool loopEntered = false; + + while (kern_read(sourceBuffer) == kern_read(destinationBuffer)) { + loopEntered = true; + sourceBuffer = (char *) sourceBuffer + KERNEL_MEMORY_COMPARE_STEP_SIZE; + destinationBuffer = (char *) destinationBuffer + KERNEL_MEMORY_COMPARE_STEP_SIZE; + length -= KERNEL_MEMORY_COMPARE_STEP_SIZE; + + if (length <= MINIMUM_KERNEL_COMPARE_LENGTH - 1) { + break; + } + } + + if (loopEntered) { + sourceBuffer -= KERNEL_MEMORY_COMPARE_STEP_SIZE; + destinationBuffer -= KERNEL_MEMORY_COMPARE_STEP_SIZE; + } + + return kern_read(sourceBuffer) - kern_read(destinationBuffer); +} \ No newline at end of file diff --git a/src/kernel/kernel_functions.c b/src/kernel/kernel_functions.c index ea23be5..6d2843a 100644 --- a/src/kernel/kernel_functions.c +++ b/src/kernel/kernel_functions.c @@ -1,9 +1,6 @@ #include -#include "common/common.h" -#include "common/kernel_defs.h" -#include "kernel/kernel_functions.h" -#include "kernel/syscalls.h" -#include "pygecko.h" +#include "../common/kernel_defs.h" +#include "../kernel/kernel_functions.h" /* our retain data */ ReducedCosAppXmlInfo cosAppXmlInfoStruct; diff --git a/src/main.c b/src/main.c index f25f6b5..028a7ce 100644 --- a/src/main.c +++ b/src/main.c @@ -44,10 +44,6 @@ int Menu_Main(void) { InitVPadFunctionPointers(); InitSysFunctionPointers(); - log_init(COMPUTER_IP_ADDRESS); - log_print("Patching functions\n"); - applyFunctionPatches(); - if (strcasecmp("men.rpx", cosAppXmlInfoStruct.rpx_name) == 0) { return EXIT_RELAUNCH_ON_LOAD; } else if (strlen(cosAppXmlInfoStruct.rpx_name) > 0 && @@ -141,15 +137,20 @@ int Menu_Main(void) { break; } - // A Button + // A Button pressed if (pressedButtons & VPAD_BUTTON_A) { unsigned int physicalCodeHandlerAddress = (unsigned int) OSEffectiveToPhysical( (void *) CODE_HANDLER_INSTALL_ADDRESS); - SC0x25_KernelCopyData((u32) physicalCodeHandlerAddress, (int) codeHandler, codeHandlerLength); + SC0x25_KernelCopyData((u32) physicalCodeHandlerAddress, (unsigned int) codeHandler, codeHandlerLength); DCFlushRange((const void *) CODE_HANDLER_INSTALL_ADDRESS, (u32) codeHandlerLength); isCodeHandlerInstalled = true; launchMethod = TCP_GECKO; + + log_init(COMPUTER_IP_ADDRESS); + log_print("Patching functions\n"); + applyFunctionPatches(); + break; } diff --git a/src/patcher/function_patcher_gx2.c b/src/patcher/function_patcher_gx2.c index 8674bdc..7ab346f 100644 --- a/src/patcher/function_patcher_gx2.c +++ b/src/patcher/function_patcher_gx2.c @@ -24,8 +24,7 @@ static volatile int executionCounter = 0; -declareFunctionHook(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *colorBuffer, s32 - scan_target) { +declareFunctionHook(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer *colorBuffer, s32 scan_target) { if (executionCounter > 120) { GX2Surface surface = colorBuffer->surface; /*s32 format = surface.format; @@ -53,8 +52,7 @@ declareFunctionHook(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer * jpeg.img_id = 0; }*/ - log_printf("GX2CopyColorBufferToScanBuffer {surface width:%d, height:%d, image size:%d, image data:%x}\n", - surface.width, surface.height, surface.image_size, surface.image_data); + log_printf("GX2CopyColorBufferToScanBuffer {surface width:%d, height:%d, image size:%d, image data:%x}\n", surface.width, surface.height, surface.image_size, surface.image_data); executionCounter = 0; } @@ -65,7 +63,7 @@ declareFunctionHook(void, GX2CopyColorBufferToScanBuffer, const GX2ColorBuffer * } FunctionHook method_hooks_gx2[] __attribute__((section(".data"))) = { - makeFunctionHook(GX2CopyColorBufferToScanBuffer, LIB_GX2, STATIC_FUNCTION) + // makeFunctionHook(GX2CopyColorBufferToScanBuffer, LIB_GX2, STATIC_FUNCTION) }; u32 method_hooks_size_gx2 __attribute__((section(".data"))) = sizeof(method_hooks_gx2) / sizeof(FunctionHook); diff --git a/src/pause.h b/src/pause.h new file mode 100644 index 0000000..bc0e21b --- /dev/null +++ b/src/pause.h @@ -0,0 +1,50 @@ +#pragma once + +#include "utils/logger.h" +#include "assertions.h" +#include "dynamic_libs/os_functions.h" +#include "common/fs_defs.h" +#include "kernel.h" + +int (*AVMGetDRCScanMode)(int); + +unsigned long getConsoleStatePatchAddress() { + if (AVMGetDRCScanMode) { + log_print("Already acquired!\n"); + } else { + // Acquire the RPL and function + log_print("Acquiring...\n"); + unsigned int avm_handle; + OSDynLoad_Acquire("avm.rpl", &avm_handle); + ASSERT_ALLOCATED(avm_handle, "avm.rpl") + OSDynLoad_FindExport((u32) avm_handle, 0, "AVMGetDRCScanMode", &AVMGetDRCScanMode); + ASSERT_ALLOCATED(AVMGetDRCScanMode, "AVMGetDRCScanMode") + log_print("Acquired!\n"); + } + + return (unsigned long) (AVMGetDRCScanMode + 0x44); +} + +typedef enum { + PAUSED = 0x38000001, + RUNNING = 0x38000000 +} ConsoleState; + +void writeConsoleState(ConsoleState state) { + // Get the value to write + int patchValue = state; + log_printf("Patch value: %x\n", patchValue); + + // Write the value + unsigned int patchAddress = getConsoleStatePatchAddress(); + log_printf("Patch address: %x\n", patchAddress); + kernelCopyData((unsigned char *) patchAddress, (unsigned char *) &patchValue, 4); +} + +bool isConsolePaused() { + unsigned int patchAddress = getConsoleStatePatchAddress(); + log_printf("Patch address: %x\n", patchAddress); + int value = *(unsigned int *) patchAddress; + + return value == PAUSED; +} \ No newline at end of file diff --git a/src/system/exception_handler.h b/src/system/exception_handler.h index f15f2a4..4f1d8ce 100644 --- a/src/system/exception_handler.h +++ b/src/system/exception_handler.h @@ -35,7 +35,31 @@ typedef struct OSContext { uint32_t exception_specific0; uint32_t exception_specific1; - /* There is actually a lot more here but we don't need the rest*/ + u32 exception_type; + u32 reserved; + + double fpscr; + double fpr[32]; + + u16 spinLockCount; + u16 state; + + u32 gqr[8]; + u32 pir; + double psf[32]; + + u64 coretime[3]; + u64 starttime; + + u32 error; + u32 attributes; + + u32 pmc1; + u32 pmc2; + u32 pmc3; + u32 pmc4; + u32 mmcr0; + u32 mmcr1; } OSContext; #define CPU_STACK_TRACE_DEPTH 10 diff --git a/src/system/hardware_breakpoints.h b/src/system/hardware_breakpoints.h new file mode 100644 index 0000000..a61d550 --- /dev/null +++ b/src/system/hardware_breakpoints.h @@ -0,0 +1,154 @@ +#include "../utils/stringify.h" +#include "../dynamic_libs/os_functions.h" +#include "threads.h" +#include "../utils/logger.h" +#include "../main.h" +#include "utilities.h" +#include "software_breakpoints.h" +#include "../common/kernel_types.h" + +#ifndef TCPGECKO_BREAKPOINTS_H +#define TCPGECKO_BREAKPOINTS_H + +// Special purpose registers +#define IABR 0x3F2 +#define DABR 0x3F5 + +// http://www.ds.ewi.tudelft.nl/vakken/in1006/instruction-set/mtspr.html +#define mtspr(spr, value) \ + __asm__ __volatile__ ("mtspr %0, %1" : : "K" (spr), "r" (value)) \ + + +// https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.alangref/idalangref_isync_ics_instrs.htm +static inline void isync() { + __asm__ __volatile__ ("isync" : : : "memory"); +} + +// https://www.ibm.com/support/knowledgecenter/en/ssw_aix_61/com.ibm.aix.alangref/idalangref_eieio_instrs.htm +static inline void eieio() { + __asm__ __volatile__ ("eieio" : : : "memory"); +} + +// https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.alangref/idalangref_rfi_retfinter_instrs.htm +static inline void rfi() { + __asm__ __volatile__ ("rfi" : : : "memory"); +} + +// https://www.manualslib.com/manual/606065/Ibm-Powerpc-750gx.html?page=64 +static inline void setIABR(unsigned int address) { + mtspr(IABR, address); + eieio(); + isync(); +} + +static inline int getIABRAddress() { + return mfspr(IABR); +} + +static inline int getDABRAddress(void *interruptedContext) { + OSContext *context = (OSContext *) interruptedContext; + return (int) context->srr0; // Offset 0xA4 +} + +static inline int getIABRMatch(void *interruptedContext) { + OSContext *context = (OSContext *) interruptedContext; + return (int) context->exception_specific1; // Offset 0x98 +} + +unsigned char breakPointHandler(void *interruptedContext); + +void registerBreakPointHandler() { + log_init(COMPUTER_IP_ADDRESS); + log_print("Registering breakpoint handler...\n"); + // TODO Not working, never called? + // OSSetExceptionCallback((u8) OS_EXCEPTION_DSI, &breakPointHandler); + // OSSetExceptionCallback((u8) OS_EXCEPTION_ISI, &breakPointHandler); + // OSSetExceptionCallback((u8) OS_EXCEPTION_PROGRAM, &breakPointHandler); + OSSetExceptionCallbackEx((u8) OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, (u8) OS_EXCEPTION_PROGRAM, &breakPointHandler); + // __OSSetInterruptHandler((u8) OS_EXCEPTION_PROGRAM, &breakPointHandler); + log_print("Breakpoint handler(s) registered!\n"); +} + +/*void forceDebuggerInitialized() { + unsigned char patchBytes[] = {0x38, 0x60, 0x00, 0x01}; + patchFunction(OSIsDebuggerInitialized, (char *) patchBytes, sizeof(patchBytes), 0x1C); +} + +void forceDebuggerPresent() { + unsigned char patchBytes[] = {0x38, 0x60, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00}; + patchFunction(OSIsDebuggerPresent, (char *) patchBytes, sizeof(patchBytes), 0x0); +}*/ + +static inline void setupBreakpointSupport() { + log_init(COMPUTER_IP_ADDRESS); + /*log_print("Clear and enable...\n"); + __OSClearAndEnableInterrupt(); + log_print("Restore...\n"); + OSRestoreInterrupts(); + log_print("Enable...\n"); + OSEnableInterrupts(); + forceDebuggerPresent(); + forceDebuggerInitialized();*/ + + registerBreakPointHandler(); +} + +void setDataBreakpoint(int address, bool read, bool write) { + setupBreakpointSupport(); + log_init(COMPUTER_IP_ADDRESS); + log_print("Setting DABR...\n"); + OSSetDABR(1, address, read, write); + log_print("DABR set\n"); + int enabled = OSIsInterruptEnabled(); + log_printf("Interrupts enabled: %i\n", enabled); +} + +void setInstructionBreakpoint(unsigned int address) { + setupBreakpointSupport(); + + // int returnedAddress; + + log_print("Setting IABR #1...\n"); + // OSSetIABR(1, address); + setIABR(address); + log_print("IABR set #1...\n"); + /* + // TODO Causes crash + returnedAddress = getIABRAddress(); + log_printf("IABR spr value: %08x\n", returnedAddress); + + log_print("Setting IABR #2...\n"); + setIABR(address); + log_print("IABR set #2...\n"); + returnedAddress = mfspr(IABR); + log_printf("IABR spr value: %08x\n", returnedAddress);*/ +} + +unsigned char breakPointHandler(void *interruptedContext) { + log_init(COMPUTER_IP_ADDRESS); + + // Check for data breakpoints + int dataAddress = getDABRAddress(interruptedContext); + if (OSIsAddressValid((const void *) dataAddress)) { + log_printf("Data breakpoint address: %x08\n", dataAddress); + } else { + log_printf("Data breakpoint invalid address: %x08\n", dataAddress); + + // Check for instruction breakpoints + int instructionAddress = getIABRMatch(interruptedContext); + if (OSIsAddressValid((const void *) instructionAddress)) { + log_printf("Instruction breakpoint address: %x08\n", dataAddress); + } else { + log_print("Instruction breakpoint failed!\n"); + } + } + + setDataBreakpoint(0, false, false); + setInstructionBreakpoint(0); + + rfi(); + + return 0; +} + +#endif \ No newline at end of file diff --git a/src/system/threads.c b/src/system/threads.c new file mode 100644 index 0000000..ad50584 --- /dev/null +++ b/src/system/threads.c @@ -0,0 +1,38 @@ +#include "threads.h" +#include "../utils/linked_list.h" +#include "../dynamic_libs/os_functions.h" +#include "../utils/logger.h" +#include "../main.h" + +struct node *getAllThreads() { + log_init(COMPUTER_IP_ADDRESS); + + struct node *threads = NULL; + int currentThreadAddress = OSGetCurrentThread(); + log_printf("Thread address: %08x\n", currentThreadAddress); + int iterationThreadAddress = currentThreadAddress; + int temporaryThreadAddress; + + // Follow "previous thread" pointers back to the beginning + while ((temporaryThreadAddress = *(int *) (iterationThreadAddress + PREVIOUS_THREAD)) != 0) { + log_printf("Temporary thread address going backwards: %08x\n", temporaryThreadAddress); + iterationThreadAddress = temporaryThreadAddress; + } + + // Now iterate over all threads + while ((temporaryThreadAddress = *(int *) (iterationThreadAddress + NEXT_THREAD)) != 0) { + // Grab the thread's address + log_printf("Temporary thread address going forward: %08x\n", temporaryThreadAddress); + threads = insert(threads, (void *) iterationThreadAddress); + log_printf("Inserted: %08x\n", iterationThreadAddress); + iterationThreadAddress = temporaryThreadAddress; + } + + // The previous while would skip the last thread so add it as well + threads = insert(threads, (void *) iterationThreadAddress); + log_printf("Inserted: %08x\n", iterationThreadAddress); + + reverse(&threads); + + return threads; +} \ No newline at end of file diff --git a/src/system/threads.h b/src/system/threads.h new file mode 100644 index 0000000..286551d --- /dev/null +++ b/src/system/threads.h @@ -0,0 +1,10 @@ +#ifndef TCPGECKO_THREADS_H +#define TCPGECKO_THREADS_H + +#define THREAD_SIZE 0x6A0 +#define PREVIOUS_THREAD 0x390 +#define NEXT_THREAD 0x38C + +struct node *getAllThreads(); + +#endif \ No newline at end of file diff --git a/src/system/utilities.h b/src/system/utilities.h index 1f74cdf..df6f255 100644 --- a/src/system/utilities.h +++ b/src/system/utilities.h @@ -17,7 +17,7 @@ void patchFunction(void *function, char *patchBytes, unsigned int patchBytesSize log_print("Patching function...\n"); void *patchAddress = function + functionOffset; log_printf("Patch address: %p\n", patchAddress); - kernelCopy((unsigned char *) patchAddress, (unsigned char *) patchBytes, patchBytesSize); + kernelCopyInt((unsigned char *) patchAddress, (unsigned char *) patchBytes, patchBytesSize); log_print("Successfully patched!\n"); } diff --git a/src/pygecko.c b/src/tcp_gecko.c similarity index 91% rename from src/pygecko.c rename to src/tcp_gecko.c index a0e9854..2280f32 100644 --- a/src/pygecko.c +++ b/src/tcp_gecko.c @@ -1,3 +1,4 @@ +#include "tcp_gecko.h" #include #include #include @@ -10,7 +11,6 @@ #include "main.h" #include "dynamic_libs/socket_functions.h" #include "dynamic_libs/gx2_functions.h" -#include "kernel/syscalls.h" #include "dynamic_libs/fs_functions.h" #include "utils/logger.h" #include "system/memory.h" @@ -18,6 +18,7 @@ #include "utils/linked_list.h" #include "address.h" #include "system/stack.h" +#include "pause.h" void *client; void *commandBlock; @@ -68,6 +69,7 @@ struct pygecko_bss_t { #define COMMAND_REMOVE_ALL_BREAKPOINTS 0xA6 #define COMMAND_POKE_REGISTERS 0xA7 #define COMMAND_GET_STACK_TRACE 0xA8 +#define COMMAND_GET_ENTRY_POINT_ADDRESS 0xB1 #define COMMAND_RUN_KERNEL_COPY_SERVICE 0xCD #define COMMAND_IOSU_HAX_READ_FILE 0xD0 #define COMMAND_GET_VERSION_HASH 0xE0 @@ -76,13 +78,12 @@ struct pygecko_bss_t { #define errno (*__gh_errno_ptr()) #define MSG_DONT_WAIT 32 #define EWOULDBLOCK 6 -#define DATA_BUFFER_SIZE 0x5000 // #define WRITE_SCREEN_MESSAGE_BUFFER_SIZE 100 -#define SERVER_VERSION "06/02/2017" +#define SERVER_VERSION "06/03/2017" #define ONLY_ZEROS_READ 0xB0 #define NON_ZEROS_READ 0xBD -#define VERSION_HASH 0x39C9444B +#define VERSION_HASH 0x3AC9444B ZEXTERN int ZEXPORT deflateEnd OF((z_streamp @@ -100,61 +101,11 @@ int flush // ########## Being kernel_copy.h ############ -// TODO Variable size, not hard-coded -unsigned char *memcpy_buffer[DATA_BUFFER_SIZE]; - -void pygecko_memcpy(unsigned char *destinationBuffer, unsigned char *sourceBuffer, unsigned int length) { - memcpy(memcpy_buffer, sourceBuffer, length); - SC0x25_KernelCopyData((unsigned int) OSEffectiveToPhysical(destinationBuffer), (unsigned int) &memcpy_buffer, - length); - DCFlushRange(destinationBuffer, (u32) length); -} - // ########## End kernel_copy.h ############ -// ########## Being pause.h ############ +// ########## Begin pause.h ############ -int (*AVMGetDRCScanMode)(int); -unsigned long getConsoleStatePatchAddress() { - if (AVMGetDRCScanMode) { - log_print("Already acquired!\n"); - } else { - // Acquire the RPL and function - log_print("Acquiring...\n"); - unsigned int avm_handle; - OSDynLoad_Acquire("avm.rpl", (u32 *) &avm_handle); - ASSERT_ALLOCATED(avm_handle, "avm.rpl") - OSDynLoad_FindExport((u32) avm_handle, 0, "AVMGetDRCScanMode", &AVMGetDRCScanMode); - ASSERT_ALLOCATED(AVMGetDRCScanMode, "AVMGetDRCScanMode") - log_print("Acquired!\n"); - } - - return (unsigned long) (AVMGetDRCScanMode + 0x44); -} - -typedef enum { - PAUSED = 0x38000001, RUNNING = 0x38000000 -} ConsoleState; - -void writeConsoleState(ConsoleState state) { - // Get the value to write - int patchValue = state; - log_printf("Patch value: %x\n", patchValue); - - // Write the value - unsigned int patchAddress = getConsoleStatePatchAddress(); - log_printf("Patch address: %x\n", patchAddress); - pygecko_memcpy((unsigned char *) patchAddress, (unsigned char *) &patchValue, 4); -} - -bool isConsolePaused() { - unsigned int patchAddress = getConsoleStatePatchAddress(); - log_printf("Patch address: %x\n", patchAddress); - int value = *(unsigned int *) patchAddress; - - return value == PAUSED; -} // ########## End pause.h ############ @@ -327,7 +278,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { destinationAddress = ((int *) buffer)[0]; value = ((int *) buffer)[1]; - pygecko_memcpy((unsigned char *) destinationAddress, (unsigned char *) &value, 4); + kernelCopyData((unsigned char *) destinationAddress, (unsigned char *) &value, 4); break; } case COMMAND_READ_MEMORY: { @@ -374,15 +325,18 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { startingAddress += length; } + break; } case COMMAND_READ_MEMORY_KERNEL: { const unsigned char *startingAddress, *endingAddress, *useKernRead; ret = recvwait(bss, clientfd, buffer, 3 * sizeof(int)); - CHECK_ERROR(ret < 0) - startingAddress = ((const unsigned char **) buffer)[0]; - endingAddress = ((const unsigned char **) buffer)[1]; - useKernRead = ((const unsigned char **) buffer)[2]; + ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (receiving data)") + + int bufferIndex = 0; + startingAddress = ((const unsigned char **) buffer)[bufferIndex++]; + endingAddress = ((const unsigned char **) buffer)[bufferIndex++]; + useKernRead = ((const unsigned char **) buffer)[bufferIndex]; while (startingAddress != endingAddress) { int length = (int) (endingAddress - startingAddress); @@ -407,12 +361,11 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { ret = sendByte(bss, clientfd, ONLY_ZEROS_READ); CHECK_ERROR(ret < 0) } else { - // Send the real bytes now buffer[0] = NON_ZEROS_READ; if (useKernRead) { - for (int offset = 0; offset < length; offset += sizeof(int)) { - *((int *) (buffer + 1) + offset / sizeof(int)) = readKernelMemory(startingAddress + offset); + for (int offset = 0; offset < length; offset += 4) { + *((int *) (buffer + 1) + offset / 4) = readKernelMemory(startingAddress + offset); } } else { memcpy(buffer + 1, startingAddress, length); @@ -430,6 +383,77 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { startingAddress += length; } break; + +/* const unsigned char *startingAddress, *endingAddress, *useKernRead; + ret = recvwait(bss, clientfd, buffer, 3 * sizeof(int)); + ASSERT_FUNCTION_SUCCEEDED(ret, "recvwait (receiving data)") + + int bufferIndex = 0; + startingAddress = ((const unsigned char **) buffer)[bufferIndex++]; + endingAddress = ((const unsigned char **) buffer)[bufferIndex++]; + useKernRead = ((const unsigned char **) buffer)[bufferIndex]; + + while (startingAddress != endingAddress) { + log_printf("Reading memory from %08x to %08x with kernel %i\n", startingAddress, endingAddress, + useKernRead); + + unsigned int length = (unsigned int) (endingAddress - startingAddress); + + // Do not smash the buffer + if (length > DATA_BUFFER_SIZE) { + length = DATA_BUFFER_SIZE; + } + + // Figure out if all bytes are zero to possibly avoid sending them + log_print("Checking for all zero bytes...\n"); + unsigned int rangeIterationIndex = 0; + for (; rangeIterationIndex < length; rangeIterationIndex++) { + int character = useKernRead ? readKernelMemory(startingAddress + rangeIterationIndex) + : startingAddress[rangeIterationIndex]; + if (character != 0) { + break; + } + } + + log_print("Preparing to send...\n"); + if (rangeIterationIndex == length) { + // No need to send all zero bytes for performance + log_print("All zero...\n"); + ret = sendByte(bss, clientfd, ONLY_ZEROS_READ); + ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (only zero bytes read byte)") + log_print("Sent!\n"); + } else { + // Send the real bytes now + log_print("Real bytes...\n"); + buffer[0] = NON_ZEROS_READ; + + if (useKernRead) { + // kernelCopy(buffer + 1, (unsigned char *) startingAddress, length); + for (unsigned int offset = 0; offset < length; offset += sizeof(int)) { + *((int *) (buffer + 1) + offset / sizeof(int)) = readKernelMemory( + startingAddress + offset); + log_printf("Offset: %x\n", offset); + } + + log_print("Done kernel reading!\n"); + } else { + log_print("Memory copying...\n"); + memcpy(buffer + 1, startingAddress, length); + log_print("Done copying!\n"); + } + + log_print("Sending everything...\n"); + ret = sendwait(bss, clientfd, buffer, length + 1); + ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (read bytes buffer)") + log_print("Sent!\n"); + } + + startingAddress += length; + } + + log_print("Done reading...\n"); + + break;*/ } case COMMAND_VALIDATE_ADDRESS_RANGE: { ret = recvwait(bss, clientfd, buffer, 8); @@ -671,7 +695,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { ret = recvwait(bss, clientfd, buffer, length); CHECK_ERROR(ret < 0) - pygecko_memcpy(current_address, buffer, (unsigned int) length); + kernelCopyData(current_address, buffer, (unsigned int) length); current_address += length; } @@ -1209,7 +1233,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { // Write the assembly to an executable code region int destinationAddress = 0x10000000 - DATA_BUFFER_SIZE; - pygecko_memcpy((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE); + kernelCopyData((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE); // Execute the assembly from there void (*function)() = (void (*)()) destinationAddress; @@ -1217,7 +1241,7 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { // Clear the memory contents again memset((void *) buffer, 0, DATA_BUFFER_SIZE); - pygecko_memcpy((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE); + kernelCopyData((unsigned char *) destinationAddress, buffer, DATA_BUFFER_SIZE); break; } @@ -1336,7 +1360,23 @@ static int processCommands(struct pygecko_bss_t *bss, int clientfd) { break; } case COMMAND_POKE_REGISTERS: { + log_print("Receiving poke registers data...\n"); + int gprSize = 4 * 32; + int fprSize = 8 * 32; + ret = recvwait(bss, clientfd, buffer, gprSize + fprSize); + log_print("Poking registers...\n"); + memcpy((void *) crashContext.gpr, (const void *) buffer, gprSize); + memcpy((void *) crashContext.fpr, (const void *) buffer, fprSize); + break; + } + case COMMAND_GET_ENTRY_POINT_ADDRESS: { + u32 *entryPointAddress = (u32 *) *((u32 *) OS_SPECIFICS->addr_OSTitle_main_entry); + ((u32 *) buffer)[0] = (u32) entryPointAddress; + ret = sendwait(bss, clientfd, buffer, sizeof(int)); + ASSERT_FUNCTION_SUCCEEDED(ret, "sendwait (Entry point address)"); + + break; } case COMMAND_RUN_KERNEL_COPY_SERVICE: { if (!kernelCopyServiceStarted) { diff --git a/src/pygecko.h b/src/tcp_gecko.h similarity index 58% rename from src/pygecko.h rename to src/tcp_gecko.h index d483633..4d38b28 100644 --- a/src/pygecko.h +++ b/src/tcp_gecko.h @@ -1,15 +1,13 @@ -#ifndef _PYGECKO_H_ -#define _PYGECKO_H_ +#pragma once -/* Main */ #ifdef __cplusplus extern "C" { #endif +#define DATA_BUFFER_SIZE 0x5000 + void startTCPGecko(void); #ifdef __cplusplus } -#endif - -#endif +#endif \ No newline at end of file diff --git a/src/title.c b/src/title.c new file mode 100644 index 0000000..f833de0 --- /dev/null +++ b/src/title.c @@ -0,0 +1,17 @@ +#include "dynamic_libs/os_functions.h" +#include "title.h" + +bool isRunningTitleID(unsigned long long int japaneseTitleID) { + unsigned long long int currentTitleID = (unsigned long long int) OSGetTitleID(); + return currentTitleID == japaneseTitleID // JAP + || currentTitleID == japaneseTitleID + 0x100 // USA + || currentTitleID == japaneseTitleID + 0x200; // EUR +} + +bool isRunningAllowedTitleID() { + return OSGetTitleID != 0 + && !isRunningTitleID(TITLE_ID_MII_VERSE) + && !isRunningTitleID(TITLE_ID_MII_MAKER) + && !isRunningTitleID(TITLE_ID_BAYONETTA_2) + && !isRunningTitleID(TITLE_ID_INTERNET_BROWSER); +} \ No newline at end of file diff --git a/src/title.h b/src/title.h new file mode 100644 index 0000000..1274bbb --- /dev/null +++ b/src/title.h @@ -0,0 +1,11 @@ +#ifndef TCPGECKO_TITLE_H +#define TCPGECKO_TITLE_H + +#define TITLE_ID_MII_VERSE 0x000500301001600A +#define TITLE_ID_MII_MAKER 0x000500101004A000 +#define TITLE_ID_BAYONETTA_2 0x0005000010172500 +#define TITLE_ID_INTERNET_BROWSER 0x000500301001200A + +bool isRunningAllowedTitleID(); + +#endif \ No newline at end of file diff --git a/src/utils/linked_list.c b/src/utils/linked_list.c new file mode 100644 index 0000000..2cd25c9 --- /dev/null +++ b/src/utils/linked_list.c @@ -0,0 +1,51 @@ +#include "linked_list.h" + +#include +#include + +void destroy(struct node *list) { + struct node *currentNode = list; + + while (currentNode != NULL) { + struct node *previousNode = currentNode; + currentNode = currentNode->next; + free(previousNode); + } +} + +struct node *insert(struct node *list, void *data) { + size_t structureSize = sizeof(struct node); + struct node *addedNode = (struct node *) malloc(structureSize); + + addedNode->data = data; + addedNode->next = list; + list = addedNode; + + return list; +} + +int length(struct node *list) { + int length = 0; + struct node *current; + + for (current = list; current != NULL; current = current->next) { + length++; + } + + return length; +} + +void reverse(struct node **list) { + struct node *previous = NULL; + struct node *current = *list; + struct node *next; + + while (current != NULL) { + next = current->next; + current->next = previous; + previous = current; + current = next; + } + + *list = previous; +} \ No newline at end of file diff --git a/src/utils/linked_list.h b/src/utils/linked_list.h new file mode 100644 index 0000000..c794d92 --- /dev/null +++ b/src/utils/linked_list.h @@ -0,0 +1,17 @@ +#ifndef LINKED_LIST_LINKED_DATA_LIST_H +#define LINKED_LIST_LINKED_DATA_LIST_H + +struct node { + void *data; + struct node *next; +}; + +void destroy(struct node *list); + +struct node *insert(struct node *list, void *data); + +int length(struct node *list); + +void reverse(struct node **list); + +#endif \ No newline at end of file diff --git a/src/utils/stringify.h b/src/utils/stringify.h new file mode 100644 index 0000000..f205831 --- /dev/null +++ b/src/utils/stringify.h @@ -0,0 +1,8 @@ +#ifndef TCPGECKO_STRINGIFY_H +#define TCPGECKO_STRINGIFY_H + +// http://elixir.free-electrons.com/linux/v2.6.24/source/include/linux/stringify.h#L9 +#define __stringify_1(x) #x +#define __stringify(x) __stringify_1(x) + +#endif \ No newline at end of file diff --git a/tcpgecko.elf b/tcpgecko.elf index 0696c2a..07946c6 100644 Binary files a/tcpgecko.elf and b/tcpgecko.elf differ