├── Makefile ├── README.md ├── bin └── getfwimg.py ├── patches ├── 0x10700000.s ├── 0x10800000.s ├── 0x4000000.s ├── 0x5000000.s ├── 0x8120000.s └── font.bin ├── scripts └── anpack.py ├── sections └── gensections.py └── wupserver ├── Makefile ├── ccd00.ld ├── ccd00.specs ├── source ├── crt0.s ├── font.c ├── font_bin.h ├── fsa.c ├── fsa.h ├── imports.c ├── imports.h ├── main.c ├── net_ifmgr_ncl.c ├── net_ifmgr_ncl.h ├── socket.c ├── socket.h ├── svc.h ├── svc.s ├── text.c ├── text.h └── types.h └── wupclient.py /Makefile: -------------------------------------------------------------------------------- 1 | INPUT := bin/fw.img.full.bin 2 | # SECTIONS := 0x10700000 0x10800000 0x8120000 0x5000000 0x5100000 0x8140000 0x4000000 3 | # BSS_SECTIONS := 0x10835000 0x5074000 0x8150000 4 | # Only enable wupserver for now 5 | SECTIONS := 0x5000000 0x5100000 0x4000000 6 | BSS_SECTIONS := 0x5074000 7 | INPUT_SECTIONS := $(addprefix sections/, $(addsuffix .bin, $(SECTIONS))) 8 | PATCHED_SECTIONS := $(addprefix patched_sections/, $(addsuffix .bin, $(SECTIONS))) 9 | 10 | .PHONY: all clean wupserver/wupserver.bin 11 | 12 | all: fw.img 13 | 14 | $(INPUT): 15 | @cd bin && python getfwimg.py 16 | 17 | sections/%.bin: $(INPUT) 18 | @mkdir -p sections 19 | @cd sections && python gensections.py 20 | @python scripts/anpack.py -in $(INPUT) -e $*,$@ 21 | 22 | extract: $(INPUT_SECTIONS) 23 | 24 | wupserver/wupserver.bin: 25 | @cd wupserver && make 26 | 27 | patched_sections/%.bin: sections/%.bin patches/%.s wupserver/wupserver.bin 28 | @mkdir -p patched_sections 29 | @echo patches/$*.s 30 | @armips patches/$*.s 31 | 32 | patch: $(PATCHED_SECTIONS) 33 | 34 | fw.img: $(INPUT) $(PATCHED_SECTIONS) 35 | @python scripts/anpack.py -in $(INPUT) -out fw.img $(foreach s,$(SECTIONS),-r $(s),patched_sections/$(s).bin) $(foreach s,$(BSS_SECTIONS),-b $(s),patched_sections/$(s).bin) 36 | 37 | clean: 38 | @rm -f fw.img 39 | @rm -f sections/*.bin 40 | @rm -fr patched_sections 41 | @make -C wupserver clean 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iosuhax 2 | 3 | this repo contains some of the tools I wrote when working on the wii u. iosuhax is essentially a set of patches for IOSU which provides extra features which can be useful for developers. I'm releasing this because I haven't really touched it since the beginning of january and don't plan on getting back to it. 4 | 5 | iosuhax does not contain any exploits or information about vulns o anything. just a custom firmware kind of thing. 6 | 7 | iosuhax current only supports fw 5.5.x i think. 8 | 9 | i wrote all the code here but iosuhax would not have been possible without the support of plutoo, yellows8, naehrwert and derrek. 10 | 11 | # features 12 | 13 | iosuhax is pretty barebones, it's mainly just the following : 14 | - software nand dumping (bunch of ways to do this, dumps slc, slccmpt and mlc, either raw or filetree or something in between) 15 | - redNAND (redirection of nand read/writes to SD card instead of actual NAND chips) 16 | - remote shell for development and experimentation (cf wupserver and wupclient, it's super useful) 17 | - some basic ARM debugging stuff (guru meditation screen) 18 | 19 | # todo 20 | 21 | I don't plan on doing any of this at this point, but the next things I was going to do for this were : 22 | - make this version-independent by replacing hardcoded offset with auto-located ones, similar to my 3ds *hax ROP stuff 23 | - put together a menu for settings and nand-dumping so that there's no need to build with different things enabled (menu would have used power/eject buttons) 24 | 25 | # how to use 26 | 27 | honestly I don't even remember all the details so anyone who's serious about using this will probably have to ask me for help if they can't figure it out, but the gist of it is : 28 | - decrypt your ancast image, prepend the raw signature header stuff to it and place it in ./bin/fw.img.full.bin 29 | - open up ./scripts/anpack.py, add your ancast keys in there 30 | - make 31 | 32 | might be missing some steps, especially for building wupserver. you're going to need devkitarm and latest armips (built from armips git probably, pretty sure this relies on some new features I asked Kingcom to add (blame nintendo for doing big endian arm)). 33 | 34 | also, fair warning : do NOT blindly use this. read the patches. running this with the wrong options enabled can/will brick your console. this release is oriented towards devs, not end users. 35 | 36 | # license 37 | 38 | feel free to use any of this code for your own projects, as long as : 39 | - you give proper credit to the original creator 40 | - you don't use any of it for commercial purposes 41 | - you consider sharing your improvements by making the code available (not required but appreciated) 42 | 43 | seriously though if you try to make money off this i will fucking end you 44 | 45 | have fun ! 46 | -------------------------------------------------------------------------------- /bin/getfwimg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # insert keys here, your keys were set correctly if the crc32 of the fw.img 4 | # is d674201b and the crc32 of the fw.img.full.bin is 9f2c91ff in the end 5 | wiiu_common_key = "you have to insert this yourself" 6 | starbuck_ancast_key = "you have to insert this yourself" 7 | 8 | # Don't edit past here 9 | 10 | import os, sys, zlib, binascii 11 | import codecs 12 | from Crypto.Cipher import AES 13 | 14 | try: 15 | from urllib.request import urlopen 16 | except ImportError: 17 | from urllib2 import urlopen 18 | 19 | print("somewhat simple 5.5.1 fw.img downloader") 20 | 21 | otpbinpath = os.path.abspath("../../otp.bin") 22 | if os.path.exists(otpbinpath): 23 | with open(otpbinpath,'rb') as f: 24 | f.seek(0x90) 25 | starbuck_ancast_key = binascii.hexlify(f.read(16)) 26 | f.seek(0xE0) 27 | wiiu_common_key = binascii.hexlify(f.read(16)) 28 | print("Using keys from otp.bin") 29 | else: 30 | print("Using keys edited into this file") 31 | 32 | #prepare keys 33 | wiiu_common_key = codecs.decode(wiiu_common_key, 'hex') 34 | starbuck_ancast_key = codecs.decode(starbuck_ancast_key, 'hex') 35 | 36 | if zlib.crc32(wiiu_common_key) & 0xffffffff != 0x7a2160de: 37 | print("wiiu_common_key is wrong") 38 | sys.exit(1) 39 | 40 | if zlib.crc32(starbuck_ancast_key) & 0xffffffff != 0xe6e36a34: 41 | print("starbuck_ancast_key is wrong") 42 | sys.exit(1) 43 | 44 | print("downloading osv10 cetk") 45 | 46 | #download osv10 cetk 47 | f = urlopen("http://ccs.cdn.wup.shop.nintendo.net/ccs/download/000500101000400A/cetk") 48 | d = f.read() 49 | if not d: 50 | print("cetk download failed!") 51 | sys.exit(2) 52 | 53 | #get cetk encrypted key 54 | enc_key = d[0x1BF:0x1BF + 0x10] 55 | 56 | #decrypt cetk key using wiiu common key 57 | iv = codecs.decode("000500101000400A0000000000000000", 'hex') 58 | cipher = AES.new(wiiu_common_key, AES.MODE_CBC,iv) 59 | dec_key = cipher.decrypt(enc_key) 60 | 61 | print("downloading fw.img") 62 | #download encrypted 5.5.1 fw img 63 | 64 | f = urlopen("http://ccs.cdn.wup.shop.nintendo.net/ccs/download/000500101000400A/0000136e") 65 | if not f: 66 | print("0000136e download failed!") 67 | sys.exit(2) 68 | 69 | print("decrypt first") 70 | #decrypt fw img with our decrypted key 71 | with open("fw.img","wb") as fout: 72 | iv = codecs.decode("00090000000000000000000000000000", "hex") 73 | cipher = AES.new(dec_key, AES.MODE_CBC, iv) 74 | while True: 75 | dec = f.read(0x40000) 76 | if len(dec) < 0x10: 77 | break 78 | enc = cipher.decrypt(dec) 79 | fout.write(enc) 80 | 81 | with open('fw.img', 'rb') as f: 82 | if (zlib.crc32(f.read()) & 0xffffffff) != 0xd674201b: 83 | print("fw.img is corrupt, try again") 84 | sys.exit(2) 85 | 86 | print("decrypt second") 87 | #decrypt ancast image with ancast key and (for now) wrong iv 88 | with open("fw.img", "rb") as f: 89 | with open("fw.img.full.bin","wb") as fout: 90 | fout.write(f.read(0x200)) 91 | fake_iv = codecs.decode("00000000000000000000000000000000", "hex") 92 | cipher = AES.new(starbuck_ancast_key, AES.MODE_CBC, fake_iv) 93 | while True: 94 | dec = f.read(0x40000) 95 | if len(dec) < 0x10: 96 | break 97 | enc = cipher.decrypt(dec) 98 | fout.write(enc) 99 | 100 | print("decrypt third") 101 | #fix up ancast image with correct iv 102 | with open('fw.img.full.bin', 'rb+') as f: 103 | #grab iv from decrypted image 104 | f.seek(0x86B3C,0) 105 | starbuck_ancast_iv = f.read(0x10) 106 | if zlib.crc32(starbuck_ancast_iv) & 0xffffffff != 0xb3f79023: 107 | print("starbuck_ancast_iv is wrong") 108 | sys.exit(1) 109 | #save key and iv for later usage 110 | with open('../scripts/keys.py', 'w') as keys_store: 111 | keys_store.write("key=\""+codecs.encode(starbuck_ancast_key, 'hex').decode()+"\"\n") 112 | keys_store.write("iv=\""+codecs.encode(starbuck_ancast_iv, 'hex').decode()+"\"\n") 113 | #calculate correct first bytes with correct iv 114 | f.seek(0x200,0) 115 | starbuck_ancast_iv = bytearray(starbuck_ancast_iv) 116 | partToXor = bytearray(f.read(0x10)) 117 | result = bytearray(0x10) 118 | for i in range(0x10): 119 | result[i] = partToXor[i]^starbuck_ancast_iv[i] 120 | f.seek(0x200,0) 121 | #write in corrected bytes 122 | f.write(result) 123 | 124 | with open('fw.img.full.bin', 'rb') as f: 125 | if (zlib.crc32(f.read()) & 0xffffffff) != 0x9f2c91ff: 126 | print("fw.img.full.bin is corrupt, try again with better keys") 127 | sys.exit(2) 128 | 129 | print("done!") 130 | -------------------------------------------------------------------------------- /patches/0x10700000.s: -------------------------------------------------------------------------------- 1 | .arm.big 2 | 3 | .open "sections/0x10700000.bin","patched_sections/0x10700000.bin",0x10700000 4 | 5 | SECTION_BASE equ 0x10700000 6 | SECTION_SIZE equ 0x000F8200 7 | CODE_BASE equ (SECTION_BASE + SECTION_SIZE) 8 | 9 | FRAMEBUFFER_ADDRESS equ (0x14000000+0x38C0000) 10 | FRAMEBUFFER_STRIDE equ (0xE00) 11 | CHARACTER_MULT equ (2) 12 | CHARACTER_SIZE equ (8*CHARACTER_MULT) 13 | 14 | USB_BASE_SECTORS equ (0x2720000) 15 | SLC_BASE_SECTORS equ (0x2F20000) 16 | SLCCMPT_BASE_SECTORS equ (0x3020000) 17 | MLC_BASE_SECTORS equ (0x3200000) 18 | SYSLOG_BASE_SECTORS equ (0x6D00000) 19 | DUMPDATA_BASE_SECTORS equ (SYSLOG_BASE_SECTORS + (0x40000 / 0x200)) 20 | 21 | FS_BSS_START equ (0x10835000 + 0x1406554) 22 | FS_MMC_SDCARD_STRUCT equ (0x1089B9F8) 23 | FS_MMC_MLC_STRUCT equ (0x1089B948) 24 | 25 | FS_SNPRINTF equ 0x107F5FB4 26 | FS_MEMCPY equ 0x107F4F7C 27 | FS_MEMSET equ 0x107F5018 28 | FS_SYSLOG_OUTPUT equ 0x107F0C84 29 | FS_SLEEP equ 0x1071D668 30 | FS_GETMDDEVICEBYID equ 0x107187C4 31 | FS_SDIO_DOREADWRITECOMMAND equ 0x10718A8C 32 | FS_SDIO_DOCOMMAND equ 0x1071C958 33 | FS_MMC_DEVICEINITSOMETHING equ 0x1071992C 34 | FS_MMC_DEVICEINITSOMETHING2 equ 0x1071A4A4 35 | FS_MMC_DEVICEINITSOMETHING3 equ 0x10719F60 36 | FS_SVC_CREATEMUTEX equ 0x107F6BBC 37 | FS_SVC_ACQUIREMUTEX equ 0x107F6BC4 38 | FS_SVC_RELEASEMUTEX equ 0x107F6BCC 39 | FS_SVC_DESTROYMUTEX equ 0x107F6BD4 40 | FS_USB_READ equ 0x1077F1C0 41 | FS_USB_WRITE equ 0x1077F35C 42 | FS_SLC_READ1 equ 0x107B998C 43 | FS_SLC_READ2 equ 0x107B98FC 44 | FS_SLC_WRITE1 equ 0x107B9870 45 | FS_SLC_WRITE2 equ 0x107B97E4 46 | FS_SLC_HANDLEMESSAGE equ 0x107B9DE4 47 | FS_MLC_READ1 equ 0x107DC760 48 | FS_MLC_READ2 equ 0x107DCDE4 49 | FS_MLC_WRITE1 equ 0x107DC0C0 50 | FS_MLC_WRITE2 equ 0x107DC73C 51 | FS_SDCARD_READ1 equ 0x107BDDD0 52 | FS_SDCARD_WRITE1 equ 0x107BDD60 53 | FS_ISFS_READWRITEBLOCKS equ 0x10720324 54 | FS_CRYPTO_HMAC equ 0x107F3798 55 | FS_RAW_READ1 equ 0x10732BC0 56 | FS_REGISTERMDPHYSICALDEVICE equ 0x10718860 57 | 58 | ; patches start here 59 | 60 | .org 0x107F0B68 61 | bl syslogOutput_hook 62 | 63 | ; null out references to slcSomething1 and slcSomething2 64 | ; (nulling them out is apparently ok; more importantly, i'm not sure what they do and would rather get a crash than unwanted slc-writing) 65 | .org 0x107B96B8 66 | .word 0x00000000 67 | .word 0x00000000 68 | 69 | ; in createDevThread 70 | .org 0x10700294 71 | b createDevThread_hook 72 | ; ; usb redirection 73 | ; .org FS_USB_READ 74 | ; b usbRead_patch 75 | ; .org FS_USB_WRITE 76 | ; b usbWrite_patch 77 | ; slc redirection 78 | .org FS_SLC_READ1 79 | b slcRead1_patch 80 | .org FS_SLC_READ2 81 | b slcRead2_patch 82 | .org FS_SLC_WRITE1 83 | b slcWrite1_patch 84 | .org FS_SLC_WRITE2 85 | b slcWrite2_patch 86 | .org 0x107206F0 87 | mov r0, #0 ; nop out hmac memcmp 88 | ; mlc redirection 89 | .org FS_SDCARD_READ1 90 | b sdcardRead_patch 91 | .org FS_SDCARD_WRITE1 92 | b sdcardWrite_patch 93 | ; FS_GETMDDEVICEBYID 94 | .org FS_GETMDDEVICEBYID + 0x8 95 | bl getMdDeviceById_hook 96 | ; call to FS_REGISTERMDPHYSICALDEVICE in mdMainThreadEntrypoint 97 | .org 0x107BD81C 98 | bl registerMdDevice_hook 99 | ; mdExit : patch out sdcard deinitialization 100 | .org 0x107BD374 101 | bx lr 102 | 103 | ; ; mlcRead1 logging 104 | ; .org FS_MLC_READ1 + 0x4 105 | ; bl mlcRead1_dbg 106 | ; .org 0x107DC7D8 107 | ; b mlcRead1_end_hook 108 | 109 | ; some selective logging function : enable all the logging ! 110 | .org 0x107F5720 111 | b FS_SYSLOG_OUTPUT 112 | 113 | ; our own custom codes starts here 114 | .org CODE_BASE 115 | sdcard_init: 116 | ; this should run *after* /dev/mmc thread is created 117 | push {lr} 118 | 119 | ; first we create our synchronization stuff 120 | mov r0, #1 121 | mov r1, #1 122 | bl FS_SVC_CREATEMUTEX 123 | ldr r1, =sdcard_access_mutex 124 | str r0, [r1] 125 | 126 | ldr r1, =dumpdata_offset 127 | mov r0, #0 128 | str r0, [r1] 129 | 130 | ; then we sleep until /dev/mmc is done initializing sdcard (TODO : better synchronization here) 131 | mov r0, #1000 132 | bl FS_SLEEP 133 | 134 | ; finally we set some flags to indicate sdcard is ready for use 135 | ldr r0, =FS_MMC_SDCARD_STRUCT 136 | ldr r1, [r0, #0x24] 137 | orr r1, #0x20 138 | str r1, [r0, #0x24] 139 | ldr r1, [r0, #0x28] 140 | bic r1, #0x4 141 | str r1, [r0, #0x28] 142 | 143 | pop {pc} 144 | 145 | mlc_init: 146 | ; this should run *after* /dev/mmc thread is created (and after sdcard_init) 147 | ; this should also only be run when you want to dump mlc; this will cause the physical mlc device to be inaccessible to regular FS code 148 | push {lr} 149 | 150 | ; finally we set some flags to indicate sdcard is ready for use 151 | ldr r0, =FS_MMC_MLC_STRUCT 152 | ldr r1, [r0, #0x24] 153 | orr r1, #0x20 154 | str r1, [r0, #0x24] 155 | ldr r1, [r0, #0x28] 156 | bic r1, #0x4 157 | str r1, [r0, #0x28] 158 | 159 | pop {pc} 160 | 161 | ; r0 : bool read (0 = read, not 0 = write), r1 : data_ptr, r2 : cnt, r3 : block_size, sparg0 : offset_blocks, sparg1 : out_callback_arg2, sparg2 : device_id 162 | sdcard_readwrite: 163 | sdcard_readwrite_stackframe_size equ (4*4+12*4) 164 | push {r4,r5,r6,lr} 165 | sub sp, #12*4 166 | 167 | ; pointer for command paramstruct 168 | add r4, sp, #0xC 169 | ; pointer for mutex (sp + 0x8 will be callback's arg2) 170 | add r5, sp, #0x4 171 | ; offset_blocks 172 | ldr r6, [sp, #sdcard_readwrite_stackframe_size] 173 | 174 | ; first of all, grab sdcard mutex 175 | push {r0,r1,r2,r3} 176 | ldr r0, =sdcard_access_mutex 177 | ldr r0, [r0] 178 | mov r1, #0 179 | bl FS_SVC_ACQUIREMUTEX 180 | ; also create a mutex for synchronization with end of operation... 181 | mov r0, #1 182 | mov r1, #1 183 | bl FS_SVC_CREATEMUTEX 184 | str r0, [r5] 185 | ; ...and acquire it 186 | mov r1, #0 187 | bl FS_SVC_ACQUIREMUTEX 188 | pop {r0,r1,r2,r3} 189 | 190 | ; block_size needs to be equal to sector_size (0x200) 191 | sdcard_readwrite_block_size_adjust: 192 | cmp r3, #0x200 193 | movgt r3, r3, lsr 1 ; block_size >>= 1; 194 | movgt r2, r2, lsl 1 ; cnt <<= 1; 195 | movgt r6, r6, lsl 1 ; offset_blocks <<= 1; 196 | bgt sdcard_readwrite_block_size_adjust 197 | 198 | ; build rw command paramstruct 199 | str r2, [r4, #0x00] ; cnt 200 | str r3, [r4, #0x04] ; block_size 201 | cmp r0, #0 202 | movne r0, #0x3 ; read operation 203 | str r0, [r4, #0x08] ; command type 204 | str r1, [r4, #0x0C] ; data_ptr 205 | mov r0, #0 206 | str r0, [r4, #0x10] ; offset_high 207 | str r6, [r4, #0x14] ; offset_low 208 | str r0, [r4, #0x18] ; callback 209 | str r0, [r4, #0x1C] ; callback_arg 210 | mvn r0, #0 211 | str r0, [r4, #0x20] ; -1 212 | 213 | ; setup parameters 214 | ldr r0, [sp, #sdcard_readwrite_stackframe_size+0x8] ; device_identifier : sdcard (real one is 0x43, but patch makes 0xDA valid) 215 | mov r1, r4 ; paramstruct 216 | mov r2, r6 ; offset_blocks 217 | adr r3, sdcard_readwrite_callback ; callback 218 | str r5, [sp] ; callback_arg (mutex ptr) 219 | 220 | ; call readwrite function 221 | bl FS_SDIO_DOREADWRITECOMMAND 222 | mov r4, r0 223 | cmp r0, #0 224 | bne sdcard_readwrite_skip_wait 225 | 226 | ; wait for callback to give the go-ahead 227 | ldr r0, [r5] 228 | mov r1, #0 229 | bl FS_SVC_ACQUIREMUTEX 230 | ldr r0, [r5, #0x4] 231 | ldr r1, [sp, #sdcard_readwrite_stackframe_size+0x4] 232 | cmp r1, #0 233 | strne r0, [r1] 234 | sdcard_readwrite_skip_wait: 235 | 236 | ; finally, release sdcard mutexes 237 | ldr r0, [r5] 238 | bl FS_SVC_DESTROYMUTEX 239 | ldr r0, =sdcard_access_mutex 240 | ldr r0, [r0] 241 | bl FS_SVC_RELEASEMUTEX 242 | 243 | ; return 244 | mov r0, r4 245 | add sp, #12*4 246 | pop {r4,r5,r6,pc} 247 | 248 | ; release mutex to let everyone know we're done 249 | sdcard_readwrite_callback: 250 | str r1, [r0, #4] 251 | ldr r0, [r0] 252 | b FS_SVC_RELEASEMUTEX 253 | 254 | createDevThread_hook: 255 | push {r0} 256 | ; check if we were initializing /dev/mmc 257 | ldr r0, [r4, #0x8] 258 | cmp r0, #0xD 259 | bne createDevThread_hook_skip1 260 | 261 | bl sdcard_init 262 | 263 | bl clear_screen 264 | 265 | mov r0, #20 266 | mov r1, #20 267 | adr r2, createDevThread_hook_welcome 268 | ldr r3, =0x050BD000 - 4 269 | ldr r3, [r3] 270 | bl _printf 271 | 272 | createDevThread_hook_skip1: 273 | 274 | ; check if we were initializing /dev/fla 275 | ldr r0, [r4, #0x8] 276 | cmp r0, #0x7 277 | bne createDevThread_hook_skip2 278 | 279 | ; b dumper_main 280 | 281 | createDevThread_hook_skip2: 282 | pop {r0,r4-r8,pc} 283 | createDevThread_hook_welcome: 284 | .ascii "welcome to redNAND %08X" 285 | .byte 0x00 286 | .align 0x4 287 | 288 | getMdDeviceById_hook: 289 | mov r4, r0 290 | cmp r0, #0xDA ; magic id (sdcard) 291 | ldreq r0, =FS_MMC_SDCARD_STRUCT 292 | popeq {r4,r5,pc} 293 | cmp r0, #0xAB ; magic id (mlc) 294 | ldreq r0, =FS_MMC_MLC_STRUCT 295 | popeq {r4,r5,pc} 296 | bx lr ; return if different 297 | 298 | registerMdDevice_hook: 299 | push {r4,lr} 300 | 301 | cmp r0, #0 302 | beq registerMdDevice_hook_skip 303 | 304 | ldr r3, [r0, #0x8] ; mmc device struct ptr 305 | ldr r4, =FS_MMC_SDCARD_STRUCT 306 | cmp r3, r4 307 | bne registerMdDevice_hook_skip 308 | 309 | ; sdcard ! fix stuff up so that registration can happen ok 310 | 311 | push {r0-r3} 312 | 313 | ; first lock that mutex 314 | ldr r0, =sdcard_access_mutex 315 | ldr r0, [r0] 316 | mov r1, #0 317 | bl FS_SVC_ACQUIREMUTEX 318 | 319 | ; then, clear the flag we set earlier (that FS_REGISTERMDPHYSICALDEVICE will set back anyway) 320 | ldr r0, =FS_MMC_SDCARD_STRUCT 321 | ldr r1, [r0, #0x24] 322 | bic r1, #0x20 323 | str r1, [r0, #0x24] 324 | 325 | pop {r0-r3} 326 | 327 | ; register it ! 328 | bl FS_REGISTERMDPHYSICALDEVICE 329 | mov r4, r0 330 | 331 | ; finally, release the mutex 332 | ldr r0, =sdcard_access_mutex 333 | ldr r0, [r0] 334 | bl FS_SVC_RELEASEMUTEX 335 | 336 | mov r0, r4 337 | pop {r4,pc} 338 | 339 | registerMdDevice_hook_skip: 340 | ; not sdcard 341 | bl FS_REGISTERMDPHYSICALDEVICE 342 | pop {r4,pc} 343 | 344 | ; read1(void *physical_device_info, int offset_high, int offset_low, int cnt, int block_size, void *data_outptr, void *callback, int callback_parameter) 345 | ; readWriteCallback_patch(bool read, int offset_offset, int offset_low, int cnt, int block_size, void *data_outptr, void *callback, int callback_parameter) 346 | readWriteCallback_patch: 347 | readWriteCallback_patch_stackframe_size equ (7*4) 348 | push {r0,r1,r2,r3,r4,r5,lr} 349 | mov r5, #0xDA 350 | str r5, [sp, #0x8] ; device id (sdcard) 351 | add r5, sp, #0xC ; out_callback_arg2 dst 352 | add r2, r1 353 | str r2, [sp] ; offset 354 | str r5, [sp, #4] ; out_callback_arg2 355 | ldr r1, [sp, #readWriteCallback_patch_stackframe_size+0x4] ; data_ptr 356 | mov r2, r3 ; cnt 357 | ldr r3, [sp, #readWriteCallback_patch_stackframe_size] ; block_size 358 | bl sdcard_readwrite 359 | mov r4, r0 360 | cmp r0, #0 361 | bne readWriteCallback_patch_skip_callback 362 | 363 | ldr r12, [sp, #readWriteCallback_patch_stackframe_size+0x8] ; callback 364 | ldr r0, [r5] 365 | ldr r1, [sp, #readWriteCallback_patch_stackframe_size+0xC] ; callback_parameter 366 | cmp r12, #0 367 | blxne r12 368 | 369 | readWriteCallback_patch_skip_callback: 370 | mov r0, r4 371 | add sp, #4 372 | pop {r1,r2,r3,r4,r5,pc} 373 | 374 | ; ; ; ; ; ; ; ; ; ; 375 | ; USB REDIRECTION ; 376 | ; ; ; ; ; ; ; ; ; ; 377 | 378 | usbReadWrite_patch: 379 | ldr r1, =USB_BASE_SECTORS ; offset_offset 380 | b readWriteCallback_patch 381 | 382 | usbRead_patch: 383 | mov r0, #1 ; read 384 | b usbReadWrite_patch 385 | 386 | usbWrite_patch: 387 | mov r0, #0 ; write 388 | b usbReadWrite_patch 389 | 390 | ; ; ; ; ; ; ; ; ; ; ; 391 | ; SDIO REDIRECTION ; 392 | ; ; ; ; ; ; ; ; ; ; ; 393 | 394 | sdcardReadWrite_patch: 395 | push {r2} 396 | ldr r2, [r0, #0x14] 397 | mov r0, r1 398 | cmp r2, #6 ; DEVICETYPE_SDCARD 399 | ldrne r1, =MLC_BASE_SECTORS ; mlc 400 | ldreq r1, =0x00000000 ; sdcard 401 | pop {r2} 402 | b readWriteCallback_patch 403 | 404 | sdcardRead_patch: 405 | mov r1, #1 ; read 406 | b sdcardReadWrite_patch 407 | 408 | sdcardWrite_patch: 409 | mov r1, #0 ; write 410 | b sdcardReadWrite_patch 411 | 412 | ; ; ; ; ; ; ; ; ; ; 413 | ; SLC REDIRECTION ; 414 | ; ; ; ; ; ; ; ; ; ; 415 | 416 | slcReadWrite_patch: 417 | push {r2} 418 | ldr r2, [r0, #4] 419 | mov r0, r1 420 | cmp r2, #0 421 | ldrne r1, =((SLC_BASE_SECTORS * 0x200) / 0x800) 422 | ldreq r1, =((SLCCMPT_BASE_SECTORS * 0x200) / 0x800) 423 | pop {r2} 424 | b readWriteCallback_patch 425 | 426 | slcRead1_patch: 427 | mov r1, #1 ; read 428 | b slcReadWrite_patch 429 | 430 | slcWrite1_patch: 431 | mov r1, #0 ; write 432 | b slcReadWrite_patch 433 | 434 | slcRead2_patch: 435 | slcRead2_patch_stackframe_size equ (0x10+7*4) 436 | push {r0-r5,lr} 437 | sub sp, #0x10 438 | ldr r4, [sp, #slcRead2_patch_stackframe_size+0x00] 439 | str r4, [sp, #0x0] ; block_size 440 | ldr r4, [sp, #slcRead2_patch_stackframe_size+0x08] 441 | str r4, [sp, #0x4] ; data_outptr 442 | ldr r4, [sp, #slcRead2_patch_stackframe_size+0x10] 443 | str r4, [sp, #0x8] ; callback 444 | ldr r4, [sp, #slcRead2_patch_stackframe_size+0x14] 445 | str r4, [sp, #0xC] ; callback_parameter 446 | bl slcRead1_patch 447 | add sp, #0x14 448 | pop {r1-r5,pc} 449 | 450 | slcWrite2_patch: 451 | slcWrite2_patch_stackframe_size equ (0x10+6*4) 452 | push {r0-r4,lr} 453 | sub sp, #0x10 454 | ldr r4, [sp, #slcWrite2_patch_stackframe_size+0x00] 455 | str r4, [sp, #0x0] ; block_size 456 | ldr r4, [sp, #slcWrite2_patch_stackframe_size+0x08] 457 | str r4, [sp, #0x4] ; data_outptr 458 | ldr r4, [sp, #slcWrite2_patch_stackframe_size+0x10] 459 | str r4, [sp, #0x8] ; callback 460 | ldr r4, [sp, #slcWrite2_patch_stackframe_size+0x14] 461 | str r4, [sp, #0xC] ; callback_parameter 462 | bl slcWrite1_patch 463 | add sp, #0x14 464 | pop {r1-r4,pc} 465 | 466 | ; ; ; ; ; ; ; ; ; ; 467 | ; DEBUG STUFF ; 468 | ; ; ; ; ; ; ; ; ; ; 469 | 470 | mlcRead1_dbg: 471 | mlcRead1_dbg_stackframe equ (4*6) 472 | mov r12, r0 473 | push {r0-r3,r12,lr} 474 | adr r0, mlcRead1_dbg_format 475 | ldr r1, [sp, #mlcRead1_dbg_stackframe+9*4] 476 | bl FS_SYSLOG_OUTPUT 477 | pop {r0-r3,lr,pc} ; replaces mov lr, r0 478 | mlcRead1_dbg_format: 479 | .ascii "mlcRead1 : %08X %08X %08X" 480 | .byte 0x0a 481 | .byte 0x00 482 | .align 0x4 483 | 484 | mlcRead1_end_hook: 485 | mlcRead1_end_hook_stackframe equ (4*10) 486 | push {r0} 487 | mov r0, #50 488 | bl FS_SLEEP 489 | ldr r0, =sdcard_read_buffer 490 | ldr r1, [sp, #mlcRead1_end_hook_stackframe+4*1] 491 | mov r2, #0x200 492 | bl FS_MEMCPY 493 | ldr r0, =sdcard_read_buffer 494 | str r6, [r0] 495 | mov r1, #0x200 496 | bl dump_data 497 | pop {r0,r4-r11,pc} 498 | 499 | ; r0 : data ptr 500 | ; r1 : size 501 | ; r2 : offset_blocks 502 | write_data_offset: 503 | push {r1,r2,r3,r4,lr} 504 | mov r3, r1, lsr 9 ; size /= 0x200 505 | cmp r3, #0 506 | moveq r3, #1 507 | mov r1, r0 ; data_ptr 508 | str r2, [sp] ; offset 509 | mov r0, #0 510 | str r0, [sp, #0x4] ; out_callback_arg2 511 | mov r0, #0xDA 512 | str r0, [sp, #0x8] ; device id 513 | mov r0, #0 ; write 514 | mov r2, r3 ; num_sectors 515 | mov r3, #0x200 ; block_size 516 | bl sdcard_readwrite 517 | add sp, #0xC 518 | pop {r4,pc} 519 | 520 | ; r0 : data ptr 521 | ; r1 : size 522 | dump_data: 523 | push {r1,r2,r3,r4,lr} 524 | mov r3, r1, lsr 9 ; size /= 0x200 525 | cmp r3, #0 526 | moveq r3, #1 527 | mov r1, r0 ; data_ptr 528 | ldr r0, =DUMPDATA_BASE_SECTORS ; offset_sectors 529 | ldr r4, =dumpdata_offset 530 | ldr r2, [r4] 531 | add r0, r2 532 | str r0, [sp] 533 | add r2, r3 534 | str r2, [r4] 535 | mov r0, #0 536 | str r0, [sp, #0x4] ; out_callback_arg2 537 | mov r0, #0xDA 538 | str r0, [sp, #0x8] ; device id 539 | mov r0, #0 ; write 540 | mov r2, r3 ; num_sectors 541 | mov r3, #0x200 ; block_size 542 | bl sdcard_readwrite 543 | add sp, #0xC 544 | pop {r4,pc} 545 | 546 | dump_lots_data: 547 | push {r4-r6,lr} 548 | mov r4, r0 ; addr 549 | mov r5, r1 ; size 550 | dump_lots_data_loop: 551 | mov r6, #0x40000 ; cur_size 552 | cmp r6, r5 553 | movgt r6, r5 554 | ldr r0, =sdcard_read_buffer 555 | mov r1, r4 556 | mov r2, r6 557 | bl FS_MEMCPY 558 | ldr r0, =sdcard_read_buffer ; data_ptr 559 | mov r1, r6 ; size 560 | bl dump_data 561 | add r4, r6 562 | sub r5, r6 563 | cmp r6, #0 564 | bne dump_lots_data_loop 565 | pop {r4-r6,pc} 566 | 567 | dump_syslog: 568 | push {r1,r2,r3,lr} 569 | ldr r0, =syslog_buffer 570 | ldr r1, =0x05095ECC ; data_ptr (syslog ptr) 571 | ldr r1, [r1] 572 | mov r2, #0x40000 573 | bl FS_MEMCPY 574 | ldr r0, =SYSLOG_BASE_SECTORS ; offset_sectors 575 | str r0, [sp] 576 | mov r0, #0 577 | str r0, [sp, #0x4] ; out_callback_arg2 578 | mov r0, #0xDA 579 | str r0, [sp, #0x8] ; device id 580 | mov r0, #0 ; write 581 | ldr r1, =syslog_buffer 582 | mov r2, #0x200 ; num_sectors (0x40000 bytes) 583 | mov r3, #0x200 ; block_size 584 | bl sdcard_readwrite 585 | add sp, #0xC 586 | pop {pc} 587 | 588 | syslogOutput_hook: 589 | push {r0} 590 | ; bl dump_syslog 591 | pop {r0,r4-r8,r10,pc} 592 | 593 | ; r0 : device id 594 | getPhysicalDeviceHandle: 595 | ldr r1, =0x1091C2EC 596 | mov r2, #0x204 597 | mla r1, r2, r0, r1 598 | ldrh r1, [r1, #6] 599 | orr r0, r1, r0, lsl 16 600 | bx lr 601 | 602 | ; r0 : dst, r1 : offset, r2 : sectors, r3 : device id 603 | ; rawRead1PhysicalDevice_(int physical_device_handle, unsigned int offset_high, unsigned int offset_low, unsigned int size, void *outptr, int (__fastcall *callback)(unsigned int, int), int callback_arg) 604 | readSlc: 605 | push {lr} 606 | sub sp, #0xC 607 | str r0, [sp] ; outptr 608 | mov r0, #0 609 | str r0, [sp, #4] ; callback 610 | str r0, [sp, #8] ; callback_arg 611 | push {r1-r3} 612 | mov r0, r3 613 | bl getPhysicalDeviceHandle 614 | pop {r1-r3} 615 | mov r3, r2 ; cnt 616 | mov r2, r1 ; offset_low 617 | mov r1, #0 ; offset_high 618 | BL FS_RAW_READ1 619 | add sp, #0xC 620 | pop {pc} 621 | 622 | slc_dump: 623 | push {r4-r7,lr} 624 | mov r4, #0 625 | mov r5, r0 626 | mov r6, r1 627 | mov r7, r2 628 | 629 | mov r0, #1000 630 | bl FS_SLEEP 631 | 632 | slc_dump_loop: 633 | mov r3, r4 634 | mov r0, #20 635 | mov r1, #0 636 | mov r2, r6 637 | bl _printf 638 | 639 | ldr r0, =sdcard_read_buffer 640 | mov r1, r4 641 | mov r2, #0x80 642 | add r4, r2 643 | mov r3, r5 644 | bl readSlc 645 | 646 | mov r0, #10 647 | bl FS_SLEEP 648 | 649 | ldr r0, =sdcard_read_buffer 650 | mov r1, #0x40000 651 | mov r2, r7 652 | add r7, r7, r1, lsr 9 653 | bl write_data_offset 654 | 655 | cmp r4, #0x40000 656 | blt slc_dump_loop 657 | 658 | pop {r4-r7,pc} 659 | 660 | retval_format: 661 | .ascii "retval = %08X" 662 | .byte 0x00 663 | .align 4 664 | 665 | slc_format: 666 | .ascii "slc = %08X" 667 | .byte 0x00 668 | .align 4 669 | 670 | slc2_format: 671 | .ascii "slc2 = %08X" 672 | .byte 0x00 673 | .align 4 674 | 675 | slccmpt_format: 676 | .ascii "slccmpt = %08X" 677 | .byte 0x00 678 | .align 4 679 | 680 | mlc_format: 681 | .ascii "mlc = %08X %08X %08X" 682 | .byte 0x00 683 | .align 4 684 | 685 | mlc_dump: 686 | push {r4,r7,lr} 687 | sub sp, #8 688 | mov r4, #0 689 | ldr r7, =MLC_BASE_SECTORS 690 | 691 | mlc_dump_loop: 692 | bl dump_syslog 693 | mov r5, #0 694 | mlc_dump_loop2: 695 | mov r3, r4 696 | mov r0, #20 697 | mov r1, #0 698 | adr r2, mlc_format 699 | bl _printf 700 | 701 | ldr r0, =sdcard_read_buffer 702 | mov r1, #0x800 703 | mov r2, r4 704 | add r4, r1 705 | bl read_mlc 706 | str r0, [sp] 707 | 708 | ldr r0, =sdcard_read_buffer 709 | mov r1, #0x100000 710 | mov r2, r7 711 | add r7, r7, r1, lsr 9 712 | bl write_data_offset 713 | str r0, [sp, #4] 714 | 715 | add r5, #1 716 | cmp r5, #0x40 717 | blt mlc_dump_loop2 718 | 719 | ldr r0, =0x3A20000 720 | cmp r4, r0 721 | blt mlc_dump_loop 722 | 723 | add sp, #8 724 | pop {r4,r7,pc} 725 | 726 | ; r0 : data ptr 727 | ; r1 : num_sectors 728 | ; r2 : offset_sectors 729 | read_mlc: 730 | push {r1,r2,r3,r4,lr} 731 | str r2, [sp] 732 | mov r2, r1 ; num_sectors 733 | mov r1, r0 ; data_ptr 734 | ldr r0, =mlc_out_callback_arg2 735 | str r0, [sp, #0x4] ; out_callback_arg2 736 | mov r0, #0xAB 737 | str r0, [sp, #0x8] ; device id 738 | mov r0, #1 ; read 739 | mov r3, #0x200 ; block_size 740 | bl sdcard_readwrite 741 | add sp, #0xC 742 | pop {r4,pc} 743 | 744 | dumper_main: 745 | push {lr} 746 | bl mlc_init 747 | mov r0, #0xE ; slc 748 | adr r1, slc_format 749 | ldr r2, =SLC_BASE_SECTORS 750 | bl slc_dump 751 | mov r0, #0xE ; slc 752 | adr r1, slc2_format 753 | ldr r2, =SLC_BASE_SECTORS 754 | bl slc_dump 755 | mov r0, #0xF ; slccmpt 756 | adr r1, slccmpt_format 757 | ldr r2, =SLCCMPT_BASE_SECTORS 758 | bl slc_dump 759 | bl mlc_dump 760 | ; intentional crash 761 | mov r0, #0 762 | ldr r0, [r0] 763 | ; pop {pc} 764 | 765 | .pool 766 | 767 | clear_screen: 768 | push {lr} 769 | ldr r0, =FRAMEBUFFER_ADDRESS ; data_ptr 770 | ldr r1, =0x00 771 | ldr r2, =FRAMEBUFFER_STRIDE*504 772 | bl FS_MEMSET 773 | pop {pc} 774 | 775 | ; r0 : x, r1 : y, r2 : format, ... 776 | ; NOT threadsafe so dont even try you idiot 777 | _printf: 778 | ldr r12, =_printf_xylr 779 | str r0, [r12] 780 | str r1, [r12, #4] 781 | str lr, [r12, #8] 782 | ldr r0, =_printf_string 783 | mov r1, #_printf_string_end-_printf_string 784 | bl FS_SNPRINTF 785 | ldr r12, =_printf_xylr 786 | ldr r1, [r12] 787 | ldr r2, [r12, #4] 788 | ldr lr, [r12, #8] 789 | push {lr} 790 | ldr r0, =_printf_string 791 | bl drawString 792 | pop {pc} 793 | 794 | 795 | ; r0 : str, r1 : x, r2 : y 796 | drawString: 797 | push {r4-r6,lr} 798 | mov r4, r0 799 | mov r5, r1 800 | mov r6, r2 801 | drawString_loop: 802 | ldrb r0, [r4], #1 803 | cmp r0, #0x00 804 | beq drawString_end 805 | mov r1, r5 806 | mov r2, r6 807 | bl drawCharacter 808 | add r5, #CHARACTER_SIZE 809 | b drawString_loop 810 | drawString_end: 811 | pop {r4-r6,pc} 812 | 813 | ; r0 : char, r1 : x, r2 : y 814 | drawCharacter: 815 | subs r0, #32 816 | ; bxlt lr 817 | push {r4-r7,lr} 818 | ldr r4, =FRAMEBUFFER_ADDRESS ; r4 : framebuffer address 819 | add r4, r1, lsl 2 ; add x * 4 820 | mov r3, #FRAMEBUFFER_STRIDE 821 | mla r4, r2, r3, r4 822 | adr r5, font_data ; r5 : character data 823 | add r5, r0, lsl 3 ; font is 1bpp, 8x8 => 8 bytes represents one character 824 | mov r1, #0xFFFFFFFF ; color 825 | mov r2, #0x0 ; empty color 826 | mov r6, #8 ; i 827 | drawCharacter_loop1: 828 | mov r3, #CHARACTER_MULT 829 | drawCharacter_loop3: 830 | mov r7, #8 ; j 831 | ldrb r0, [r5] 832 | drawCharacter_loop2: 833 | tst r0, #1 834 | ; as many STRs as CHARACTER_MULT (would be nice to do this in preproc...) 835 | streq r1, [r4], #4 836 | streq r1, [r4], #4 837 | strne r2, [r4], #4 838 | strne r2, [r4], #4 839 | mov r0, r0, lsr #1 840 | subs r7, #1 841 | bne drawCharacter_loop2 842 | add r4, #FRAMEBUFFER_STRIDE-CHARACTER_SIZE*4 843 | subs r3, #1 844 | bne drawCharacter_loop3 845 | add r5, #1 846 | subs r6, #1 847 | bne drawCharacter_loop1 848 | pop {r4-r7,pc} 849 | 850 | .pool 851 | 852 | font_data: 853 | .incbin "patches/font.bin" 854 | 855 | .Close 856 | 857 | .create "patched_sections/0x10835000.bin",0x10835000 858 | 859 | .org FS_BSS_START 860 | sdcard_access_mutex: 861 | .word 0x00000000 862 | dumpdata_offset: 863 | .word 0x00000000 864 | mlc_out_callback_arg2: 865 | .word 0x00000000 866 | _printf_xylr: 867 | .word 0x00000000 868 | .word 0x00000000 869 | .word 0x00000000 870 | _printf_string: 871 | .fill ((_printf_string + 0x100) - .), 0x00 872 | _printf_string_end: 873 | .align 0x40 874 | syslog_buffer: 875 | .fill ((syslog_buffer + 0x40000) - .), 0x00 876 | .align 0x40 877 | sdcard_read_buffer: 878 | .fill ((sdcard_read_buffer + 0x100000) - .), 0x00 879 | 880 | .Close 881 | -------------------------------------------------------------------------------- /patches/0x10800000.s: -------------------------------------------------------------------------------- 1 | .arm.big 2 | 3 | .open "sections/0x10800000.bin","patched_sections/0x10800000.bin",0x10800000 4 | 5 | .org 0x10800000 6 | 7 | .Close 8 | -------------------------------------------------------------------------------- /patches/0x4000000.s: -------------------------------------------------------------------------------- 1 | .arm.big 2 | 3 | .open "sections/0x4000000.bin","patched_sections/0x4000000.bin",0x04000000 4 | 5 | SECTION_BASE equ 0x04000000 6 | SECTION_SIZE equ 0x00017020 7 | CODE_BASE equ (SECTION_BASE + SECTION_SIZE) 8 | 9 | ; nop out memcmp hash checks 10 | .org 0x040017E0 11 | mov r0, #0 12 | .org 0x040019C4 13 | mov r0, #0 14 | .org 0x04001BB0 15 | mov r0, #0 16 | .org 0x04001D40 17 | mov r0, #0 18 | 19 | .Close 20 | -------------------------------------------------------------------------------- /patches/0x5000000.s: -------------------------------------------------------------------------------- 1 | .arm.big 2 | 3 | .open "sections/0x5000000.bin","patched_sections/0x5000000.bin",0x05000000 4 | 5 | SECTION_BASE equ 0x05000000 6 | SECTION_SIZE equ 0x000598f0 7 | CODE_BASE equ (SECTION_BASE + SECTION_SIZE) 8 | MCP_BSS_START equ (0x5074000 + 0x48574) 9 | 10 | MCP_FSA_OPEN_T equ 0x05059160 11 | MCP_FSA_MOUNT_T equ 0x05059530 12 | MCP_SYSLOG_OUTPUT_T equ 0x05059140 13 | 14 | MCP_SVC_CREATETHREAD equ 0x050567EC 15 | MCP_SVC_STARTTHREAD equ 0x05056824 16 | 17 | NEW_TIMEOUT equ (0xFFFFFFFF) ; over an hour 18 | 19 | ; fix 10 minute timeout that crashes MCP after 10 minutes of booting 20 | .org 0x05022474 21 | .word NEW_TIMEOUT 22 | 23 | ; hook main thread to start our thread ASAP 24 | .org 0x05056718 25 | .arm 26 | bl mcpMainThread_hook 27 | 28 | ; patch OS launch sig check 29 | .org 0x0500A818 30 | .thumb 31 | mov r0, #0 32 | mov r0, #0 33 | 34 | ; patch IOSC_VerifyPubkeySign to always succeed 35 | .org 0x05052C44 36 | .arm 37 | mov r0, #0 38 | bx lr 39 | 40 | .org 0x050282AE 41 | .thumb 42 | bl launch_os_hook 43 | 44 | ; patch pointer to fw.img loader path 45 | .org 0x050284D8 46 | .word fw_img_path 47 | 48 | .org CODE_BASE 49 | .arm 50 | mcpMainThread_hook: 51 | mov r11, r0 52 | push {r0-r11,lr} 53 | sub sp, #8 54 | 55 | mov r0, #0x78 56 | str r0, [sp] ; prio 57 | mov r0, #1 58 | str r0, [sp, #4] ; detached 59 | ldr r0, =wupserver_entrypoint ; thread entrypoint 60 | mov r1, #0 ; thread arg 61 | ldr r2, =wupserver_stacktop ; thread stacktop 62 | mov r3, #wupserver_stacktop - wupserver_stack ; thread stack size 63 | bl MCP_SVC_CREATETHREAD 64 | 65 | cmp r0, #0 66 | blge MCP_SVC_STARTTHREAD 67 | 68 | ldr r1, =0x050BD000 - 4 69 | str r0, [r1] 70 | 71 | add sp, #8 72 | pop {r0-r11,pc} 73 | 74 | .thumb 75 | launch_os_hook: 76 | bx pc 77 | .align 0x4 78 | .arm 79 | push {r0-r3,lr} 80 | sub sp, #8 81 | 82 | bl MCP_SYSLOG_OUTPUT_T 83 | 84 | mov r0, #0 85 | bl MCP_FSA_OPEN_T 86 | 87 | ldr r1, =launch_os_hook_devicepath 88 | ldr r2, =launch_os_hook_mountpath 89 | mov r3, #0 90 | str r3, [sp] 91 | str r3, [sp, #4] 92 | bl MCP_FSA_MOUNT_T 93 | 94 | add sp, #8 95 | pop {r0-r3,pc} 96 | launch_os_hook_devicepath: 97 | .ascii "/dev/sdcard01" 98 | .byte 0x00 99 | launch_os_hook_mountpath: 100 | .ascii "/vol/sdcard" 101 | .byte 0x00 102 | .align 0x4 103 | 104 | fw_img_path: 105 | .ascii "/vol/sdcard" 106 | .byte 0x00 107 | .align 0x4 108 | 109 | .pool 110 | 111 | .Close 112 | 113 | .open "sections/0x5100000.bin","patched_sections/0x5100000.bin",0x05100000 114 | 115 | ; append wupserver code 116 | .org 0x5116000 117 | wupserver_entrypoint: 118 | .incbin "wupserver/wupserver.bin" 119 | .align 0x100 120 | 121 | .Close 122 | 123 | .create "patched_sections/0x5074000.bin",0x5074000 124 | 125 | .org MCP_BSS_START 126 | 127 | .org 0x050BD000 128 | wupserver_stack: 129 | .fill ((wupserver_stack + 0x1000) - .), 0x00 130 | wupserver_stacktop: 131 | wupserver_bss: 132 | .fill ((wupserver_bss + 0x2000) - .), 0x00 133 | 134 | .Close 135 | -------------------------------------------------------------------------------- /patches/0x8120000.s: -------------------------------------------------------------------------------- 1 | .arm.big 2 | 3 | .open "sections/0x8120000.bin","patched_sections/0x8120000.bin",0x8120000 4 | 5 | CODE_SECTION_BASE equ 0x08120000 6 | CODE_SECTION_SIZE equ 0x00015000 7 | CODE_BASE equ (CODE_SECTION_BASE + CODE_SECTION_SIZE) 8 | 9 | RODATA_SECTION_BASE equ 0x08140000 10 | RODATA_SECTION_SIZE equ 0x00002478 11 | RODATA_BASE equ (RODATA_SECTION_BASE + RODATA_SECTION_SIZE) 12 | 13 | KERNEL_BSS_START equ (0x8150000 + 0x61230) 14 | 15 | FRAMEBUFFER_ADDRESS equ (0x14000000+0x38C0000) 16 | ; FRAMEBUFFER_ADDRESS equ (0x00708000 + 0x1B9000) 17 | FRAMEBUFFER_STRIDE equ (0xE00) 18 | CHARACTER_MULT equ (2) 19 | CHARACTER_SIZE equ (8*CHARACTER_MULT) 20 | 21 | KERNEL_MEMSET equ 0x08131DA0 22 | KERNEL_SNPRINTF equ 0x08132988 23 | KERNEL_MCP_IOMAPPINGS_STRUCT equ 0x08140DE0 24 | 25 | ; patch domains 26 | .org 0x081253C4 27 | str r3, [r7,#0x10] 28 | str r3, [r7,#0x0C] 29 | str r3, [r7,#0x04] 30 | str r3, [r7,#0x14] 31 | str r3, [r7,#0x08] 32 | str r3, [r7,#0x34] 33 | 34 | ; guru meditation patch ! 35 | .org 0x0812A134 ; prefetch abort 36 | mov r3, #0 37 | b crash_handler 38 | .org 0x0812A1AC ; data abort 39 | mov r3, #1 40 | b crash_handler 41 | .org 0x08129E50 ; undef inst 42 | mov r3, #2 43 | b crash_handler 44 | 45 | .org CODE_BASE 46 | 47 | crash_handler: 48 | sub sp, #4 49 | mov r4, r0 50 | mov r5, r3 51 | 52 | ldr r0, =FRAMEBUFFER_ADDRESS 53 | ldr r1, =0xFF 54 | ldr r2, =896*504*4 55 | bl KERNEL_MEMSET 56 | 57 | mov r0, #0 58 | mov r1, #0 59 | cmp r5, #1 60 | adrlt r2, crash_handler_format_prefetch 61 | adreq r2, crash_handler_format_data 62 | adrgt r2, crash_handler_format_undef 63 | ldr r3, [r4, #0x40] 64 | bl _printf 65 | 66 | mov r6, #0 67 | crash_handler_loop: 68 | 69 | mov r0, #20 70 | mul r1, r6, r0 71 | add r1, #40 72 | cmp r6, #10 73 | adrlt r2, crash_handler_format2 74 | adrge r2, crash_handler_format3 75 | add r3, r4, #4 76 | ldr r3, [r3, r6, lsl 2] 77 | str r3, [sp] 78 | mov r3, r6 79 | bl _printf 80 | 81 | add r6, #1 82 | cmp r6, #0x10 83 | blt crash_handler_loop 84 | 85 | mov r0, #400 86 | mov r1, #20 87 | adr r2, crash_handler_format4 88 | ldr r3, [r4, #0x40] 89 | ldr r3, [r3] 90 | bl _printf 91 | 92 | crash_handler_loop2: 93 | b crash_handler_loop2 94 | crash_handler_format_data: 95 | .ascii "GURU MEDITATION ERROR (data abort)" 96 | .byte 0x00 97 | .align 0x4 98 | crash_handler_format_prefetch: 99 | .ascii "GURU MEDITATION ERROR (prefetch abort)" 100 | .byte 0x00 101 | .align 0x4 102 | crash_handler_format_undef: 103 | .ascii "GURU MEDITATION ERROR (undefined instruction)" 104 | .byte 0x00 105 | .align 0x4 106 | crash_handler_format2: 107 | .ascii "r%d = %08X" 108 | .byte 0x00 109 | .align 0x4 110 | crash_handler_format3: 111 | .ascii "r%d = %08X" 112 | .byte 0x00 113 | .align 0x4 114 | crash_handler_format4: 115 | .ascii "%08X" 116 | .byte 0x00 117 | .align 0x4 118 | 119 | ; r0 : x, r1 : y, r2 : format, ... 120 | ; NOT threadsafe so dont even try you idiot 121 | _printf: 122 | ldr r12, =_printf_xylr 123 | str r0, [r12] 124 | str r1, [r12, #4] 125 | str lr, [r12, #8] 126 | ldr r0, =_printf_string 127 | mov r1, #_printf_string_end-_printf_string 128 | bl KERNEL_SNPRINTF 129 | ldr r12, =_printf_xylr 130 | ldr r1, [r12] 131 | ldr r2, [r12, #4] 132 | ldr lr, [r12, #8] 133 | push {lr} 134 | ldr r0, =_printf_string 135 | bl drawString 136 | pop {pc} 137 | 138 | ; r0 : str, r1 : x, r2 : y 139 | drawString: 140 | push {r4-r6,lr} 141 | mov r4, r0 142 | mov r5, r1 143 | mov r6, r2 144 | drawString_loop: 145 | ldrb r0, [r4], #1 146 | cmp r0, #0x00 147 | beq drawString_end 148 | mov r1, r5 149 | mov r2, r6 150 | bl drawCharacter 151 | add r5, #CHARACTER_SIZE 152 | b drawString_loop 153 | drawString_end: 154 | pop {r4-r6,pc} 155 | 156 | ; r0 : char, r1 : x, r2 : y 157 | drawCharacter: 158 | subs r0, #32 159 | ; bxlt lr 160 | push {r4-r7,lr} 161 | ldr r4, =FRAMEBUFFER_ADDRESS ; r4 : framebuffer address 162 | add r4, r1, lsl 2 ; add x * 4 163 | mov r3, #FRAMEBUFFER_STRIDE 164 | mla r4, r2, r3, r4 165 | adr r5, font_data ; r5 : character data 166 | add r5, r0, lsl 3 ; font is 1bpp, 8x8 => 8 bytes represents one character 167 | mov r1, #0xFFFFFFFF ; color 168 | mov r2, #0x0 ; empty color 169 | mov r6, #8 ; i 170 | drawCharacter_loop1: 171 | mov r3, #CHARACTER_MULT 172 | drawCharacter_loop3: 173 | mov r7, #8 ; j 174 | ldrb r0, [r5] 175 | drawCharacter_loop2: 176 | tst r0, #1 177 | ; as many STRs as CHARACTER_MULT (would be nice to do this in preproc...) 178 | streq r1, [r4], #4 179 | streq r1, [r4], #4 180 | strne r2, [r4], #4 181 | strne r2, [r4], #4 182 | mov r0, r0, lsr #1 183 | subs r7, #1 184 | bne drawCharacter_loop2 185 | add r4, #FRAMEBUFFER_STRIDE-CHARACTER_SIZE*4 186 | subs r3, #1 187 | bne drawCharacter_loop3 188 | add r5, #1 189 | subs r6, #1 190 | bne drawCharacter_loop1 191 | pop {r4-r7,pc} 192 | 193 | .pool 194 | 195 | font_data: 196 | .incbin "patches/font.bin" 197 | 198 | .Close 199 | 200 | 201 | .open "sections/0x8140000.bin","patched_sections/0x8140000.bin",0x8140000 202 | 203 | .org KERNEL_MCP_IOMAPPINGS_STRUCT 204 | .word mcpIoMappings_patch ; ptr to iomapping structs 205 | .word 0x00000003 ; number of iomappings 206 | .word 0x00000001 ; pid (MCP) 207 | 208 | .org RODATA_BASE 209 | mcpIoMappings_patch: 210 | ; mapping 1 211 | .word 0x0D000000 ; vaddr 212 | .word 0x0D000000 ; paddr 213 | .word 0x001C0000 ; size 214 | .word 0x00000000 ; ? 215 | .word 0x00000003 ; ? 216 | .word 0x00000000 ; ? 217 | ; mapping 2 218 | .word 0x0D800000 ; vaddr 219 | .word 0x0D800000 ; paddr 220 | .word 0x001C0000 ; size 221 | .word 0x00000000 ; ? 222 | .word 0x00000003 ; ? 223 | .word 0x00000000 ; ? 224 | ; mapping 3 225 | .word 0x0C200000 ; vaddr 226 | .word 0x0C200000 ; paddr 227 | .word 0x00100000 ; size 228 | .word 0x00000000 ; ? 229 | .word 0x00000003 ; ? 230 | .word 0x00000000 ; ? 231 | 232 | .Close 233 | 234 | 235 | .create "patched_sections/0x8150000.bin",0x8150000 236 | 237 | .org KERNEL_BSS_START 238 | _printf_xylr: 239 | .word 0x00000000 240 | .word 0x00000000 241 | .word 0x00000000 242 | _printf_string: 243 | .fill ((_printf_string + 0x100) - .), 0x00 244 | _printf_string_end: 245 | .align 0x40 246 | 247 | .Close 248 | -------------------------------------------------------------------------------- /patches/font.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FIX94/iosuhax/0d49a141070581ddc1b2536b519dde3f29fc57dd/patches/font.bin -------------------------------------------------------------------------------- /scripts/anpack.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import sys 3 | import struct 4 | import hashlib 5 | import keys 6 | from Crypto.Cipher import AES 7 | 8 | class elf32_ehdr: 9 | def __init__(self, file, offset): 10 | file.seek(offset) 11 | self.e_ident = file.read(16) 12 | self.e_type = struct.unpack(">H", file.read(2))[0] 13 | self.e_machine = struct.unpack(">H", file.read(2))[0] 14 | self.e_version = struct.unpack(">I", file.read(4))[0] 15 | self.e_entry = struct.unpack(">I", file.read(4))[0] 16 | self.e_phoff = struct.unpack(">I", file.read(4))[0] 17 | self.e_shoff = struct.unpack(">I", file.read(4))[0] 18 | self.e_flags = struct.unpack(">I", file.read(4))[0] 19 | self.e_ehsize = struct.unpack(">H", file.read(2))[0] 20 | self.e_phentsize = struct.unpack(">H", file.read(2))[0] 21 | self.e_phnum = struct.unpack(">H", file.read(2))[0] 22 | self.e_shentsize = struct.unpack(">H", file.read(2))[0] 23 | self.e_shnum = struct.unpack(">H", file.read(2))[0] 24 | self.e_shstrndx = struct.unpack(">H", file.read(2))[0] 25 | 26 | def write(self, file, offset): 27 | file.seek(offset) 28 | file.write(self.e_ident) 29 | file.write(struct.pack(">HHIIIIIHHHHHH", self.e_type, self.e_machine, self.e_version, self.e_entry, self.e_phoff, self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx)) 30 | 31 | def _print(self): 32 | # print("e_ident : " + (self.e_ident)) 33 | print("e_type : " + hex(self.e_type)) 34 | print("e_machine : " + hex(self.e_machine)) 35 | print("e_version : " + hex(self.e_version)) 36 | print("e_entry : " + hex(self.e_entry)) 37 | print("e_phoff : " + hex(self.e_phoff)) 38 | print("e_shoff : " + hex(self.e_shoff)) 39 | print("e_flags : " + hex(self.e_flags)) 40 | print("e_ehsize : " + hex(self.e_ehsize)) 41 | print("e_phentsize : " + hex(self.e_phentsize)) 42 | print("e_phnum : " + hex(self.e_phnum)) 43 | print("e_shentsize : " + hex(self.e_shentsize)) 44 | print("e_shnum : " + hex(self.e_shnum)) 45 | print("e_shstrndx : " + hex(self.e_shstrndx)) 46 | 47 | class elf32_phdr: 48 | def __init__(self, file, offset, hdr, id): 49 | if id >= hdr.e_phnum: 50 | raise ValueError("id is invalid yo") 51 | file.seek(offset + hdr.e_phoff + hdr.e_phentsize * id) 52 | self.id = id 53 | self.p_type = struct.unpack(">I", file.read(4))[0] 54 | self.p_offset = struct.unpack(">I", file.read(4))[0] 55 | self.p_vaddr = struct.unpack(">I", file.read(4))[0] 56 | self.p_paddr = struct.unpack(">I", file.read(4))[0] 57 | self.p_filesz = struct.unpack(">I", file.read(4))[0] 58 | self.p_memsz = struct.unpack(">I", file.read(4))[0] 59 | self.p_flags = struct.unpack(">I", file.read(4))[0] 60 | self.p_align = struct.unpack(">I", file.read(4))[0] 61 | file.seek(offset + self.p_offset) 62 | self.content = file.read(self.p_filesz) 63 | 64 | def replace_content(self, file): 65 | file.seek(0) 66 | self.content = file.read() 67 | # should check that it's not too big 68 | self.p_filesz = len(self.content) 69 | self.p_memsz = self.p_filesz 70 | 71 | def write(self, file, offset, hdr, id, write_content = True): 72 | file.seek(offset + hdr.e_phoff + hdr.e_phentsize * id) 73 | file.write(struct.pack(">IIIIIIII", self.p_type, self.p_offset, self.p_vaddr, self.p_paddr, self.p_filesz, self.p_memsz, self.p_flags, self.p_align)) 74 | if write_content: 75 | file.seek(offset + self.p_offset) 76 | file.write(self.content) 77 | return len(self.content) 78 | return 0 79 | 80 | def _print(self): 81 | print("p_type : " + hex(self.p_type)) 82 | print("p_offset : " + hex(self.p_offset)) 83 | print("p_vaddr : " + hex(self.p_vaddr)) 84 | print("p_paddr : " + hex(self.p_paddr)) 85 | print("p_filesz : " + hex(self.p_filesz)) 86 | print("p_memsz : " + hex(self.p_memsz)) 87 | print("p_flags : " + hex(self.p_flags)) 88 | print("p_align : " + hex(self.p_align)) 89 | 90 | class elf32: 91 | def __init__(self, file, offset): 92 | self.hdr = elf32_ehdr(file, offset) 93 | self.phdrs = [elf32_phdr(file, offset, self.hdr, i) for i in range(self.hdr.e_phnum)] 94 | 95 | def write(self, file, offset): 96 | self.hdr.write(file, offset) 97 | data_offset = self.hdr.e_phoff + self.hdr.e_phentsize * self.hdr.e_phnum 98 | for i in range(self.hdr.e_phnum): 99 | special = (i == 0 or i == 2) 100 | if not(special): 101 | self.phdrs[i].p_offset = data_offset 102 | data_offset += self.phdrs[i].write(file, offset, self.hdr, i, not(special)) 103 | 104 | def extract_sections(self, sections): 105 | for adr in sections: 106 | for i in range(self.hdr.e_phnum): 107 | if self.phdrs[i].p_vaddr == adr: 108 | open(sections[adr], "wb").write(self.phdrs[i].content) 109 | break 110 | 111 | def replace_sections(self, sections): 112 | for adr in sections: 113 | for i in range(self.hdr.e_phnum): 114 | if self.phdrs[i].p_vaddr == adr: 115 | self.phdrs[i].replace_content(open(sections[adr], "rb")) 116 | break 117 | 118 | def bss_sections(self, sections): 119 | for adr in sections: 120 | for i in range(self.hdr.e_phnum): 121 | if self.phdrs[i].p_vaddr == adr: 122 | f = open(sections[adr], "rb") 123 | f.seek(0, 2) 124 | self.phdrs[i].p_filesz = 0 125 | self.phdrs[i].p_memsz = f.tell() 126 | break 127 | 128 | def _print(self): 129 | self.hdr._print() 130 | for i in range(self.hdr.e_phnum): 131 | print("PHDR " + hex(i)) 132 | self.phdrs[i]._print() 133 | 134 | class ancast: 135 | def __init__(self, file): 136 | file.seek(0) 137 | self.header = file.read(0x804) 138 | self.elf = elf32(file, len(self.header)) 139 | # self.elf._print() 140 | 141 | def write(self, file): 142 | file.seek(0) 143 | file.write(self.header) 144 | self.elf.write(file, len(self.header)) 145 | file.seek(0, 2) 146 | size = file.tell() - 0x200 147 | total_size = (size + 0xfff) & ~0xfff 148 | file.write(bytearray([0x00] * (total_size - size))) 149 | hash = self.encrypt(file, 0x200) 150 | file.seek(0x1AC) 151 | file.write(struct.pack(">I", total_size)) 152 | file.write(hash) 153 | 154 | def extract_sections(self, sections): 155 | self.elf.extract_sections(sections) 156 | 157 | def replace_sections(self, sections): 158 | self.elf.replace_sections(sections) 159 | 160 | def bss_sections(self, sections): 161 | self.elf.bss_sections(sections) 162 | 163 | def encrypt(self, file, offset): 164 | key = codecs.decode(keys.key, 'hex') 165 | iv = codecs.decode(keys.iv, 'hex') 166 | file.seek(offset) 167 | buffer = b"" 168 | hash = hashlib.sha1() 169 | while True: 170 | cipher = AES.new(key, AES.MODE_CBC, iv) 171 | dec = file.read(0x4000) 172 | if len(dec) < 0x10: 173 | break 174 | enc = cipher.encrypt(dec) 175 | hash.update(enc) 176 | buffer += enc 177 | iv = enc[-0x10:] 178 | file.seek(offset) 179 | file.write(buffer) 180 | return hash.digest() 181 | 182 | input_fn = None 183 | output_fn = None 184 | replace_sections = {} 185 | extract_sections = {} 186 | bss_sections = {} 187 | 188 | i = 1 189 | while i < len(sys.argv): 190 | option = sys.argv[i] 191 | if option[0] == '-': 192 | if option[1:] == "in": 193 | input_fn = sys.argv[i+1] 194 | i += 1 195 | elif option[1:] == "out": 196 | output_fn = sys.argv[i+1] 197 | i += 1 198 | elif option[1:] == "b": 199 | param = sys.argv[i+1] 200 | i += 1 201 | param = param.split(",") 202 | bss_sections[int(param[0], 0)] = param[1] 203 | elif option[1:] == "r": 204 | param = sys.argv[i+1] 205 | i += 1 206 | param = param.split(",") 207 | replace_sections[int(param[0], 0)] = param[1] 208 | elif option[1:] == "e": 209 | param = sys.argv[i+1] 210 | i += 1 211 | param = param.split(",") 212 | extract_sections[int(param[0], 0)] = param[1] 213 | else: 214 | print(" unknown option " + option) 215 | i += 1 216 | 217 | if input_fn != None: 218 | fw = ancast(open(input_fn, "rb")) 219 | fw.extract_sections(extract_sections) 220 | fw.replace_sections(replace_sections) 221 | fw.bss_sections(bss_sections) 222 | if output_fn != None: 223 | fw.write(open(output_fn, "w+b")) 224 | # fw.elf._print() 225 | -------------------------------------------------------------------------------- /sections/gensections.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | with open('../bin/fw.img.full.bin', 'rb') as infw: 4 | with open('0x5100000.bin', 'wb') as fout: 5 | infw.seek(0x89F1C, 0) 6 | fout.write(infw.read(0x15D6C)) 7 | 8 | with open('0x8140000.bin', 'wb') as fout: 9 | infw.seek(0xB4C88, 0) 10 | fout.write(infw.read(0x2478)) 11 | -------------------------------------------------------------------------------- /wupserver/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(strip $(DEVKITARM)),) 2 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 3 | endif 4 | 5 | ifeq ($(filter $(DEVKITARM)/bin,$(PATH)),) 6 | export PATH:=$(DEVKITARM)/bin:$(PATH) 7 | endif 8 | 9 | CC = arm-none-eabi-gcc 10 | # LINK = arm-none-eabi-gcc 11 | LINK = arm-none-eabi-ld 12 | AS = arm-none-eabi-as 13 | OBJCOPY = arm-none-eabi-objcopy 14 | CFLAGS += -Wall -mbig-endian -std=c99 -march=armv5 -Os -I$(DEVKITPRO)/libnds/include 15 | LDFLAGS += --script=ccd00.ld -EB -L"$(DEVKITARM)/arm-none-eabi/lib" 16 | 17 | CFILES = $(wildcard source/*.c) 18 | BINFILES = $(wildcard data/*.bin) 19 | OFILES = $(BINFILES:data/%.bin=build/%.bin.o) 20 | OFILES += $(CFILES:source/%.c=build/%.o) 21 | DFILES = $(CFILES:source/%.c=build/%.d) 22 | SFILES = $(wildcard source/*.s) 23 | OFILES += $(SFILES:source/%.s=build/%.o) 24 | PROJECTNAME = ${shell basename "$(CURDIR)"} 25 | CWD = "$(CURDIR)"" 26 | 27 | #--------------------------------------------------------------------------------- 28 | # canned command sequence for binary data, taken from devkitARM 29 | #--------------------------------------------------------------------------------- 30 | define bin2o 31 | bin2s $< | $(AS) -o $(@) 32 | echo "extern const u8" `(echo $( source/`(echo $(> source/`(echo $(> source/`(echo $( build/$*.d 60 | 61 | build/%.o: source/%.s 62 | $(CC) $(CFLAGS) -xassembler-with-cpp -c $< -o $@ 63 | @$(CC) -MM $< > build/$*.d 64 | 65 | build/%.bin.o: data/%.bin 66 | @echo $(notdir $<) 67 | @$(bin2o) 68 | -------------------------------------------------------------------------------- /wupserver/ccd00.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(arm) 2 | 3 | MEMORY 4 | { 5 | RAMX (rx) : ORIGIN = 0x05116000, LENGTH = 0x00006000 6 | RAMRW (rw!i) : ORIGIN = 0x050BE000, LENGTH = 0x00002000 7 | } 8 | 9 | SECTIONS 10 | { 11 | .text : ALIGN(0x100) { 12 | build/crt0.o(.init) 13 | *(.text) 14 | *(.rodata) 15 | } 16 | 17 | .bss : { 18 | _bss_start = .; 19 | *(.bss); 20 | } 21 | _bss_end = .; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /wupserver/ccd00.specs: -------------------------------------------------------------------------------- 1 | %rename link old_link 2 | 3 | *link: 4 | %(old_link) -T ./ccd00.ld%s 5 | -------------------------------------------------------------------------------- /wupserver/source/crt0.s: -------------------------------------------------------------------------------- 1 | .section ".init" 2 | .arm 3 | .align 4 4 | 5 | .extern _main 6 | .type _main, %function 7 | 8 | .extern memset 9 | .type memset, %function 10 | 11 | _start: 12 | 13 | @ initialize bss 14 | ldr r0, =_bss_start 15 | mov r1, #0x00 16 | ldr r2, =_bss_end 17 | sub r2, r0 18 | bl memset 19 | 20 | b _main 21 | -------------------------------------------------------------------------------- /wupserver/source/font.c: -------------------------------------------------------------------------------- 1 | const unsigned char font_bin[] = { 2 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x18, 0x18, 0x00, 0x0C, 0x00, 3 | 0x00, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, 4 | 0x00, 0x18, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x18, 0x10, 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 5 | 0x00, 0x3C, 0x66, 0x3C, 0x1C, 0xE6, 0x66, 0xFC, 0x00, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 6 | 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x18, 0x0C, 0x00, 7 | 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 8 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 9 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 10 | 0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00, 0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00, 11 | 0x00, 0x3C, 0x62, 0x30, 0x0C, 0x06, 0x7E, 0x00, 0x00, 0x3C, 0x62, 0x38, 0x60, 0x66, 0x3C, 0x00, 12 | 0x00, 0x6C, 0x6C, 0x66, 0xFE, 0x60, 0x60, 0x00, 0x00, 0x7E, 0x06, 0x7E, 0x60, 0x66, 0x3C, 0x00, 13 | 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x7E, 0x30, 0x30, 0x18, 0x18, 0x18, 0x00, 14 | 0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x3C, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00, 15 | 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x0C, 0x00, 16 | 0x00, 0x70, 0x1C, 0x06, 0x06, 0x1C, 0x70, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, 17 | 0x00, 0x0E, 0x38, 0x60, 0x60, 0x38, 0x0E, 0x00, 0x00, 0x3C, 0x66, 0x30, 0x18, 0x00, 0x18, 0x00, 18 | 0x00, 0x3C, 0x66, 0x76, 0x76, 0x06, 0x46, 0x3C, 0x00, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 19 | 0x00, 0x3E, 0x66, 0x3E, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x3C, 0x66, 0x06, 0x06, 0x66, 0x3C, 0x00, 20 | 0x00, 0x1E, 0x36, 0x66, 0x66, 0x36, 0x1E, 0x00, 0x00, 0x7E, 0x06, 0x1E, 0x06, 0x06, 0x7E, 0x00, 21 | 0x00, 0x3E, 0x06, 0x1E, 0x06, 0x06, 0x06, 0x00, 0x00, 0x3C, 0x66, 0x06, 0x76, 0x66, 0x3C, 0x00, 22 | 0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 23 | 0x00, 0x78, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x66, 0x36, 0x1E, 0x1E, 0x36, 0x66, 0x00, 24 | 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7E, 0x00, 0x00, 0x46, 0x6E, 0x7E, 0x56, 0x46, 0x46, 0x00, 25 | 0x00, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 26 | 0x00, 0x3E, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x70, 0x00, 27 | 0x00, 0x3E, 0x66, 0x3E, 0x1E, 0x36, 0x66, 0x00, 0x00, 0x3C, 0x66, 0x0C, 0x30, 0x66, 0x3C, 0x00, 28 | 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 29 | 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x46, 0x46, 0x56, 0x7E, 0x6E, 0x46, 0x00, 30 | 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, 31 | 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 32 | 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 33 | 0x00, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 34 | 0x00, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x7C, 0x00, 35 | 0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00, 0x3C, 0x06, 0x06, 0x06, 0x3C, 0x00, 36 | 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00, 37 | 0x00, 0x38, 0x0C, 0x3E, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x7C, 0x66, 0x7C, 0x40, 0x3C, 0x00, 38 | 0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x18, 0x00, 0x1C, 0x18, 0x18, 0x3C, 0x00, 39 | 0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x1E, 0x00, 0x00, 0x06, 0x06, 0x36, 0x1E, 0x36, 0x66, 0x00, 40 | 0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x66, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, 41 | 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 42 | 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x00, 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x00, 43 | 0x00, 0x00, 0x3E, 0x66, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x00, 44 | 0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 45 | 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0xC6, 0xD6, 0xFE, 0x7C, 0x6C, 0x00, 46 | 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x66, 0x66, 0x7C, 0x60, 0x3C, 0x00, 47 | 0x00, 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x7E, 0x00, 0x00, 0x00, 0x18, 0x08, 0x08, 0x04, 0x08, 0x08, 48 | 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x0C, 0x08, 0x08, 0x10, 0x08, 0x08, 49 | }; 50 | -------------------------------------------------------------------------------- /wupserver/source/font_bin.h: -------------------------------------------------------------------------------- 1 | extern const u8 font_bin[]; -------------------------------------------------------------------------------- /wupserver/source/fsa.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "svc.h" 5 | #include "imports.h" 6 | #include "fsa.h" 7 | 8 | static void* allocIobuf() 9 | { 10 | void* ptr = svcAlloc(0xCAFF, 0x828); 11 | 12 | memset(ptr, 0x00, 0x828); 13 | 14 | return ptr; 15 | } 16 | 17 | static void freeIobuf(void* ptr) 18 | { 19 | svcFree(0xCAFF, ptr); 20 | } 21 | 22 | int FSA_Mount(int fd, char* device_path, char* volume_path, u32 flags, char* arg_string, int arg_string_len) 23 | { 24 | u8* iobuf = allocIobuf(); 25 | u8* inbuf8 = iobuf; 26 | u8* outbuf8 = &iobuf[0x520]; 27 | iovec_s* iovec = (iovec_s*)&iobuf[0x7C0]; 28 | u32* inbuf = (u32*)inbuf8; 29 | u32* outbuf = (u32*)outbuf8; 30 | 31 | strncpy((char*)&inbuf8[0x04], device_path, 0x27F); 32 | strncpy((char*)&inbuf8[0x284], volume_path, 0x27F); 33 | inbuf[0x504 / 4] = (u32)flags; 34 | inbuf[0x508 / 4] = (u32)arg_string_len; 35 | 36 | iovec[0].ptr = inbuf; 37 | iovec[0].len = 0x520; 38 | iovec[1].ptr = arg_string; 39 | iovec[1].len = arg_string_len; 40 | iovec[2].ptr = outbuf; 41 | iovec[2].len = 0x293; 42 | 43 | int ret = svcIoctlv(fd, 0x01, 2, 1, iovec); 44 | 45 | freeIobuf(iobuf); 46 | return ret; 47 | } 48 | 49 | int FSA_Unmount(int fd, char* path, u32 flags) 50 | { 51 | u8* iobuf = allocIobuf(); 52 | u32* inbuf = (u32*)iobuf; 53 | u32* outbuf = (u32*)&iobuf[0x520]; 54 | 55 | strncpy((char*)&inbuf[0x01], path, 0x27F); 56 | inbuf[0x284 / 4] = flags; 57 | 58 | int ret = svcIoctl(fd, 0x02, inbuf, 0x520, outbuf, 0x293); 59 | 60 | freeIobuf(iobuf); 61 | return ret; 62 | } 63 | 64 | int FSA_MakeDir(int fd, char* path, u32 flags) 65 | { 66 | u8* iobuf = allocIobuf(); 67 | u32* inbuf = (u32*)iobuf; 68 | u32* outbuf = (u32*)&iobuf[0x520]; 69 | 70 | strncpy((char*)&inbuf[0x01], path, 0x27F); 71 | inbuf[0x284 / 4] = flags; 72 | 73 | int ret = svcIoctl(fd, 0x07, inbuf, 0x520, outbuf, 0x293); 74 | 75 | freeIobuf(iobuf); 76 | return ret; 77 | } 78 | 79 | int FSA_OpenDir(int fd, char* path, int* outHandle) 80 | { 81 | u8* iobuf = allocIobuf(); 82 | u32* inbuf = (u32*)iobuf; 83 | u32* outbuf = (u32*)&iobuf[0x520]; 84 | 85 | strncpy((char*)&inbuf[0x01], path, 0x27F); 86 | 87 | int ret = svcIoctl(fd, 0x0A, inbuf, 0x520, outbuf, 0x293); 88 | 89 | if(outHandle) *outHandle = outbuf[1]; 90 | 91 | freeIobuf(iobuf); 92 | return ret; 93 | } 94 | 95 | int FSA_ReadDir(int fd, int handle, directoryEntry_s* out_data) 96 | { 97 | u8* iobuf = allocIobuf(); 98 | u32* inbuf = (u32*)iobuf; 99 | u32* outbuf = (u32*)&iobuf[0x520]; 100 | 101 | inbuf[1] = handle; 102 | 103 | int ret = svcIoctl(fd, 0x0B, inbuf, 0x520, outbuf, 0x293); 104 | 105 | if(out_data) memcpy(out_data, &outbuf[1], sizeof(directoryEntry_s)); 106 | 107 | freeIobuf(iobuf); 108 | return ret; 109 | } 110 | 111 | int FSA_CloseDir(int fd, int handle) 112 | { 113 | u8* iobuf = allocIobuf(); 114 | u32* inbuf = (u32*)iobuf; 115 | u32* outbuf = (u32*)&iobuf[0x520]; 116 | 117 | inbuf[1] = handle; 118 | 119 | int ret = svcIoctl(fd, 0x0D, inbuf, 0x520, outbuf, 0x293); 120 | 121 | freeIobuf(iobuf); 122 | return ret; 123 | } 124 | 125 | int FSA_OpenFile(int fd, char* path, char* mode, int* outHandle) 126 | { 127 | u8* iobuf = allocIobuf(); 128 | u32* inbuf = (u32*)iobuf; 129 | u32* outbuf = (u32*)&iobuf[0x520]; 130 | 131 | strncpy((char*)&inbuf[0x01], path, 0x27F); 132 | strncpy((char*)&inbuf[0xA1], mode, 0x10); 133 | 134 | int ret = svcIoctl(fd, 0x0E, inbuf, 0x520, outbuf, 0x293); 135 | 136 | if(outHandle) *outHandle = outbuf[1]; 137 | 138 | freeIobuf(iobuf); 139 | return ret; 140 | } 141 | 142 | int _FSA_ReadWriteFile(int fd, void* data, u32 size, u32 cnt, int fileHandle, u32 flags, bool read) 143 | { 144 | u8* iobuf = allocIobuf(); 145 | u8* inbuf8 = iobuf; 146 | u8* outbuf8 = &iobuf[0x520]; 147 | iovec_s* iovec = (iovec_s*)&iobuf[0x7C0]; 148 | u32* inbuf = (u32*)inbuf8; 149 | u32* outbuf = (u32*)outbuf8; 150 | 151 | inbuf[0x08 / 4] = size; 152 | inbuf[0x0C / 4] = cnt; 153 | inbuf[0x14 / 4] = fileHandle; 154 | inbuf[0x18 / 4] = flags; 155 | 156 | iovec[0].ptr = inbuf; 157 | iovec[0].len = 0x520; 158 | 159 | iovec[1].ptr = data; 160 | iovec[1].len = size * cnt; 161 | 162 | iovec[2].ptr = outbuf; 163 | iovec[2].len = 0x293; 164 | 165 | int ret; 166 | if(read) ret = svcIoctlv(fd, 0x0F, 1, 2, iovec); 167 | else ret = svcIoctlv(fd, 0x10, 2, 1, iovec); 168 | 169 | freeIobuf(iobuf); 170 | return ret; 171 | } 172 | 173 | int FSA_ReadFile(int fd, void* data, u32 size, u32 cnt, int fileHandle, u32 flags) 174 | { 175 | return _FSA_ReadWriteFile(fd, data, size, cnt, fileHandle, flags, true); 176 | } 177 | 178 | int FSA_WriteFile(int fd, void* data, u32 size, u32 cnt, int fileHandle, u32 flags) 179 | { 180 | return _FSA_ReadWriteFile(fd, data, size, cnt, fileHandle, flags, false); 181 | } 182 | 183 | int FSA_StatFile(int fd, int handle, fileStat_s* out_data) 184 | { 185 | u8* iobuf = allocIobuf(); 186 | u32* inbuf = (u32*)iobuf; 187 | u32* outbuf = (u32*)&iobuf[0x520]; 188 | 189 | inbuf[1] = handle; 190 | 191 | int ret = svcIoctl(fd, 0x14, inbuf, 0x520, outbuf, 0x293); 192 | 193 | if(out_data) memcpy(out_data, &outbuf[1], sizeof(fileStat_s)); 194 | 195 | freeIobuf(iobuf); 196 | return ret; 197 | } 198 | 199 | // int FSA_CloseFile(int fd, int fileHandle) 200 | // { 201 | // return _FSA_CloseFile(fd, fileHandle); 202 | // } 203 | 204 | // type 4 : 205 | // 0x08 : device size in sectors (u64) 206 | // 0x10 : device sector size (u32) 207 | int FSA_GetDeviceInfo(int fd, char* device_path, int type, u32* out_data) 208 | { 209 | u8* iobuf = allocIobuf(); 210 | u32* inbuf = (u32*)iobuf; 211 | u32* outbuf = (u32*)&iobuf[0x520]; 212 | 213 | strncpy((char*)&inbuf[0x01], device_path, 0x27F); 214 | inbuf[0x284 / 4] = type; 215 | 216 | int ret = svcIoctl(fd, 0x18, inbuf, 0x520, outbuf, 0x293); 217 | 218 | int size = 0; 219 | 220 | switch(type) 221 | { 222 | case 0: case 1: case 7: 223 | size = 0x8; 224 | break; 225 | case 2: 226 | size = 0x4; 227 | break; 228 | case 3: 229 | size = 0x1E; 230 | break; 231 | case 4: 232 | size = 0x28; 233 | break; 234 | case 5: 235 | size = 0x64; 236 | break; 237 | case 6: case 8: 238 | size = 0x14; 239 | break; 240 | } 241 | 242 | memcpy(out_data, &outbuf[1], size); 243 | 244 | freeIobuf(iobuf); 245 | return ret; 246 | } 247 | 248 | int FSA_RawOpen(int fd, char* device_path, int* outHandle) 249 | { 250 | u8* iobuf = allocIobuf(); 251 | u32* inbuf = (u32*)iobuf; 252 | u32* outbuf = (u32*)&iobuf[0x520]; 253 | 254 | strncpy((char*)&inbuf[0x01], device_path, 0x27F); 255 | 256 | int ret = svcIoctl(fd, 0x6A, inbuf, 0x520, outbuf, 0x293); 257 | 258 | if(outHandle) *outHandle = outbuf[1]; 259 | 260 | freeIobuf(iobuf); 261 | return ret; 262 | } 263 | 264 | int FSA_RawClose(int fd, int device_handle) 265 | { 266 | u8* iobuf = allocIobuf(); 267 | u32* inbuf = (u32*)iobuf; 268 | u32* outbuf = (u32*)&iobuf[0x520]; 269 | 270 | inbuf[1] = device_handle; 271 | 272 | int ret = svcIoctl(fd, 0x6D, inbuf, 0x520, outbuf, 0x293); 273 | 274 | freeIobuf(iobuf); 275 | return ret; 276 | } 277 | 278 | // offset in blocks of 0x1000 bytes 279 | int FSA_RawRead(int fd, void* data, u32 size_bytes, u32 cnt, u64 blocks_offset, int device_handle) 280 | { 281 | u8* iobuf = allocIobuf(); 282 | u8* inbuf8 = iobuf; 283 | u8* outbuf8 = &iobuf[0x520]; 284 | iovec_s* iovec = (iovec_s*)&iobuf[0x7C0]; 285 | u32* inbuf = (u32*)inbuf8; 286 | u32* outbuf = (u32*)outbuf8; 287 | 288 | // note : offset_bytes = blocks_offset * size_bytes 289 | inbuf[0x08 / 4] = (blocks_offset >> 32); 290 | inbuf[0x0C / 4] = (blocks_offset & 0xFFFFFFFF); 291 | inbuf[0x10 / 4] = cnt; 292 | inbuf[0x14 / 4] = size_bytes; 293 | inbuf[0x18 / 4] = device_handle; 294 | 295 | iovec[0].ptr = inbuf; 296 | iovec[0].len = 0x520; 297 | 298 | iovec[1].ptr = data; 299 | iovec[1].len = size_bytes * cnt; 300 | 301 | iovec[2].ptr = outbuf; 302 | iovec[2].len = 0x293; 303 | 304 | int ret = svcIoctlv(fd, 0x6B, 1, 2, iovec); 305 | 306 | freeIobuf(iobuf); 307 | return ret; 308 | } 309 | 310 | int FSA_RawWrite(int fd, void* data, u32 size_bytes, u32 cnt, u64 blocks_offset, int device_handle) 311 | { 312 | u8* iobuf = allocIobuf(); 313 | u8* inbuf8 = iobuf; 314 | u8* outbuf8 = &iobuf[0x520]; 315 | iovec_s* iovec = (iovec_s*)&iobuf[0x7C0]; 316 | u32* inbuf = (u32*)inbuf8; 317 | u32* outbuf = (u32*)outbuf8; 318 | 319 | inbuf[0x08 / 4] = (blocks_offset >> 32); 320 | inbuf[0x0C / 4] = (blocks_offset & 0xFFFFFFFF); 321 | inbuf[0x10 / 4] = cnt; 322 | inbuf[0x14 / 4] = size_bytes; 323 | inbuf[0x18 / 4] = device_handle; 324 | 325 | iovec[0].ptr = inbuf; 326 | iovec[0].len = 0x520; 327 | 328 | iovec[1].ptr = data; 329 | iovec[1].len = size_bytes * cnt; 330 | 331 | iovec[2].ptr = outbuf; 332 | iovec[2].len = 0x293; 333 | 334 | int ret = svcIoctlv(fd, 0x6C, 2, 1, iovec); 335 | 336 | freeIobuf(iobuf); 337 | return ret; 338 | } 339 | -------------------------------------------------------------------------------- /wupserver/source/fsa.h: -------------------------------------------------------------------------------- 1 | #ifndef FSA_H 2 | #define FSA_H 3 | 4 | typedef struct 5 | { 6 | u32 unk[0x19]; 7 | char name[0x100]; 8 | }directoryEntry_s; 9 | 10 | typedef struct 11 | { 12 | u32 unk1[0x4]; 13 | u32 size; // size in bytes 14 | u32 physsize; // physical size on disk in bytes 15 | u32 unk2[0x13]; 16 | }fileStat_s; 17 | 18 | #define FSA_MOUNTFLAGS_BINDMOUNT (1 << 0) 19 | #define FSA_MOUNTFLAGS_GLOBAL (1 << 1) 20 | 21 | int FSA_Open(); 22 | 23 | int FSA_Mount(int fd, char* device_path, char* volume_path, u32 flags, char* arg_string, int arg_string_len); 24 | int FSA_Unmount(int fd, char* path, u32 flags); 25 | 26 | int FSA_GetDeviceInfo(int fd, char* device_path, int type, u32* out_data); 27 | 28 | int FSA_MakeDir(int fd, char* path, u32 flags); 29 | int FSA_OpenDir(int fd, char* path, int* outHandle); 30 | int FSA_ReadDir(int fd, int handle, directoryEntry_s* out_data); 31 | int FSA_CloseDir(int fd, int handle); 32 | 33 | int FSA_OpenFile(int fd, char* path, char* mode, int* outHandle); 34 | int FSA_ReadFile(int fd, void* data, u32 size, u32 cnt, int fileHandle, u32 flags); 35 | int FSA_WriteFile(int fd, void* data, u32 size, u32 cnt, int fileHandle, u32 flags); 36 | int FSA_StatFile(int fd, int handle, fileStat_s* out_data); 37 | int FSA_CloseFile(int fd, int fileHandle); 38 | 39 | int FSA_RawOpen(int fd, char* device_path, int* outHandle); 40 | int FSA_RawRead(int fd, void* data, u32 size_bytes, u32 cnt, u64 sector_offset, int device_handle); 41 | int FSA_RawWrite(int fd, void* data, u32 size_bytes, u32 cnt, u64 sector_offset, int device_handle); 42 | int FSA_RawClose(int fd, int device_handle); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /wupserver/source/imports.c: -------------------------------------------------------------------------------- 1 | #include "imports.h" 2 | 3 | void usleep(u32 time) 4 | { 5 | ((void (*const)(u32))0x050564E4)(time); 6 | } 7 | 8 | void* memset(void* dst, int val, size_t size) 9 | { 10 | char* _dst = dst; 11 | 12 | int i; 13 | for(i = 0; i < size; i++) _dst[i] = val; 14 | 15 | return dst; 16 | } 17 | 18 | void* (*const _memcpy)(void* dst, void* src, int size) = (void*)0x05054E54; 19 | 20 | void* memcpy(void* dst, const void* src, size_t size) 21 | { 22 | return _memcpy(dst, (void*)src, size); 23 | } 24 | 25 | char* strncpy(char* dst, const char* src, size_t size) 26 | { 27 | int i; 28 | for(i = 0; i < size; i++) 29 | { 30 | dst[i] = src[i]; 31 | if(src[i] == '\0') return dst; 32 | } 33 | 34 | return dst; 35 | } 36 | 37 | int vsnprintf(char * s, size_t n, const char * format, va_list arg) 38 | { 39 | return ((int (*const)(char*, size_t, const char *, va_list))0x05055C40)(s, n, format, arg); 40 | } 41 | -------------------------------------------------------------------------------- /wupserver/source/imports.h: -------------------------------------------------------------------------------- 1 | #ifndef IMPORTS_H 2 | #define IMPORTS_H 3 | 4 | #include 5 | #include 6 | #include "types.h" 7 | 8 | #define MCP_SVC_BASE ((void*)0x050567EC) 9 | 10 | void usleep(u32 time); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /wupserver/source/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "imports.h" 5 | #include "net_ifmgr_ncl.h" 6 | #include "socket.h" 7 | #include "fsa.h" 8 | #include "svc.h" 9 | #include "text.h" 10 | 11 | // TODO : errno stuff ? 12 | 13 | bool serverKilled; 14 | 15 | // overwrites command_buffer with response 16 | // returns length of response (or 0 for no response, negative for error) 17 | int serverCommandHandler(u32* command_buffer, u32 length) 18 | { 19 | if(!command_buffer || !length) return -1; 20 | 21 | int out_length = 4; 22 | 23 | switch(command_buffer[0]) 24 | { 25 | case 0: 26 | // write 27 | // [cmd_id][addr] 28 | { 29 | void* dst = (void*)command_buffer[1]; 30 | 31 | memcpy(dst, &command_buffer[2], length - 8); 32 | } 33 | break; 34 | case 1: 35 | // read 36 | // [cmd_id][addr][length] 37 | { 38 | void* src = (void*)command_buffer[1]; 39 | length = command_buffer[2]; 40 | 41 | memcpy(&command_buffer[1], src, length); 42 | out_length = length + 4; 43 | } 44 | break; 45 | case 2: 46 | // svc 47 | // [cmd_id][svc_id] 48 | { 49 | int svc_id = command_buffer[1]; 50 | int size_arguments = length - 8; 51 | 52 | u32 arguments[8]; 53 | memset(arguments, 0x00, sizeof(arguments)); 54 | memcpy(arguments, &command_buffer[2], (size_arguments < 8 * 4) ? size_arguments : (8 * 4)); 55 | 56 | // return error code as data 57 | out_length = 8; 58 | command_buffer[1] = ((int (*const)(u32, u32, u32, u32, u32, u32, u32, u32))(MCP_SVC_BASE + svc_id * 8))(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]); 59 | } 60 | break; 61 | case 3: 62 | // kill 63 | // [cmd_id] 64 | { 65 | serverKilled = true; 66 | } 67 | break; 68 | case 4: 69 | // memcpy 70 | // [dst][src][size] 71 | { 72 | void* dst = (void*)command_buffer[1]; 73 | void* src = (void*)command_buffer[2]; 74 | int size = command_buffer[3]; 75 | 76 | memcpy(dst, src, size); 77 | } 78 | break; 79 | case 5: 80 | // repeated-write 81 | // [address][value][n] 82 | { 83 | u32* dst = (u32*)command_buffer[1]; 84 | u32* cache_range = (u32*)(command_buffer[1] & ~0xFF); 85 | u32 value = command_buffer[2]; 86 | u32 n = command_buffer[3]; 87 | 88 | u32 old = *dst; 89 | int i; 90 | for(i = 0; i < n; i++) 91 | { 92 | if(*dst != old) 93 | { 94 | if(*dst == 0x0) old = *dst; 95 | else 96 | { 97 | *dst = value; 98 | svcFlushDCache(cache_range, 0x100); 99 | break; 100 | } 101 | }else 102 | { 103 | svcInvalidateDCache(cache_range, 0x100); 104 | usleep(50); 105 | } 106 | } 107 | } 108 | break; 109 | default: 110 | // unknown command 111 | return -2; 112 | break; 113 | } 114 | 115 | // no error ! 116 | command_buffer[0] = 0x00000000; 117 | return out_length; 118 | } 119 | 120 | void serverClientHandler(int sock) 121 | { 122 | u32 command_buffer[0x180]; 123 | 124 | while(!serverKilled) 125 | { 126 | int ret = recv(sock, command_buffer, sizeof(command_buffer), 0); 127 | 128 | if(ret <= 0) break; 129 | 130 | ret = serverCommandHandler(command_buffer, ret); 131 | 132 | if(ret > 0) 133 | { 134 | send(sock, command_buffer, ret, 0); 135 | }else if(ret < 0) 136 | { 137 | send(sock, &ret, sizeof(int), 0); 138 | } 139 | } 140 | 141 | closesocket(sock); 142 | } 143 | 144 | void serverListenClients() 145 | { 146 | serverKilled = false; 147 | 148 | int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 149 | 150 | struct sockaddr_in server; 151 | 152 | memset(&server, 0x00, sizeof(server)); 153 | 154 | server.sin_family = AF_INET; 155 | server.sin_port = 1337; 156 | server.sin_addr.s_addr = 0; 157 | 158 | int ret = bind(sock, (struct sockaddr *)&server, sizeof(server)); 159 | 160 | while(!serverKilled) 161 | { 162 | ret = listen(sock, 1); 163 | 164 | if(ret >= 0) 165 | { 166 | int csock = accept(sock, NULL, NULL); 167 | 168 | // TODO : threading 169 | serverClientHandler(csock); 170 | }else usleep(1000); 171 | } 172 | } 173 | 174 | void _main() 175 | { 176 | clearScreen(0xFF0000FF); 177 | 178 | while(ifmgrnclInit() <= 0) 179 | { 180 | print(0, 0, "opening /dev/net/ifmgr/ncl..."); 181 | usleep(1000); 182 | } 183 | 184 | while(true) 185 | { 186 | u16 out0, out1; 187 | 188 | int ret0 = IFMGRNCL_GetInterfaceStatus(0, &out0); 189 | if(!ret0 && out0 == 1) break; 190 | 191 | int ret1 = IFMGRNCL_GetInterfaceStatus(1, &out1); 192 | if(!ret1 && out1 == 1) break; 193 | 194 | print(0, 0, "initializing /dev/net/ifmgr/ncl... %08X %08X %08X %08X ", ret0, ret1, out0, out1); 195 | 196 | usleep(1000); 197 | } 198 | 199 | while(socketInit() <= 0) 200 | { 201 | print(0, 0, "opening /dev/socket..."); 202 | usleep(1000); 203 | } 204 | 205 | print(0, 0, "opened /dev/socket !"); 206 | usleep(5*1000*1000); 207 | print(0, 10, "attempting sockets !"); 208 | 209 | serverListenClients(); 210 | 211 | while(1) 212 | { 213 | usleep(1000*1000); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /wupserver/source/net_ifmgr_ncl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "net_ifmgr_ncl.h" 4 | #include "imports.h" 5 | #include "svc.h" 6 | 7 | static int ifmgrncl_handle = 0; 8 | 9 | int ifmgrnclInit() 10 | { 11 | if(ifmgrncl_handle) return ifmgrncl_handle; 12 | 13 | int ret = svcOpen("/dev/net/ifmgr/ncl", 0); 14 | 15 | if(ret > 0) 16 | { 17 | ifmgrncl_handle = ret; 18 | return ifmgrncl_handle; 19 | } 20 | 21 | return ret; 22 | } 23 | 24 | int ifmgrnclExit() 25 | { 26 | int ret = svcClose(ifmgrncl_handle); 27 | 28 | ifmgrncl_handle = 0; 29 | 30 | return ret; 31 | } 32 | 33 | static void* allocIobuf(u32 size) 34 | { 35 | void* ptr = svcAlloc(0xCAFF, size); 36 | 37 | if(ptr) memset(ptr, 0x00, size); 38 | 39 | return ptr; 40 | } 41 | 42 | static void freeIobuf(void* ptr) 43 | { 44 | svcFree(0xCAFF, ptr); 45 | } 46 | 47 | int IFMGRNCL_GetInterfaceStatus(u16 interface_id, u16* out_status) 48 | { 49 | u8* iobuf1 = allocIobuf(0x2); 50 | u16* inbuf = (u16*)iobuf1; 51 | u8* iobuf2 = allocIobuf(0x8); 52 | u16* outbuf = (u16*)iobuf2; 53 | 54 | inbuf[0] = interface_id; 55 | 56 | int ret = svcIoctl(ifmgrncl_handle, 0x14, inbuf, 0x2, outbuf, 0x8); 57 | 58 | if(!ret && out_status) *out_status = outbuf[2]; 59 | 60 | freeIobuf(iobuf1); 61 | freeIobuf(iobuf2); 62 | return ret; 63 | } 64 | -------------------------------------------------------------------------------- /wupserver/source/net_ifmgr_ncl.h: -------------------------------------------------------------------------------- 1 | #ifndef NET_IFMGR_NCL 2 | #define NET_IFMGR_NCL 3 | 4 | #include "types.h" 5 | 6 | int ifmgrnclInit(); 7 | int ifmgrnclExit(); 8 | 9 | int IFMGRNCL_GetInterfaceStatus(u16 interface_id, u16* out_status); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /wupserver/source/socket.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "socket.h" 5 | #include "svc.h" 6 | #include "text.h" 7 | 8 | static int socket_handle = 0; 9 | 10 | int socketInit() 11 | { 12 | if(socket_handle) return socket_handle; 13 | 14 | int ret = svcOpen("/dev/socket", 0); 15 | 16 | if(ret > 0) 17 | { 18 | socket_handle = ret; 19 | return socket_handle; 20 | } 21 | 22 | return ret; 23 | } 24 | 25 | int socketExit() 26 | { 27 | int ret = svcClose(socket_handle); 28 | 29 | socket_handle = 0; 30 | 31 | return ret; 32 | } 33 | 34 | static void* allocIobuf(u32 size) 35 | { 36 | void* ptr = svcAlloc(0xCAFF, size); 37 | 38 | if(ptr) memset(ptr, 0x00, size); 39 | 40 | return ptr; 41 | } 42 | 43 | static void freeIobuf(void* ptr) 44 | { 45 | svcFree(0xCAFF, ptr); 46 | } 47 | 48 | int socket(int domain, int type, int protocol) 49 | { 50 | u8* iobuf = allocIobuf(0xC); 51 | u32* inbuf = (u32*)iobuf; 52 | 53 | inbuf[0] = domain; 54 | inbuf[1] = type; 55 | inbuf[2] = protocol; 56 | 57 | int ret = svcIoctl(socket_handle, 0x11, inbuf, 0xC, NULL, 0); 58 | 59 | freeIobuf(iobuf); 60 | return ret; 61 | } 62 | 63 | int closesocket(int sockfd) 64 | { 65 | u8* iobuf = allocIobuf(0x4); 66 | u32* inbuf = (u32*)iobuf; 67 | 68 | inbuf[0] = sockfd; 69 | 70 | int ret = svcIoctl(socket_handle, 0x3, inbuf, 0x4, NULL, 0); 71 | 72 | freeIobuf(iobuf); 73 | return ret; 74 | } 75 | 76 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) 77 | { 78 | u8* iobuf = allocIobuf(0x18); 79 | u32* inbuf = (u32*)iobuf; 80 | u32* outbuf = (u32*)inbuf; 81 | 82 | inbuf[0] = sockfd; 83 | 84 | int ret = -1; 85 | 86 | if(addr && addrlen && *addrlen == 0x10) 87 | { 88 | inbuf[5] = *addrlen; 89 | 90 | ret = svcIoctl(socket_handle, 0x1, inbuf, 0x18, outbuf, 0x18); 91 | 92 | if(ret >= 0) 93 | { 94 | memcpy(addr, &outbuf[1], outbuf[5]); 95 | *addrlen = outbuf[5]; 96 | } 97 | }else{ 98 | inbuf[5] = 0x10; 99 | 100 | ret = svcIoctl(socket_handle, 0x1, inbuf, 0x18, outbuf, 0x18); 101 | } 102 | 103 | freeIobuf(iobuf); 104 | return ret; 105 | } 106 | 107 | int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) 108 | { 109 | if(addrlen != 0x10) return -1; 110 | 111 | u8* iobuf = allocIobuf(0x18); 112 | u32* inbuf = (u32*)iobuf; 113 | 114 | inbuf[0] = sockfd; 115 | memcpy(&inbuf[1], addr, addrlen); 116 | inbuf[5] = addrlen; 117 | 118 | int ret = svcIoctl(socket_handle, 0x2, inbuf, 0x18, NULL, 0); 119 | 120 | freeIobuf(iobuf); 121 | return ret; 122 | } 123 | 124 | int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) 125 | { 126 | if(addrlen != 0x10) return -1; 127 | 128 | u8* iobuf = allocIobuf(0x18); 129 | u32* inbuf = (u32*)iobuf; 130 | 131 | inbuf[0] = sockfd; 132 | memcpy(&inbuf[1], addr, addrlen); 133 | inbuf[5] = addrlen; 134 | 135 | int ret = svcIoctl(socket_handle, 0x4, inbuf, 0x18, NULL, 0); 136 | 137 | freeIobuf(iobuf); 138 | return ret; 139 | } 140 | 141 | int listen(int sockfd, int backlog) 142 | { 143 | u8* iobuf = allocIobuf(0x8); 144 | u32* inbuf = (u32*)iobuf; 145 | 146 | inbuf[0] = sockfd; 147 | inbuf[1] = backlog; 148 | 149 | int ret = svcIoctl(socket_handle, 0xA, inbuf, 0x8, NULL, 0); 150 | 151 | freeIobuf(iobuf); 152 | return ret; 153 | } 154 | 155 | int recv(int sockfd, void *buf, size_t len, int flags) 156 | { 157 | if(!len) return -101; 158 | 159 | // TODO : size checks, split up data into multiple vectors if necessary 160 | void* data_buf = svcAllocAlign(0xCAFF, len, 0x40); 161 | if(!data_buf) return -100; 162 | 163 | u8* iobuf = allocIobuf(0x38); 164 | iovec_s* iovec = (iovec_s*)iobuf; 165 | u32* inbuf = (u32*)&iobuf[0x30]; 166 | 167 | inbuf[0] = sockfd; 168 | inbuf[1] = flags; 169 | 170 | iovec[0].ptr = inbuf; 171 | iovec[0].len = 0x8; 172 | iovec[1].ptr = (void*)data_buf; 173 | iovec[1].len = len; 174 | 175 | int ret = svcIoctlv(socket_handle, 0xC, 1, 3, iovec); 176 | 177 | if(ret > 0 && buf) 178 | { 179 | memcpy(buf, data_buf, ret); 180 | } 181 | 182 | freeIobuf(data_buf); 183 | freeIobuf(iobuf); 184 | return ret; 185 | } 186 | 187 | int send(int sockfd, const void *buf, size_t len, int flags) 188 | { 189 | if(!buf || !len) return -101; 190 | 191 | // TODO : size checks, split up data into multiple vectors if necessary 192 | void* data_buf = svcAllocAlign(0xCAFF, len, 0x40); 193 | if(!data_buf) return -100; 194 | 195 | u8* iobuf = allocIobuf(0x38); 196 | iovec_s* iovec = (iovec_s*)iobuf; 197 | u32* inbuf = (u32*)&iobuf[0x30]; 198 | 199 | memcpy(data_buf, buf, len); 200 | 201 | inbuf[0] = sockfd; 202 | inbuf[1] = flags; 203 | 204 | iovec[0].ptr = inbuf; 205 | iovec[0].len = 0x8; 206 | iovec[1].ptr = (void*)data_buf; 207 | iovec[1].len = len; 208 | 209 | int ret = svcIoctlv(socket_handle, 0xE, 4, 0, iovec); 210 | 211 | freeIobuf(data_buf); 212 | freeIobuf(iobuf); 213 | return ret; 214 | } 215 | -------------------------------------------------------------------------------- /wupserver/source/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef SOCKET_H 2 | #define SOCKET_H 3 | 4 | // slightly stolen from ctrulib 5 | #include 6 | 7 | #define SOL_SOCKET 0xFFFF 8 | 9 | #define PF_UNSPEC 0 10 | #define PF_INET 2 11 | #define PF_INET6 10 12 | 13 | #define AF_UNSPEC PF_UNSPEC 14 | #define AF_INET PF_INET 15 | #define AF_INET6 PF_INET6 16 | 17 | #define SOCK_STREAM 1 18 | #define SOCK_DGRAM 2 19 | 20 | #define MSG_CTRUNC 0x01000000 21 | #define MSG_DONTROUTE 0x02000000 22 | #define MSG_EOR 0x04000000 23 | #define MSG_OOB 0x08000000 24 | #define MSG_PEEK 0x10000000 25 | #define MSG_TRUNC 0x20000000 26 | #define MSG_WAITALL 0x40000000 27 | 28 | #define SHUT_RD 0 29 | #define SHUT_WR 1 30 | #define SHUT_RDWR 2 31 | 32 | #define SO_DEBUG 0x0001 33 | #define SO_ACCEPTCONN 0x0002 34 | #define SO_REUSEADDR 0x0004 35 | #define SO_KEEPALIVE 0x0008 36 | #define SO_DONTROUTE 0x0010 37 | #define SO_BROADCAST 0x0020 38 | #define SO_USELOOPBACK 0x0040 39 | #define SO_LINGER 0x0080 40 | #define SO_OOBINLINE 0x0100 41 | #define SO_REUSEPORT 0x0200 42 | #define SO_SNDBUF 0x1001 43 | #define SO_RCVBUF 0x1002 44 | #define SO_SNDLOWAT 0x1003 45 | #define SO_RCVLOWAT 0x1004 46 | #define SO_SNDTIMEO 0x1005 47 | #define SO_RCVTIMEO 0x1006 48 | #define SO_ERROR 0x1007 49 | #define SO_TYPE 0x1008 50 | 51 | #define INADDR_ANY 0x00000000 52 | #define INADDR_BROADCAST 0xFFFFFFFF 53 | #define INADDR_NONE 0xFFFFFFFF 54 | 55 | #define INET_ADDRSTRLEN 16 56 | 57 | #define INADDR_LOOPBACK 0x7f000001 58 | #define INADDR_ANY 0x00000000 59 | #define INADDR_BROADCAST 0xFFFFFFFF 60 | #define INADDR_NONE 0xFFFFFFFF 61 | 62 | #define INET_ADDRSTRLEN 16 63 | 64 | #define IPPROTO_IP 0 /* dummy for IP */ 65 | #define IPPROTO_UDP 17 /* user datagram protocol */ 66 | #define IPPROTO_TCP 6 /* tcp */ 67 | 68 | #define IP_TOS 7 69 | #define IP_TTL 8 70 | #define IP_MULTICAST_LOOP 9 71 | #define IP_MULTICAST_TTL 10 72 | #define IP_ADD_MEMBERSHIP 11 73 | #define IP_DROP_MEMBERSHIP 12 74 | 75 | typedef uint32_t socklen_t; 76 | typedef uint16_t sa_family_t; 77 | 78 | struct sockaddr 79 | { 80 | sa_family_t sa_family; 81 | char sa_data[]; 82 | }; 83 | 84 | struct sockaddr_storage 85 | { 86 | sa_family_t ss_family; 87 | char __ss_padding[14]; 88 | }; 89 | 90 | typedef uint16_t in_port_t; 91 | typedef uint32_t in_addr_t; 92 | 93 | struct in_addr { 94 | in_addr_t s_addr; 95 | }; 96 | 97 | struct sockaddr_in { 98 | sa_family_t sin_family; 99 | in_port_t sin_port; 100 | struct in_addr sin_addr; 101 | unsigned char sin_zero[8]; 102 | }; 103 | 104 | struct linger 105 | { 106 | int l_onoff; 107 | int l_linger; 108 | }; 109 | 110 | int socketInit(); 111 | int socketExit(); 112 | 113 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 114 | int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 115 | int closesocket(int sockfd); 116 | int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 117 | int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 118 | int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 119 | int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); 120 | int listen(int sockfd, int backlog); 121 | ssize_t recv(int sockfd, void *buf, size_t len, int flags); 122 | ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 123 | ssize_t send(int sockfd, const void *buf, size_t len, int flags); 124 | ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); 125 | int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 126 | int shutdown(int sockfd, int how); 127 | int socket(int domain, int type, int protocol); 128 | int sockatmark(int sockfd); 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /wupserver/source/svc.h: -------------------------------------------------------------------------------- 1 | #ifndef SVC_H 2 | #define SVC_H 3 | 4 | #include "types.h" 5 | 6 | typedef struct 7 | { 8 | void* ptr; 9 | u32 len; 10 | u32 unk; 11 | }iovec_s; 12 | 13 | void* svcAlloc(u32 heapid, u32 size); 14 | void* svcAllocAlign(u32 heapid, u32 size, u32 align); 15 | void svcFree(u32 heapid, void* ptr); 16 | int svcOpen(char* name, int mode); 17 | int svcClose(int fd); 18 | int svcIoctl(int fd, u32 request, void* input_buffer, u32 input_buffer_len, void* output_buffer, u32 output_buffer_len); 19 | int svcIoctlv(int fd, u32 request, u32 vector_count_in, u32 vector_count_out, iovec_s* vector); 20 | int svcInvalidateDCache(void* address, u32 size); 21 | int svcFlushDCache(void* address, u32 size); 22 | int svcBackdoor(u32, u32, u32, void* func); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /wupserver/source/svc.s: -------------------------------------------------------------------------------- 1 | .section ".text" 2 | .arm 3 | .align 4 4 | 5 | .global svcAlloc 6 | .type svcAlloc, %function 7 | svcAlloc: 8 | .word 0xE7F027F0 9 | bx lr 10 | 11 | .global svcAllocAlign 12 | .type svcAllocAlign, %function 13 | svcAllocAlign: 14 | .word 0xE7F028F0 15 | bx lr 16 | 17 | .global svcFree 18 | .type svcFree, %function 19 | svcFree: 20 | .word 0xE7F029F0 21 | bx lr 22 | 23 | .global svcOpen 24 | .type svcOpen, %function 25 | svcOpen: 26 | .word 0xE7F033F0 27 | bx lr 28 | 29 | .global svcClose 30 | .type svcClose, %function 31 | svcClose: 32 | .word 0xE7F034F0 33 | bx lr 34 | 35 | .global svcIoctl 36 | .type svcIoctl, %function 37 | svcIoctl: 38 | .word 0xE7F038F0 39 | bx lr 40 | 41 | .global svcIoctlv 42 | .type svcIoctlv, %function 43 | svcIoctlv: 44 | .word 0xE7F039F0 45 | bx lr 46 | 47 | .global svcInvalidateDCache 48 | .type svcInvalidateDCache, %function 49 | svcInvalidateDCache: 50 | .word 0xE7F051F0 51 | bx lr 52 | 53 | .global svcFlushDCache 54 | .type svcFlushDCache, %function 55 | svcFlushDCache: 56 | .word 0xE7F052F0 57 | bx lr 58 | 59 | .global svcBackdoor 60 | .type svcBackdoor, %function 61 | svcBackdoor: 62 | .word 0xE7F081F0 63 | bx lr 64 | -------------------------------------------------------------------------------- /wupserver/source/text.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "imports.h" 5 | #include "font_bin.h" 6 | 7 | #define FRAMEBUFFER_ADDRESS (0x14000000+0x38C0000) 8 | #define FRAMEBUFFER_STRIDE (0xE00) 9 | #define FRAMEBUFFER_STRIDE_WORDS (FRAMEBUFFER_STRIDE >> 2) 10 | 11 | #define CHAR_SIZE_X (8) 12 | #define CHAR_SIZE_Y (8) 13 | 14 | u32* const framebuffer = (u32*)FRAMEBUFFER_ADDRESS; 15 | 16 | void clearScreen(u32 color) 17 | { 18 | int i; 19 | for(i = 0; i < 896 * 504; i++) 20 | { 21 | framebuffer[i] = color; 22 | } 23 | } 24 | 25 | void drawCharacter(char c, int x, int y) 26 | { 27 | if(c < 32)return; 28 | c -= 32; 29 | u8* charData = (u8*)&font_bin[(CHAR_SIZE_X * CHAR_SIZE_Y * c) / 8]; 30 | u32* fb = &framebuffer[x + y * FRAMEBUFFER_STRIDE_WORDS]; 31 | int i, j; 32 | for(i = 0; i < CHAR_SIZE_Y; i++) 33 | { 34 | u8 v= *(charData++); 35 | for(j = 0; j < CHAR_SIZE_X; j++) 36 | { 37 | if(v & 1) *fb = 0x00000000; 38 | else *fb = 0xFFFFFFFF; 39 | v >>= 1; 40 | fb++; 41 | } 42 | fb += FRAMEBUFFER_STRIDE_WORDS - CHAR_SIZE_X; 43 | } 44 | } 45 | 46 | void drawString(char* str, int x, int y) 47 | { 48 | if(!str) return; 49 | int k; 50 | int dx = 0, dy = 0; 51 | for(k = 0; str[k]; k++) 52 | { 53 | if(str[k] >= 32 && str[k] < 128) drawCharacter(str[k], x + dx, y + dy); 54 | 55 | dx += 8; 56 | 57 | if(str[k] == '\n') 58 | { 59 | dx = 0; 60 | dy -= 8; 61 | } 62 | } 63 | } 64 | 65 | void print(int x, int y, const char *format, ...) 66 | { 67 | va_list args; 68 | va_start(args, format); 69 | 70 | static char buffer[0x100]; 71 | 72 | vsnprintf(buffer, 0xFF, format, args); 73 | drawString(buffer, x, y); 74 | 75 | va_end(args); 76 | } 77 | -------------------------------------------------------------------------------- /wupserver/source/text.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXT_H 2 | #define TEXT_H 3 | 4 | #include "types.h" 5 | 6 | void clearScreen(u32 color); 7 | void drawString(char* str, int x, int y); 8 | void print(int x, int y, const char *format, ...); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /wupserver/source/types.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPES_H 2 | #define TYPES_H 3 | 4 | #include 5 | #include 6 | 7 | #define U64_MAX UINT64_MAX 8 | 9 | typedef uint8_t u8; 10 | typedef uint16_t u16; 11 | typedef uint32_t u32; 12 | typedef uint64_t u64; 13 | 14 | typedef int8_t s8; 15 | typedef int16_t s16; 16 | typedef int32_t s32; 17 | typedef int64_t s64; 18 | 19 | typedef volatile u8 vu8; 20 | typedef volatile u16 vu16; 21 | typedef volatile u32 vu32; 22 | typedef volatile u64 vu64; 23 | 24 | typedef volatile s8 vs8; 25 | typedef volatile s16 vs16; 26 | typedef volatile s32 vs32; 27 | typedef volatile s64 vs64; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /wupserver/wupclient.py: -------------------------------------------------------------------------------- 1 | # may or may not be inspired by plutoo's ctrrpc 2 | import errno 3 | import socket 4 | import os 5 | import sys 6 | import struct 7 | import codecs 8 | from time import sleep 9 | 10 | def buffer(size): 11 | return bytearray([0x00] * size) 12 | 13 | def copy_string(buffer, s, offset): 14 | s += "\0" 15 | buffer[offset : (offset + len(s))] = bytearray(s, "ascii") 16 | 17 | def copy_word(buffer, w, offset): 18 | buffer[offset : (offset + 4)] = struct.pack(">I", w) 19 | 20 | def get_string(buffer, offset): 21 | s = buffer[offset:] 22 | if b'\x00' in s: 23 | return s[:s.index(b'\x00')].decode("utf-8") 24 | else: 25 | return s.decode("utf-8") 26 | 27 | class wupclient: 28 | s=None 29 | 30 | def __init__(self, ip='192.168.0.197', port=1337): 31 | self.s=socket.socket() 32 | self.s.connect((ip, port)) 33 | self.fsa_handle = None 34 | self.cwd = "/vol/storage_mlc01" 35 | 36 | def __del__(self): 37 | if self.fsa_handle != None: 38 | self.close(self.fsa_handle) 39 | self.fsa_handle = None 40 | 41 | # fundamental comms 42 | def send(self, command, data): 43 | request = struct.pack('>I', command) + data 44 | 45 | self.s.send(request) 46 | response = self.s.recv(0x600) 47 | 48 | ret = struct.unpack(">I", response[:4])[0] 49 | return (ret, response[4:]) 50 | 51 | # core commands 52 | def read(self, addr, len): 53 | data = struct.pack(">II", addr, len) 54 | ret, data = self.send(1, data) 55 | if ret == 0: 56 | return data 57 | else: 58 | print("read error : %08X" % ret) 59 | return None 60 | 61 | def send_and_exit(self, command, data): 62 | request = struct.pack('>I', command) + data 63 | self.s.send(request) 64 | self.s.close() 65 | self.s = None 66 | self.fsa_handle = None 67 | exit() 68 | 69 | def write(self, addr, data): 70 | data = struct.pack(">I", addr) + data 71 | ret, data = self.send(0, data) 72 | if ret == 0: 73 | return ret 74 | else: 75 | print("write error : %08X" % ret) 76 | return None 77 | 78 | def svc(self, svc_id, arguments): 79 | data = struct.pack(">I", svc_id) 80 | for a in arguments: 81 | data += struct.pack(">I", a) 82 | ret, data = self.send(2, data) 83 | if ret == 0: 84 | return struct.unpack(">I", data)[0] 85 | else: 86 | print("svc error : %08X" % ret) 87 | return None 88 | 89 | def svc_and_exit(self, svc_id, arguments): 90 | data = struct.pack(">I", svc_id) 91 | for a in arguments: 92 | data += struct.pack(">I", a) 93 | self.send_and_exit(2, data) 94 | 95 | def kill(self): 96 | ret, _ = self.send(3, bytearray()) 97 | return ret 98 | 99 | def memcpy(self, dst, src, len): 100 | data = struct.pack(">III", dst, src, len) 101 | ret, data = self.send(4, data) 102 | if ret == 0: 103 | return ret 104 | else: 105 | print("memcpy error : %08X" % ret) 106 | return None 107 | 108 | def repeatwrite(self, dst, val, n): 109 | data = struct.pack(">III", dst, val, n) 110 | ret, data = self.send(5, data) 111 | if ret == 0: 112 | return ret 113 | else: 114 | print("repeatwrite error : %08X" % ret) 115 | return None 116 | 117 | # derivatives 118 | def alloc(self, size, align = None): 119 | if size == 0: 120 | return 0 121 | if align == None: 122 | return self.svc(0x27, [0xCAFF, size]) 123 | else: 124 | return self.svc(0x28, [0xCAFF, size, align]) 125 | 126 | def free(self, address): 127 | if address == 0: 128 | return 0 129 | return self.svc(0x29, [0xCAFF, address]) 130 | 131 | def load_buffer(self, b, align = None): 132 | if len(b) == 0: 133 | return 0 134 | address = self.alloc(len(b), align) 135 | self.write(address, b) 136 | return address 137 | 138 | def load_string(self, s, align = None): 139 | return self.load_buffer(bytearray(s + "\0", "ascii"), align) 140 | 141 | def open(self, device, mode): 142 | address = self.load_string(device) 143 | handle = self.svc(0x33, [address, mode]) 144 | self.free(address) 145 | return handle 146 | 147 | def close(self, handle): 148 | return self.svc(0x34, [handle]) 149 | 150 | def ioctl(self, handle, cmd, inbuf, outbuf_size): 151 | in_address = self.load_buffer(inbuf) 152 | out_data = None 153 | if outbuf_size > 0: 154 | out_address = self.alloc(outbuf_size) 155 | ret = self.svc(0x38, [handle, cmd, in_address, len(inbuf), out_address, outbuf_size]) 156 | out_data = self.read(out_address, outbuf_size) 157 | self.free(out_address) 158 | else: 159 | ret = self.svc(0x38, [handle, cmd, in_address, len(inbuf), 0, 0]) 160 | self.free(in_address) 161 | return (ret, out_data) 162 | 163 | def iovec(self, vecs): 164 | data = bytearray() 165 | for (a, s) in vecs: 166 | data += struct.pack(">III", a, s, 0) 167 | return self.load_buffer(data) 168 | 169 | def ioctlv(self, handle, cmd, inbufs, outbuf_sizes, inbufs_ptr = [], outbufs_ptr = []): 170 | inbufs = [(self.load_buffer(b, 0x40), len(b)) for b in inbufs] 171 | outbufs = [(self.alloc(s, 0x40), s) for s in outbuf_sizes] 172 | iovecs = self.iovec(inbufs + inbufs_ptr + outbufs_ptr + outbufs) 173 | out_data = [] 174 | ret = self.svc(0x39, [handle, cmd, len(inbufs + inbufs_ptr), len(outbufs + outbufs_ptr), iovecs]) 175 | for (a, s) in outbufs: 176 | out_data += [self.read(a, s)] 177 | for (a, _) in (inbufs + outbufs): 178 | self.free(a) 179 | self.free(iovecs) 180 | return (ret, out_data) 181 | 182 | # fsa 183 | def FSA_Mount(self, handle, device_path, volume_path, flags): 184 | inbuffer = buffer(0x520) 185 | copy_string(inbuffer, device_path, 0x0004) 186 | copy_string(inbuffer, volume_path, 0x0284) 187 | copy_word(inbuffer, flags, 0x0504) 188 | (ret, _) = self.ioctlv(handle, 0x01, [inbuffer, bytearray()], [0x293]) 189 | return ret 190 | 191 | def FSA_Unmount(self, handle, path, flags): 192 | inbuffer = buffer(0x520) 193 | copy_string(inbuffer, path, 0x4) 194 | copy_word(inbuffer, flags, 0x284) 195 | (ret, _) = self.ioctl(handle, 0x02, inbuffer, 0x293) 196 | return ret 197 | 198 | def FSA_RawOpen(self, handle, device): 199 | inbuffer = buffer(0x520) 200 | copy_string(inbuffer, device, 0x4) 201 | (ret, data) = self.ioctl(handle, 0x6A, inbuffer, 0x293) 202 | return (ret, struct.unpack(">I", data[4:8])[0]) 203 | 204 | def FSA_OpenDir(self, handle, path): 205 | inbuffer = buffer(0x520) 206 | copy_string(inbuffer, path, 0x4) 207 | (ret, data) = self.ioctl(handle, 0x0A, inbuffer, 0x293) 208 | return (ret, struct.unpack(">I", data[4:8])[0]) 209 | 210 | def FSA_ReadDir(self, handle, dir_handle): 211 | inbuffer = buffer(0x520) 212 | copy_word(inbuffer, dir_handle, 0x4) 213 | (ret, data) = self.ioctl(handle, 0x0B, inbuffer, 0x293) 214 | data = bytearray(data[4:]) 215 | unk = data[:0x64] 216 | if ret == 0: 217 | return (ret, {"name" : get_string(data, 0x64), "is_file" : (unk[0] & 128) != 128, "unk" : unk}) 218 | else: 219 | return (ret, None) 220 | 221 | def FSA_CloseDir(self, handle, dir_handle): 222 | inbuffer = buffer(0x520) 223 | copy_word(inbuffer, dir_handle, 0x4) 224 | (ret, data) = self.ioctl(handle, 0x0D, inbuffer, 0x293) 225 | return ret 226 | 227 | def FSA_OpenFile(self, handle, path, mode): 228 | inbuffer = buffer(0x520) 229 | copy_string(inbuffer, path, 0x4) 230 | copy_string(inbuffer, mode, 0x284) 231 | (ret, data) = self.ioctl(handle, 0x0E, inbuffer, 0x293) 232 | return (ret, struct.unpack(">I", data[4:8])[0]) 233 | 234 | def FSA_MakeDir(self, handle, path, flags): 235 | inbuffer = buffer(0x520) 236 | copy_string(inbuffer, path, 0x4) 237 | copy_word(inbuffer, flags, 0x284) 238 | (ret, _) = self.ioctl(handle, 0x07, inbuffer, 0x293) 239 | return ret 240 | 241 | def FSA_ReadFile(self, handle, file_handle, size, cnt): 242 | inbuffer = buffer(0x520) 243 | copy_word(inbuffer, size, 0x08) 244 | copy_word(inbuffer, cnt, 0x0C) 245 | copy_word(inbuffer, file_handle, 0x14) 246 | (ret, data) = self.ioctlv(handle, 0x0F, [inbuffer], [size * cnt, 0x293]) 247 | return (ret, data[0]) 248 | 249 | def FSA_WriteFile(self, handle, file_handle, data): 250 | inbuffer = buffer(0x520) 251 | copy_word(inbuffer, 1, 0x08) # size 252 | copy_word(inbuffer, len(data), 0x0C) # cnt 253 | copy_word(inbuffer, file_handle, 0x14) 254 | (ret, data) = self.ioctlv(handle, 0x10, [inbuffer, data], [0x293]) 255 | return (ret) 256 | 257 | def FSA_ReadFilePtr(self, handle, file_handle, size, cnt, ptr): 258 | inbuffer = buffer(0x520) 259 | copy_word(inbuffer, size, 0x08) 260 | copy_word(inbuffer, cnt, 0x0C) 261 | copy_word(inbuffer, file_handle, 0x14) 262 | (ret, data) = self.ioctlv(handle, 0x0F, [inbuffer], [0x293], [], [(ptr, size*cnt)]) 263 | return (ret, data[0]) 264 | 265 | def FSA_WriteFilePtr(self, handle, file_handle, size, cnt, ptr): 266 | inbuffer = buffer(0x520) 267 | copy_word(inbuffer, size, 0x08) 268 | copy_word(inbuffer, cnt, 0x0C) 269 | copy_word(inbuffer, file_handle, 0x14) 270 | (ret, data) = self.ioctlv(handle, 0x10, [inbuffer], [0x293], [(ptr, size*cnt)], []) 271 | return (ret) 272 | 273 | def FSA_GetStatFile(self, handle, file_handle): 274 | inbuffer = buffer(0x520) 275 | copy_word(inbuffer, file_handle, 0x4) 276 | (ret, data) = self.ioctl(handle, 0x14, inbuffer, 0x64) 277 | return (ret, struct.unpack(">IIIIIIIIIIIIIIIIIIIIIIIII", data)) 278 | 279 | def FSA_CloseFile(self, handle, file_handle): 280 | inbuffer = buffer(0x520) 281 | copy_word(inbuffer, file_handle, 0x4) 282 | (ret, data) = self.ioctl(handle, 0x15, inbuffer, 0x293) 283 | return ret 284 | 285 | def FSA_ChangeMode(self, handle, path, mode): 286 | mask = 0x777 287 | inbuffer = buffer(0x520) 288 | copy_string(inbuffer, path, 0x0004) 289 | copy_word(inbuffer, mode, 0x0284) 290 | copy_word(inbuffer, mask, 0x0288) 291 | (ret, _) = self.ioctl(handle, 0x20, inbuffer, 0x293) 292 | return ret 293 | 294 | def FSA_Remove(self, handle, path): 295 | inbuffer = buffer(0x520) 296 | copy_string(inbuffer, path, 0x04) 297 | (ret, _) = self.ioctl(handle, 0x08, inbuffer, 0x293) 298 | return ret 299 | 300 | # mcp 301 | def MCP_InstallGetInfo(self, handle, path): 302 | inbuffer = buffer(0x27F) 303 | copy_string(inbuffer, path, 0x0) 304 | (ret, data) = self.ioctlv(handle, 0x80, [inbuffer], [0x16]) 305 | return (ret, struct.unpack(">IIIIIH", data[0])) 306 | 307 | def MCP_Install(self, handle, path): 308 | inbuffer = buffer(0x27F) 309 | copy_string(inbuffer, path, 0x0) 310 | (ret, _) = self.ioctlv(handle, 0x81, [inbuffer], []) 311 | return ret 312 | 313 | def MCP_InstallGetProgress(self, handle): 314 | (ret, data) = self.ioctl(handle, 0x82, [], 0x24) 315 | return (ret, struct.unpack(">IIIIIIIII", data)) 316 | 317 | def MCP_DeleteTitle(self, handle, path, flush): 318 | inbuffer = buffer(0x38) 319 | copy_string(inbuffer, path, 0x0) 320 | inbuffer2 = buffer(0x4) 321 | copy_word(inbuffer2, flush, 0x0) 322 | (ret, _) = self.ioctlv(handle, 0x83, [inbuffer, inbuffer2], []) 323 | return ret 324 | 325 | def MCP_CopyTitle(self, handle, path, dst_device_id, flush): 326 | inbuffer = buffer(0x27F) 327 | copy_string(inbuffer, path, 0x0) 328 | inbuffer2 = buffer(0x4) 329 | copy_word(inbuffer2, dst_device_id, 0x0) 330 | inbuffer3 = buffer(0x4) 331 | copy_word(inbuffer3, flush, 0x0) 332 | (ret, _) = self.ioctlv(handle, 0x85, [inbuffer, inbuffer2, inbuffer3], []) 333 | return ret 334 | 335 | def MCP_InstallSetTargetDevice(self, handle, device): 336 | inbuffer = buffer(0x4) 337 | copy_word(inbuffer, device, 0x0) 338 | (ret, _) = self.ioctl(handle, 0x8D, inbuffer, 0) 339 | return ret 340 | 341 | def MCP_InstallSetTargetUsb(self, handle, device): 342 | inbuffer = buffer(0x4) 343 | copy_word(inbuffer, device, 0x0) 344 | (ret, _) = self.ioctl(handle, 0xF1, inbuffer, 0) 345 | return ret 346 | 347 | # syslog (tmp) 348 | def dump_syslog(self): 349 | syslog_address = struct.unpack(">I", self.read(0x05095ECC, 4))[0] + 0x10 350 | block_size = 0x400 351 | for i in range(0, 0x40000, block_size): 352 | data = self.read(syslog_address + i, 0x400) 353 | # if 0 in data: 354 | # print(data[:data.index(0)].decode("ascii")) 355 | # break 356 | # else: 357 | print(data.decode("ascii")) 358 | 359 | # file management 360 | def get_fsa_handle(self): 361 | if self.fsa_handle == None: 362 | self.fsa_handle = self.open("/dev/fsa", 0) 363 | return self.fsa_handle 364 | 365 | def mkdir(self, path, flags): 366 | fsa_handle = self.get_fsa_handle() 367 | if path[0] != "/": 368 | path = self.cwd + "/" + path 369 | ret = w.FSA_MakeDir(fsa_handle, path, flags) 370 | if ret == 0: 371 | return 0 372 | else: 373 | print("mkdir error (%s, %08X)" % (path, ret)) 374 | return ret 375 | 376 | def chmod(self, filename, flags): 377 | fsa_handle = self.get_fsa_handle() 378 | if filename[0] != "/": 379 | filename = self.cwd + "/" + filename 380 | ret = w.FSA_ChangeMode(fsa_handle, filename, flags) 381 | print("chmod returned : " + hex(ret)) 382 | 383 | def cd(self, path): 384 | if path[0] != "/" and self.cwd[0] == "/": 385 | return self.cd(self.cwd + "/" + path) 386 | fsa_handle = self.get_fsa_handle() 387 | ret, dir_handle = self.FSA_OpenDir(fsa_handle, path if path != None else self.cwd) 388 | if ret == 0: 389 | self.cwd = path 390 | self.FSA_CloseDir(fsa_handle, dir_handle) 391 | return 0 392 | else: 393 | print("cd error : path does not exist (%s)" % (path)) 394 | return -1 395 | 396 | def ls(self, path = None, return_data = False): 397 | fsa_handle = self.get_fsa_handle() 398 | if path != None and path[0] != "/": 399 | path = self.cwd + "/" + path 400 | ret, dir_handle = self.FSA_OpenDir(fsa_handle, path if path != None else self.cwd) 401 | if ret != 0x0: 402 | print("opendir error : " + hex(ret)) 403 | return [] if return_data else None 404 | entries = [] 405 | while True: 406 | ret, data = self.FSA_ReadDir(fsa_handle, dir_handle) 407 | if ret != 0: 408 | break 409 | if not(return_data): 410 | if data["is_file"]: 411 | print(" %s" % data["name"]) 412 | else: 413 | print(" %s/" % data["name"]) 414 | else: 415 | entries += [data] 416 | ret = self.FSA_CloseDir(fsa_handle, dir_handle) 417 | return entries if return_data else None 418 | 419 | def dldir(self, path): 420 | if path[0] != "/": 421 | path = self.cwd + "/" + path 422 | entries = self.ls(path, True) 423 | for e in entries: 424 | if e["is_file"]: 425 | print(e["name"]) 426 | self.dl(path + "/" + e["name"],path[1:]) 427 | else: 428 | print(e["name"] + "/") 429 | self.dldir(path + "/" + e["name"]) 430 | 431 | def cpdir(self, srcpath, dstpath): 432 | entries = self.ls(srcpath, True) 433 | q = [(srcpath, dstpath, e) for e in entries] 434 | while len(q) > 0: 435 | _srcpath, _dstpath, e = q.pop() 436 | _srcpath += "/" + e["name"] 437 | _dstpath += "/" + e["name"] 438 | if e["is_file"]: 439 | print(e["name"]) 440 | self.cp(_srcpath, _dstpath) 441 | else: 442 | self.mkdir(_dstpath, 0x600) 443 | entries = self.ls(_srcpath, True) 444 | q += [(_srcpath, _dstpath, e) for e in entries] 445 | 446 | def pwd(self): 447 | return self.cwd 448 | 449 | def cp(self, filename_in, filename_out): 450 | fsa_handle = self.get_fsa_handle() 451 | ret, in_file_handle = self.FSA_OpenFile(fsa_handle, filename_in, "r") 452 | if ret != 0x0: 453 | print("cp error : could not open " + filename_in) 454 | return 455 | ret, out_file_handle = self.FSA_OpenFile(fsa_handle, filename_out, "w") 456 | if ret != 0x0: 457 | print("cp error : could not open " + filename_out) 458 | return 459 | block_size = 0x10000 460 | buffer = self.alloc(block_size, 0x40) 461 | k = 0 462 | while True: 463 | ret, _ = self.FSA_ReadFilePtr(fsa_handle, in_file_handle, 0x1, block_size, buffer) 464 | k += ret 465 | ret = self.FSA_WriteFilePtr(fsa_handle, out_file_handle, 0x1, ret, buffer) 466 | sys.stdout.write(hex(k) + "\r"); sys.stdout.flush(); 467 | if ret < block_size: 468 | break 469 | self.free(buffer) 470 | ret = self.FSA_CloseFile(fsa_handle, out_file_handle) 471 | ret = self.FSA_CloseFile(fsa_handle, in_file_handle) 472 | 473 | def df(self, filename_out, src, size): 474 | fsa_handle = self.get_fsa_handle() 475 | ret, out_file_handle = self.FSA_OpenFile(fsa_handle, filename_out, "w") 476 | if ret != 0x0: 477 | print("df error : could not open " + filename_out) 478 | return 479 | block_size = 0x10000 480 | buffer = self.alloc(block_size, 0x40) 481 | k = 0 482 | while k < size: 483 | cur_size = min(size - k, block_size) 484 | self.memcpy(buffer, src + k, cur_size) 485 | k += cur_size 486 | ret = self.FSA_WriteFilePtr(fsa_handle, out_file_handle, 0x1, cur_size, buffer) 487 | sys.stdout.write(hex(k) + " (%f) " % (float(k * 100) / size) + "\r"); sys.stdout.flush(); 488 | self.free(buffer) 489 | ret = self.FSA_CloseFile(fsa_handle, out_file_handle) 490 | 491 | def dl_buf(self, filename, show_progress = True): 492 | fsa_handle = self.get_fsa_handle() 493 | if filename[0] != "/": 494 | filename = self.cwd + "/" + filename 495 | ret, file_handle = self.FSA_OpenFile(fsa_handle, filename, "r") 496 | if ret != 0x0: 497 | print("dl error : could not open " + filename) 498 | return None 499 | buf = bytearray() 500 | block_size = 0x400 501 | while True: 502 | ret, data = self.FSA_ReadFile(fsa_handle, file_handle, 0x1, block_size) 503 | buf += data[:ret] 504 | if show_progress: 505 | sys.stdout.write(hex(len(buf)) + "\r") 506 | sys.stdout.flush() 507 | if ret < block_size: 508 | break 509 | self.FSA_CloseFile(fsa_handle, file_handle) 510 | return buf 511 | 512 | def dl(self, filename, directorypath = None, local_filename = None): 513 | buf = self.dl_buf(filename) 514 | if buf == None: 515 | return -1 516 | if local_filename == None: 517 | if "/" in filename: 518 | local_filename = filename[[i for i, x in enumerate(filename) if x == "/"][-1]+1:] 519 | else: 520 | local_filename = filename 521 | if directorypath == None: 522 | open(local_filename, "wb").write(buf) 523 | else: 524 | dir_path = os.path.dirname(os.path.abspath(sys.argv[0])).replace('\\','/') 525 | fullpath = dir_path + "/" + directorypath + "/" 526 | fullpath = fullpath.replace("//","/") 527 | mkdir_p(fullpath) 528 | open(fullpath + local_filename, "wb").write(buf) 529 | return 0 530 | 531 | def mkdir_p(path): 532 | try: 533 | os.makedirs(path) 534 | except OSError as exc: # Python >2.5 535 | if exc.errno == errno.EEXIST and os.path.isdir(path): 536 | pass 537 | else: 538 | raise 539 | 540 | def fr(self, filename, offset, size): 541 | fsa_handle = self.get_fsa_handle() 542 | if filename[0] != "/": 543 | filename = self.cwd + "/" + filename 544 | ret, file_handle = self.FSA_OpenFile(fsa_handle, filename, "r") 545 | if ret != 0x0: 546 | print("fr error : could not open " + filename) 547 | return 548 | buffer = bytearray() 549 | block_size = 0x400 550 | while True: 551 | ret, data = self.FSA_ReadFile(fsa_handle, file_handle, 0x1, block_size if (block_size < size) else size) 552 | buffer += data[:ret] 553 | sys.stdout.write(hex(len(buffer)) + "\r"); sys.stdout.flush(); 554 | if len(buffer) >= size: 555 | break 556 | ret = self.FSA_CloseFile(fsa_handle, file_handle) 557 | return buffer 558 | 559 | def fw(self, filename, offset, buffer): 560 | fsa_handle = self.get_fsa_handle() 561 | if filename[0] != "/": 562 | filename = self.cwd + "/" + filename 563 | ret, file_handle = self.FSA_OpenFile(fsa_handle, filename, "r+") 564 | if ret != 0x0: 565 | print("fw error : could not open " + filename) 566 | return 567 | block_size = 0x400 568 | k = 0 569 | while True: 570 | cur_size = min(len(buffer) - k, block_size) 571 | if cur_size <= 0: 572 | break 573 | sys.stdout.write(hex(k) + "\r"); sys.stdout.flush(); 574 | ret = self.FSA_WriteFile(fsa_handle, file_handle, buffer[k:(k+cur_size)]) 575 | k += cur_size 576 | ret = self.FSA_CloseFile(fsa_handle, file_handle) 577 | 578 | def stat(self, filename): 579 | fsa_handle = self.get_fsa_handle() 580 | if filename[0] != "/": 581 | filename = self.cwd + "/" + filename 582 | ret, file_handle = self.FSA_OpenFile(fsa_handle, filename, "r") 583 | if ret != 0x0: 584 | print("stat error : could not open " + filename) 585 | return 586 | (ret, stats) = self.FSA_GetStatFile(fsa_handle, file_handle) 587 | if ret != 0x0: 588 | print("stat error : " + hex(ret)) 589 | else: 590 | print("flags: " + hex(stats[1])) 591 | print("mode: " + hex(stats[2])) 592 | print("owner: " + hex(stats[3])) 593 | print("group: " + hex(stats[4])) 594 | print("size: " + hex(stats[5])) 595 | ret = self.FSA_CloseFile(fsa_handle, file_handle) 596 | 597 | def askyesno(self): 598 | yes = set(['yes', 'ye', 'y']) 599 | no = set(['no','n', '']) 600 | while True: 601 | choice = raw_input().lower() 602 | if choice in yes: 603 | return True 604 | elif choice in no: 605 | return False 606 | else: 607 | print("Please respond with 'y' or 'n'") 608 | 609 | def rm(self, filename): 610 | fsa_handle = self.get_fsa_handle() 611 | if filename[0] != "/": 612 | filename = self.cwd + "/" + filename 613 | ret, file_handle = self.FSA_OpenFile(fsa_handle, filename, "r") 614 | if ret != 0x0: 615 | print("rm error : could not open " + filename + " (" + hex(ret) + ")") 616 | return 617 | self.FSA_CloseFile(fsa_handle, file_handle) 618 | print("WARNING: REMOVING A FILE CAN BRICK YOUR CONSOLE, ARE YOU SURE (Y/N)?") 619 | if self.askyesno() == True: 620 | ret = self.FSA_Remove(fsa_handle, filename) 621 | print("rm : " + hex(ret)) 622 | else: 623 | print("rm aborted") 624 | 625 | def rmdir(self, path): 626 | fsa_handle = self.get_fsa_handle() 627 | if path[0] != "/": 628 | path = self.cwd + "/" + path 629 | ret, dir_handle = self.FSA_OpenDir(fsa_handle, path) 630 | if ret != 0x0: 631 | print("rmdir error : could not open " + path + " (" + hex(ret) + ")") 632 | return 633 | self.FSA_CloseDir(fsa_handle, dir_handle) 634 | if len(self.ls(path, True)) != 0: 635 | print("rmdir error : directory not empty!") 636 | return 637 | print("WARNING: REMOVING A DIRECTORY CAN BRICK YOUR CONSOLE, ARE YOU SURE (Y/N)?") 638 | if self.askyesno() == True: 639 | ret = self.FSA_Remove(fsa_handle, path) 640 | print("rmdir : " + hex(ret)) 641 | else: 642 | print("rmdir aborted") 643 | 644 | def up(self, local_filename, filename = None): 645 | fsa_handle = self.get_fsa_handle() 646 | if filename == None: 647 | if "/" in local_filename: 648 | filename = local_filename[[i for i, x in enumerate(local_filename) if x == "/"][-1]+1:] 649 | else: 650 | filename = local_filename 651 | if filename[0] != "/": 652 | filename = self.cwd + "/" + filename 653 | f = open(local_filename, "rb") 654 | ret, file_handle = self.FSA_OpenFile(fsa_handle, filename, "w") 655 | if ret != 0x0: 656 | print("up error : could not open " + filename) 657 | return 658 | progress = 0 659 | block_size = 0x400 660 | while True: 661 | data = f.read(block_size) 662 | ret = self.FSA_WriteFile(fsa_handle, file_handle, data) 663 | progress += len(data) 664 | sys.stdout.write(hex(progress) + "\r"); sys.stdout.flush(); 665 | if len(data) < block_size: 666 | break 667 | ret = self.FSA_CloseFile(fsa_handle, file_handle) 668 | 669 | def mkdir_p(path): 670 | try: 671 | os.makedirs(path) 672 | except OSError as exc: 673 | if exc.errno == errno.EEXIST and os.path.isdir(path): 674 | pass 675 | else: 676 | raise 677 | 678 | def mount_sd(): 679 | handle = w.open("/dev/fsa", 0) 680 | print(hex(handle)) 681 | 682 | ret = w.FSA_Mount(handle, "/dev/sdcard01", "/vol/storage_sdcard", 2) 683 | print(hex(ret)) 684 | 685 | ret = w.close(handle) 686 | print(hex(ret)) 687 | 688 | def unmount_sd(): 689 | handle = w.open("/dev/fsa", 0) 690 | print(hex(handle)) 691 | 692 | ret = w.FSA_Unmount(handle, "/vol/storage_sdcard", 2) 693 | print(hex(ret)) 694 | 695 | ret = w.close(handle) 696 | print(hex(ret)) 697 | 698 | def mount_slccmpt01(): 699 | handle = w.open("/dev/fsa", 0) 700 | print(hex(handle)) 701 | 702 | ret = w.FSA_Mount(handle, "/dev/slccmpt01", "/vol/storage_slccmpt01", 2) 703 | print(hex(ret)) 704 | 705 | ret = w.close(handle) 706 | print(hex(ret)) 707 | 708 | def unmount_slccmpt01(): 709 | handle = w.open("/dev/fsa", 0) 710 | print(hex(handle)) 711 | 712 | ret = w.FSA_Unmount(handle, "/vol/storage_slccmpt01", 2) 713 | print(hex(ret)) 714 | 715 | ret = w.close(handle) 716 | print(hex(ret)) 717 | 718 | def mount_odd_content(): 719 | handle = w.open("/dev/fsa", 0) 720 | print(hex(handle)) 721 | 722 | ret = w.FSA_Mount(handle, "/dev/odd03", "/vol/storage_odd_content", 2) 723 | print(hex(ret)) 724 | 725 | ret = w.close(handle) 726 | print(hex(ret)) 727 | 728 | def unmount_odd_content(): 729 | handle = w.open("/dev/fsa", 0) 730 | print(hex(handle)) 731 | 732 | ret = w.FSA_Unmount(handle, "/vol/storage_odd_content", 2) 733 | print(hex(ret)) 734 | 735 | ret = w.close(handle) 736 | print(hex(ret)) 737 | 738 | def mount_odd_update(): 739 | handle = w.open("/dev/fsa", 0) 740 | print(hex(handle)) 741 | 742 | ret = w.FSA_Mount(handle, "/dev/odd02", "/vol/storage_odd_update", 2) 743 | print(hex(ret)) 744 | 745 | ret = w.close(handle) 746 | print(hex(ret)) 747 | 748 | def unmount_odd_update(): 749 | handle = w.open("/dev/fsa", 0) 750 | print(hex(handle)) 751 | 752 | ret = w.FSA_Unmount(handle, "/vol/storage_odd_update", 2) 753 | print(hex(ret)) 754 | 755 | ret = w.close(handle) 756 | print(hex(ret)) 757 | 758 | def mount_odd_tickets(): 759 | handle = w.open("/dev/fsa", 0) 760 | print(hex(handle)) 761 | 762 | ret = w.FSA_Mount(handle, "/dev/odd01", "/vol/storage_odd_tickets", 2) 763 | print(hex(ret)) 764 | 765 | ret = w.close(handle) 766 | print(hex(ret)) 767 | 768 | def unmount_odd_tickets(): 769 | handle = w.open("/dev/fsa", 0) 770 | print(hex(handle)) 771 | 772 | ret = w.FSA_Unmount(handle, "/vol/storage_odd_tickets", 2) 773 | print(hex(ret)) 774 | 775 | ret = w.close(handle) 776 | print(hex(ret)) 777 | 778 | def get_tik_keys(): 779 | base_path = "/vol/system/rights/ticket/apps" 780 | entries = w.ls(base_path, True) 781 | #parse subfolder contents to get tik location 782 | tikFiles = [] 783 | for e in entries: 784 | if not e["is_file"]: 785 | path = base_path + "/" + e["name"] 786 | subentries = w.ls(path, True) 787 | for se in subentries: 788 | if se["is_file"] and se["name"].endswith(".tik"): 789 | tikFiles.append(path + "/" + se["name"]) 790 | #go through all tiks 791 | tikList = [] 792 | for tikF in tikFiles: 793 | tikContent = w.dl_buf(tikF, False) 794 | if tikContent == None: 795 | continue 796 | checkTik = True 797 | tikP = 0 798 | while checkTik == True: 799 | checkTik = False 800 | curTik = tikContent[tikP:] 801 | if(curTik[0:4] != b'\x00\x01\x00\x04'): 802 | print("Unhandled tik start at %i with ticket %s!" % (tikP, tikF)) 803 | break 804 | titleId = codecs.encode(curTik[0x1DC:0x1E4], 'hex').decode() 805 | titleKey = codecs.encode(curTik[0x1BF:0x1CF], 'hex').decode() 806 | tikFprint = tikF[tikF.rfind("apps/")+5:] 807 | tikList.append(titleId + " " + titleKey + " (" + tikFprint + " @ " + hex(tikP) + ")") 808 | if(len(curTik) > 0x354): 809 | if(curTik[0x2B0:0x2B2] == b'\x00\x00' and curTik[0x2B8:0x2BC] == b'\x00\x01\x00\x04'): 810 | tikP += 0x2B8 811 | checkTik = True 812 | elif(curTik[0x2B0:0x2B2] == b'\x00\x01' and curTik[0x350:0x354] == b'\x00\x01\x00\x04'): 813 | tikP += 0x350 814 | checkTik = True 815 | else: 816 | print("Unhandled packed tik at %i with ticket %s!" % (tikP, tikF)) 817 | #print out all sorted and unique tiks 818 | uniqueTiks = sorted(set(tikList)) 819 | print("Found %i unique tickets" % len(uniqueTiks)) 820 | for tikCnt in uniqueTiks: 821 | print(tikCnt) 822 | 823 | #path=root folder of installed/extracted title, only works if title is deleted 824 | #on the destination device beforehand; path can also be a sd card location! 825 | def copy_title(path, installToUsb = 0, flush = 0): 826 | mcp_handle = w.open("/dev/mcp", 0) 827 | print(hex(mcp_handle)) 828 | 829 | ret = w.MCP_CopyTitle(mcp_handle, path, installToUsb, flush) 830 | print(hex(ret)) 831 | 832 | ret = w.close(mcp_handle) 833 | print(hex(ret)) 834 | 835 | #path=path to sd card folder on device root 836 | def install_title(path, installToUsb = 0): 837 | mcp_handle = w.open("/dev/mcp", 0) 838 | print(hex(mcp_handle)) 839 | 840 | ret, data = w.MCP_InstallGetInfo(mcp_handle, "/vol/storage_sdcard/"+path) 841 | print("install info : " + hex(ret), [hex(v) for v in data]) 842 | if ret != 0: 843 | ret = w.close(mcp_handle) 844 | print(hex(ret)) 845 | return 846 | 847 | ret = w.MCP_InstallSetTargetDevice(mcp_handle, installToUsb) 848 | print("install set target device : " + hex(ret)) 849 | if ret != 0: 850 | ret = w.close(mcp_handle) 851 | print(hex(ret)) 852 | return 853 | 854 | ret = w.MCP_InstallSetTargetUsb(mcp_handle, installToUsb) 855 | print("install set target usb : " + hex(ret)) 856 | if ret != 0: 857 | ret = w.close(mcp_handle) 858 | print(hex(ret)) 859 | return 860 | 861 | ret = w.MCP_Install(mcp_handle, "/vol/storage_sdcard/"+path) 862 | print("install : " + hex(ret)) 863 | 864 | ret = w.close(mcp_handle) 865 | print(hex(ret)) 866 | 867 | #path=full path, for example "/vol/storage_mlc01/usr/title/00050000/10179C00" 868 | def delete_title(path, flush = 0): 869 | mcp_handle = w.open("/dev/mcp", 0) 870 | print(hex(mcp_handle)) 871 | 872 | ret = w.MCP_DeleteTitle(mcp_handle, path, flush) 873 | print("delete title : " + hex(ret)) 874 | 875 | ret = w.close(mcp_handle) 876 | print(hex(ret)) 877 | 878 | def ios_shutdown(): 879 | w.svc_and_exit(0x72,[0]) 880 | 881 | def ios_reset(): 882 | w.svc_and_exit(0x72,[1]) 883 | 884 | def get_nim_status(): 885 | nim_handle = w.open("/dev/nim", 0) 886 | print(hex(nim_handle)) 887 | 888 | inbuffer = buffer(0x80) 889 | (ret, data) = w.ioctlv(nim_handle, 0x00, [inbuffer], [0x80]) 890 | 891 | print(hex(ret), "".join("%02X" % v for v in data[0])) 892 | 893 | ret = w.close(nim_handle) 894 | print(hex(ret)) 895 | 896 | def read_and_print(adr, size): 897 | data = w.read(adr, size) 898 | data = struct.unpack(">%dI" % (len(data) // 4), data) 899 | for i in range(0, len(data), 4): 900 | print(" ".join("%08X"%v for v in data[i:i+4])) 901 | 902 | def read_and_dump(adr,size): 903 | f=open("dump.bin","wb") 904 | for i in range(0,size,1024): 905 | data = w.read(adr+i,1024) 906 | f.write(data) 907 | f.close() 908 | 909 | if __name__ == '__main__': 910 | w = wupclient() 911 | mount_sd() 912 | # mount_odd_content() 913 | 914 | # print(w.pwd()) 915 | # w.ls() 916 | # w.dump_syslog() 917 | # w.mkdir("/vol/storage_sdcard/usr", 0x600) 918 | # install_title("test") 919 | # get_nim_status() 920 | # w.kill() 921 | --------------------------------------------------------------------------------