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