├── Makefile ├── README.MD ├── icon.png ├── include ├── global_backdoor.h └── kernel_patches.h ├── payload ├── 3ds.ld ├── Makefile ├── _start.s └── main.c └── source ├── global_backdoor.s ├── kernel_patches.c └── main.c /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITARM)/3ds_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | # 19 | # NO_SMDH: if set to anything, no SMDH file is generated. 20 | # ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) 21 | # APP_TITLE is the name of the app stored in the SMDH file (Optional) 22 | # APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) 23 | # APP_AUTHOR is the author of the app stored in the SMDH file (Optional) 24 | # ICON is the filename of the icon (.png), relative to the project folder. 25 | # If not set, it attempts to use one of the following (in this order): 26 | # - .png 27 | # - icon.png 28 | # - /default_icon.png 29 | #--------------------------------------------------------------------------------- 30 | TARGET := $(notdir $(CURDIR)) 31 | BUILD := build 32 | SOURCES := source 33 | DATA := data 34 | INCLUDES := include 35 | ROMFS := romfs 36 | 37 | APP_TITLE := safehax 38 | APP_DESCRIPTION := ARM9 code execution 39 | APP_AUTHOR := TiniVi 40 | 41 | #--------------------------------------------------------------------------------- 42 | # options for code generation 43 | #--------------------------------------------------------------------------------- 44 | ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft 45 | 46 | CFLAGS := -g -Wall -O2 -mword-relocations \ 47 | -fomit-frame-pointer -ffunction-sections \ 48 | $(ARCH) 49 | 50 | CFLAGS += $(INCLUDE) -DARM11 -D_3DS 51 | 52 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 53 | 54 | ASFLAGS := -g $(ARCH) 55 | LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 56 | 57 | LIBS := -lctru -lm 58 | 59 | #--------------------------------------------------------------------------------- 60 | # list of directories containing libraries, this must be the top level containing 61 | # include and lib 62 | #--------------------------------------------------------------------------------- 63 | LIBDIRS := $(CTRULIB) 64 | 65 | 66 | #--------------------------------------------------------------------------------- 67 | # no real need to edit anything past this point unless you need to add additional 68 | # rules for different file extensions 69 | #--------------------------------------------------------------------------------- 70 | ifneq ($(BUILD),$(notdir $(CURDIR))) 71 | #--------------------------------------------------------------------------------- 72 | 73 | export OUTPUT := $(CURDIR)/$(TARGET) 74 | export TOPDIR := $(CURDIR) 75 | 76 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 77 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 78 | 79 | export DEPSDIR := $(CURDIR)/$(BUILD) 80 | 81 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 82 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 83 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 84 | PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) 85 | SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) 86 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 87 | 88 | #--------------------------------------------------------------------------------- 89 | # use CXX for linking C++ projects, CC for standard C 90 | #--------------------------------------------------------------------------------- 91 | ifeq ($(strip $(CPPFILES)),) 92 | #--------------------------------------------------------------------------------- 93 | export LD := $(CC) 94 | #--------------------------------------------------------------------------------- 95 | else 96 | #--------------------------------------------------------------------------------- 97 | export LD := $(CXX) 98 | #--------------------------------------------------------------------------------- 99 | endif 100 | #--------------------------------------------------------------------------------- 101 | 102 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 103 | $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ 104 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 105 | 106 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 107 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 108 | -I$(CURDIR)/$(BUILD) 109 | 110 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 111 | 112 | ifeq ($(strip $(ICON)),) 113 | icons := $(wildcard *.png) 114 | ifneq (,$(findstring $(TARGET).png,$(icons))) 115 | export APP_ICON := $(TOPDIR)/$(TARGET).png 116 | else 117 | ifneq (,$(findstring icon.png,$(icons))) 118 | export APP_ICON := $(TOPDIR)/icon.png 119 | endif 120 | endif 121 | else 122 | export APP_ICON := $(TOPDIR)/$(ICON) 123 | endif 124 | 125 | ifeq ($(strip $(NO_SMDH)),) 126 | export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh 127 | endif 128 | 129 | ifneq ($(ROMFS),) 130 | export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) 131 | endif 132 | 133 | .PHONY: $(BUILD) payload clean all 134 | 135 | #--------------------------------------------------------------------------------- 136 | all: payload $(BUILD) 137 | 138 | payload: 139 | @cd payload && make 140 | 141 | $(BUILD): 142 | @[ -d $@ ] || mkdir -p $@ 143 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 144 | 145 | #--------------------------------------------------------------------------------- 146 | clean: 147 | @echo clean ... 148 | @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf 149 | @cd romfs && rm arm11.bin 150 | @rmdir romfs 151 | 152 | 153 | #--------------------------------------------------------------------------------- 154 | else 155 | 156 | DEPENDS := $(OFILES:.o=.d) 157 | 158 | #--------------------------------------------------------------------------------- 159 | # main targets 160 | #--------------------------------------------------------------------------------- 161 | ifeq ($(strip $(NO_SMDH)),) 162 | $(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh 163 | else 164 | $(OUTPUT).3dsx : $(OUTPUT).elf 165 | endif 166 | 167 | $(OUTPUT).elf : $(OFILES) 168 | 169 | #--------------------------------------------------------------------------------- 170 | # you need a rule like this for each extension you use as binary data 171 | #--------------------------------------------------------------------------------- 172 | %.bin.o : %.bin 173 | #--------------------------------------------------------------------------------- 174 | @echo $(notdir $<) 175 | @$(bin2o) 176 | 177 | #--------------------------------------------------------------------------------- 178 | # rules for assembling GPU shaders 179 | #--------------------------------------------------------------------------------- 180 | define shader-as 181 | $(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@))) 182 | picasso -o $(CURBIN) $1 183 | bin2s $(CURBIN) | $(AS) -o $@ 184 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h 185 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h 186 | echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h 187 | endef 188 | 189 | %.shbin.o : %.v.pica %.g.pica 190 | @echo $(notdir $^) 191 | @$(call shader-as,$^) 192 | 193 | %.shbin.o : %.v.pica 194 | @echo $(notdir $<) 195 | @$(call shader-as,$<) 196 | 197 | %.shbin.o : %.shlist 198 | @echo $(notdir $<) 199 | @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file))) 200 | 201 | -include $(DEPENDS) 202 | 203 | #--------------------------------------------------------------------------------------- 204 | endif 205 | #--------------------------------------------------------------------------------------- 206 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # safehax/safefirmhax 2 | ## What this is: 3 | 4 | _**THIS DOES NOT WORK ON SYSTEM FIRMWARES ABOVE 11.3**_ 5 | 6 | Basically, safehax works like Brahma's arm9 bootstrap, but for a wider range of firmwares. It will attempt to load one of the listed payloads from the SD card root: 7 | 8 | * `safehaxpayload.bin` 9 | * `arm9.bin` 10 | * `arm9loaderhax.bin` 11 | 12 | These files are ordered by load priority, so if `safehaxpayload.bin` is present it will not try and load either of the other payloads. After this, it'll load the payload it finds into memory @ 0x23F00000. This is useful for a number of things, including installing otpless arm9loaderhax on N3DS, dumping your nand on the newer firmwares, and running CFW. 13 | 14 | A Kernel11 exploit must be run ahead of time that either enables all srv/svc access, or gives us the global svc 0x30 backdoor. 15 | 16 | ## How it works: 17 | 18 | Because 'SAFE_MODE' firm is out of date (~3.0 on O3DS, ~8.1 on N3DS), it's still vulnerable to firmlaunch-hax, which allows us to overwrite the arm9 entry pointer on firmlaunch. Knowing this, we can trigger a firmlaunch, so that 'SAFE_MODE' arm9 can run, then we sync up with arm9 until we can send another firmlaunch request to it. From there, we can do firmlaunch-hax like normal and gain arm9 code execution. 19 | 20 | ## 11.3.0 - The fix (not really!): 21 | 22 | Nintendo added a flag under Process9 which, when set, triggers a panic on SAFE_MODE launch. This flag is set when certain titles are launched, ensuring that SAFE_MODE can only be launched early in the boot process. 23 | 24 | However, this is incredibly easy to circumvate since you can just relaunch NATIVE_FIRM and the flag will be reset. Therefore, all we need to do to allow 11.3 support is to relaunch NATIVE_FIRM before performing the attack. 25 | 26 | # Credits 27 | 28 | - [Normmatt](https://github.com/Normmatt) - Finding the vuln, helping work out an issue during KSync. 29 | - 'Everyone' - For also finding the [vuln](https://3dbrew.org/wiki/3DS_System_Flaws#Process9). 30 | - [shinyquagsire23](https://github.com/shinyquagsire23)/[patois](https://github.com/patois)(/etc?) - The [firmlaunch-hax](https://github.com/patois/Brahma) code that this uses snippets from, and was used as reference. 31 | - [Mrrraou](https://github.com/Mrrraou)/[nedwill](https://github.com/nedwill) - [Snippets](https://gist.github.com/Mrrraou/c74572c04d13c586d363bf64eba0d3a1) for the changes described [here](https://github.com/nedwill/fasthax#for-homebrew-application-developers). 32 | - [zoogie](https://gbatemp.net/members/zoogie.357147/) - Finding a fix for the cartridge bug. 33 | - [3DBrew](https://www.3dbrew.org/wiki/Main_Page)'s Users - VAddrs, and other useful information in general. 34 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TiniVi/safehax/200dbcfc2e76246bc0b5907bbbdf69aca6a95233/icon.png -------------------------------------------------------------------------------- /include/global_backdoor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include <3ds/types.h> 4 | 5 | 6 | typedef u32(*backdoor_fn)(u32 arg0, u32 arg1); 7 | u32 svc_30(void *entry_fn, ...); // can pass up to two arguments to entry_fn(...) 8 | 9 | Result svcGlobalBackdoor(s32 (*callback)(void)); 10 | 11 | bool checkSvcGlobalBackdoor(void); -------------------------------------------------------------------------------- /include/kernel_patches.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include <3ds/types.h> 4 | 5 | 6 | void initsrv_allservices(void); 7 | void patch_svcaccesstable(void); 8 | -------------------------------------------------------------------------------- /payload/3ds.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | 4 | ENTRY(_start) 5 | 6 | SECTIONS 7 | { 8 | . = 0x23FFF000; 9 | start_addr = .; 10 | .text.start : { *(.text.start) } 11 | .text : { *(.text) *(.text*) } 12 | .rodata : { *(.rodata) *(.rodata*) } 13 | .data : { *(.data) *(.data*) } 14 | .bss : { *(.bss) *(.bss*) } 15 | total_size = . - start_addr; 16 | } 17 | -------------------------------------------------------------------------------- /payload/Makefile: -------------------------------------------------------------------------------- 1 | CC=arm-none-eabi-gcc 2 | CP=arm-none-eabi-g++ 3 | OC=arm-none-eabi-objcopy 4 | OD=arm-none-eabi-objdump 5 | LD=arm-none-eabi-ld 6 | 7 | all: 8 | $(CC) -g *.c -c -march=armv6z -O2 -ffast-math -std=c99 -Wall 9 | $(CC) -g *.s -c -march=armv6z 10 | $(CC) -T 3ds.ld *.o 11 | mkdir -p ../romfs 12 | $(OC) --set-section-flags .bss=alloc,load,contents -O binary a.out ../romfs/arm11.bin 13 | rm *.out *.o 14 | -------------------------------------------------------------------------------- /payload/_start.s: -------------------------------------------------------------------------------- 1 | .arm 2 | .section .text.start 3 | 4 | CPSID AIF 5 | MOV SP, #0x27800000 6 | 7 | MOV R0, #0 8 | MCR P15, 0, R0, C7, C10, 0 9 | MCR P15, 0, R0, C7, C5, 0 10 | 11 | BL _start 12 | B . 13 | -------------------------------------------------------------------------------- /payload/main.c: -------------------------------------------------------------------------------- 1 | #define PXI_SYNC11 ((volatile unsigned char *)0x10163000) 2 | #define PXI_CNT11 *((volatile unsigned int *)0x10163004) 3 | #define PXI_SEND11 *((volatile unsigned int *)0x10163008) 4 | #define PXI_RECV11 *((volatile unsigned int *)0x1016300C) 5 | 6 | #define ARM9_TEST *((volatile unsigned int *)0x1FF80000) 7 | #define ARM9SYNC ((volatile unsigned char *)0x1FFFFFF0) 8 | #define ARM11Entry *((volatile unsigned int *)0x1FFFFFF8) 9 | #define ARM9Entry *((volatile unsigned int *)0x2400000C) 10 | 11 | #define FW_TID_HIGH 0x00040138 12 | #define FW_TID_LOW ((*((volatile unsigned int *)0x10140FFC) == 7) ? 0x20000003 : 0x00000003) 13 | #define PXI_FULL (PXI_CNT11 & 2) 14 | #define PXI_EMPTY (PXI_CNT11 & 0x100) 15 | 16 | #define LCDCFG_FILL *((volatile unsigned int *)0x10202204) 17 | #define DEBUG_FLAG *((volatile _Bool *)0x23FFFE10) 18 | #define DEBUG(c) if (DEBUG_FLAG) LCDCFG_FILL = 0x01000000 | (c & 0xFFFFFF); 19 | 20 | void pxi_send(unsigned int cmd){ 21 | while (PXI_FULL); 22 | PXI_SEND11 = cmd; 23 | } 24 | 25 | unsigned int pxi_recv(void){ 26 | while (PXI_EMPTY); 27 | return PXI_RECV11; 28 | } 29 | 30 | void _start(void){ 31 | DEBUG(0x0000FF); //red 32 | 33 | for (volatile int i = 0; i < 2; i++){ //The global SAFE_MODE flag is cleared now, let's launch SAFE_MODE! 34 | /* Init */ 35 | pxi_send(0x44846); //Signal ARM9 to complete the initial FIRMLaunches 36 | 37 | /* KSync */ 38 | ARM9SYNC[0] = 1; //SAFE_MODE Kernel9 @ 0x0801B148 & SAFE_MODE Kernel11 @ 0x1FFDB498 39 | while (ARM9SYNC[0] != 2); 40 | ARM9SYNC[0] = 1; 41 | while (ARM9SYNC[0] != 2); 42 | 43 | DEBUG(0x007FFF); //orange 44 | 45 | if (ARM9SYNC[1] == 3){ 46 | ARM9SYNC[0] = 1; 47 | while (ARM9SYNC[0] != 2); 48 | ARM9SYNC[0] = 3; 49 | } 50 | 51 | while (!PXI_FULL) pxi_send(0); //SAFE_MODE Process9 @ 0x0806C594 & SAFE_MODE pxi @ 0x101388 52 | PXI_SYNC11[1] = 1; 53 | while (PXI_SYNC11[0] != 1); 54 | 55 | DEBUG(0x00FFFF); //yellow 56 | 57 | while (!PXI_EMPTY) pxi_recv(); 58 | PXI_SYNC11[1] = 2; 59 | while (PXI_SYNC11[0] != 2); 60 | 61 | DEBUG(0x00FF00); //green 62 | 63 | /* FIRMLaunch */ 64 | pxi_send(0); //pxi:mc //https://github.com/patois/Brahma/blob/master/source/arm11.s#L11 & SAFE_MODE pxi @ 0x100618 65 | PXI_SYNC11[3] |= 0x40; 66 | pxi_send(0x10000); //pxi shutdown 67 | 68 | DEBUG(0xFFFF00); //cyan 69 | 70 | do pxi_send(0x44836); //SAFE_MODE Process9 @ 0x08086788 & SAFE_MODE Kernel11 @ 0xFFF620C0 71 | while (PXI_EMPTY || (pxi_recv() != 0x964536)); 72 | pxi_send(0x44837); 73 | 74 | pxi_send(FW_TID_HIGH); 75 | pxi_send(FW_TID_LOW); 76 | 77 | DEBUG(0xFF0000); //blue 78 | } 79 | 80 | DEBUG(0xFF007F); //purple 81 | 82 | /* FIRMLaunchHax */ 83 | ARM11Entry = 0; 84 | ARM9_TEST = 0xDEADC0DE; 85 | pxi_send(0x44846); 86 | 87 | volatile unsigned int reg = 0; 88 | while (ARM9_TEST == 0xDEADC0DE) //wait for arm9 to start writing a new arm11 binary 89 | __asm__ volatile ("MCR P15, 0, %0, C7, C6, 0" :: "r"(reg)); //invalidate dcache 90 | 91 | ARM9Entry = 0x23F00000; 92 | LCDCFG_FILL = 0; 93 | while (!ARM11Entry); 94 | ((void (*)())ARM11Entry)(); 95 | } 96 | -------------------------------------------------------------------------------- /source/global_backdoor.s: -------------------------------------------------------------------------------- 1 | .global svc_30 2 | .type svc_30, %function 3 | svc_30: 4 | push {r0, r1, r2} 5 | mov r3, sp 6 | adr r0, svc_30_callback 7 | svc 0x30 8 | add sp, sp, #8 9 | ldr r0, [sp], #4 10 | bx lr 11 | svc_30_callback: 12 | cpsid aif 13 | ldr r2, [r3], #4 14 | ldmfd r3!, {r0, r1} 15 | push {r3, lr} 16 | blx r2 17 | pop {r3, lr} 18 | str r0, [r3, #-4]! 19 | mov r0, #0 20 | bx lr 21 | 22 | 23 | .global svcGlobalBackdoor 24 | .type svcGlobalBackdoor, %function 25 | svcGlobalBackdoor: 26 | svc 0x30 27 | bx lr 28 | 29 | 30 | .global checkSvcGlobalBackdoor 31 | .type checkSvcGlobalBackdoor, %function 32 | checkSvcGlobalBackdoor: 33 | adr r0, checkSvcGlobalBackdoor_callback 34 | mov r3, #0 35 | svc 0x30 36 | mov r0, r3 37 | bx lr 38 | checkSvcGlobalBackdoor_callback: 39 | cpsid aif 40 | mov r3, #1 41 | bx lr 42 | -------------------------------------------------------------------------------- /source/kernel_patches.c: -------------------------------------------------------------------------------- 1 | #include "kernel_patches.h" 2 | #include <3ds.h> 3 | #include 4 | #include 5 | #include "global_backdoor.h" 6 | 7 | 8 | // Shamelessly copied from 9 | // https://github.com/Steveice10/memchunkhax2/blob/master/source/memchunkhax2.c#L16 10 | 11 | #define OLDNEW(x) (g_is_new3ds ? x ## _NEW : x ## _OLD) 12 | 13 | #define CURRENT_KTHREAD (*((u8**)0xFFFF9000)) 14 | #define CURRENT_KPROCESS (*((u8**)0xFFFF9004)) 15 | 16 | #define SVC_ACL_SIZE (0x10) 17 | 18 | #define KPROCESS_ACL_START_OLD (0x88) 19 | #define KPROCESS_ACL_START_NEW (0x90) 20 | 21 | #define KPROCESS_PID_OFFSET_OLD (0xB4) 22 | #define KPROCESS_PID_OFFSET_NEW (0xBC) 23 | 24 | #define KTHREAD_THREADPAGEPTR_OFFSET (0x8C) 25 | #define KSVCTHREADAREA_BEGIN_OFFSET (0xC8) 26 | 27 | 28 | static bool g_is_new3ds = false; 29 | static u32 g_original_pid = 0; 30 | 31 | static void K_PatchPID(void) 32 | { 33 | u8 *proc = CURRENT_KPROCESS; 34 | u32 *pidPtr = (u32*)(proc + OLDNEW(KPROCESS_PID_OFFSET)); 35 | 36 | g_original_pid = *pidPtr; 37 | 38 | // We're now PID zero, all we have to do is reinitialize the service manager in user-mode. 39 | *pidPtr = 0; 40 | } 41 | 42 | static void K_RestorePID(void) 43 | { 44 | u8 *proc = CURRENT_KPROCESS; 45 | u32 *pidPtr = (u32*)(proc + OLDNEW(KPROCESS_PID_OFFSET)); 46 | 47 | // Restore the original PID 48 | *pidPtr = g_original_pid; 49 | } 50 | 51 | static void K_PatchACL(void) 52 | { 53 | // Patch the process first (for newly created threads). 54 | u8 *proc = CURRENT_KPROCESS; 55 | u8 *procacl = proc + OLDNEW(KPROCESS_ACL_START); 56 | memset(procacl, 0xFF, SVC_ACL_SIZE); 57 | 58 | // Now patch the current thread. 59 | u8 *thread = CURRENT_KTHREAD; 60 | u8 *thread_pageend = *(u8**)(thread + KTHREAD_THREADPAGEPTR_OFFSET); 61 | u8 *thread_page = thread_pageend - KSVCTHREADAREA_BEGIN_OFFSET; 62 | memset(thread_page, 0xFF, SVC_ACL_SIZE); 63 | } 64 | 65 | 66 | void initsrv_allservices(void) 67 | { 68 | APT_CheckNew3DS(&g_is_new3ds); 69 | 70 | printf("Patching PID\n"); 71 | svc_30(K_PatchPID); 72 | 73 | printf("Reiniting srv:\n"); 74 | srvExit(); 75 | srvInit(); 76 | 77 | printf("Restoring PID\n"); 78 | svc_30(K_RestorePID); 79 | } 80 | 81 | void patch_svcaccesstable(void) 82 | { 83 | APT_CheckNew3DS(&g_is_new3ds); 84 | 85 | printf("Patching SVC access table\n"); 86 | svc_30(K_PatchACL); 87 | } 88 | -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | #include 3 | #include 4 | #include 5 | 6 | #include "global_backdoor.h" 7 | #include "kernel_patches.h" 8 | 9 | #define FCRAM(x) (void *)((kver < SYSTEM_VERSION(2, 44, 6)) ? (0xF0000000 + x) : (0xE0000000 + x)) //0x20000000 10 | #define AXIWRAM(x) (void *)((kver < SYSTEM_VERSION(2, 44, 6)) ? (0xEFF00000 + x) : (0xDFF00000 + x)) //0x1FF00000 11 | #define KMEMORY ((u32 *)AXIWRAM(0xF4000)) //0x1FFF4000 12 | 13 | //if condition 'x' is true, print string 'y' and exit 14 | #define PANIC(x,y) if (x){\ 15 | error = y;\ 16 | goto exit;\ 17 | } 18 | #define DEBUG(x) if (debug) printf(" %s\n", x); 19 | 20 | static Result pm_res = -1; 21 | static s32 backdoor_res = -1; 22 | static char *error = "FAILED TO RELOAD!"; 23 | 24 | static void *payload_buf = NULL; 25 | static u32 payload_size = 0; 26 | 27 | static bool debug = false; 28 | static u32 kver = 0; 29 | 30 | extern void gfxSetFramebufferInfo(gfxScreen_t screen, u8 id); 31 | 32 | s32 patch_arm11_codeflow(void){ 33 | __asm__ volatile ( "CPSID AIF\n" "CLREX" ); 34 | 35 | memcpy(FCRAM(0x3F00000), payload_buf, payload_size); //Huge payloads seem to crash when being copied (?) 36 | memcpy(FCRAM(0x3FFF000), payload_buf + 0xFF000, 0xE20); 37 | 38 | for (unsigned int i = 0; i < 0x2000/4; i++){ 39 | if (KMEMORY[i] == 0xE12FFF14 && KMEMORY[i+2] == 0xE3A01000){ //hook arm11 launch 40 | KMEMORY[i+3] = 0xE51FF004; //LDR PC, [PC,#-4] 41 | KMEMORY[i+4] = 0x23FFF000; 42 | backdoor_res = 0; 43 | break; 44 | } 45 | } 46 | 47 | __asm__ volatile ( //flush & invalidate the caches 48 | "MOV R0, #0\n" 49 | "MCR P15, 0, R0, C7, C10, 0\n" 50 | "MCR P15, 0, R0, C7, C5, 0" 51 | ); 52 | 53 | return backdoor_res; 54 | } 55 | 56 | u32 FileRead(void *buffer, const char *filename, u32 maxsize){ //lol 57 | u32 size = 0; 58 | FILE * handle = fopen(filename, "rb"); 59 | if (handle){ 60 | fseek(handle, 0, SEEK_END); 61 | size = ftell(handle); 62 | rewind(handle); 63 | 64 | if (size && (size <= maxsize)) fread(buffer, 1, size, handle); 65 | else size = 0; 66 | fclose(handle); 67 | } 68 | return size; 69 | } 70 | 71 | int main(int argc, char **argv){ 72 | gfxInitDefault(); 73 | fsInit(); 74 | aptInit(); 75 | sdmcInit(); 76 | romfsInit(); 77 | 78 | kver = osGetKernelVersion(); 79 | if (kver > SYSTEM_VERSION(2, 53, 0)) //11.4^ 80 | PANIC(true, "UNSUPPORTED FIRMWARE!"); 81 | 82 | if (checkSvcGlobalBackdoor()){ 83 | initsrv_allservices(); 84 | patch_svcaccesstable(); 85 | } 86 | 87 | PANIC(pmInit(), "PM INIT FAILED!"); 88 | 89 | hidScanInput(); 90 | if (hidKeysDown() & KEY_B){ //Hold B to enable debugging 91 | consoleInit(GFX_TOP, NULL); 92 | printf("\n\x1b[37;1m"); 93 | debug = true; 94 | } 95 | 96 | /* Map the Payloads */ 97 | 98 | DEBUG("Allocating memory..."); 99 | payload_buf = memalign(0x1000, 0x100000); 100 | PANIC(!payload_buf, "FAILED TO ALLOCATE MEMORY!"); 101 | 102 | DEBUG("Reading payload..."); 103 | payload_size = FileRead(payload_buf, "romfs:/arm9.bin", 0xFF000); //check for a bundled arm9 payload 104 | if (!payload_size) payload_size = FileRead(payload_buf, "sdmc:/safehaxpayload.bin", 0xFF000); 105 | if (!payload_size) payload_size = FileRead(payload_buf, "sdmc:/arm9.bin", 0xFF000); 106 | if (!payload_size) payload_size = FileRead(payload_buf, "sdmc:/arm9loaderhax.bin", 0xFF000); 107 | PANIC(!payload_size, "FAILED TO READ THE PAYLOAD!"); 108 | 109 | DEBUG("Reading ARM11 payload..."); 110 | PANIC(!FileRead(payload_buf + 0xFF000, "romfs:/arm11.bin", 0xE00), "FAILED TO READ THE ARM11 PAYLOAD!"); 111 | 112 | /* Setup Framebuffers */ //https://github.com/mid-kid/CakeBrah/blob/master/source/brahma.c#L364 113 | 114 | DEBUG("Setting framebuffers..."); 115 | *((u32 *)(payload_buf + 0xFFE00)) = (u32)gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL) + 0xC000000; 116 | *((u32 *)(payload_buf + 0xFFE04)) = (u32)gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL) + 0xC000000; 117 | *((u32 *)(payload_buf + 0xFFE08)) = (u32)gfxGetFramebuffer(GFX_BOTTOM, 0, NULL, NULL) + 0xC000000; 118 | gfxSwapBuffers(); 119 | 120 | /* Patch ARM11 */ 121 | 122 | DEBUG("Patching ARM11..."); 123 | 124 | *((bool *)(payload_buf + 0xFFE10)) = debug; //for safehax post-reload color-fill-based debugging 125 | 126 | if (checkSvcGlobalBackdoor()) //use this where applicable 127 | svcGlobalBackdoor(patch_arm11_codeflow); 128 | else 129 | svcBackdoor(patch_arm11_codeflow); 130 | 131 | PANIC(backdoor_res, "FAILED TO PATCH THE KERNEL!"); 132 | 133 | /* Relaunch Firmware */ //This will clear the global flag preventing SAFE_MODE launch. 134 | 135 | DEBUG("Reloading firmware..."); 136 | pm_res = PM_LaunchFIRMSetParams(2, 0, NULL); 137 | 138 | exit: 139 | if (pm_res){ 140 | if (!debug) consoleInit(GFX_TOP, NULL); 141 | printf("\n\x1b[31;1m [!] %s\n", error); 142 | printf("\n\x1b[37;1m Press [START] to exit."); 143 | 144 | while (aptMainLoop()){ 145 | hidScanInput(); 146 | 147 | if (hidKeysDown() & KEY_START) break; 148 | 149 | gfxFlushBuffers(); 150 | gfxSwapBuffers(); 151 | 152 | gspWaitForVBlank(); 153 | } 154 | } else if (debug){ //fix framebuffer on exit 155 | consoleClear(); 156 | gfxSetScreenFormat(GFX_TOP, GSP_BGR8_OES); 157 | gfxSetFramebufferInfo(GFX_TOP, 0); 158 | gfxFlushBuffers(); 159 | gfxSwapBuffers(); 160 | gspWaitForVBlank(); 161 | } 162 | 163 | pmExit(); 164 | romfsExit(); 165 | sdmcExit(); 166 | aptExit(); 167 | fsExit(); 168 | gfxExit(); 169 | return 0; 170 | } 171 | --------------------------------------------------------------------------------