├── README.md ├── amiibo_crypto.c ├── common.py ├── dec_ancast.py ├── dld.py ├── extract_fw ├── Makefile ├── auxvec.h └── extract_fw.c ├── ios_structs.py └── notes ├── arm_boot0 └── boot0.txt ├── arm_ios ├── dev_act.txt ├── dev_bsp.txt ├── dev_cbl.txt ├── dev_ccr_nfc.txt ├── dev_crypto.txt ├── dev_fpd.txt ├── dev_im.txt ├── dev_iopsh.txt ├── dev_mcp.txt ├── dev_nsec_nss.txt ├── dev_pm.txt ├── dev_socket.txt ├── dev_udscntl.txt ├── dev_usr_cfg.txt ├── devices.txt └── mcp │ └── firm_launch.txt ├── arm_kernel ├── devices.txt ├── events.txt ├── heaps.txt ├── ipc.txt ├── memory_mapping.txt ├── msg_queue.txt ├── syscalls.txt └── threads.txt ├── arm_memorymaps └── virt_phys.txt ├── common ├── ancast.txt ├── ipc.txt └── pids.txt ├── firm_diffs └── os11_550.txt ├── io ├── ahb.txt ├── io_map.txt ├── misc.txt ├── otp.txt └── tcl.txt ├── ppc_hw └── spr.txt ├── ppc_kernel ├── IPCKDriver.c ├── KeDriver.c ├── copy_user.txt ├── kernel.txt ├── kernel_heap.c ├── memory_structs.txt ├── pids.txt └── syscalls.txt ├── ppc_memorymaps ├── bats.txt ├── memory_maps.txt └── memory_syscalls.txt ├── ppc_modules ├── coreinit.txt ├── loader.txt └── smd.c └── system_flaws.txt /README.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | 3 | These are just random notes taken by me (plutoo) and naehrwert mostly when we 4 | first looked into the Wii U. yellows8, smea and derrek also made contributions. 5 | 6 | ### Things 7 | 8 | A few things that might be interesting: 9 | 10 | * [System flaws](notes/system_flaws.txt) 11 | * [OTP notes](notes/io/otp.txt) 12 | * [IOSU memory map](notes/arm_memorymaps/virt_phys.txt) 13 | * [IOSU syscall table](notes/arm_kernel/syscalls.txt) 14 | * [/dev/crypto key table](notes/arm_ios/dev_crypto.txt) 15 | * [PowerPC memory map](notes/ppc_kernel/memory_maps.txt) 16 | * [PowerPC syscall table](notes/ppc_kernel/syscalls.txt) 17 | * ... 18 | 19 | ### Tools 20 | 21 | * [Firmware downloader and extractor](dld.py) 22 | * [Ancast image decryptor](dec_ancast.py) 23 | * [Amiibo crypto algo](amiibo_crypto.c) 24 | -------------------------------------------------------------------------------- /amiibo_crypto.c: -------------------------------------------------------------------------------- 1 | // Amiibo crypto 2 | // -- plutoo 2015 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef unsigned long long int u64; 10 | typedef unsigned int uint; 11 | typedef unsigned char u8; 12 | 13 | #define DEBUG_KEYS 14 | 15 | u8 random_key[16] = { 16 | 0xC1, /* Censored. */ 17 | }; 18 | 19 | u8 random_iv[16] = { 20 | 0x4F, /* Censored. */ 21 | }; 22 | 23 | u8 amiibo_constant0[14] = { 24 | #ifdef DEBUG_KEYS 25 | 0xDB, /* Censored. */ 26 | #else 27 | 0x52, /* Censored. */ 28 | #endif 29 | }; 30 | 31 | u8 amiibo_constant1[16] = { 32 | #ifdef DEBUG_KEYS 33 | 0xFD, /* Censored. */ 34 | #else 35 | 0x4C, /* Censored. */ 36 | #endif 37 | }; 38 | 39 | u8 hmac_key0[16] = { 40 | #ifdef DEBUG_KEYS 41 | 0x1D, /* Censored. */ 42 | #else 43 | 0xED, /* Censored. */ 44 | #endif 45 | }; 46 | 47 | u8 hmac_key1[16] = { 48 | #ifdef DEBUG_KEYS 49 | 0x7F, /* Censored. */ 50 | #else 51 | 0x83, /* Censored. */ 52 | #endif 53 | }; 54 | 55 | u8 type_string0[] = "unfixed infos\x00"; 56 | u8 type_string1[] = "locked secret\x00"; 57 | 58 | 59 | void sha256_hmac(u8* key, uint keylen, u8* in, uint inlen, u8* out) { 60 | HMAC_CTX ctx, *c=&ctx; 61 | uint outlen = 0x20; 62 | HMAC_CTX_init(c); 63 | HMAC_Init(c, key, keylen, EVP_sha256()); 64 | HMAC_Update(c, in, inlen); 65 | HMAC_Final(c, out, &outlen); 66 | HMAC_CTX_cleanup(c); 67 | } 68 | 69 | void sha256(u8* in, uint inlen, u8* out) { 70 | SHA256_CTX ctx; 71 | SHA256_Init(&ctx); 72 | SHA256_Update(&ctx, in, inlen); 73 | SHA256_Final(out, &ctx); 74 | } 75 | 76 | void aes128(u8* key, u8* in, u8* out) { 77 | u8 iv[16]; 78 | AES_KEY aes_key; 79 | memset(iv, 0, 16); 80 | AES_set_encrypt_key(key, 128, &aes_key); 81 | AES_cbc_encrypt(in, out, 16, &aes_key, iv, AES_ENCRYPT); 82 | } 83 | 84 | void aes128_ctr(u8* key, u8* iv, u8* in_out, uint len) { 85 | u8 buf[16], out[16]; 86 | memcpy(buf, iv, 16); 87 | 88 | uint i; 89 | for(i=0; iI', s)[0] 10 | def be16(s): 11 | return struct.unpack('>H', s)[0] 12 | 13 | def get_key(name): 14 | try: 15 | return open(os.environ['WIIU']+'/keys/'+name,'rb').read() 16 | except: 17 | print 'Key \'%s\' not found..' % name 18 | print 'You need to set WIIU environment variable.' 19 | sys.exit(1) 20 | 21 | def aes_cbc_dec_file(inf, outf, key, iv, offset=0): 22 | in_file = open(inf, 'rb') 23 | out_file = open(outf, 'wb') 24 | 25 | in_file.seek(offset) 26 | 27 | while True: 28 | cipher = AES.new(key, AES.MODE_CBC, iv) 29 | 30 | enc = in_file.read(16) 31 | if len(enc) == 0: 32 | break 33 | if len(enc) < 16: 34 | break 35 | 36 | dec = cipher.decrypt(enc) 37 | out_file.write(dec) 38 | 39 | iv = enc 40 | 41 | in_file.close() 42 | out_file.close() 43 | 44 | def aes_cbc_dec(inb, key, iv='\x00'*16): 45 | return AES.new(key, AES.MODE_CBC, iv).decrypt(inb) 46 | 47 | def aes_cbc_enc(inb, key, iv='\x00'*16): 48 | return AES.new(key, AES.MODE_CBC, iv).encrypt(inb) 49 | 50 | def sha1(buf): 51 | m = hashlib.sha1() 52 | m.update(buf) 53 | return m.digest() 54 | -------------------------------------------------------------------------------- /dec_ancast.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # dec_ancast.py -- Decrypt Wii U ancast images 3 | # plutoo, nwert 4 | 5 | import sys 6 | import struct 7 | from common import * 8 | import hashlib 9 | 10 | def rsa_verify(sig, hdr, N): 11 | e = 0x10001 12 | sig = int(sig.encode("hex"), 16) 13 | p = "{0:X}".format(pow(sig, rsa_e, N)) 14 | p = ("0" if len(p)%2 else "") + p 15 | p = p.decode("hex") 16 | return p[0xEB:] == hashlib.sha1(hdr).digest() 17 | 18 | if len(sys.argv) != 2: 19 | print '%s file.img' % sys.argv[0] 20 | sys.exit(1) 21 | 22 | in_file = sys.argv[1] 23 | out_file = in_file + '.bin' 24 | 25 | header = open(in_file, 'rb').read(0x200) 26 | 27 | magic = be32(header[0:4]) 28 | if magic != 0xEFA282D9: 29 | print 'Bad magic!' 30 | sys.exit(1) 31 | 32 | sig_type = be32(header[0x20:0x24]) 33 | 34 | if sig_type == 1: 35 | offset = 0x100 36 | elif sig_type == 2: 37 | offset = 0x200 38 | else: 39 | print 'Unknown signature type %x..' % sig_type 40 | sys.exit(1) 41 | 42 | types = {0x11: 'ppc_wiiu', 0x13: 'ppc_vwii', 0x21: 'arm'} 43 | type_raw = be32(header[offset-0x5C:offset-0x58]) 44 | 45 | if type_raw not in types: 46 | print 'Unknown type %x..' % type 47 | sys.exit(1) 48 | 49 | type = types[type_raw] 50 | if type == 'arm': 51 | if be32(header[offset-0x54:offset-0x50]) in [0xE000, 0xC000]: 52 | type += '_boot1' 53 | else: 54 | type += '_iosu' 55 | 56 | btypes = {1: 'debug', 2: 'retail'} 57 | btype_raw = be32(header[offset-0x58:offset-0x54]) 58 | type += '_'+btypes[btype_raw] 59 | 60 | print 'Type:', type 61 | 62 | #print "Signature verified:", rsa_verify(header[0x24:0x124], header[0x1A0:0x200], Ns[btype]) 63 | 64 | key = get_key('ancast_%s_key' % type) 65 | iv = get_key('ancast_%s_iv' % type) 66 | 67 | aes_cbc_dec_file(in_file, out_file, key, iv, offset) 68 | 69 | out = open(out_file, 'rb').read() 70 | elf_pos = out.find('\x7FELF') 71 | 72 | if elf_pos != -1: 73 | elf_file = in_file + '.elf' 74 | elf = open(elf_file, 'wb') 75 | elf.write(out[elf_pos:]) 76 | -------------------------------------------------------------------------------- /dld.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # dld.py -- Download Wii U firmware from update servers. 3 | # plutoo 4 | 5 | import urllib 6 | import urllib2 7 | import sys 8 | import os 9 | import struct 10 | import argparse 11 | from Crypto.Cipher import AES 12 | 13 | from common import * 14 | 15 | NUS_SERVER='http://nus.cdn.c.shop.nintendowifi.net/ccs/download/%s/%s' 16 | key = '' 17 | 18 | 19 | def get_file(tid, remote, local): 20 | retries = 0 21 | wget = urllib.URLopener() 22 | 23 | while True: 24 | try: 25 | wget.retrieve(NUS_SERVER % (tid, remote), local) 26 | print '[!] Download done.' 27 | return 28 | 29 | except IOError, e: 30 | if e[0] == 'http error' and e[1] in [404, 401]: 31 | print '[!] Download failed..' 32 | raise Exception('Download failed.') 33 | 34 | print 'Got error: %s, retrying..' % repr(e) 35 | 36 | def remove_sig(buf): 37 | sig_type = be32(buf[0:4]) 38 | buf_off = 0 39 | 40 | if sig_type == 0x10000 or sig_type == 0x10003: 41 | buf_off = 0x240 42 | elif sig_type == 0x10001 or sig_type == 0x10004: 43 | buf_off = 0x140 44 | elif sig_type == 0x10002 or sig_type == 0x10005: 45 | buf_off = 0x80 46 | else: 47 | raise Exception('Unknown signature size.') 48 | 49 | return buf[buf_off:] 50 | 51 | def decrypt_title_key(tik): 52 | key = tik[0x7f:0x8f] 53 | iv = tik[0x9c:0xa4] + ('\x00'*8) 54 | return aes_cbc_dec(key, get_key('common_key'), iv) 55 | 56 | def mkdir(d): 57 | try: 58 | os.makedirs(d) 59 | except Exception, e: 60 | if '[Errno 17]' not in str(e): 61 | raise 62 | 63 | def parse_fst_entry(buf): 64 | return {'type': buf[0], 65 | 'str_off': be32(buf[0:4]) & 0xffffff, 66 | 'prev_dir': be32(buf[4:8]), # for dir only 67 | 'next_dir': be32(buf[8:0xc]), # for dir only 68 | 'offset': be32(buf[4:8]), # for files only 69 | 'size': be32(buf[8:0xc]), # for files only 70 | 'flags': be16(buf[0xc:0xe]), 71 | 'index': be16(buf[0xe:0x10])} 72 | 73 | def walk_fst_tree(dstdir, nodes, pos, path): 74 | node = nodes[pos] 75 | 76 | if node['type'] == '\x01': # dir 77 | if path == '': 78 | node['path'] = '.' 79 | else: 80 | node['path'] = path+'/'+node['name'] 81 | mkdir('%s/%s' % (dstdir, node['path'])) 82 | 83 | new_pos = pos + 1 84 | while new_pos < node['next_dir']: 85 | new_pos = walk_fst_tree(dstdir, nodes, new_pos, node['path']) 86 | 87 | return node['next_dir'] 88 | 89 | else: # file 90 | node['path'] = path+'/'+node['name'] 91 | return pos + 1 92 | 93 | def parse_fst(dstdir, fst): 94 | print '[!] Parsing FST file..' 95 | fst = open(fst, 'rb').read() 96 | magic, offset_align, num_contents = struct.unpack('>LLL', fst[0:0xc]) 97 | 98 | if magic != 0x46535400: 99 | print '[!] Wrong magic FST.. skipping.' 100 | return 101 | 102 | entries_off = 0x20 + 0x20 * num_contents 103 | 104 | root = parse_fst_entry(fst[entries_off:entries_off+0x10]) 105 | root['name'] = '.' 106 | 107 | entries_off += 0x10 108 | strtbl_off = 0x20 + 0x20 * num_contents + 0x10 * root['size'] 109 | 110 | nodes = [] 111 | nodes.append(root) 112 | 113 | for i in range(0, root['size']-1): 114 | node = parse_fst_entry(fst[entries_off:entries_off+0x10]) 115 | 116 | name = fst[strtbl_off + node['str_off']:] 117 | name = name[:name.find('\x00')] 118 | 119 | node['name'] = name 120 | 121 | nodes.append(node) 122 | entries_off += 0x10 123 | 124 | walk_fst_tree(dstdir, nodes, 0, '') 125 | 126 | for node in nodes: 127 | if node['type'] == '\x00': # files 128 | print '[!] File %s.' % node['path'] 129 | 130 | off = node['offset'] * offset_align 131 | sz = node['size'] 132 | 133 | #print ' off:', off, 'sz:', sz, 'id:', node['index'], 'flags', hex(node['flags']) 134 | 135 | f = open('%s/%08x' % (dstdir, node['index']), 'rb') 136 | out = open('%s/%s' % (dstdir, node['path']), 'wb') 137 | 138 | if node['flags'] & 0x440: 139 | f.seek((off/0xFC00) * 0x10000) 140 | chunk_off = off - ((off / 0xFC00) * 0xFC00) 141 | 142 | block = (off / 0xFC00) % 16 143 | left = sz 144 | 145 | def sxor(s1,s2): 146 | return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1,s2)) 147 | 148 | while left > 0: 149 | buf = f.read(0x10000) 150 | 151 | iv = ('%04x' % node['index']).decode('hex') + ('\x00'*14) 152 | hashes = aes_cbc_dec(buf[:0x400], key, iv) 153 | 154 | iv = hashes[20*block : 20*block+16] 155 | if block == 0: 156 | content_id = ('%04x' % node['index']).decode('hex') + ('\x00'*14) 157 | iv = sxor(iv, content_id) 158 | 159 | dec = aes_cbc_dec(buf[0x400:], key, iv) 160 | 161 | h = sha1(dec) 162 | if block == 0: 163 | content_id = ('%04x' % node['index']).decode('hex') + ('\x00'*18) 164 | h = sxor(h, content_id) 165 | 166 | if h != hashes[block*20 : block*20+20]: 167 | print '[!] Hash mismatch detected!' 168 | 169 | rd = 0xFC00 - chunk_off 170 | wr = left if left < rd else rd 171 | out.write(dec[chunk_off : chunk_off+wr]) 172 | 173 | chunk_off = 0 174 | block = (block + 1) % 16 175 | left -= wr 176 | else: 177 | iv = ('%04x' % node['index']).decode('hex') + ('\x00'*14) 178 | 179 | left = sz 180 | while left > 0: 181 | rd = left if left < 0x8000 else 0x8000 182 | rd_aligned = ((rd + 15)/16 * 16) 183 | b = f.read(rd_aligned) 184 | dec = aes_cbc_dec(b, key, iv) 185 | 186 | out.write(dec[:rd]) 187 | iv = b[-0x10:] 188 | left -= rd 189 | 190 | def parse_tmd(tid, dstdir): 191 | print '[!] Parsing tmd and tik..' 192 | 193 | tmd = remove_sig(open('%s/tmd' % dstdir, 'rb').read()) 194 | num_contents = be16(tmd[0x9e:0xa0]) 195 | off = 0x9c4 196 | 197 | tik = remove_sig(open('%s/cetk' % dstdir, 'rb').read()) 198 | global key 199 | key = decrypt_title_key(tik) 200 | 201 | print '[!] Decrypted title key: ' + key.encode('hex') 202 | 203 | for i in range(0, num_contents): 204 | print '[!] Downloading content %08x..' % i 205 | info = tmd[off:off+0x30] 206 | sha1 = info[0x10:0x24] 207 | cid = be32(info[0:4]) 208 | idx = be16(info[4:6]) 209 | 210 | dst_file = '%s/%08x' % (dstdir, i) 211 | get_file(tid, '%08x' % cid, dst_file) 212 | 213 | iv = info[4:6] + ('\x00'*14) 214 | 215 | if i == 0: 216 | print '[!] Found FST-file, decrypting directly..' 217 | aes_cbc_dec_file(dst_file, '%s/fst.bin' % dstdir, key, iv) 218 | 219 | off += 0x30 220 | 221 | parse_fst(dstdir, '%s/fst.bin' % dstdir) 222 | 223 | def get_title(tidver, dstdir=None): 224 | if dstdir == None: 225 | dstdir = tidver 226 | 227 | tidver = tidver.split('.') 228 | tid = tidver[0] 229 | ver = tidver[1] if len(tidver)==2 else None 230 | 231 | print '[!] Getting title %s, version: %s.' % (tid, ver if ver else 'latest') 232 | 233 | mkdir(dstdir) 234 | 235 | try: 236 | print '[!] Downloading tmd and tik..' 237 | 238 | if ver: 239 | get_file(tid, 'tmd.%s' % ver, '%s/tmd' % dstdir) 240 | else: 241 | get_file(tid, 'tmd', '%s/tmd' % dstdir) 242 | 243 | get_file(tid, 'cetk', '%s/cetk' % dstdir) 244 | parse_tmd(tid, dstdir) 245 | 246 | except Exception, e: 247 | #os.rmdir(dstdir) # XXX: ? 248 | raise 249 | 250 | def get_csv(url, dstdir, y8, onlylatest): 251 | csv = urllib2.urlopen(url).read() 252 | csv = csv.splitlines()[1:] 253 | 254 | titles = {} 255 | 256 | def add_to_dl_queue(tid, ver, region): 257 | tv = (tid, ver) 258 | 259 | if tv not in titles: 260 | titles[tv] = [region] 261 | else: 262 | titles[tv].append(region) 263 | 264 | for line in csv: 265 | if line.strip() == '': 266 | continue 267 | 268 | line = line.split(',') 269 | tid = line[0] 270 | region = line[1] 271 | vers = line[2].split(' ') 272 | 273 | # Only download last version. 274 | if onlylatest: 275 | add_to_dl_queue(tid, vers[-1], region) 276 | else: 277 | # Download all versions. 278 | for ver in vers: 279 | add_to_dl_queue(tid, ver, region) 280 | 281 | for tv in titles: 282 | tid = tv[0] 283 | ver = tv[1] 284 | 285 | tidver = '%s.%s' % (tid, ver[1:]) 286 | 287 | # If multiple regions has this title+version, only download it once. 288 | if len(titles[tv]) > 1: 289 | tdir = '%s/%s/%s' % (dstdir, tid, ver) 290 | get_title(tidver, tdir) 291 | else: 292 | region = titles[tv][0] 293 | if y8: 294 | tdir = '%s/%s/%s/%s' % (dstdir, tid, region, ver) 295 | else: 296 | tdir = '%s/%s/%s/%s' % (dstdir, region, tid, ver) 297 | 298 | get_title(tidver, tdir) 299 | 300 | def main(args): 301 | parser = argparse.ArgumentParser() 302 | parser.add_argument('--dl', help='Download title, format: title_id[.version].') 303 | parser.add_argument('--dlcsv', help='Download titles based on csv-file from url.') 304 | parser.add_argument('--dir', help='Output directory.') 305 | parser.add_argument('--y8', action='store_true', help='Compatibility with ctr-titletool.') 306 | parser.add_argument('--onlylatest', action='store_true', help='If many versions, pick last one.') 307 | 308 | args = parser.parse_args() 309 | 310 | if args.dl and args.dlcsv: 311 | print 'You can either download a title id or an csv. Not both!' 312 | return 1 313 | 314 | elif args.dl: 315 | if args.dir: 316 | get_title(args.dl, args.dir) 317 | else: 318 | get_title(args.dl) 319 | 320 | elif args.dlcsv: 321 | if not args.dir: 322 | print 'You must supply --dir for --dlcsv!' 323 | return 1 324 | 325 | get_csv(args.dlcsv, args.dir, args.y8, args.onlylatest) 326 | 327 | else: 328 | parser.print_help() 329 | 330 | return 0 331 | 332 | sys.exit(main(sys.argv)) 333 | -------------------------------------------------------------------------------- /extract_fw/Makefile: -------------------------------------------------------------------------------- 1 | extract_fw: extract_fw.c 2 | gcc -o extract_fw extract_fw.c 3 | -------------------------------------------------------------------------------- /extract_fw/auxvec.h: -------------------------------------------------------------------------------- 1 | #ifndef _AUXVEC_H_ 2 | #define _AUXVEC_H_ 3 | 4 | typedef struct 5 | { 6 | int a_type; 7 | union 8 | { 9 | int a_val; 10 | } a_un; 11 | } __attribute__((packed)) Elf32_auxv_t; 12 | 13 | #define AT_NULL 0 14 | #define AT_ENTRY 9 15 | #define AT_UID 11 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /ios_structs.py: -------------------------------------------------------------------------------- 1 | from idc import AddStrucEx, AddStrucMember 2 | 3 | id = AddStrucEx(-1, "ios_request", 0) 4 | AddStrucMember(id, "cmd", 0, 0x20000400, -1, 4) 5 | AddStrucMember(id, "field_4", 0x4, 0x20000400, -1, 4) 6 | AddStrucMember(id, "fd", 0x8, 0x20000400, -1, 4) 7 | AddStrucMember(id, "field_C", 0xC, 0x20000400, -1, 4) 8 | AddStrucMember(id, "coreid", 0x10, 0x20000400, -1, 4) 9 | AddStrucMember(id, "rampid", 0x14, 0x20000400, -1, 4) 10 | AddStrucMember(id, "titleid_hi", 0x18, 0x20000400, -1, 4) 11 | AddStrucMember(id, "titleid_lo", 0x1C, 0x20000400, -1, 4) 12 | AddStrucMember(id, "field_20", 0x20, 0x20000400, -1, 4) 13 | AddStrucMember(id, "arg0", 0x24, 0x20000400, -1, 4) 14 | AddStrucMember(id, "arg1", 0x28, 0x20000400, -1, 4) 15 | AddStrucMember(id, "arg2", 0x2C, 0x20000400, -1, 4) 16 | AddStrucMember(id, "arg3", 0x30, 0x20000400, -1, 4) 17 | AddStrucMember(id, "arg4", 0x34, 0x20000400, -1, 4) 18 | 19 | id = AddStrucEx(-1, "ioctlv_vec", 0) 20 | AddStrucMember(id, "pyhs", 0, 0x20000400, -1, 4) 21 | AddStrucMember(id, "size", 0x4, 0x20000400, -1, 4) 22 | AddStrucMember(id, "virt", 0x8, 0x20000400, -1, 4) 23 | -------------------------------------------------------------------------------- /notes/arm_boot0/boot0.txt: -------------------------------------------------------------------------------- 1 | 2 | boot0 is the bootrom of the arm9. It is possible to dump from vWii mode, which 3 | it shouldn't be. It heavily outputs on the debug GPIOs. 4 | 5 | 6 | -------------------------------------------------------------------------------- 7 | MainBootFunc: (TODO: Describe path leading up to this..) 8 | 9 | In order of execution: 10 | 11 | Interesting write "0xD80018C |= 0xC00;". (boot0 control register) 12 | It then inits AES/SHA1 HW by writing zeroes to all regs. 13 | Interesting write "0xD8005B4 = (0xD8005B4 & 0xFFFFF3FF) | 0xC00;" 14 | 15 | Then it reads the following from OTP: 16 | KeyId: 0x20, Size: 4 bytes, Name: otp::DebugConfig 17 | KeyId: 0x21, Size: 4 bytes, Name: otp::HardwareConfig 18 | KeyId: 0x22, Size: 4 bytes, Name: otp::SeepromConfig 19 | KeyId: 0x28, Size: 16 bytes, Name: otp::KeyA 20 | KeyId: 0xE8, Size: 16 bytes, Name: otp::KeyB 21 | 22 | (otp::KeyA is a key used for SEEPROM decryption.) 23 | (otp::KeyB is a key used to decrypt boot1 (?), that's probably why it is in 24 | another OTP bank from the rest; it's very secret.) 25 | 26 | Then it outputs some bits of otp::HardwareConfig on debug GPIO's. 27 | After that, it outputs 0xA on debug port. 28 | 29 | Then it inits the SEEPROM using the otp::SeepromConfig, and outputs 30 | 0xB on the debug port. 31 | 32 | If bit30 of otp::DebugConfig is not set, it will output 0xC1 and panic with 33 | val=2. Panic means: 34 | 35 | 0. Memset the buf for both otp::KeyA/B to 00's. 36 | 1. Output . 37 | 2. Sleep for a while. 38 | 3. Output a byte from some internal state. 39 | 4. Sleep for a while. 40 | 5. Mirror debug gpio inputs. 41 | 6. Goto 1. 42 | 43 | If bit31 of otp::DebugConfig is set, it means RSA+AES crypto is enabled. 44 | 45 | Bit27 and bit28 decide what RSA key to use. If both are set, it outputs 0xC2 46 | and panics with val=1. Assumed is that one key is debug key, and that one is 47 | retail. 48 | 49 | Next, it loads things from the SEEPROM. It reads the following offsets: 50 | Offset: 0x1C0, Size: 16 bytes, Name: seeprom::BlockA (nand_cfg) 51 | Offset: 0x1D0, Size: 16 bytes, Name: seeprom::BlockB (partition_blk0) 52 | Offset: 0x1E0, Size: 16 bytes, Name: seeprom::BlockC (partition_blk1) 53 | 54 | Each one is decrypted with otp::KeyA if AES crypto enabled, otherwise 55 | plaintext is used. 56 | 57 | If AES crypto enabled, it then verifies that: 58 | crc32(seeprom::BlockA, 0xC) == *(u32*)seeprom::BlockA[0xC] 59 | If mismatch, it outputs 0xC3 on debug port and panics with val=1. 60 | 61 | If AES crypto disabled, then two things can happen on crc32 mismatch: 62 | * It ignores it. 63 | * It memsets entire seeprom::BlockA to 00's. This happens if last u32 of 64 | BlockA is non-zero. 65 | 66 | BlockA is read as if it has the following structure: 67 | u16 unk1; // bit10-14: ? 68 | u16 flags; // bit15: Unknown NAND-reading flag. 69 | u32 nand_cfg; // Written to NAND_CFG. 70 | u32 nand_unk1; // Written to NAND_UNK1. 71 | u32 crc; 72 | 73 | Next, it outputs 0xF on the debug port, and verifies that blockB and blockC 74 | have valid CRC32's. If both have invalid CRC32's, it outputs 0xC4 and 75 | panics with val=1. 76 | 77 | These blocks are now called the nand_partition_block. There are two of them 78 | in OTP (one for each partition maybe?), and it will pick the valid one. 79 | 80 | The nand_partition_blocks have the following structure: 81 | u16 prio; // Highest value decides which block to use. 82 | u16 sector; // NAND sector to read from, bit12: unknown NAND flag? 83 | 84 | u32 crc; 85 | 86 | It will then choose to use either blockB or blockC, as follows: 87 | blockC if only blockC is valid. 88 | blockB if only blockB is valid. 89 | blockC if both are valid and blockB.prio < blockC.prio. 90 | blockB if both are valid and opposite of ^. 91 | 92 | 93 | 94 | Bit12 of the nand_partition_block::flags field decides whether or not to set 95 | bit1 of 0xD010018 (unknown what it does). 96 | 97 | Next up it tries to init the NAND, this init can fail somehow. If it fails, 98 | a variable error_code is set to 0xDA and it continues at 99 | [AFTER_NAND_READING]. 100 | 101 | If it succeeds, 0x14 is outputted on debug port and it starts a NAND read: 102 | * output_ptr = 0xD400000 103 | * nand_sector = nand_partition_block.sector << 6 104 | * num_sectors = 1 (each sector = 0x800 bytes) 105 | 106 | This is reading the boot1 header from NAND. 107 | 108 | If NAND read fails, error_code is set to 0xD1 and it continues at 109 | [AFTER_NAND_READING]. 110 | 111 | It then memcpy's 0xD4001A0 size 0x60 to an internal buffer. This is actually useless and 112 | gets overwritten by ParseNandHeaderAndVerifyRsa() later on. 113 | 114 | Then it reads the size of boot1 from the NAND header. It makes sure that it 115 | isn't greater than 0xF000, since reading more than that would overflow the 116 | buffer @ 0xD400000 into the code (which is based at 0xD410000). 117 | 118 | Because boot1 starts @ offset 0x200 from the sector, it first adds 0x200 and 119 | then rounds upwards to 0x800-alignment. 120 | 121 | The code looks like this: 122 | boot1_size = (*(u32*)0xD4001AC + 0x9FF) & 0xFFFFF800; // in bytes. 123 | 124 | if(boot1_size == 0 || boot1_size > 0xF000) { 125 | error_code = 0xD6; 126 | goto [AFTER_NAND_READING]; 127 | } 128 | 129 | It is possible to overflow the size check, which causes boot0 to read only 1 sector 130 | in the following code. 131 | This triggers a useless bug in the ParseNandHeaderAndVerifyRsa() function when the 132 | boot1 header (read by the following nand_read() call) contains a larger boot1 size 133 | than what has been actually read. 134 | 135 | Then it does another NAND-read: 136 | * output_ptr = 0xD400000 (same as before) 137 | * nand_sector = nand_partition_block.sector << 6 (same as before) 138 | * num_sectors = boot1_size / 0x800 139 | 140 | If NAND read fails, error_code is set to 0xD1 and it continues at 141 | [AFTER_NAND_READING]. 142 | 143 | Now, if crypto is enabled, the header read from NAND is parsed somewhat and 144 | RSA signature is verified @ 0xD400000 using the RSA modulus selected above. 145 | 146 | If RSA signature check fails, error_code is set by reading debug port GPIO 147 | pins from the HW register. It assumes that this has retained the same value 148 | as written to it by the ParseNandHeaderAndVerifyRsa() function that 149 | previously failed. 150 | 151 | All fail-paths in the ParseNandHeaderAndVerifyRsa() function sets those 152 | debug GPIO's to something non-zero. 153 | 154 | After the header has been parsed and RSA-verified, it decrypts boot1 at ptr 155 | (u8*)(0xD4000000+0x200) in chunks of 0x1000 bytes with otp::KeyB. 156 | 157 | IV used for this decryption is hardcoded: 4FCD24A0E4D3AB6FAE8DFD8108581DCF. 158 | If all this has gone well, error_code is 0. 159 | 160 | [AFTER_NAND_READING]: 161 | (This is where it ends up both when NAND reading succeeded and failed for 162 | various reasons. It will also end up here if failed to verify the header 163 | read from NAND; both RSA and otherwise.) 164 | 165 | It outputs 0x1B on debug port. Then it does something with the HW_TIMER 166 | based on blockA::unk1 bit 10-14. 167 | 168 | Next it writes "0xD800194 |= 0x100000;", which would perform a reboot on 169 | an old Wii. This does something else on WiiU. After that, it does 170 | a lot of crap with the HW @ addr 0xD006800+. 171 | 172 | This new HW pokes can enable a second bootup procedure instead of panic:ing. 173 | In that case, it outputs 0x1E on the debug port, and sleeps based on 174 | blockA::flags bit8-10. The secondary bootup medium is the sdcard slot. 175 | 176 | If this second bootup procedure is not found/enabled, any non-zero 177 | error_code will cause a panic. 178 | 179 | TODO: Describe the second-bootup-procedure in more detail. 180 | 181 | Then, finally: 182 | * It outputs 0x25 on the debug port. 183 | * Memsets both AES-keys to zeroes. 184 | * Writes number-of-timer-ticks-it-took-to-execute-entire-bootrom to 185 | 0xD417FE0. 186 | * Returns 0xD400200; 187 | 188 | 189 | -------------------------------------------------------------------------------- 190 | Nand Header 191 | 192 | We can deduce that the header read from NAND has the following structure: 193 | 194 | char unsigned_info_region[0x20]; // Offset 0 195 | char rsa_sig[0x180]; // Offset 0x20 196 | char signed_info_region[0x60]; // Offset 0x1A0 197 | 198 | (total size: 0x200 bytes) 199 | 200 | The signed_info_region has the following structure, and is covered by the RSA 201 | signature: 202 | 203 | u16 zero1; // Must be 0 or error. 204 | u16 zero2; // Must be 0 or error. 205 | u32 sector_type; // Must be 0x21(recovery) or 0x22(normal), otherwise 206 | // panic. 207 | u32 boot1_size_align; // This is a size of boot1 aligned to 0x1000. If not 208 | // aligned to 0x1000 or zero, error. 209 | u32 boot1_size; // This is the size of boot1 in bytes. First time 210 | // this is read w/o checking RSA signature. 211 | u8 boot1_hash[0x14]; // SHA1 of boot1, size being aligned to 0x1000. 212 | 213 | 214 | -------------------------------------------------------------------------------- 215 | ParseNandHeaderAndVerifyRsa(): 216 | 217 | It copies 0x180 bytes from nand_header+0x20 to an rsa_buf. 218 | 219 | It overwrites the old_header_buf with nand_header+0x1A0 size 0x60, and 220 | calculates the SHA1 of this region. It then verifies that the signature 221 | found in rsa_buf+4 decrypts to the SHA1 calculated above. 222 | 223 | If RSA verification fails, it outputs 0xE1 on debug port and returns 0. 224 | 225 | Then it verifies that the u16 @ buf[0] is 0, otherwise it outputs 0xE2 and 226 | returns 0. And then it verifies that the u16 @ buf[1] is 0, otherwise 0xE3 227 | and returns 0. 228 | 229 | Then it verifies that bytes @ offsets 0x28..0x60 (not inclusive) is 0. (?) 230 | Otherwise it outputs 0xE4 and returns 0. 231 | 232 | Then it reads the u32 @ buf+4. 233 | 234 | If it is 0x21: 235 | It checks if is_recovery_mode is 1, and if so errors with 0xE7. 236 | 237 | Next it checks if the u32 @ buf+0x24 == nand_partition_block.prio, and 238 | if not it errors with 0xE6. 239 | 240 | If it is 0x22: 241 | It checks if is_recovery_mode is 0, and if so errors with 0xE7. 242 | 243 | If it is anything else, it errors with 0xE7. 244 | 245 | Then, it checks that the u32 at buf+0xC is aligned to 0x1000 and nonzero. If 246 | not, it errors with 0xE8. 247 | 248 | Next up, it hashes 0xD400200+ with SHA1 in chunks of 0x1000 bytes. This is 249 | pretty odd because it's not aligned to 0x1000. 250 | 251 | The number of chunks to hash is given by the upper 20 bits of the u32 at 252 | buf+0xC. 253 | 254 | And then it verifies that this hash matches the hash found at buf+0x10. 255 | On mismatch it errors with 0xE9. Otherwise it returns upper 20 bits of the 256 | u32 at buf+0xC. 257 | 258 | 259 | -------------------------------------------------------------------------------- 260 | Rsa::Verify(): 261 | 262 | TODO 263 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_act.txt: -------------------------------------------------------------------------------- 1 | == /dev/act == 2 | Port of the 3DS "act" service. 3 | 4 | == Ioctls == 5 | +----------+------------------------------------+----+-----------+-----------+------ 6 | | Ioctl # | Name | v? | In size | Out size | Input Args 7 | +----------+------------------------------------+----+-----------+-----------+------ 8 | | 0 | Execute3DSServiceCommand | Ye | ? | ? | < see 3ds_services.txt > 9 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_bsp.txt: -------------------------------------------------------------------------------- 1 | == /dev/bsp == 2 | Seems to be an interface to read/parse EEPROM config? 3 | 4 | == Ioctls == 5 | +----------+------------------------------------+----+-----------+-----------+------ 6 | | Ioctl # | Name | v? | In size | Out size | Input Args 7 | +----------+------------------------------------+----+-----------+-----------+------ 8 | | 1 | GetEntityVersion | No | 0x48 | 4 | struct { u32 unk[0x11]; u32 must_be_4; } 9 | | 2 | GetHardwareVersion | No | 0x48 | 4 | struct { u32 ignored[0x11]; u32 must_be_4; } 10 | | 3 | ? | No | ? | ? | struct { u32 ignored[0x11]; u32 must_be_4; } 11 | | 4 | Query | No | 0x48 | var | struct { u8 name0[32]; u32 max_version; u8 name1[32]; u32 len_out; } 12 | | 5 | Read | No | 0x48 | var | struct { u8 name0[32]; u32 max_version; u8 name1[32]; u32 len_out; } 13 | | 6 | Write | No | 0x48+var | 0 | struct { u8 name0[32]; u32 max_version; u8 name1[32]; u32 len_out; } 14 | | 7 | Initialize | No | 0x48+var | 0 | struct { u32 unk[0x11]; u32 bytes_to_write; char buf[]/*var size*/; } 15 | | 8 | Shutdown | No | 0x48 | 0 | struct { u8 name0[32]; u32 max_version; u8 name1[32]; u32 len_out; } 16 | | 9 | GetConsoleTypeRaw | No | 0x48 | 4 | struct { u32 ignored[0x11]; u32 must_be_4; } 17 | +----------+------------------------------------+----+-----------+-----------+------ 18 | 19 | BSP has a bunch of "subsystems" with "properties" that you can do read/query/ 20 | /write/initialize/shutdown operations on. 21 | 22 | Each property is described by a struct like this: 23 | 24 | typedef struct { 25 | const char* name; // @+0 26 | u32 flags; // @+4 bit0-1: 1=query-data is u32, 2=query-data is ptr 27 | u32 misc; // @+8 28 | u32 query_data; // @+0xC 29 | u32 size; // @+0x10, this field must match up with len_out for query/read/write cmds. 30 | int (*read)(u32 x, bsp_property* p, u32* out); // @+0x14 31 | int (*query)(); // @+0x18 32 | int (*write)(u32 x, bsp_property* p, u32* in); // @+0x1C 33 | int (*initialize)(); // @+0x20 34 | int (*shutdown)(); // @+0x24 35 | u32 unk; 36 | } bsp_property; // sizeof=0x2C 37 | 38 | 39 | == EEPROM Interface == 40 | "EE" is short for EEPROM. 41 | "control" is the control register. 0x4 = EnableWriteAccess 42 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_cbl.txt: -------------------------------------------------------------------------------- 1 | == /dev/cbl == 2 | Some kind of logging mechanism. 3 | 4 | == Ioctls == 5 | +----------+------------------------------------+----+-----------+-----------+------ 6 | | Ioctl # | Name | v? | In size | Out size | Input Args 7 | +----------+------------------------------------+----+-----------+-----------+------ 8 | | 1 | Log? | Ye | 0v | 1v | 0: [0x204 bytes] 9 | | 1 | AllocBlock? | No | Even | - | ? 10 | | 2 | FreeBlock? | No | - | - | < Args ignored.. > 11 | | 3 | SaveLog? | No | 8 | ? | u64 request_mask; 12 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_ccr_nfc.txt: -------------------------------------------------------------------------------- 1 | == Device /dev/ccr_nfc == 2 | Amiibo crypto. 3 | 4 | == Ioctls == 5 | +----------+------------------------------------+----+-----------+-----------+------ 6 | | Ioctl # | Name | v? | In size | Out size | Input Args 7 | +----------+------------------------------------+----+-----------+-----------+------ 8 | | 0 | EncryptAmiibo | No | 0x248 | 0x248 | 9 | | 1 | RencryptAmiibo? | No | 0x248 | 0x248 | 10 | | 2 | DecryptAndVerifyAmiibo | No | 0x248 | 0x248 | 11 | | 3 | ? | Ye | 2v | 1v | [0x224, 0x20], [0x248] 12 | | 4 | Stubbed.. returns 0 | No | 0x248 | 0x248 | 13 | +----------+------------------------------------+----+-----------+-----------+------ 14 | 15 | Due to a "bug" in the code, ioctl 3 does also exist but doesn't do anything useful. 16 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_crypto.txt: -------------------------------------------------------------------------------- 1 | == /dev/crypto == 2 | Le crypto. 3 | 4 | The used curve for elliptic curve cryptography (ECC) is sect233r1. 5 | 6 | == Ioctls == 7 | +----------+--------------------------------------+----+-----------+-----------+------ 8 | | Ioctl # | Name | v? | In size | Out size | Input Args 9 | +----------+--------------------------------------+----+-----------+-----------+------ 10 | | 1 | IOSC_CreateObject | No | 0x10 | 4 | int unk1, int type(0=aes-cbc?,7=hmac?), int ignored, int ignored2 11 | | 2 | IOSC_DeleteObject | No | 4 | 0 | int handle 12 | | 3 | IOSC_ImportSecretKey | Ye | 4v | 0v | ? 13 | | 4 | hmac | Ye | ? | ? | 14 | | 5 | IOSC_ImportPubkey | Ye | 5v | 3v | 15 | | 6 | IOSC_ExportRootKey | Ye | 1v | 2v | 16 | | 7 | IOSC_ComputeSharedKey | No | ? | ? | 17 | | 8 | sets 4 bytes from input | Ye | ? | ? | 18 | | 9 | IOSC_GetDeviceId | Ye | 1v | 1v | 19 | | a | get keysize from keyid | No | ? | ? | 20 | | b | get keysize again? | No | ? | ? | 21 | | c | IOSC_GenerateHash | Ye | 3v | 1v | 22 | | d | IOSC_Encrypt | Ye | 3v | 1v | 23 | | e | IOSC_Decrypt | Ye | 3v | 1v | 24 | | f | IOSC_VerifyPubkeySign | Ye | 3v | 0v | 25 | | 10 | hmac | Ye | ? | ? | 26 | | 11 | IOSC_ImportAndVerifyCert? | Ye | 2v | 0v | 27 | | 12 | IOSC_GetDeviceCert | No | 0 | 0x180 | 28 | | 13 | IOSC_SetTitleKeyOwnership | Ye | 2v | 0v | 29 | | 14 | get info from keyid | Ye | ? | ? | 30 | | 16 | IOSC_GenerateSecretKey | No | 4 | 0 | 31 | | 17 | IOSC_SignCert | Ye | 2v | 1v | 32 | | 18 | IOSC_GenerateCert | Ye | 2v | 1v | 33 | | 1A | hw crypto | Ye | ? | ? | 34 | | 1B | hw crypto | Ye | ? | ? | 35 | | 1D | IOSC_ReadHashedBlock | Ye | 5v | 1v | sizes need to be {0x24, 0x4, *, 0x400, 0xFC00}, {0xFC00} 36 | | 15 | GenRand | No | ? | ? | 37 | | 1C | | No | ? | ? | 38 | | 1E | IOSC_GetSeepromCert | No | ? | ? | 39 | | 1F | IOSC_UsbhddKeyGenerator | No | ? | ? | Only PID1 (MCP) allowed to use this. 40 | | 16 | | No | ? | ? | 41 | | 20 | EncryptSw | No | ? | ? | 42 | | 21 | DecryptSw | No | ? | ? | 43 | | 22 | SetCryptoThreadPriority? | No | 4 | 0 | Used by MCP. 44 | | 23 | | No | ? | ? | Used by MCP. 45 | +----------+--------------------------------------+----+-----------+-----------+------ 46 | 47 | == Key table == 48 | +-------+------------+------+------------------- 49 | | KeyID | Otp Offset | Size | Note 50 | +-------+------------+------+------------------- 51 | | 0 | 0x88 | 30 | Some ECC key? 52 | | 1 | 0x87 | 4? | 53 | | 2 | 0x5C | 16 | WiiU NAND-key 54 | | 3 | 0x78 | 20 | WiiU NAND HMAC-key 55 | | 4 | 5 | 16 | vWii common-key 56 | | 5 | 0x58 | 16 | 57 | | 6 | - | 16 | ???. Hardcoded in binary. 58 | | 7 | 0x28 | 16 | Used by MCP. Used for something related to /vol/storage_mlc01/sys/import? 59 | | 8 | - | ??? | 60 | | 9 | - | ??? | 61 | | 10 | - | ??? | 62 | | 11 | 0xD2 | 16 | 63 | | 12 | - | ??? | 64 | | 13 | 0x24 | 16 | ARM Ancast-key. Used by MCP. 65 | | 14 | - | 256 | ARM Ancast RSA-modulus, hardcoded in binary. Exponent 0x10001. 66 | | 15 | - | 256 | Boot1 Ancast RSA-modulus. Hardcoded in binary. Exponent 0x10001. 67 | | 16 | 0x38 | 16 | WiiU common-key 68 | | 17 | 0x60 | 16 | 69 | | 18 | - | 16 | This is setup by /dev/crypto ioctl 0x1F. Used in MCP for usbhdd crypto. 70 | | 19 | 0x16 | 16 | vWii NAND-key 71 | | 20 | 0x11 | 20 | vWii NAND HMAC-key 72 | | 21 | 0x34 | 16 | 73 | | 22 | 0x68 | 16 | 74 | | 23 | - | 16 | Used by IOS-NET. Calculated by xor with OTP, see below. 75 | | 24 | - | 16 | Used by IOS-NET. Calculated by xor with OTP, see below. 76 | | 25 | - | 16 | Used by IOS-ACP. Calculated by xor with OTP, see below. 77 | | 26 | 0x50 | 16 | 78 | | 27 | 0x48 | 16 | Used by IOS-NSEC. 79 | | 28 | 0x90 | 30 | Some ECC key? 80 | | 29 | 0xD8 | 30 | Some ECC key? 81 | | 30 | 0x90 | 16 | Appstore .objdata key. Used by IOS-NSEC. 82 | | 31 | - | 16 | Calculated by xor with OTP, see below. Used by NIM-BOSS. 83 | | 32 | - | 64 | Calculated by xor with OTP, see below. 84 | | 33 | - | 64 | Calculated by xor with OTP, see below. 85 | | 34 | - | 16 | Calculated by xor with OTP, see below. Amiibo Hmac-key #0. WTF: Key-size is actually set to 0x40?! 86 | | 35 | - | 16 | Calculated by xor with OTP, see below. Amiibo Hmac-key #1. WTF: Key-size is actually set to 0x40?! 87 | | 36 | - | 16 | Calculated by xor with OTP, see below. Amiibo Random-key 88 | | 37 | - | 16 | Calculated by xor with OTP, see below. Amiibo ??? 89 | | 38 | - | 16 | Calculated by xor with OTP, see below. ??? 90 | | 39 | - | 16 | Calculated by xor with OTP, see below. Used by NIM-BOSS. 91 | +-------+------------+------+------------------- 92 | 93 | == OTP XOR trick == 94 | Some keys are hardcoded in the IOS-CRYPTO binary, but are stored xor'd with a 95 | fixed key from OTP offset 0x54. This means that an OTP dump is required 96 | to calculate the actual key. 97 | 98 | For key-ids 32,33 the xor-key from otp is still 16-byte but is extended to 64 99 | byte by just repeating itself. 100 | 101 | 102 | == IOSC_UsbhddKeyGenerator == 103 | This function encrypts a 16-byte input with AES-ECB using a key loaded from OTP 104 | offset 0x4C. It then proceeds to set up key-id 18 with the output. 105 | 106 | If no input is given, it will read 16 bytes from EEPROM offset 0x58. The first 107 | 4 bytes of this must be identical to OTP offset 0x87, otherwise an error is 108 | returned. 109 | 110 | From MCP, it looks like the input-block can come from: 111 | * /vol/system_slc/security/ivs.bin 112 | * all zeroes 113 | * NULL 114 | 115 | 116 | == AES HW function == 117 | enum aes_op { 118 | 0=??, 119 | 1=??, 120 | 2=WAIT_ASYNC 121 | }; 122 | 123 | enum aes_hw { 124 | AES_HW_NONE=0, 125 | AES_HW_AES =1, 126 | AES_HW_AESS=2 127 | }; 128 | 129 | struct aes_blk { 130 | int aes_ctrl;// @+0, 2=enc, 3=dec 131 | u8* iv_buf; // @+4 132 | size_t iv_len; // @+8 133 | u8* key_buf; // @+12 134 | size_t key_len; // @+16 135 | u8* in_buf; // @+20 136 | size_t in_len; // @+24 137 | u8* out_buf; // @+28 138 | size_t out_len; // @+32 139 | int iv_cfg; // @+36 1=continue_cbc, 2=reset_iv 140 | 141 | }; 142 | 143 | int aes::ControlHw(enum aes_hw hw, 144 | bool async, 145 | enum aes_op op, 146 | struct aes_blk* cfg_blk); 147 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_fpd.txt: -------------------------------------------------------------------------------- 1 | == /dev/fpd == 2 | Friend and Playtime deamon? 3 | 4 | == Ioctls == 5 | +----------+------------------------------------+----+-----------+-----------+------ 6 | | Ioctl # | Name | v? | In size | Out size | Input Args 7 | +----------+------------------------------------+----+-----------+-----------+------ 8 | | 2775 | Login | Ye?| ? | ? | 9 | | 2776 | Logout | Ye | ? | ? | 10 | | 2777 | HasLoggedIn | Ye | ? | ? | 11 | | 2778 | IsOnline | Ye | ? | ? | 12 | | 27D9 | GetMyPrincipalId | Ye | ? | ? | 13 | | 27DA | GetMyAccountId | Ye | ? | ? | 14 | | 27DB | GetMyScreenName | Ye | ? | ? | 15 | | 27DC | GetMyMii | Ye | ? | ? | 16 | | 27DD | GetMyProfile | Ye | ? | ? | 17 | | 27DE | GetMyPreference | Ye | ? | ? | 18 | | 27DF | GetMyPresence | Ye | ? | ? | 19 | | 27E0 | IsPreferenceValid | Ye | ? | ? | 20 | | 27E1 | IsFriendRequestAllowed | Ye | ? | ? | 21 | | 283D | GetFriendList | Ye | ? | ? | 22 | | 283E | GetFriendListAll | Ye | ? | ? | 23 | | 283F | GetFriendAccountId | Ye | ? | ? | 24 | | 2840 | GetFriendScreenName | Ye | ? | ? | check for vuln? 25 | | 2841 | GetFriendMii | Ye | ? | ? | 26 | | 2842 | GetFriendProfile | Ye | ? | ? | 27 | | 2843 | GetFriendApprovalTime | Ye | ? | ? | 28 | | 2844 | GetFriendSortTime | Ye | ? | ? | 29 | | 2845 | GetFriendPresence | Ye | ? | ? | 30 | | 2846 | GetFriendRelationship | Ye | ? | ? | 31 | | 28A1 | GetBlackList | Ye | ? | ? | 32 | | 28A2 | GetBlackListAccountId | Ye | ? | ? | 33 | | 28A3 | GetBlackListAdditionalTime | Ye | ? | ? | 34 | | 2905 | GetFriendRequestList | Ye | ? | ? | 35 | | 2906 | GetFriendRequestAccountId | Ye | ? | ? | 36 | | 2907 | GetFriendRequestMessageId | Ye | ? | ? | 37 | | 2969 | UpdateGameMode | Ye | ? | ? | 38 | | 296A | UpdateGameMode | Ye | ? | ? | 39 | | 296B | UpdateGameModeDescription | Ye | ? | ? | 40 | | 2AF9 | AddRecentPlayRecord | Ye | ? | ? | check for vuln? 41 | | 2B5D | ? | Ye | ? | ? | check for vuln? 42 | | 4EE9 | GetMyComment | Ye | ? | ? | 43 | | 4EEA | GetMyPlayingGame | Ye | ? | ? | 44 | | 4F4D | GetFriendComment | Ye | 1v | 2v | [0]: {u32 size}, [1]: {u32 pidList[size]} 45 | | 4F4E | GetFriendPlayingGame | Ye | ? | ? | 46 | | 4F4F | GetFriendPresenceEx | Ye | ? | ? | 47 | | 4FB1 | GetRecentPlayRecord | Ye | ? | ? | check for vuln? 48 | | 4FB2 | IsRecentPlayRecordCorrupted | Ye | ? | ? | 49 | | 5015 | UpdateGameModeEx | Ye | ? | ? | 50 | | 5079 | ? | Ye | ? | ? | check for vuln? 51 | | 50DD | UnlockParentalControlTemporarily | Ye | ? | ? | 52 | | 7595 | IsRequestBlockForced | Ye | ? | ? | 53 | | 75F9 | GetFriendListEx | Ye | ? | ? | 54 | | 76C1 | GetFriendRequestListEx | Ye | ? | ? | 55 | | 765D | GetBlackListEx | Ye | ? | ? | 56 | | 7728 | UpdatePlayingGame | Ye | ? | ? | 57 | | 77ED | ? | Ye | 0v | 2v | 58 | | 77EE | ? | Ye | 0v | 1v | 59 | | 7851 | ? | Ye | 0v | 1v | 60 | | 7852 | ? | Ye | 0v | 1v | 61 | | 7853 | ? | Ye | 0v | 1v | 62 | | 78B5 | AddRecentPlayRecordEx | Ye | 0v | 2v | 63 | | 78B6 | DeleteRecentPlayRecordAll | Ye | ?v | ?v | ? 64 | | 7919 | ? | Ye | 3v | 0v | 65 | | 7854 | ? | Ye | 0v | 2v | 66 | | 797D | ? | Ye | 0v | 1v | 67 | | 79E1 | DeleteSaveDirectory | Ye | 0v | 2v | 68 | | 9CA5 | UpdatePlayingOverlayApplication | Ye | 0v | 2v | 69 | | 9CA6 | UpdateGameModeForOverlayApplication| Ye | 0v | 2v | 70 | | 9D09 | GetLastLedEvent | Ye | 0v | 2v | 71 | | 9D0A | ClearLedEvent | Ye | 0v | 2v | 72 | | 9D0B | SetLedEventMask | Ye | 0v | 2v | 73 | | 15FF5 | SetNotificationHandler | Ye | 0v | 1v | [u32 notificationMask] This one sets the mask in a table indexed by pid. 74 | | 15FF6 | ? | Ye | 2v | 0v | 75 | | 15FF7 | ResultToErrorCode | Ye | 1v | 0v | 76 | 77 | .. and more .. 78 | 79 | 80 | == Ioctlv handling == 81 | FPD has its own way of handling ioctlv's. Pseudocode: 82 | 83 | class fpd_ioctlv_cmd { 84 | // @+0: vtable 85 | ios_request* req; // @+4 86 | u32 cmd_id; // @+8 87 | u32 pid; // @+12 88 | std::vector safe_vectors; // @+16 89 | // u32 _vec_unk; // @+16 90 | // void* _vec_last_free_element; // @+20 91 | // void* _vec_last_element; // @+24 92 | }; 93 | 94 | int fpd_ioctlv_cmd::fill(fpd_ioctlv_cmd* this, ios_request* req) { 95 | for(i=0; inum_out; i++) { 96 | ios_vec v; 97 | v.size = req->vec[req->num_in+i].size; 98 | v.virt = 0; 99 | 100 | v.phys = malloc(v.size); 101 | if(v != NULL) { 102 | memcpy(v.phys, req->vec[req->num_in+i].phys, v_size); 103 | } 104 | 105 | this.safe_vectors.push_back(v); 106 | } 107 | } 108 | 109 | int ioctlv_cmd::execute(fpd_ioctlv_cmd* this) { 110 | switch(this->cmd_id) { 111 | // < Different cmd handlers here.. > 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_im.txt: -------------------------------------------------------------------------------- 1 | == /dev/im == 2 | Very buggy? Very buggy! 3 | 4 | == Ioctls == 5 | 0 SetSystemPolicy 6 | 1 SetNvParameter(u32 id, u32 value); 7 | 2 SetParameter 8 | 3 GetParameter 9 | 4 SetDeviceState 10 | 5 GetEventNotify 11 | 6 CancelGetEventNotify 12 | 7 GetHomeButtonParams 13 | 8 GetTimerRemaining 14 | 9 GetNvParameter 15 | 10 SetTimerElpasedSeconds 16 | 11 SetHomeButtonParams 17 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_iopsh.txt: -------------------------------------------------------------------------------- 1 | == Device /dev/iopsh == 2 | IOP shell. This is used by one coreinit function. That function has no symbol. 3 | 4 | PowerPC syscall 0x7B00 "IopShell_InjectCommand" related.. 5 | 6 | 7 | == Ioctls == 8 | +----------+------------------------------------+----+-----------+-----------+------ 9 | | Ioctl # | Name | v? | In size | Out size | Input Args 10 | +----------+------------------------------------+----+-----------+-----------+------ 11 | | 4 | ExecuteShellCommand? | No | var | 0 | const char* cmd; // Seen: "ack_kill %d " 12 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_mcp.txt: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | u32 ctr1; 3 | u32 ctr2; 4 | u32 start_addr; 5 | u32 unk3; 6 | u32 end_addr; 7 | // sizeof=0x14 8 | } per_pid_entry; 9 | 10 | 11 | == Ioctl 0x51 == 12 | Must be invoked from kernel pid. 13 | < TODO.. > 14 | 15 | 16 | == Ioctl 0x5C == 17 | Must be invoked from kernel pid. 18 | 19 | size_in: 0x12D8 20 | buf_out: 0 21 | size_out: 0 22 | 23 | typedef struct { 24 | int pid; // @+0x18 25 | // sizeof=0x12D8 26 | } ioctl_5C; 27 | 28 | int ioctl_5C_inner(int pid) { 29 | per_pid_entry* ent = get_entry(pid); 30 | 31 | if(ent == NULL) 32 | return -4; 33 | 34 | if(ent->start_addr == ent->end_addr) { 35 | start_addr = 0x28000000; 36 | size = 0xA8000000; 37 | ent->ctr2++; 38 | } 39 | else { 40 | start_addr = ent->start_addr; 41 | size = ent->end_addr - ent->start_addr; 42 | ent->ctr1++; 43 | } 44 | 45 | return syscall_84(2, 0, 0, start_addr, size); 46 | } 47 | 48 | 49 | == Ioctl 0x210 == 50 | Must be invoked from kernel pid. 51 | 52 | size_in: 0x12D8 53 | buf_out: 0 54 | size_out: 0 55 | 56 | typedef struct { 57 | int pid; // @+0x18 58 | int addr; // @+0x1C, set to 0x50000000 59 | // sizeof=0x12D8 60 | } ioctl_210; 61 | 62 | int ioctl_210_inner(int pid, u32 addr) { 63 | per_pid_entry* ent = get_entry(pid); 64 | 65 | if(ent == NULL) 66 | return -4; 67 | 68 | ent->start_addr = addr; 69 | if(ent->end_addr == addr) 70 | return -4; 71 | 72 | return syscall_84(2, 0, 0, addr, addr - ent->end_addr); 73 | } 74 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_nsec_nss.txt: -------------------------------------------------------------------------------- 1 | == /dev/nsec/nss == 2 | Signatures, certificates and things like that? 3 | 4 | == Ioctlvs == 5 | +----------+------------------------------------+----+-----------+-----------+------ 6 | | Ioctl # | Name | v? | In size | Out size | Input Args 7 | +----------+------------------------------------+----+-----------+-----------+------ 8 | | 1 | NSSSecureStoreImportObject | Ye | 2v | 0v | 9 | | 2 | NSSSecureStoreExportObject | Ye | 1v | 2v | 10 | | 3 | NSSSecureStoreDeleteObject | No | 8 | 0 | 11 | | 4 | NSSExportDeviceCertChain | Ye | 1v | 3v | 12 | | 5 | NSSCreateSignatureContext | No | 0xC | 0 | 13 | | 6 | NSSDestroySignatureContext | No | 8 | 0 | 14 | | 7 | NSSSignatureSetPrivateKey | No | 0xC | 0 | 15 | | 8 | NSSSignatureGetSignatureLength | No | 4 | 0 | 16 | | 9 | NSSSignatureSignDigest | Ye | 1v | 3v | 17 | | 0xA | NSSSignatureSetPrivateKeyExternal | Ye | 2v | 0v | 18 | | 0xB | NSSSecureStoreDeleteAllObjects | No | 4 | 0 | 19 | | 0xC | NSSSecureStoreDeleteTitleObjStore | No | 8 | 0 | 20 | +----------+------------------------------------+----+-----------+-----------+------ 21 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_pm.txt: -------------------------------------------------------------------------------- 1 | == /dev/pm == 2 | Process Manager. Device is implemented in MCP. 3 | All ioctls must be invoked with coreid == 0 (ARM). 4 | 5 | 6 | == Ioctls == 7 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_socket.txt: -------------------------------------------------------------------------------- 1 | == Device /dev/socket/ == 2 | POSIX sockets. 3 | 4 | == Ioctls == 5 | +----------+------------------------------------+----+-----------+-----------+------ 6 | | Ioctl # | Name | v? | In size | Out size | Input Args 7 | +----------+------------------------------------+----+-----------+-----------+------ 8 | | 1 | accept() | No | 0x18 | 0x18 | 9 | | 2 | bind() | No | 0x18 | 0 | 10 | | 3 | socketclose() | No | 4 | 0 | 11 | | 4 | connect() | No | 0x18 | 0 | 12 | | 6 | getpeername() | No | 0x18 | var | 13 | | 7 | getsockname() | No | 0x18 | var | 14 | | 8 | getsockopt() | Ye | -- TODO -- 15 | | 9 | setsockopt() | Ye | -- TODO -- 16 | | 0xa | listen() | No | 8 | 0 | 17 | | 0xc | recv() | Ye | -- TODO -- 18 | | 0xd | recvfrom() | Ye | -- TODO -- 19 | | 0xe | send() | Ye | -- TODO -- 20 | | 0xf | sendto() | Ye | -- TODO -- 21 | | 0x10 | shutdown() | No | 8 | 0 | 22 | | 0x11 | socket() | No | 0xC | 0 | {arg0, arg1, arg2} 23 | | 0x14 | simple_ping() | No | 0x14 | 0 | 24 | | 0x26 | ?? | Ye | -- TODO -- 25 | | 0x27 | select() | No | 0x1C | 0x1C | 26 | | 0x29 | simple_ping_result() | No | 4 | 4 | 27 | | 0x2d | socketclose_all() | No | 4 | 0 | 28 | | 0x2e | SOGetProxyConfig() | No | 4 | 0x1C8 | 29 | | 0x2f | getsocklibopt() | No | 8 | 8 | 30 | | 0x30 | setsocklibopt() | No | 8 | 0 | 31 | | 0x32 | clear_resolver_cache() | No | ? | ? | 32 | | 0x33 | sendto_multi() | Ye | -- TODO -- 33 | | 0x35 | recvfrom_multi() | Ye | -- TODO -- 34 | | 0x36 | sendto_multi_ex() | Ye | -- TODO -- 35 | | 0x37 | dns_abort_by_hname() | No | 0x100 | 0 | 36 | | 0x38 | recvfrom_ex() | Ye | -- TODO -- 37 | 38 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_udscntl.txt: -------------------------------------------------------------------------------- 1 | == /dev/udscntl == 2 | Port of the 3DS "nwm::UDS" service. The ioctlv ids match exactly with the 3DS 3 | service (those less than 0x100). 4 | 5 | == Ioctls == 6 | +----------+------------------------------------+----+-----------+-----------+---------------------------+-----------------------+ 7 | | Ioctl # | Name | v? | In size | Out size | In vector sizes | Out vector sizes | //X = checked for vuln 8 | +----------+------------------------------------+----+-----------+-----------+---------------------------+-----------------------+ 9 | | 2 | Scrap | Ye | ?v | ?v | - | - | 10 | | 3 | Finalize | Ye | 0v | 1v | [] | [4] | //X 11 | | 4 | CreateNetwork | Ye | 2v | 1v | [0x108, var] | [4] | *(u32*)vec2.phys = uds_CreateNetwork(*(this+0x20), unk, 0, vec0.phys, vec1.phys, vec1.size, 0, 0, 0, 0, 0, &ios_request, vec2.phys); //? 12 | | 5 | EjectClient | Ye | 1v | 1v | [2] | [4] | *(u32*)vec1.phys = uds_EjectClient(*(this+0x20), *(u16*)vec0.phys); //X 13 | | 6 | EjectSpectator | Ye | 0v | 1v | [] | [4] | *(u32*)vec0.phys = uds_EjectSpectator(*(this+0x20)); //X 14 | | 7 | UpdateNetworkAttribute | Ye | 2v | 1v | [2, 1] | [4] | *(u32*)vec2.phys = uds_UpdateNetworkAttribute(*(this+0x20), *(u16*)vec0.phys /*bitmask*/, *(u8*)vec1.phys /*clear_or_set*/); //X 15 | | 8 | DestroyNetwork | Ye | 0v | 1v | [] | [4] | *(u32*)vec0.phys = uds_DestroyNetwork(*(this+0x20)); //X 16 | | 0xB | GetConnectionStatus | Ye | 0v | 2v | [] | [4, 0x30] | //X 17 | | 0xD | GetNodeInformation | Ye | 1v | 2v | [2] | [4, 0x28] | *(u32*)vec1.phys = uds_GetNodeInformation(*(this+0x20), vec2.phys, *(u16*)vec0.phys); //X 18 | | 0x10 | SetApplicationData | Ye | 1v | 1v | [var] | [4] | *(u32*)vec1.phys = uds_SetApplicationData(*(this+0x20), vec0.phys, vec0.size); //X 19 | | 0x11 | GetApplicationData | Ye | 0v | 3v | [] | [4, var, 4] | //X 20 | | 0x12 | Bind | Ye | 4v | 1v | [4, var, 1, 2] | [4] | *(u32*)vec4.phys = uds_Bind(*(this+0x20), *(u32*)vec0.phys, vec1.phys, vec1.size, *(u8*)vec2.phys, *(u16*)vec3.phys); //? 21 | | 0x13 | Unbind | Ye | 1v | 2v | [4] | [4, 0x10] | //X 22 | | 0x14 | PullPacket | Ye | 2v | 4v | [4, 1] | [4, var, 4, 2] | //? 23 | | 0x15 | SetMaxSendDelay | Ye | 1v | 1v | [8] | [4] | //X 24 | | 0x17 | SendTo | Ye | 6v | 1v | [4, 2, 1, var, 4, 1] | [4] | //? 25 | | 0x1A | GetChannel | Ye | 0v | 2v | [] | [4, 1] | //X 26 | | 0x1B | InitializeWithVersion | Ye | 2v | 1v | [0x28, 2] | [4] | //X 27 | | 0x20 | Flush | Ye | 1v | 1v | [4] | [4] | //? 28 | | 0x101 | ? | Ye | ?v | ?v | [4, 4] | [] | 29 | | 0x102 | PollStateChange | Ye | 1v | 1v | [1] | [4] | 30 | | 0x103 | ? | Ye | ?v | ?v | [] | [4, 6] | 31 | | 0x104 | CreateBridgeNetwork | Ye | 5v | 1v | [0x108, var, var, var, 1] | [4, 6] | 32 | | 0x105 | GetConnectionStatusForBridge | Ye | 0v | 2v | [] | [4, 0x94] | 33 | | 0x106 | GetNodeInformationForBridge | Ye | 1v | 2v | [2] | [4, 0x28] | 34 | | 0x107 | AddOutsideClient | Ye | 1v | 1v | [0x28] | [4] | //X 35 | | 0x108 | DeleteOutsideClient | Ye | 1v | 1v | [2] | [4] | 36 | | 0x109 | SendBridgedPacket | Ye | 7v | 1v | [4, 2, 2, 1, var, 4, 1] | [] | 37 | | 0x10A | BindForBridge | Ye | 2v | 1v | [4, var] | [4] | 38 | | 0x10B | PullBridgedPacket | Ye | 2v | 6v | [4, 1] | [4, var, 4, 2, 2, 1] | 39 | | 0x10C | GetIopUtilizationRate | Ye | 0v | 2v | [] | [4, 4] | //X 40 | | 0x10D | GetIobPoolsUtilization | Ye | 0v | 3v | [] | [4, 4, 0x60] | //X 41 | | 0x301 | ? | Ye | 1v | 2v | [0x10] | [4, 4] | //X 42 | +----------+------------------------------------+----+-----------+-----------+---------------------------+-----------------------+ 43 | -------------------------------------------------------------------------------- /notes/arm_ios/dev_usr_cfg.txt: -------------------------------------------------------------------------------- 1 | == /dev/usr_cfg == 2 | User config, shortened to "UC" in coreinit. 3 | 4 | == Ioctls == 5 | +----------+------------------------------------+----+-----------+-----------+------ 6 | | Ioctl # | Name | v? | In size | Out size | Input Args 7 | +----------+------------------------------------+----+-----------+-----------+------ 8 | | 0x30 | ReadSysConfig | Ye | 0w | var w | 9 | | 0x31 | WriteSysConfig | Ye | 0w | var w | // wtf, no input vectors? 10 | | 0x32 | DeleteSysConfig | Ye | 1w | 1w | 11 | | 0x33 | ListSysConfig | Ye | 0w | 1w | 12 | | 0x34 | QuerySysConfig | Ye | 0w | 1w | 13 | +----------+------------------------------------+----+-----------+-----------+------ 14 | 15 | 16 | == The ioctlv >= 8 fix == 17 | To get around the vuln with kernel not copying ioctlv vectors if there are more 18 | than 8, the user_cfg process copied the vectors into internal memory and 19 | verifies it itself. 20 | 21 | The loop looks like this: 22 | 23 | -- 24 | For each vector: // Wat does this do? 25 | If vector.physaddr == 0 And vector.size != 0: 26 | If syscall_8A(vector.physaddr, vector.size) < 0: 27 | Fail; 28 | Else: 29 | Break; 30 | 31 | For each vector: 32 | If vector.physaddr == 0 And vector.size == 0: 33 | Continue; 34 | If syscall_ValidatePowerPCRange(vector.physaddr, vector.size) < 0: 35 | Fail; 36 | 37 | Pass; 38 | -- 39 | -------------------------------------------------------------------------------- /notes/arm_ios/devices.txt: -------------------------------------------------------------------------------- 1 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 2 | | Pid | Device path | FeatureId | open() Limits | Max Vecs | Notes 3 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 4 | | 1 | /dev/mcp | 0xD | ? | 8 | 5 | | 1 | /dev/mcp_recovery | - | ? | 8 | 6 | | 1 | /dev/volflush | 0xD | ? | 8 | 7 | | 1 | /dev/pm | - | coreid==0 | 8 | 8 | | 1 | /dev/syslog | - | ? | 8 | 9 | | 1 | /dev/usb_syslog | - | ? | 8 | 10 | | 1 | /dev/dk_syslog | - | ? | 8 | 11 | | 1 | /dev/ppc_app | - | ? | 8 | 12 | | 1 | /dev/ppc_kernel | - | ? | 8 | 13 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 14 | | 2 | /dev/bsp | 1 | None | 8 | open,close,ioctl 15 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 16 | | 3 | /dev/crypto | 2 | None | 8 | open,close,ioctlv 17 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 18 | | 4 | /dev/usbproc1 | ? | ? | | 19 | | 4 | /dev/usbproc2 | ? | ? | | 20 | | 4 | /dev/uhs | ? | ? | | 21 | | 6 | /dev/uhs/%d | ? | ? | | 22 | | 4 | /dev/usb_cdc | ? | ? | | 23 | | 4 | /dev/usb_hid | ? | ? | | 24 | | 4 | /dev/usb_uac | ? | ? | | 25 | | 4 | /dev/usb_midi | ? | ? | | 26 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 27 | | 5 | /dev/fsa | ? | ? | | 28 | | 5 | /dev/dk | ? | ? | | 29 | | 5 | /dev/odm | ? | ? | | 30 | | 5 | /dev/ramdisk_svc | ? | ? | | 31 | | 5 | /dev/ums | ? | ? | | 32 | | 5 | /dev/df | ? | ? | | 33 | | 5 | /dev/atfs | ? | ? | | 34 | | 5 | /dev/isfs | ? | ? | | 35 | | 5 | /dev/wfs | ? | ? | | 36 | | 5 | /dev/pcfs | ? | ? | | 37 | | 5 | /dev/rbfs | ? | ? | | 38 | | 5 | /dev/fat | ? | ? | | 39 | | 5 | /dev/fla | ? | ? | | 40 | | 5 | /dev/ahcimgr | ? | ? | | 41 | | 5 | /dev/shdd | ? | ? | | 42 | | 5 | /dev/md | ? | ? | | 43 | | 5 | /dev/scfm | ? | ? | | 44 | | 5 | /dev/mmc | ? | ? | | 45 | | 5 | /dev/timetrace | ? | ? | | 46 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 47 | | 6 | /dev/ccr_io | 0x3E8 | ? | 8 | 48 | | 6 | /dev/ccr_cdc | - | ? | 8 | 49 | | 6 | /dev/ccr_hid | - | ? | 8 | 50 | | 6 | /dev/ccr_nfc | - | ? | 8 | 51 | | 6 | /dev/ccr_uac | - | ? | 8 | 52 | | 6 | /dev/ccr_uvc | - | ? | 8 | 53 | | 6 | /dev/usb/btrm | - | ? | 8 | 54 | | 6 | /dev/usb/early_btrm | 0x3E8 | ? | 8 | 55 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 56 | | 7 | /dev/network | 0 | ? | 8 | 57 | | 7 | /dev/socket | ? | ? | 8 | 58 | | 7 | /dev/ifnet | 0x1A | ? | 8 | 59 | | 7 | /dev/net/ifnet | ? | ? | 8 | 60 | | 7 | /dev/net/ifnet/wd | ? | ? | 8 | 61 | | 7 | /dev/net/ifnet/ncl | ? | ? | 8 | 62 | | 7 | /dev/net/ifnet/usbeth | ? | ? | 8 | 63 | | 7 | /dev/ifuds | ? | ? | 8 | write and ioctl only for pid7 (IOS_NET) 64 | | 7 | /dev/udscntrl | ? | ? | 8 | 65 | | 7 | /dev/wl0 | 8 | ? | 8 | 66 | | 7 | /dev/wifidata | ? | ? | 8 | 67 | | 7 | /dev/wifi24 | - | ? | 8 | 68 | | 7 | /dev/ac_main | ? | ? | 8 | 69 | | 7 | /dev/ndm | ? | ? | 8 | 70 | | 7 | /dev/dlp | 0x19 | ? | 8 | 71 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 72 | | 8 | /dev/acpproc | - | ? | 8 | 73 | | 8 | /dev/acp_main | 0x12 | ? | 8 | 74 | | 8 | /dev/emd | - | ? | 8 | 75 | | 8 | /dev/pdm | 0x13 | ? | 8 | 76 | | 8 | /dev/nnsm | - | ? | 8 | 77 | | 8 | /dev/nnmisc | - | ? | 8 | 78 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 79 | | 9 | /dev/nsec | - | ? | 8 | 80 | | 9 | /dev/nsec/nss | 0x16 | ? | 8 | 81 | | 9 | /dev/nsec/nssl | 0x16 | ? | 8 | 82 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 83 | | 10 | /dev/auxilproc | - | ? | 8 | 84 | | 10 | /dev/im | - | ? | 8 | 85 | | 10 | /dev/usr_cfg | - | ? | 0x3D | 86 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 87 | | 11 | /dev/nim | 0xE | ? | 8 | 88 | | 11 | /dev/boss | 0x11 | ? | 8 | 89 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 90 | | 12 | /dev/fpd | 0x10 | ? | 8 | 91 | | 12 | /dev/act | 0xF | ? | 8 | 92 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 93 | | 13 | /dev/testproc1 | - | ? | 8 | 94 | | 13 | /dev/testproc2 | - | ? | 8 | 95 | | 13 | /dev/iopsh | - | ? | 8 | 96 | | 13 | /dev/cbl | - | ? | 8 | 97 | | 13 | /debug/prof | - | ? | 8 | 98 | | 13 | /test/ppcprotviol | - | ? | 8 | 99 | | 13 | /test/sp | - | ? | 8 | 100 | | 13 | /test/test_rm | - | ? | 8 | 101 | +-----+-----------------------+-----------+-------------------------------+-----------+--------- 102 | -------------------------------------------------------------------------------- /notes/arm_ios/mcp/firm_launch.txt: -------------------------------------------------------------------------------- 1 | MCP has an internal messagequeue where things like firmlaunches are posted. 2 | 3 | Notification types: 4 | - 0x102: POWER_OFF 5 | - 0x103: LAUNCH_OS/RESTART 6 | - 0x104: ? 7 | - 0x105: LAUNCH_C2W 8 | - 0x106: PANIC_HW_WATCHDOG 9 | - 0x107: RESTART_INTO_NORMAL_BG_MODE 10 | - 0x108: ? 11 | - 0x109: ? 12 | -------------------------------------------------------------------------------- /notes/arm_kernel/devices.txt: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | char path[32]; // @+0 3 | int msgqueue_id; // @+0x20 4 | unk* unk; // @+0x24 5 | int feature_id; // @+0x28 6 | u16 path_len; // @+0x2C 7 | u32 max_vecs; // @+0x3C, default: 8 8 | } device; 9 | -------------------------------------------------------------------------------- /notes/arm_kernel/events.txt: -------------------------------------------------------------------------------- 1 | 0xA - PpcProtViolationCheck 2 | -------------------------------------------------------------------------------- /notes/arm_kernel/heaps.txt: -------------------------------------------------------------------------------- 1 | Heap id 0xCAFF is an alias for the process cross heap. 2 | Heap id 0xCAFE is an alias for the process local heap. 3 | Heap id 1 is the shared-process-heap, a heap used for the cross-process heaps. 4 | 5 | typedef struct { 6 | void* base_addr; // @+0 7 | u32 owner_pid; // @+4 8 | u32 size; // @+8 9 | u32 freechunk_ptr; // @+0xC 10 | int ref_cnt; // @+0x10, 1+num_chunks 11 | u32 num_errors; // @+0x1C 12 | u32 flags; // @+0x20, bit0: is_local_heap, bit1: is_cross_heap 13 | u32 usage; // @+0x24, number of bytes currently allocated. 14 | u32 nerr_free; // @+0x34, number of times Free() has failed. 15 | // sizeof=0x40 16 | } heap_t; 17 | 18 | typedef struct { 19 | u32 magic; // 0xBABE0000=free, 0xBABE0001=alloc1, 0xBABE0002=alloc2 20 | u32 chunk_size; // in bytes, excluding this header. 21 | heap_chunk_t* prev; 22 | heap_chunk_t* next; 23 | } heap_chunk_t; 24 | 25 | 26 | The CreateHeap function writes the following header to heap base_addr+0: 27 | 0xBABE0000 28 | heap_size - sizeof(heap_chunk_t) 29 | 0 30 | 0 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /notes/arm_kernel/ipc.txt: -------------------------------------------------------------------------------- 1 | == PowerPC == 2 | The following are the allowed address-ranges for IPC requests, checked by IOS 3 | kernel: 4 | 0x28000000-0xD0000000, 5 | 0x14000000-0x1D000000, 6 | 0x00000000-0x02000000, 7 | 0x08000000-0x08120000 8 | 9 | The IOS kernel checks for integer overflow. 10 | 11 | 12 | When receiving a request, it first checks the ptr for the request-struct (size: 13 | 56 bytes). Then it invalidates the ptr and memcpy's into an internal buffer. 14 | 15 | The struct looks like this: 16 | 17 | typedef struct { 18 | u32 cmd; // 1=Open, 2=Close, 3=Read, 4=Write, 5=Seek, 6=Ioctl, 7=Ioctlv 19 | u32 err; // @+4 Set to error-code by ARM9. 20 | u32 fd; // @+8 21 | u32 unk; // @+0xC 22 | u32 node_id; // @+0x10 23 | u32 pid; // @+0x14, pid of the requester. Must be < 16 for PowerPC. 24 | u64 title_id; // @+0x18 25 | u32 unk; // @+0x20 26 | u32 args[5]; // @+0x24 27 | } ipc_request; 28 | 29 | Before passed to internal functions for Ioctl(v) handling, the powerpc_pid is 30 | converted to a IOS pid by adding 14 to it. 31 | 32 | The handlers for the various cmds use the pid to determine what type of addrs 33 | are allowed: 34 | If 0 < pid < 16 then it checks using ValidateArm9ProcessAddr, 35 | else it uses the ValidatePowerPcAddr 36 | 37 | For the Open-cmd, it does not check the OpenFlagsStr ptr before reading it, when 38 | comparing it to various fixed strings, probably "r", "rw", "r+", etc.. 39 | 40 | For the Ioctlv-cmd, if the sum (num_in+num_out) is <= 8, it will copy the vector 41 | to an internal buffer. Otherwise it will not, and use the PowerPc provided ptr. 42 | 43 | If any of the cmds return non-zero, indicating an error, the error code is 44 | written to @+4 of the struct. Then it calls the ipcFlush function to flush 45 | the result to the PowerPC: 46 | 47 | switch(struct.cmd) { 48 | case 3: 49 | FlushDCache(struct.arg0, struct.err); 50 | break; 51 | 52 | case 6: 53 | FlushDCache(struct.arg1, struct.arg2); 54 | FlushDCache(struct.arg3, struct.arg4); 55 | break; 56 | 57 | case 7: 58 | u32 num = struct.num_in + struct.num_out; 59 | 60 | for(u32 i=0; icur_pos; 27 | if(idx < 0) 28 | idx += queue->max_entries; 29 | 30 | kernel::arm::SetAccessControl(0); 31 | u32* ptr = &queue->data_ptr[idx]; 32 | *ptr = msg; 33 | kernel::sc::FlushDcache(ptr, 4); 34 | kernel::arm::SetAccessControl(cur_thread->pid); 35 | 36 | queue->num_entries++; 37 | 38 | JamMessage does the following: 39 | queue->cur_pos--; 40 | if(queue->cur_pos < 0) 41 | queue->cur_pos += queue->max_entries; 42 | 43 | kernel::arm::SetAccessControl(0); 44 | u32* ptr = &queue->data_ptr[queue->cur_pos]; 45 | *ptr = msg; 46 | kernel::sc::FlushDcache(ptr, 4); 47 | kernel::arm::SetAccessControl(cur_thread->pid); 48 | 49 | queue->num_entries++; 50 | 51 | ReceiveMessage does the following: 52 | if(out != NULL) 53 | *out = queue->data_ptr[queue->cur_pos]; 54 | 55 | queue->num_entries--; 56 | queue->cur_pos++; 57 | 58 | if(queue->cur_pos >= queue->max_entries) 59 | queue->cur_pos -= queue->max_entries; 60 | 61 | 62 | == Message Types == 63 | The message types over IPC that have been seen are: 64 | 65 | * IOS_OPEN - 1 66 | * IOS_CLOSE - 2 67 | * IOS_READ - 3 68 | * IOS_WRITE - 4 69 | * IOS_SEEK - 5 70 | * IOS_IOCTL - 6 71 | * IOS_IOCTLV - 7 72 | * IOS_REPLY - 8 73 | * IOS_IPC_MSG0 - 9 74 | * IOS_IPC_MSG1 - 10 75 | * IOS_IPC_MSG2 - 11 76 | * IOS_SUSPEND - 12 77 | * IOS_RESUME - 13 78 | * IOS_SVCMSG - 14 79 | 80 | * IOS_FASTRELAUNCH?? - 14? 81 | * IOS_PPCPROTVIOL - 0x100, 0x101: Used to verify DMA addresses for PowerPC? 82 | -------------------------------------------------------------------------------- /notes/arm_kernel/syscalls.txt: -------------------------------------------------------------------------------- 1 | Syscalls are not invoked using the SVC-instruction. Rather, it uses an undefined 2 | instruction: 0xE7F0XXF0 where XX is the syscall number. 3 | 4 | There are 0x93 syscalls on older firm, and 0x88 syscalls on latest firm. 5 | 6 | +--------+--------+-------+-------------------------------------+----------- 7 | | 5.3 | 5.5 | Args | Name | Notes 8 | +--------+--------+-------+-------------------------------------+----------- 9 | | 0 | 0 | 4+2 | CreateThread | 10 | | 1 | 1 | 2 | JoinThread? | 11 | | 2 | 2 | 2 | DestroyThread? | 12 | | 3 | 3 | 0 | GetCurrentThreadId | 13 | | 4 | 4 | 0 | | 14 | | 5 | 5 | 0 | GetCurrentProcessId | 15 | | 6 | 6 | 2 | GetProcessName | 16 | | 7 | 7 | 1 | StartThread | 17 | | 8 | 8 | 1 | StopThread? | 18 | | 9 | 9 | 1 | YieldThread | 19 | | A | A | 1 | GetThreadPriority | 20 | | B | B | 2 | SetThreadPriority | 21 | | C | C | 2 | CreateMessageQueue | 22 | | D | D | 1 | DestroyMessageQueue | 23 | | E | E | 3 | SendMessage | .. (int msgqueue_id, u32 msg, bool noblock) 24 | | F | F | 3 | JamMessage | .. (int msgqueue_id, u32 msg, bool noblock) 25 | | 10 | 10 | 3 | ReceiveMessage | .. (int msgqueue_id, u32* msg, bool noblock) 26 | | 11 | 11 | 3 | RegisterEventHandler | .. (int irq_id, int msgqueue_id, int flags?) 27 | | 12 | 12 | 1 | UnregisterEventHandler | .. (int irq_id) 28 | | 13 | 13 | 4 | CreateTimer | 29 | | 14 | 14 | 3 | RestartTimer | 30 | | 15 | 15 | 1 | StopTimer | 31 | | 16 | 16 | 1 | DestroyTimer | 32 | | 17 | 17 | 0 |