├── 3dsfuse ├── Makefile ├── crypto.c ├── crypto.h ├── fs.c ├── fs.h ├── fuse_glue.c ├── fuse_glue.h ├── helper.c ├── helper.h ├── main.c ├── types.h ├── utils.c ├── utils.h ├── wearlevel.c └── wearlevel.h ├── README ├── decrypt ├── Makefile ├── include │ ├── save.h │ └── utils.h └── src │ ├── decrypt.c │ └── utils.c └── spi_flash ├── .cproject ├── .project ├── Debug ├── input ├── makefile ├── objects.mk ├── sources.mk ├── spi_read.xe ├── spi_write.xe ├── src │ └── subdir.mk └── subdir.mk ├── XK-1.xn └── src ├── flash.c ├── flash.h ├── io.c ├── io.h ├── main.xc ├── master.h └── master.xc /3dsfuse/Makefile: -------------------------------------------------------------------------------- 1 | OBJS = fs.o crypto.o wearlevel.o helper.o fuse_glue.o utils.o main.o 2 | PACKAGES = openssl fuse 3 | CFLAGS = $(shell pkg-config --cflags $(PACKAGES)) -Wall -ggdb -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=25 -DDEBUG 4 | LDFLAGS = $(shell pkg-config --libs $(PACKAGES)) 5 | LIBS = -lcrypto 6 | OUTPUT = 3dsfuse 7 | main: $(OBJS) 8 | gcc $(CFLAGS) -o $(OUTPUT) $(LIBS) $(OBJS) $(LDFLAGS) 9 | clean: 10 | rm -f $(OUTPUT) $(OBJS) 11 | -------------------------------------------------------------------------------- /3dsfuse/crypto.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "types.h" 6 | #include "crypto.h" 7 | #include "helper.h" 8 | 9 | int find_key(u8 *buf, size_t len, u8 *out) { 10 | int i, j, count=0, found=0, rec_idx=0, rec_count=0; 11 | hash_entry **hash_list; 12 | u8 hash[16]; 13 | 14 | u8 ff_hash[16]="\xde\x03\xfe\x65\xa6\x76\x5c\xaa\x8c\x91\x34\x3a\xcc\x62\xcf\xfc"; 15 | 16 | hash_list = malloc(sizeof(hash_entry*) * ((len / 0x200)+1)); 17 | memset(hash_list, 0, sizeof(hash_entry*) * ((len / 0x200)+1)); 18 | 19 | for(i = 0; i < (len / 0x200); i++) { 20 | md5_buf(buf + (i*0x200), hash, 0x200); 21 | 22 | if(memcmp(hash, ff_hash, 16) == 0) 23 | continue; 24 | 25 | found = 0; 26 | 27 | for(j = 0; j < count; j++) { 28 | if (memcmp(hash_list[j]->hash, hash, 16) == 0) { 29 | hash_list[j]->count++; 30 | found = 1; 31 | break; 32 | } 33 | } 34 | 35 | // push new hashlist entry 36 | if(found == 0) { 37 | hash_list[count] = malloc(sizeof(hash_entry)); 38 | memcpy(hash_list[count]->hash, hash, 16); 39 | hash_list[count]->count = 1; 40 | hash_list[count]->block_idx = i; 41 | count++; 42 | } 43 | } 44 | 45 | for(i = 0; i < count; i++) { 46 | if (hash_list[i]->count > rec_count) { 47 | rec_count = hash_list[i]->count; 48 | rec_idx = i; 49 | } 50 | } 51 | 52 | if (rec_count == 0) 53 | return -1; 54 | 55 | #ifdef DEBUG 56 | printf("key hash: "); md5_print(hash_list[rec_idx]->hash); printf("\n"); 57 | #endif 58 | 59 | memcpy(out, buf + (hash_list[rec_idx]->block_idx * 0x200), 0x200); 60 | 61 | for(i = 0; i < count; i++)free(hash_list[i]); 62 | free(hash_list); 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /3dsfuse/crypto.h: -------------------------------------------------------------------------------- 1 | #ifndef __CRYPTO_H__ 2 | #define __CRYPTO_H__ 3 | 4 | #include "types.h" 5 | 6 | typedef struct { 7 | u8 hash[16]; 8 | u32 block_idx; 9 | u32 count; 10 | } hash_entry; 11 | 12 | int find_key(u8 *buf, size_t len, u8 *out); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /3dsfuse/fs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "types.h" 6 | #include "utils.h" 7 | #include "helper.h" 8 | #include "fs.h" 9 | 10 | #define FST_BLOCK_OFFSET 0x6c 11 | #define FILESTORE_OFFSET 0x58 12 | 13 | typedef struct { 14 | u8 *sav; 15 | int sav_updated; 16 | int savetype; 17 | 18 | u32 total_partentries; 19 | u32 savepart_offset; 20 | u32 datapart_offset; 21 | 22 | u32 activepart_table; 23 | u32 save_activepart; 24 | u32 data_activepart; 25 | 26 | u32 datapart_filebase; 27 | u32 activepart_tableoffset; 28 | u32 part_tablesize; 29 | 30 | u32 primary_tableoffset; 31 | u32 secondary_tableoffset; 32 | 33 | u8 activepart_tablehash[0x20]; 34 | } fs_savectx; 35 | 36 | static fs_savectx savectx; 37 | 38 | int fs_initsave_disa(); 39 | int fs_checkheaderhashes(int update); 40 | 41 | int fs_initsave(u8 *nsav) { 42 | u32 magic; 43 | 44 | savectx.sav = nsav; 45 | savectx.sav_updated = 0; 46 | savectx.savetype = 0; 47 | 48 | magic = getle32(&nsav[0x100]); 49 | 50 | if(magic == 0x41534944) { 51 | savectx.savetype = 0; 52 | if(fs_initsave_disa())return 1; 53 | } 54 | else if(magic == 0x46464944) { 55 | savectx.savetype = 1; 56 | printf("extdata DIFF is not yet supported.\n"); 57 | return 1; 58 | } 59 | else { 60 | printf("unknown magic %x @ 0x100.\n", magic); 61 | return 1; 62 | } 63 | 64 | return fs_checkheaderhashes(0); 65 | } 66 | 67 | int fs_initsave_disa() { 68 | disa_header *disa = (disa_header*)&savectx.sav[0x100]; 69 | 70 | savectx.total_partentries = (u32)getle64(disa->total_partentries); 71 | savectx.savepart_offset = (u32)getle64(disa->save_partoffset); 72 | savectx.datapart_offset = (u32)getle64(disa->data_partoffset); 73 | 74 | savectx.activepart_table = 0; 75 | if(getle32(disa->activepart_table) & 0xff)savectx.activepart_table = 1; 76 | savectx.save_activepart = savectx.activepart_table; 77 | savectx.data_activepart = savectx.activepart_table; 78 | 79 | savectx.primary_tableoffset = (u32)getle64(disa->primarytable_offset); 80 | savectx.secondary_tableoffset = (u32)getle64(disa->secondarytable_offset); 81 | 82 | if(savectx.activepart_table)savectx.activepart_tableoffset = savectx.secondary_tableoffset; 83 | if(!savectx.activepart_table)savectx.activepart_tableoffset = savectx.primary_tableoffset; 84 | 85 | savectx.datapart_filebase = 0; 86 | if(savectx.datapart_offset)savectx.datapart_filebase = getle32(disa->activepart_table) & ~0xFF; 87 | 88 | savectx.part_tablesize = (u32)getle64(disa->part_tablesize); 89 | memcpy(savectx.activepart_tablehash, disa->activepart_tablehash, 0x20); 90 | 91 | return 0; 92 | } 93 | 94 | void fs_setupdateflags() 95 | { 96 | savectx.sav_updated = 1; 97 | } 98 | 99 | void fs_updateheaderhash() 100 | { 101 | disa_header *disa = (disa_header*)&savectx.sav[0x100]; 102 | 103 | if(savectx.savetype == 0) { 104 | memcpy(disa->activepart_tablehash, savectx.activepart_tablehash, 0x20); 105 | } 106 | } 107 | 108 | int fs_calcivfchash(partition_table *table, int datapart, int *update) 109 | { 110 | int partno = 1; 111 | u8 *part, *part2; 112 | u8 *lvl1_buf; 113 | u32 blksz; 114 | u8 calchash[0x20]; 115 | u8 calchash2[0x20]; 116 | 117 | if(savectx.activepart_table)partno = 0; 118 | 119 | memset(calchash, 0, 0x20); 120 | memset(calchash2, 0, 0x20); 121 | 122 | part = fs_part(savectx.sav, 0, datapart, 1-partno);//-1 123 | part2 = fs_part(savectx.sav, 0, datapart, partno); 124 | if(part == NULL || part2 == NULL) { 125 | printf("invalid partition.\n"); 126 | return 1; 127 | } 128 | 129 | blksz = 1 << getle32(table->ivfc.levels[0].blksize); 130 | lvl1_buf = (u8*)malloc(blksz); 131 | if(lvl1_buf==NULL) { 132 | printf("failed to allocate level1 buffer.\n"); 133 | return 3; 134 | } 135 | 136 | memset(lvl1_buf, 0, blksz); 137 | memcpy(lvl1_buf, part, (u32)getle64(table->ivfc.levels[0].size)); 138 | sha256(lvl1_buf, blksz, calchash); 139 | 140 | memset(lvl1_buf, 0, blksz); 141 | memcpy(lvl1_buf, part2, (u32)getle64(table->ivfc.levels[0].size)); 142 | sha256(lvl1_buf, blksz, calchash2); 143 | free(lvl1_buf); 144 | 145 | if(*update == 0) { 146 | if(memcmp(table->ivfcpart_masterhash, calchash, 0x20)!=0) { 147 | if(memcmp(table->ivfcpart_masterhash, calchash2, 0x20)==0) { 148 | if(datapart==0)savectx.save_activepart = (u32)partno; 149 | if(datapart)savectx.data_activepart = (u32)partno; 150 | 151 | printf("using part %d instead, for datapart %d.\n", partno, datapart); 152 | return 0; 153 | } 154 | 155 | printf("master hash over the IVFC partition is invalid, datapart %d.\n", datapart); 156 | return 2; 157 | } 158 | } 159 | else { 160 | if(memcmp(table->ivfcpart_masterhash, calchash, 0x20)!=0) { 161 | memcpy(table->ivfcpart_masterhash, calchash, 0x20); 162 | } 163 | else { 164 | *update = 0; 165 | } 166 | } 167 | 168 | return 0; 169 | } 170 | 171 | int fs_checkheaderhashes(int update) 172 | { 173 | partition_table *savetable = NULL, *datatable = NULL; 174 | int saveupdated = 0, dataupdated = 0; 175 | int updated; 176 | int ret; 177 | u8 calchash[0x20]; 178 | 179 | savetable = (partition_table*)&savectx.sav[savectx.activepart_tableoffset]; 180 | if(savectx.datapart_offset)datatable = (partition_table*)&savectx.sav[savectx.activepart_tableoffset + 0x130]; 181 | 182 | saveupdated = update; 183 | dataupdated = update; 184 | 185 | ret = fs_calcivfchash(savetable, 0, &saveupdated); 186 | if(ret)return ret; 187 | 188 | if(datatable) { 189 | ret = fs_calcivfchash(datatable, 1, &dataupdated); 190 | if(ret)return ret; 191 | } 192 | 193 | updated = saveupdated | dataupdated; 194 | 195 | sha256(&savectx.sav[savectx.activepart_tableoffset], savectx.part_tablesize, calchash); 196 | if(memcmp(savectx.activepart_tablehash, calchash, 0x20)!=0) { 197 | if(updated) { 198 | memcpy(savectx.activepart_tablehash, calchash, 0x20); 199 | fs_updateheaderhash(); 200 | } 201 | else { 202 | printf("active table hash from header is invalid.\n"); 203 | return 2; 204 | } 205 | } 206 | 207 | if(updated) { 208 | savectx.sav_updated = 1; 209 | } 210 | 211 | return 0; 212 | } 213 | 214 | partition_table *fs_part_get_info(u8 *buf, u32 part_no) { 215 | return (partition_table*)(buf + savectx.activepart_tableoffset + (part_no * 0x130)); 216 | } 217 | 218 | u8 *fs_part(u8 *buf, int fs, int datapart, int part_tableno) { 219 | u32 pos = 0; 220 | u32 fs_off = 0, fs_sz = 0; 221 | u8 *p = buf + savectx.primary_tableoffset; 222 | partition_table *part = (partition_table*)p; 223 | int num = 0; 224 | 225 | if(!datapart)pos = savectx.savepart_offset; 226 | if(datapart)pos = savectx.datapart_offset; 227 | pos += (u32)getle64(part->dpfs.ivfcpart.offset); 228 | 229 | if(part_tableno==-1) 230 | { 231 | if(datapart==0)part_tableno = (int)savectx.save_activepart; 232 | if(datapart)part_tableno = (int)savectx.data_activepart; 233 | } 234 | 235 | for(num=0; num<2; num++) { 236 | if(num==1)p = buf + savectx.secondary_tableoffset; 237 | if(datapart)p += 0x130; 238 | 239 | part = (partition_table*)p; 240 | if(getle32(part->difi.magic) != 0x49464944) { 241 | printf("invalid DIFI magic\n"); 242 | return NULL; 243 | } 244 | 245 | fs_off = (u32)getle64(part->ivfc.levels[3].offset); 246 | fs_sz = (u32)getle64(part->ivfc.levels[3].size); 247 | 248 | if(num == part_tableno) { 249 | printf("datapart %x pos %x ivfcpartsize %x fs off %x fs sz %x\n", datapart, pos, (u32)getle64(part->dpfs.ivfcpart.size), fs_off, fs_sz); 250 | if(!fs)return buf + pos; 251 | return buf + pos + fs_off; 252 | } 253 | 254 | pos += (u32)getle64(part->dpfs.ivfcpart.size); 255 | } 256 | 257 | return NULL; 258 | } 259 | 260 | int fs_dpfs_getpartno(int datapart, u32 part_offset) 261 | { 262 | u32 part_base = 0; 263 | u32 maskstart, blksz, blockpos; 264 | u32 tableno; 265 | u32 mask = 0; 266 | u32 total_entries; 267 | u8 *part; 268 | partition_table *part_table = (partition_table*)&savectx.sav[savectx.activepart_tableoffset]; 269 | 270 | tableno = savectx.activepart_table; 271 | part_base = savectx.savepart_offset; 272 | if(datapart)part_base = savectx.datapart_offset; 273 | 274 | maskstart = (u32)getle64(part_table->dpfs.tables[tableno].offset) + (u32)getle64(part_table->dpfs.tables[tableno].size); 275 | blksz = getle32(part_table->dpfs.ivfcpart.blksize); 276 | total_entries = (u32)getle64(part_table->dpfs.ivfcpart.size) >> blksz; 277 | 278 | part = savectx.sav + part_base + maskstart; 279 | blockpos = (total_entries) - (part_offset >> blksz); 280 | 281 | printf("fs_dpfs_getpartflag: part_offset %x blockpos %x maskstart %x total_entries %x\n", part_offset, blockpos, maskstart, total_entries); 282 | 283 | if(total_entries > 0x1e) { 284 | return 0; 285 | //return -1; 286 | } 287 | 288 | if(tableno==0)part+= 4; 289 | 290 | return (part[blockpos / 8] << ((7-blockpos) % 8)) & 1; 291 | 292 | //mask = getle32(part); 293 | 294 | //if(mask & (1 << ( 30 - blockpos)))return 1; 295 | //return 0; 296 | } 297 | 298 | u8 *fs_get_savepart(u8 *buf) 299 | { 300 | return fs_part(buf, 1, 0, fs_dpfs_getpartno(0, 0)); 301 | } 302 | 303 | u8 *fs_getfilebase() 304 | { 305 | u8 *part; 306 | 307 | if(savectx.datapart_offset==0) { 308 | part = fs_part(savectx.sav, 1, 0, -1); 309 | return part + fs_get_offset(part); 310 | } 311 | 312 | return fs_part(savectx.sav, 1, 1, -1); 313 | } 314 | 315 | u32 fs_get_offset(u8 *buf) { 316 | return getle32(buf + FILESTORE_OFFSET); 317 | } 318 | 319 | u32 fs_getsave_mediasize(u8 *part) 320 | { 321 | return getle32(part + 0x24); 322 | } 323 | 324 | u32 fs_get_start(u8 *part) { 325 | u32 fst_offset, filestore_offset; 326 | 327 | fst_offset = getle32(part + 0x78); 328 | filestore_offset = getle32(part + 0x58); 329 | 330 | if(savectx.datapart_offset==0)return (fst_offset * fs_getsave_mediasize(part)) + filestore_offset; 331 | return fst_offset; 332 | } 333 | 334 | int fs_num_entries(u8 *part) { 335 | return getle32(part + fs_get_start(part)); 336 | } 337 | 338 | fst_entry *fs_get_by_name(u8 *part, const char *name) { 339 | fst_entry *e; 340 | char name_buf[0x11]; 341 | int i; 342 | 343 | if (part == NULL) { 344 | return NULL; 345 | } 346 | 347 | memset(name_buf, 0, 0x11); 348 | 349 | e = (fst_entry*)(part + fs_get_start(part) + sizeof(fst_entry)); 350 | 351 | for(i = 0; i < fs_num_entries(part)-1; i++) { 352 | if(e->name[0]==0x09) { 353 | e++; 354 | continue; 355 | } 356 | 357 | memcpy(name_buf, e->name, 0x10); 358 | if (strcmp(name_buf, name) == 0) 359 | return e; 360 | 361 | e++; 362 | } 363 | 364 | return NULL; 365 | } 366 | 367 | int fs_verifyhashtree(u8 *part, u8 *part2, int datapart, u8 *hashtree, ivfc_header *ivfc, u32 offset, u32 size, u8 *databuf, u32 level, int update) 368 | { 369 | int i; 370 | int updated = 0; 371 | int ret = 0; 372 | int partno; 373 | 374 | u32 blksz, levelsize, chunksize; 375 | u32 aligned_imgpos = 0, hashpos = 0, datapos = 0; 376 | u32 databuf_chunksize = 0; 377 | u32 curhashpos = 0, totalhashsize = 0x20; 378 | u32 hashtable_pos; 379 | 380 | u8 *hashtable; 381 | u8 *hashdata; 382 | u8 *cur_part; 383 | u8 calchash[0x20]; 384 | 385 | hashtable_pos = (u32)getle64(ivfc->levels[level-1].offset); 386 | hashtable = hashtree + hashtable_pos; 387 | 388 | blksz = 1 << getle32(ivfc->levels[level].blksize); 389 | levelsize = (u32)getle64(ivfc->levels[level].size); 390 | aligned_imgpos = offset; 391 | chunksize = blksz; 392 | 393 | aligned_imgpos >>= getle32(ivfc->levels[level].blksize); 394 | hashpos = aligned_imgpos * 0x20; 395 | aligned_imgpos <<= getle32(ivfc->levels[level].blksize); 396 | 397 | curhashpos = hashpos; 398 | 399 | hashdata = (u8*)malloc(blksz); 400 | if(hashdata == NULL) { 401 | printf("failed to alloc hashdata buffer with block size %x.\n", blksz); 402 | return 1; 403 | } 404 | 405 | databuf_chunksize = blksz - (offset & (blksz-1)); 406 | 407 | while(datapos < size) { 408 | if(chunksize > levelsize - aligned_imgpos)chunksize = levelsize - aligned_imgpos; 409 | if(databuf_chunksize > size - datapos)databuf_chunksize = size - datapos; 410 | 411 | partno = 0; 412 | if(datapart!=-1) { 413 | partno = fs_dpfs_getpartno(datapart, aligned_imgpos); 414 | } 415 | if(partno==-1)return 2; 416 | 417 | memset(hashdata, 0, blksz); 418 | 419 | cur_part = part; 420 | if(part2 && partno==1) { 421 | cur_part = part2; 422 | } 423 | 424 | if(update && databuf)memcpy(&cur_part[offset], &databuf[datapos], databuf_chunksize); 425 | 426 | memcpy(hashdata, &cur_part[aligned_imgpos], chunksize); 427 | sha256(hashdata, blksz, calchash); 428 | 429 | if(memcmp(hashtable + curhashpos, calchash, 0x20)!=0) { 430 | if(update) { 431 | updated = 1; 432 | memcpy(hashtable + curhashpos, calchash, 0x20); 433 | } 434 | else { 435 | printf("hash entry in lvl%u for lvl%u is invalid for part%d, aligned_imgpos %x hashpos %x blksz %x\n", level, level+1, partno, aligned_imgpos, curhashpos, blksz); 436 | printf("calc hash: \n"); 437 | for(i=0; i<0x20; i++)printf("%02x", calchash[i]); 438 | printf("\nhash-table hash: \n"); 439 | for(i=0; i<0x20; i++)printf("%02x", hashtable[curhashpos + i]); 440 | printf("\n"); 441 | printf("part off %x\n", (u32)part - (u32)savectx.sav); 442 | if(part2)printf("part2 off %x\n", (u32)part2 - (u32)savectx.sav); 443 | //free(hashdata); 444 | //return 3; 445 | } 446 | } 447 | else { 448 | //printf("hash entry in lvl%u for lvl%u is valid for part%d, aligned_imgpos %x hashpos %x blksz %x\n", level, level+1, partno, aligned_imgpos, curhashpos, blksz); 449 | 450 | if(!update && databuf)memcpy(&databuf[datapos], &cur_part[offset], databuf_chunksize); 451 | } 452 | 453 | datapos += databuf_chunksize; 454 | offset += databuf_chunksize; 455 | databuf_chunksize = blksz; 456 | aligned_imgpos += blksz; 457 | curhashpos += 0x20; 458 | totalhashsize += 0x20; 459 | } 460 | 461 | free(hashdata); 462 | 463 | if(level>=2) { 464 | ret = fs_verifyhashtree(hashtable, NULL, -1, hashtree, ivfc, hashtable_pos + hashpos, totalhashsize, NULL, level-1, updated); 465 | if(ret)return ret; 466 | } 467 | 468 | ret = 0; 469 | if(level==1)ret = fs_checkheaderhashes(updated); 470 | 471 | return ret; 472 | } 473 | 474 | int fs_verifyupdatehashtree_fsdata(u32 offset, u32 size, u8 *databuf, int filedata, int update) 475 | { 476 | int ret, datapart; 477 | u8 *hashtree; 478 | u8 *part, *part2; 479 | partition_table *table; 480 | 481 | if(savectx.datapart_offset==0 || !filedata) { 482 | datapart = 0; 483 | part = fs_part(savectx.sav, 1, 0, 0); 484 | part2 = fs_part(savectx.sav, 1, 0, 1); 485 | offset += fs_get_offset(fs_get_savepart(savectx.sav)); 486 | 487 | hashtree = fs_part(savectx.sav, 0, 0, -1); 488 | table = fs_part_get_info(savectx.sav, 0); 489 | } 490 | else { 491 | datapart = 1; 492 | part = fs_part(savectx.sav, 1, 1, 0); 493 | part2 = fs_part(savectx.sav, 1, 1, 1); 494 | table = fs_part_get_info(savectx.sav, 1); 495 | 496 | hashtree = fs_part(savectx.sav, 0, 1, -1); 497 | } 498 | 499 | ret = fs_verifyhashtree(part, part2, datapart, hashtree, &table->ivfc, offset, size, databuf, 3, update); 500 | 501 | return ret; 502 | } 503 | 504 | -------------------------------------------------------------------------------- /3dsfuse/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef __FS_H__ 2 | #define __FS_H__ 3 | 4 | #include "types.h" 5 | 6 | typedef struct { 7 | u8 magic[4];//"DISA" 8 | u8 magicnum[4];//0x40000 9 | u8 total_partentries[8]; 10 | u8 secondarytable_offset[8]; 11 | u8 primarytable_offset[8]; 12 | u8 part_tablesize[8]; 13 | 14 | u8 save_partentoffset[8]; 15 | u8 save_partentsize[8]; 16 | u8 data_partentoffset[8]; 17 | u8 data_partentsize[8]; 18 | 19 | u8 save_partoffset[8]; 20 | u8 save_partsize[8]; 21 | u8 data_partoffset[8]; 22 | u8 data_partsize[8]; 23 | 24 | u8 activepart_table[4]; 25 | u8 activepart_tablehash[0x20]; 26 | u8 reserved[0x74]; 27 | } disa_header; 28 | 29 | typedef struct { 30 | u8 magic[4];//"DIFI" 31 | u8 magicnum[4];//0x10000 32 | 33 | u8 ivfc_offset[8];//Relative to this DIFI 34 | u8 ivfc_size[8]; 35 | u8 dpfs_offset[8]; 36 | u8 dpfs_size[8]; 37 | u8 parthash_offset[8]; 38 | u8 parthash_size[8]; 39 | u8 flags[4];//When the low 8-bits are non-zero, this is a DATA partition 40 | u8 filebase_offset[8];//DATA partition only 41 | } difi_header; 42 | 43 | typedef struct { 44 | u8 offset[8]; 45 | u8 size[8]; 46 | u8 blksize[4]; 47 | u8 reserved[4]; 48 | } ivfclevel_header; 49 | 50 | typedef struct { 51 | u8 magic[4];//"IVFC" 52 | u8 magicnum[4];//0x20000 53 | u8 masterhash_size[8];//Size of the hash which hashes lvl1 54 | 55 | ivfclevel_header levels[4]; 56 | u8 unknown[8]; 57 | } ivfc_header; 58 | 59 | typedef struct { 60 | u8 magic[4];//"DPFS" 61 | u8 magicnum[4];//0x10000 62 | 63 | ivfclevel_header tables[2]; 64 | ivfclevel_header ivfcpart; 65 | } dpfs_header; 66 | 67 | typedef struct { 68 | difi_header difi; 69 | ivfc_header ivfc; 70 | dpfs_header dpfs; 71 | u8 ivfcpart_masterhash[0x20]; 72 | } partition_table; 73 | 74 | typedef struct { 75 | u8 parent_dirid[4]; 76 | u8 name[0x10]; 77 | u8 id[4]; 78 | u8 unk1[4]; 79 | u8 block_no[4]; 80 | u8 size[8]; 81 | u8 unk4[4]; 82 | u8 unk5[4]; 83 | } fst_entry; 84 | 85 | int fs_initsave(u8 *nsav); 86 | void fs_setupdateflags(); 87 | u32 fs_get_start(u8 *buf); 88 | u32 fs_get_offset(u8 *buf); 89 | 90 | partition_table *fs_part_get_info(u8 *buf, u32 part_no); 91 | u8 *fs_part(u8 *buf, int fs, int datapart, int part_tableno); 92 | u8 *fs_get_savepart(u8 *buf); 93 | u8 *fs_getfilebase(); 94 | int fs_verifyupdatehashtree_fsdata(u32 offset, u32 size, u8 *databuf, int filedata, int update); 95 | 96 | fst_entry *fs_get_by_name(u8 *part, const char *name); 97 | int fs_num_entries(u8 *buf); 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /3dsfuse/fuse_glue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "fuse_glue.h" 9 | #include "fs.h" 10 | #include "wearlevel.h" 11 | #include "types.h" 12 | #include "helper.h" 13 | 14 | /** icky globals **/ 15 | static u8 *sav_buf; 16 | static u8 *out_buf; 17 | static u8 *xorpad_buf; 18 | static u32 sav_size=0; 19 | static u32 xorpad_size=0; 20 | 21 | static struct fuse_operations sav_operations = { 22 | .getattr = sav_getattr, 23 | .readdir = sav_readdir, 24 | .open = sav_open, 25 | .read = sav_read, 26 | .write = sav_write 27 | }; 28 | 29 | u8 *path_to_part(const char *path) { 30 | if (strncmp(path, "/part_00/", 9) != 0) { 31 | return NULL; 32 | } 33 | 34 | return fs_get_savepart(sav_buf); 35 | } 36 | 37 | int fuse_sav_init(u8 *buf, u32 size, u8 *xorpad, u32 xorpad_sz, int argc, char *argv[]) { 38 | // lets keep this locally for the FUSE driver, these 39 | // images arent very huge anyway 40 | sav_buf = malloc(size); 41 | out_buf = malloc(size+0x2000); 42 | 43 | sav_size = size; 44 | xorpad_size = xorpad_sz; 45 | memcpy(sav_buf, buf, size); 46 | 47 | xorpad_buf = malloc(xorpad_size); 48 | memcpy(xorpad_buf, xorpad, xorpad_size); 49 | 50 | memset(out_buf, 0xff, size+0x2000); 51 | xor(sav_buf, sav_size, out_buf+0x2000, xorpad_buf, xorpad_size); 52 | 53 | if(fs_initsave(sav_buf)) 54 | return 1; 55 | 56 | return fuse_main(argc, argv, &sav_operations); 57 | } 58 | 59 | int sav_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { 60 | char name_buf[0x11]; 61 | fst_entry *entries; 62 | int j; 63 | u8 *part; 64 | 65 | // root? 66 | if (strcmp(path, "/") == 0) { 67 | filler(buf, ".", NULL, 0); 68 | filler(buf, "..", NULL, 0); 69 | 70 | part = fs_get_savepart(sav_buf); 71 | if(part) { 72 | if (strncmp((char*)part, "SAVE", 4) != 0) { 73 | printf("SAVE partition is invalid.\n"); 74 | hexdump(part, 0x100); 75 | } 76 | else { 77 | filler(buf, "part_00", NULL, 0); 78 | } 79 | } 80 | 81 | filler(buf, "clean.sav", NULL, 0); 82 | filler(buf, "output.sav", NULL, 0); 83 | filler(buf, "xorpad.bin", NULL, 0); 84 | 85 | return 0; 86 | } 87 | 88 | if (strncmp(path, "/part_00", 8) == 0) { 89 | part = fs_get_savepart(sav_buf); 90 | if(part == NULL)return -ENOENT; 91 | 92 | filler(buf, ".", NULL, 0); 93 | filler(buf, "..", NULL, 0); 94 | 95 | if (strncmp((char*)part, "SAVE", 4) != 0) { 96 | printf("SAVE partition is invalid.\n"); 97 | return 0; 98 | } 99 | 100 | printf("@@ part valid\n"); 101 | 102 | // skip over root entry 103 | entries = (fst_entry*)(part + fs_get_start(part) + sizeof(fst_entry)); 104 | 105 | for(j = 0; j < fs_num_entries(part)-1; j++) { 106 | if(entries->name[0]==0x09) { 107 | entries++; 108 | continue; 109 | } 110 | 111 | memset(name_buf, 0, 0x11); 112 | memcpy(name_buf, entries->name, 0x10); 113 | printf("@@ name: '%s'\n", name_buf); 114 | filler(buf, name_buf, NULL, 0); 115 | entries++; 116 | } 117 | } 118 | 119 | return 0; 120 | } 121 | 122 | int sav_getattr(const char *path, struct stat *stbuf) { 123 | fst_entry *e; 124 | u8 *part; 125 | 126 | memset(stbuf, 0, sizeof(struct stat)); 127 | 128 | stbuf->st_mode = S_IFREG | 0444; 129 | stbuf->st_nlink = 1; 130 | 131 | if (strcmp(path, "/") == 0) { 132 | stbuf->st_mode = S_IFDIR | 0445; 133 | stbuf->st_nlink = 2 + 1; // always 2 since we dont do subdirs yet 134 | } else if (strncmp(path, "/part_", 6) == 0 && strlen(path) == 8) { 135 | if(fs_get_savepart(sav_buf) == NULL)return -ENOENT; 136 | stbuf->st_mode = S_IFDIR | 0445; 137 | stbuf->st_nlink = 2; 138 | } else if (strcmp(path, "/clean.sav") == 0) { 139 | stbuf->st_size = sav_size; 140 | } else if (strcmp(path, "/xorpad.bin") == 0) { 141 | stbuf->st_size = xorpad_size; 142 | } else if (strcmp(path, "/output.sav") == 0) { 143 | stbuf->st_size = sav_size+0x2000; 144 | } else { 145 | part = fs_get_savepart(sav_buf); 146 | if(part == NULL)return -ENOENT; 147 | 148 | if (strncmp((char*)part, "SAVE", 4) != 0) { 149 | printf("SAVE partition is invalid.\n"); 150 | return -ENOENT; 151 | } 152 | 153 | e = fs_get_by_name(path_to_part(path), path + 9); 154 | 155 | if (e == NULL) { 156 | return -ENOENT; 157 | } 158 | 159 | stbuf->st_size = (size_t)getle64(e->size); 160 | } 161 | 162 | printf("@@ stat done\n"); 163 | 164 | return 0; 165 | } 166 | 167 | int sav_open(const char *path, struct fuse_file_info *fi) { 168 | fst_entry *e; 169 | u8 *part = NULL; 170 | 171 | if (strcmp(path, "/clean.sav") == 0) { 172 | return 0; 173 | } 174 | 175 | if (strcmp(path, "/xorpad.bin") == 0) { 176 | return 0; 177 | } 178 | 179 | if (strcmp(path, "/output.sav") == 0) { 180 | if((fi->flags & 3) != O_RDONLY) 181 | return -EACCES; 182 | 183 | return 0; 184 | } 185 | 186 | part = path_to_part(path); 187 | 188 | if (part == NULL) 189 | return -ENOENT; 190 | 191 | if (strncmp((char*)part, "SAVE", 4) != 0) { 192 | printf("SAVE partition is invalid.\n"); 193 | hexdump(part, 0x100); 194 | return -ENOENT; 195 | } 196 | 197 | e = fs_get_by_name(part, path + 9); 198 | 199 | if (e == NULL) 200 | return -ENOENT; 201 | 202 | return 0; 203 | } 204 | 205 | int sav_read(const char *path, char *buf, size_t size, off_t offset, 206 | struct fuse_file_info *fi) { 207 | 208 | fst_entry *e; 209 | u8 *part; 210 | u16 crc=0; 211 | u32 saveoff, buf_offs=0, block_offs=0, jour_offs=0, nblocks=0, journalentrycount=0; 212 | int i, j; 213 | 214 | if (strcmp(path, "/clean.sav") == 0) { 215 | memcpy(buf, sav_buf + offset, size); 216 | return size; 217 | } 218 | 219 | if (strcmp(path, "/xorpad.bin") == 0) { 220 | memcpy(buf, xorpad_buf + offset, size); 221 | return size; 222 | } 223 | 224 | if (strcmp(path, "/output.sav") == 0) { 225 | // build flat blockmap and empty journal 226 | nblocks = ((sav_size+0x2000) >> 12); 227 | 228 | memset(out_buf, 0x00, 0x08); // clear first 8 bytes, unknown 229 | for(i=8; i<0x1000; i++)out_buf[i] = 0xff; 230 | xor(sav_buf, sav_size, out_buf+0x2000, xorpad_buf, xorpad_size); 231 | 232 | for(i=0; i> 8) ^ (crc & 0xff); 241 | } 242 | } 243 | 244 | buf_offs = 8 + (nblocks-1)*10; 245 | 246 | crc = crc16(out_buf, buf_offs, 0xFFFF); 247 | printf("+++ BLOCKMAP CRC %04x\n", crc); 248 | out_buf[buf_offs+0] = crc & 0xff; 249 | out_buf[buf_offs+1] = (crc >> 8); 250 | 251 | /*journalentrycount = (0x1000 - ((nblocks-2) * sizeof(blockmap_entry))) / 32; 252 | i = 0; 253 | for(jour_offs = nblocks * 10; jour_offs < 0x1000; jour_offs += 0x20, i++, journalentrycount--) { 254 | journal_entry* journal = (journal_entry*)(out_buf + jour_offs); 255 | 256 | journal->data.virt_no = i; 257 | journal->data.virt_prev_no = i; 258 | journal->data.phys_no = i+2; 259 | journal->data.phys_prev_no = i+2; 260 | journal->data.phys_realloc_cnt = 1; 261 | journal->data.virt_realloc_cnt = 1; 262 | 263 | buf_offs = 8 + (i*10); 264 | for(j=0; j<8; j++)journal->data.checksum[j] = out_buf[buf_offs+2+j]; 265 | 266 | memcpy(&journal->dupe_data, &journal->data, sizeof(journal_data)); 267 | }*/ 268 | 269 | // mirror blockmap+journal 270 | memcpy(out_buf+0x1000, out_buf, 0x1000); 271 | 272 | memcpy(buf, out_buf + offset, size); 273 | 274 | return size; 275 | } 276 | 277 | part = path_to_part(path); 278 | if (part == NULL) 279 | return -ENOENT; 280 | 281 | e = fs_get_by_name(part, path + 9); 282 | 283 | if (e == NULL) 284 | return -ENOENT; 285 | 286 | if (offset >= (size_t)getle64(e->size)) 287 | return 0; 288 | 289 | if (offset+size > (size_t)getle64(e->size)) 290 | size = (size_t)getle64(e->size) - offset; 291 | 292 | saveoff = (getle32(e->block_no) * fs_getsave_mediasize(part)) + offset; 293 | 294 | if(fs_verifyupdatehashtree_fsdata(saveoff, size, (u8*)buf, 1, 0))return -EIO; 295 | 296 | return size; 297 | } 298 | 299 | int sav_write(const char *path, const char *buf, size_t size, off_t offset, 300 | struct fuse_file_info *fi) { 301 | 302 | fst_entry *e; 303 | u8 *part; 304 | u32 saveoff; 305 | 306 | if (strcmp(path, "/clean.sav") == 0) { 307 | memcpy(sav_buf + offset, buf, size); 308 | fs_setupdateflags(); 309 | return size; 310 | } 311 | 312 | if (strcmp(path, "/xorpad.bin") == 0) { 313 | memcpy(xorpad_buf + offset, buf, size); 314 | return size; 315 | } 316 | 317 | 318 | if (strcmp(path, "/output.sav") == 0) { 319 | return -EINVAL; 320 | } 321 | 322 | part = path_to_part(path); 323 | if (part == NULL) 324 | return -ENOENT; 325 | 326 | e = fs_get_by_name(part, path + 9); 327 | 328 | if (e == NULL) 329 | return -ENOENT; 330 | 331 | if (offset >= (size_t)getle64(e->size)) 332 | return 0; 333 | 334 | if (offset+size > (size_t)getle64(e->size)) 335 | size = (size_t)getle64(e->size) - offset; 336 | 337 | saveoff = (getle32(e->block_no) * fs_getsave_mediasize(part)) + offset; 338 | 339 | if(fs_verifyupdatehashtree_fsdata(saveoff, size, NULL, 1, 0))return -EIO;//the hashtree must be already valid before writing any data. 340 | 341 | if(fs_verifyupdatehashtree_fsdata(saveoff, size, (u8*)buf, 1, 1))return -EIO; 342 | 343 | return size; 344 | } 345 | 346 | -------------------------------------------------------------------------------- /3dsfuse/fuse_glue.h: -------------------------------------------------------------------------------- 1 | #ifndef __FUSE_GLUE_H__ 2 | #define __FUSE_GLUE_H__ 3 | 4 | #include "types.h" 5 | 6 | int fuse_sav_init(u8 *buf, u32 size, u8 *xorpad, u32 xorpad_sz, int argc, char *argv[]); 7 | int sav_getattr(const char *path, struct stat *stbuf); 8 | int sav_open(const char *path, struct fuse_file_info *fi); 9 | int sav_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi); 10 | int sav_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi); 11 | int sav_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /3dsfuse/helper.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "types.h" 7 | 8 | u16 const crc16_table[256] = { 9 | 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 10 | 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 11 | 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 12 | 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 13 | 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 14 | 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 15 | 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 16 | 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 17 | 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 18 | 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 19 | 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 20 | 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 21 | 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 22 | 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 23 | 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 24 | 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 25 | 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 26 | 0x8081, 0x4040 27 | }; 28 | 29 | void md5_print(u8 *buf) { 30 | int i; 31 | 32 | for(i = 0; i < 16; i++) 33 | printf("%02x", buf[i]); 34 | } 35 | 36 | void md5_buf(u8 *buf, u8 *out, size_t len) { 37 | EVP_MD_CTX mdctx; 38 | u32 md_len=0; 39 | 40 | EVP_DigestInit(&mdctx, EVP_md5()); 41 | EVP_DigestUpdate(&mdctx, (void*)buf, len); 42 | EVP_DigestFinal_ex(&mdctx, out, &md_len); 43 | EVP_MD_CTX_cleanup(&mdctx); 44 | } 45 | 46 | 47 | u16 crc16(u8 *buf, u32 size, u16 seed) 48 | { 49 | u16 crc = seed; 50 | u8 val; 51 | 52 | while(size>0) 53 | { 54 | size--; 55 | val = *buf++; 56 | crc = crc16_table[(val ^ crc) & 0xff] ^ (crc >> 8); 57 | } 58 | 59 | return crc; 60 | } 61 | 62 | 63 | u16 calc_crc16(u8 *buf, u32 size, u16 seed, u16 outxor) 64 | { 65 | return crc16(buf, size, seed) ^ outxor; 66 | } 67 | 68 | void xor(u8 *in, u32 len, u8 *out, u8 *key, u32 keylen) { 69 | int i; 70 | u8 kb; 71 | 72 | for(i = 0; i < len; i++) { 73 | kb = key[i % keylen]; 74 | 75 | if (out == NULL) 76 | in[i] ^= kb; 77 | else 78 | out[i] = in[i] ^ kb; 79 | } 80 | } 81 | 82 | u16 getle16(const void* ptr) 83 | { 84 | u8* p = (u8*)ptr; 85 | 86 | return p[0] | (p[1]<<8); 87 | } 88 | 89 | u16 getbe16(const void* ptr) 90 | { 91 | u8* p = (u8*)ptr; 92 | 93 | return (p[0]<<8) | (p[1]<<0); 94 | } 95 | 96 | u32 getle32(const void* ptr) 97 | { 98 | u8* p = (u8*)ptr; 99 | 100 | return (p[0]<<0) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); 101 | } 102 | 103 | u32 getbe32(const void* ptr) 104 | { 105 | u8* p = (u8*)ptr; 106 | 107 | return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | (p[3]<<0); 108 | } 109 | 110 | u64 getle64(const void* ptr) 111 | { 112 | u8* p = (u8*)ptr; 113 | u64 n = p[0]; 114 | 115 | n |= (u64)p[1]<<8; 116 | n |= (u64)p[2]<<16; 117 | n |= (u64)p[3]<<24; 118 | n |= (u64)p[4]<<32; 119 | n |= (u64)p[5]<<40; 120 | n |= (u64)p[6]<<48; 121 | n |= (u64)p[7]<<56; 122 | return n; 123 | } 124 | 125 | u64 getbe64(const void* ptr) 126 | { 127 | u8* p = (u8*)ptr; 128 | u64 n = 0; 129 | 130 | n |= (u64)p[0]<<56; 131 | n |= (u64)p[1]<<48; 132 | n |= (u64)p[2]<<40; 133 | n |= (u64)p[3]<<32; 134 | n |= (u64)p[4]<<24; 135 | n |= (u64)p[5]<<16; 136 | n |= (u64)p[6]<<8; 137 | n |= (u64)p[7]<<0; 138 | return n; 139 | } 140 | 141 | 142 | void hexdump(void *ptr, int buflen) { 143 | unsigned char *buf = (unsigned char*)ptr; 144 | int i, j; 145 | for (i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "types.h" 8 | #include "helper.h" 9 | #include "wearlevel.h" 10 | #include "crypto.h" 11 | #include "fs.h" 12 | #include "fuse_glue.h" 13 | 14 | 15 | int main(int argc, char *argv[]) { 16 | FILE *f; 17 | u8 *save_buf, *out_buf, *xorpad_buf = NULL; 18 | u8 zerobuf[0x10]; 19 | unsigned int size=0, xorpad_size = 0x0; 20 | int i; 21 | int enable_wearlevel = 1, enable_xorpad = 1; 22 | int load_xorpad = 0; 23 | 24 | int fargc = 1, argi = 1; 25 | char **fargv; 26 | struct stat filestat; 27 | char xorpad_path[256]; 28 | 29 | f = fopen(argv[1], "rb"); 30 | if(f == NULL) { 31 | fprintf(stderr, "error: failed to open %s\n", argv[1]); 32 | return -1; 33 | } 34 | 35 | fseek(f, 0, SEEK_END); 36 | size = ftell(f); 37 | fseek(f, 0, SEEK_SET); 38 | save_buf = malloc(size); 39 | out_buf = malloc(size); 40 | fread(save_buf, size, 1, f); 41 | 42 | fclose(f); 43 | 44 | memset(xorpad_path, 0, 256); 45 | 46 | for(i = 1; i < argc - 1; i++) { 47 | if(strncmp(argv[i + 1], "--", 2))fargc++; 48 | } 49 | 50 | fargv = (char **) malloc(fargc * sizeof(char *)); 51 | 52 | fargv[0] = argv[0]; 53 | 54 | for(i = 1; i < argc - 1; i++) { 55 | #ifdef DEBUG 56 | printf("arg: '%s'\n", argv[i + 1]); 57 | #endif 58 | if(strncmp(argv[i + 1], "--nowear", 8)==0) { 59 | enable_wearlevel = 0; 60 | } 61 | else if(strncmp(argv[i + 1], "--xorpad=", 9)==0) { 62 | load_xorpad = 1; 63 | strncpy(xorpad_path, &argv[i + 1][9], 255); 64 | } 65 | else { 66 | fargv[argi] = argv[i + 1]; 67 | argi++; 68 | } 69 | } 70 | 71 | if(enable_wearlevel) { 72 | if(rearrange(save_buf, out_buf, size) != 0) { 73 | free(save_buf); 74 | free(out_buf); 75 | return -2; 76 | } 77 | else { 78 | size -= 0x2000; 79 | } 80 | } 81 | else { 82 | memcpy(out_buf, save_buf, size); 83 | } 84 | 85 | memset(zerobuf, 0, 0x10); 86 | if(memcmp(&out_buf[0x10], zerobuf, 0x10)==0)enable_xorpad = 0; 87 | 88 | f = fopen("rawimage.bin", "wb"); 89 | fwrite(out_buf, 1, size, f); 90 | fclose(f); 91 | 92 | if(enable_xorpad) { 93 | if(load_xorpad) { 94 | if(stat(xorpad_path, &filestat)==-1) { 95 | fprintf(stderr, "error: failed to stat %s\n", xorpad_path); 96 | free(save_buf); 97 | free(out_buf); 98 | return -1; 99 | } 100 | xorpad_size = (unsigned int)filestat.st_size; 101 | 102 | xorpad_buf = (u8*)malloc(xorpad_size); 103 | if(xorpad_buf==NULL) { 104 | fprintf(stderr, "error: failed to allocate xorpad buffer.\n"); 105 | free(save_buf); 106 | free(out_buf); 107 | return -1; 108 | } 109 | memset(xorpad_buf, 0, xorpad_size); 110 | 111 | f = fopen(xorpad_path, "rb"); 112 | fread(xorpad_buf, 1, xorpad_size, f); 113 | fclose(f); 114 | } 115 | else 116 | { 117 | xorpad_size = 0x200; 118 | xorpad_buf = (u8*)malloc(xorpad_size); 119 | if(xorpad_buf==NULL) { 120 | fprintf(stderr, "error: failed to allocate xorpad buffer.\n"); 121 | free(save_buf); 122 | free(out_buf); 123 | return -1; 124 | } 125 | memset(xorpad_buf, 0, xorpad_size); 126 | 127 | if(find_key(out_buf, size, xorpad_buf) == -1) { 128 | fprintf(stderr, "error: could not find xorpad block :(\n"); 129 | free(save_buf); 130 | free(out_buf); 131 | return -1; 132 | } 133 | } 134 | 135 | xor(out_buf, size, NULL, xorpad_buf, xorpad_size); 136 | 137 | } 138 | else 139 | { 140 | xorpad_size = 0x200; 141 | xorpad_buf = (u8*)malloc(xorpad_size); 142 | if(xorpad_buf==NULL) { 143 | fprintf(stderr, "error: failed to allocate xorpad buffer.\n"); 144 | free(save_buf); 145 | free(out_buf); 146 | return -1; 147 | } 148 | memset(xorpad_buf, 0, xorpad_size); 149 | } 150 | 151 | f = fopen("logical.bin", "wb"); 152 | fwrite(out_buf, 1, size, f); 153 | fclose(f); 154 | 155 | #ifdef DEBUG 156 | printf("** FUSE GO! **\n"); 157 | #endif 158 | 159 | return fuse_sav_init(out_buf, size, xorpad_buf, xorpad_size, fargc, fargv); 160 | } 161 | -------------------------------------------------------------------------------- /3dsfuse/types.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYTYPES_H__ 2 | #define __MYTYPES_H__ 3 | 4 | #include 5 | 6 | typedef uint8_t u8; 7 | typedef uint16_t u16; 8 | typedef uint32_t u32; 9 | typedef uint64_t u64; 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /3dsfuse/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | void sha256(void* buf, size_t size, uint8_t* result){ 4 | SHA256_CTX ctx; 5 | SHA256_Init(&ctx); 6 | SHA256_Update(&ctx, buf, size); 7 | SHA256_Final(result, &ctx); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /3dsfuse/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H 2 | #define _UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | void sha256(void* buffer, size_t size, uint8_t* result); 8 | 9 | #endif /* _UTILS_H */ 10 | -------------------------------------------------------------------------------- /3dsfuse/wearlevel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "wearlevel.h" 6 | #include "helper.h" 7 | 8 | 9 | int rearrange(u8* savebuffer, u8* out, int savesize) { 10 | u16 calcedcrc; 11 | u16 havecrc; 12 | u32 blkmapsize; 13 | u32 offs; 14 | u16 shortcrc; 15 | u8 bytecrc; 16 | u32 blknum; 17 | int blkmapentrycount; 18 | int journalentrycount; 19 | u32 i, j; 20 | blockmap blkmap; 21 | 22 | if (savesize == 0x80000) 23 | blkmapentrycount = 0x80; 24 | else 25 | blkmapentrycount = 0x20; 26 | 27 | blkmapsize = blkmapentrycount * sizeof(blockmap_entry) - 2; 28 | journalentrycount = (0x1000 - blkmapentrycount * sizeof(blockmap_entry)) / 32; 29 | 30 | calcedcrc = crc16(savebuffer, blkmapsize, CRC16_DEFAULT_SEED); 31 | havecrc = getle16(savebuffer + blkmapsize); 32 | #ifdef DEBUG 33 | printf("Blockmap entrycount: 0x%X\n", blkmapentrycount); 34 | printf("Calculated CRC: 0x%04X\n", calcedcrc); 35 | printf("Have CRC: 0x%04X\n", havecrc); 36 | #endif 37 | if (calcedcrc != havecrc) 38 | return -1; 39 | 40 | memcpy(blkmap.header, savebuffer, blkmapsize); 41 | 42 | 43 | 44 | for(offs = blkmapentrycount * 10; offs < 0x1000; offs += 0x20, journalentrycount--) { 45 | journal_entry* journal = (journal_entry*)(savebuffer + offs); 46 | 47 | // TODO compare journal with dupe 48 | 49 | if (journal->data.phys_no == 0) 50 | continue; 51 | 52 | if (journal->data.virt_no >= (blkmapentrycount-1)) 53 | break; 54 | #ifdef DEBUG 55 | printf("Applying journal:\n"); 56 | hexdump(journal, sizeof(journal_entry)); 57 | #endif 58 | 59 | blkmap.entries[journal->data.virt_no].phys_no = 0x80 | (journal->data.phys_no & 0x7F); 60 | blkmap.entries[journal->data.virt_no].unk = journal->data.phys_realloc_cnt; 61 | for(i=0; i<8; i++) 62 | blkmap.entries[journal->data.virt_no].checksum[i] = journal->data.checksum[i]; 63 | 64 | if ( (journal->data.phys_no & 0x7F) != (journal->data.phys_prev_no & 0x7F) ) { 65 | #ifdef DEBUG 66 | printf("Applying prev-journal\n"); 67 | #endif 68 | blkmap.entries[journal->data.virt_prev_no].phys_no = journal->data.phys_prev_no & 0x7F; 69 | blkmap.entries[journal->data.virt_prev_no].unk = journal->data.virt_realloc_cnt; 70 | 71 | for(i=0; i<8; i++) 72 | blkmap.entries[journal->data.virt_prev_no].checksum[i] = 0; 73 | } 74 | } 75 | 76 | for(i=0; i<(blkmapentrycount-2); i++) { 77 | blknum = blkmap.entries[i].phys_no; 78 | 79 | if (0 == (blknum & 0x80)) 80 | { 81 | memset(out + i*0x1000, 0xFF, 0x1000); 82 | 83 | continue; 84 | } 85 | 86 | 87 | blknum &= 0x7F; 88 | offs = blknum * 0x1000; 89 | 90 | if (blknum > (blkmapentrycount-1)) 91 | { 92 | printf("Invalid physical block for logical block %d (physical 0x%02x).\n", i, blknum); 93 | return -1; 94 | } 95 | 96 | for(j=0; j<8; j++) { 97 | shortcrc = crc16(savebuffer + offs + j*0x200, 0x200, CRC16_DEFAULT_SEED); 98 | bytecrc = shortcrc ^ (shortcrc>>8); 99 | 100 | if (blkmap.entries[i].checksum[j] != bytecrc) { 101 | printf("Invalid CRC for logical block %d (physical 0x%02x). [%02x vs %02x]\n", i, blknum, bytecrc, blkmap.entries[i].checksum[j]); 102 | return -2; 103 | } 104 | } 105 | 106 | memcpy(out + i*0x1000, savebuffer + offs, 0x1000); 107 | } 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /3dsfuse/wearlevel.h: -------------------------------------------------------------------------------- 1 | #ifndef __WEARLEVEL_H__ 2 | #define __WEARLEVEL_H__ 3 | 4 | #include "types.h" 5 | 6 | #define MAXBLOCKMAPENTRIES 0x80 7 | 8 | typedef struct { 9 | u8 phys_no; 10 | u8 virt_no; 11 | } mapping_entry; 12 | 13 | typedef struct { 14 | u8 phys_no; 15 | u8 unk; 16 | u8 checksum[8]; 17 | } blockmap_entry; 18 | 19 | typedef struct { 20 | u8 header[8]; 21 | blockmap_entry entries[MAXBLOCKMAPENTRIES]; 22 | u8 crc[2]; 23 | } blockmap; 24 | 25 | 26 | typedef struct { 27 | u8 virt_no; 28 | u8 virt_prev_no; 29 | u8 phys_no; 30 | u8 phys_prev_no; 31 | u8 phys_realloc_cnt; 32 | u8 virt_realloc_cnt; 33 | u8 checksum[8]; 34 | } journal_data; 35 | 36 | typedef struct { 37 | journal_data data; 38 | journal_data dupe_data; 39 | u8 magic[4]; 40 | } journal_entry; 41 | 42 | int rearrange(u8 *buf, u8 *out, int size); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ohai. 2 | -------------------------------------------------------------------------------- /decrypt/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | INCLUDES=include 3 | CFLAGS=-c -Wall -O2 -std=c99 4 | LDFLAGS=-lcrypto 5 | SOURCES=src/decrypt.c src/utils.c 6 | OBJECTS=$(SOURCES:.c=.o) 7 | EXECUTABLE=decrypt 8 | 9 | all: $(SOURCES) $(EXECUTABLE) 10 | 11 | $(EXECUTABLE): $(OBJECTS) 12 | $(CC) $(LDFLAGS) $(OBJECTS) -o $@ 13 | 14 | .c.o: 15 | $(CC) -I$(INCLUDES) $(CFLAGS) $< -o $@ 16 | 17 | clean: 18 | rm $(OBJECTS) $(EXECUTABLE) 19 | -------------------------------------------------------------------------------- /decrypt/include/save.h: -------------------------------------------------------------------------------- 1 | #ifndef _SAVE_H 2 | #define _SAVE_H 3 | 4 | #define SECTOR_MAGIC 0x080d6ce0 5 | #define FILE_MAGIC 0x00117bd5 6 | 7 | struct fs_entry { 8 | uint32_t node_cnt; 9 | char filename[0x10]; 10 | uint32_t node_id; 11 | uint32_t magic; // Possibly magic. 12 | uint32_t block_nr; 13 | uint32_t size; 14 | uint32_t unk1; 15 | uint32_t unk2; // flags and/or date? 16 | uint32_t unk3; 17 | } __attribute__((__packed__)); 18 | 19 | struct header_entry { 20 | uint8_t chksums[8]; 21 | uint8_t phys_sec; 22 | uint8_t unk; 23 | } __attribute__((__packed__)); 24 | 25 | struct sector_entry { 26 | uint8_t virt_sec; // Mapped to sector 27 | uint8_t prev_virt_sec; // Physical sector previously mapped to 28 | uint8_t phys_sec; // Mapped from sector 29 | uint8_t prev_phys_sec; // Virtual sector previously mapped to 30 | uint8_t phys_realloc_cnt; // Amount of times physical sector has been remapped 31 | uint8_t virt_realloc_cnt; // Amount of times virtual sector has been remapped 32 | uint8_t chksums[8]; 33 | } __attribute__((__packed__)); 34 | 35 | struct long_sector_entry{ 36 | struct sector_entry sector; 37 | struct sector_entry dupe; 38 | uint32_t magic; 39 | }__attribute__((__packed__)); 40 | 41 | #endif /* _SAVE_H */ 42 | -------------------------------------------------------------------------------- /decrypt/include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H 2 | #define _UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | void sha256(void* buffer, size_t size, uint8_t* result); 8 | 9 | #endif /* _UTILS_H */ 10 | -------------------------------------------------------------------------------- /decrypt/src/decrypt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define XORPAD_SIZE 512 11 | #define SECTOR_SIZE 0x1000 12 | 13 | #define DEBUG 14 | 15 | int do_wearlevel = 0; 16 | int do_decrypt = 0; 17 | int do_filesys = 0; 18 | 19 | size_t file_size(FILE* fp){ 20 | long tmp = ftell(fp); 21 | fseek(fp, 0, SEEK_END); 22 | long ret = ftell(fp); 23 | fseek(fp, tmp, SEEK_SET); 24 | return ret; 25 | } 26 | 27 | void decrypt(void* buf, size_t size, void* xorpad){ 28 | for(int i = 0; i < size; i++) 29 | ((uint8_t*)buf)[i] ^= ((uint8_t*)xorpad)[i % XORPAD_SIZE]; 30 | } 31 | 32 | int wearlevel(void* buffer, size_t size, void* dest){ 33 | uint8_t* buf = (uint8_t*)buffer; 34 | 35 | struct sector_entry* entries; 36 | struct long_sector_entry *sectors = (struct long_sector_entry*)buffer; 37 | 38 | // Find the start 39 | int num_entries = (size / SECTOR_SIZE) - 1; 40 | entries = calloc(num_entries, sizeof(*entries)); 41 | sectors = (struct long_sector_entry*)(buf + ((num_entries + 1) * sizeof(*entries))); 42 | 43 | // Populate the base state 44 | struct header_entry *header = (struct header_entry*)buf; 45 | for(int i = 0; i < num_entries; i++){ 46 | entries[i].phys_sec = header[i].phys_sec; 47 | entries[i].virt_sec = i; 48 | memcpy(entries[i].chksums, header[i].chksums, sizeof(entries[0].chksums)); 49 | } 50 | 51 | // Apply the journal 52 | while(sectors->magic == SECTOR_MAGIC){ 53 | int sec = sectors->sector.virt_sec; 54 | entries[sec] = sectors->sector; 55 | entries[sec].phys_sec |= 0x80; 56 | sectors++; 57 | } 58 | 59 | #ifdef DEBUG 60 | for(int i = 0; i < num_entries; i++){ 61 | printf("virt: %d, phys: %d, cnt: %d,%s", 62 | entries[i].virt_sec, 63 | entries[i].phys_sec & 0x7F, 64 | entries[i].virt_realloc_cnt, 65 | entries[i].phys_sec & 0x80 ? " (in use) " : " "); 66 | printf("chksums: "); 67 | for(int j = 0; j < sizeof(entries[i].chksums); j++) 68 | printf("%02X ", entries[i].chksums[j]); 69 | printf("\n"); 70 | } 71 | #endif /* DEBUG */ 72 | 73 | for(int i = 0; i < num_entries; i++){ 74 | if((entries[i].phys_sec & 0x7F) * SECTOR_SIZE < size){ 75 | memcpy( &((uint8_t*)dest)[SECTOR_SIZE * i], 76 | &buf[SECTOR_SIZE * (entries[i].phys_sec & 0x7F)], 77 | SECTOR_SIZE); 78 | } else { 79 | fprintf(stderr, "Illegal physical sector (%d) in blockmap (%d)\n", entries[i].phys_sec, i); 80 | } 81 | #ifdef SEGHER 82 | for(int j = 0; j < 8; j++){ 83 | char filename[256]; 84 | if(entries[i].chksums[j]){ 85 | snprintf(filename, sizeof(filename), "block_%02X_%02X%02X%02X%02X)", 86 | entries[i].chksums[j], 87 | buf[(SECTOR_SIZE * entries[i].phys_sec) + j * 0x200], 88 | buf[(SECTOR_SIZE * entries[i].phys_sec) + j * 0x200 + 1], 89 | buf[(SECTOR_SIZE * entries[i].phys_sec) + j * 0x200 + 2], 90 | buf[(SECTOR_SIZE * entries[i].phys_sec) + j * 0x200 + 3]); 91 | 92 | FILE* out = fopen(filename, "wb"); 93 | fwrite(&buf[(SECTOR_SIZE * (entries[i].phys_sec & 0x7F)) + j * 0x200], 1, 0x200, out); 94 | fclose(out); 95 | } 96 | } 97 | #endif 98 | 99 | } 100 | return 0; 101 | } 102 | 103 | void parse_fs(void*, char*); 104 | 105 | #define PART_BASE 0x2000 106 | #define HASH_TBL_LEN_OFF 0x9C 107 | #define PARTITION_LEN_OFF 0xA4 108 | #define FS_INDEX_OFF 0x39 109 | #define DIFI_LENGTH 0x130 110 | 111 | void parse_partitions(void* buf, char* target_dir){ 112 | char part_dir[256]; 113 | uint8_t hash[SHA256_DIGEST_LENGTH]; 114 | uint8_t* buffer = (uint8_t*)buf + 0x200; 115 | uint32_t cur_part = PART_BASE; 116 | uint32_t part_length = 0; 117 | uint32_t hash_tbl_length = 0; 118 | 119 | if(mkdir(target_dir, 0766) < 0){ 120 | fprintf(stderr, "Couldn't create dir %s\n", target_dir); 121 | return; 122 | } 123 | 124 | chdir(target_dir); 125 | while(!strncmp((char*)buffer, "DIFI", 4)){ 126 | snprintf(part_dir, sizeof(part_dir), "part-%d", buffer[FS_INDEX_OFF]); 127 | hash_tbl_length = *((uint32_t*)&buffer[HASH_TBL_LEN_OFF]); 128 | part_length = *((uint32_t*)&buffer[PARTITION_LEN_OFF]); 129 | for(int i = 0; i < hash_tbl_length; i += SHA256_DIGEST_LENGTH){ 130 | for(int j = 0; j < part_length; j += SECTOR_SIZE){ 131 | sha256(buf + j + cur_part + hash_tbl_length, (part_length - j > SECTOR_SIZE) ? SECTOR_SIZE : part_length - j, hash); 132 | if(!memcmp(buf + i + cur_part, hash, sizeof(hash))){ 133 | printf("Block (%08X) matches entry %d (", j + cur_part + hash_tbl_length, i / SHA256_DIGEST_LENGTH); 134 | for(int x = 0; x < SHA256_DIGEST_LENGTH; x++) 135 | printf("%02x", hash[x]); 136 | printf(")\n"); 137 | } 138 | } 139 | } 140 | #ifdef DEBUG 141 | printf("Partition @ %06X (%06X)\n", cur_part, part_length + hash_tbl_length); 142 | #endif /* DEBUG */ 143 | cur_part += hash_tbl_length; 144 | parse_fs(buf + cur_part, part_dir); 145 | cur_part += part_length; 146 | buffer += DIFI_LENGTH; 147 | } 148 | } 149 | 150 | #define FST_OFF_OFFSET (0x6C) 151 | #define FST_BASE_OFFSET (0x58) 152 | 153 | #define FS_BLOCK_SIZE 0x200 154 | 155 | void parse_fs(void* buf, char* target_dir){ 156 | uint8_t* buffer = (uint8_t*)buf; 157 | struct fs_entry* entries; 158 | int nr_entries = 0; 159 | 160 | if(strncmp((char*)buffer, "SAVE", 4)){ 161 | fprintf(stderr, "Failed to find filesystem.\n"); 162 | return; 163 | } 164 | 165 | uint16_t fst_base = *((uint16_t*)&buffer[FST_BASE_OFFSET]); 166 | uint32_t fst_offset = (*((uint32_t*)&buffer[FST_OFF_OFFSET])) * FS_BLOCK_SIZE; 167 | entries = (struct fs_entry*)(&buffer[fst_offset + fst_base]); 168 | nr_entries = entries->node_cnt; 169 | 170 | if(nr_entries < 2){ 171 | fprintf(stderr, "Filesystem contains no file nodes.\n"); 172 | return; 173 | } 174 | 175 | // Skip the root node 176 | entries++; 177 | nr_entries--; 178 | 179 | #ifdef DEBUG 180 | for(int i = 0; i < nr_entries; i++){ 181 | if(entries[i].magic != FILE_MAGIC){ 182 | fprintf(stderr, "fs_entry %d did not have file magic.\n", i); 183 | return; 184 | } 185 | 186 | fprintf(stderr, "File: %s, size: %u bytes, block_nr: %u\n", 187 | entries[i].filename, 188 | (unsigned int)entries[i].size, 189 | entries[i].block_nr); 190 | } 191 | #endif /* DEBUG */ 192 | if(mkdir(target_dir, 0766) < 0){ 193 | fprintf(stderr, "Couldn't create dir %s\n", target_dir); 194 | return; 195 | } 196 | 197 | char filename[256]; 198 | for(int i = 0; i < nr_entries; i++){ 199 | if(entries[i].magic != FILE_MAGIC){ 200 | fprintf(stderr, "fs_entry %d did not have file magic.\n", i); 201 | break; 202 | } 203 | 204 | snprintf(filename, sizeof(filename), "%s/%s", target_dir, entries[i].filename); 205 | FILE* fp = fopen(filename, "wb"); 206 | if(!fp){ 207 | fprintf(stderr, "Failed to open %s\n", filename); 208 | continue; 209 | } 210 | 211 | fwrite(&buffer[fst_base + (entries[i].block_nr * FS_BLOCK_SIZE)], 212 | 1, 213 | entries[i].size, 214 | fp); 215 | fclose(fp); 216 | } 217 | } 218 | 219 | int main(int argc, char** argv){ 220 | FILE* fp; 221 | uint8_t* buf = NULL; 222 | size_t size; 223 | uint8_t xorpad[XORPAD_SIZE]; 224 | uint8_t* wearlevel_buf = NULL; 225 | 226 | if(argc < 5){ 227 | fprintf(stderr, "Syntax: %s \n", argv[0]); 228 | goto exit; 229 | } 230 | 231 | if(!strcmp("decrypt", argv[1])){ 232 | do_decrypt = 1; 233 | } else if(!strcmp("wearlevel", argv[1])){ 234 | do_decrypt = 1; 235 | do_wearlevel = 1; 236 | } else if(!strcmp("filesystem", argv[1])){ 237 | do_decrypt = 1; 238 | do_wearlevel = 1; 239 | do_filesys = 1; 240 | } 241 | 242 | // Read in the savefile 243 | fp = fopen(argv[2], "rb"); 244 | if(fp == NULL){ 245 | fprintf(stderr, "Failed to open %s\n", argv[2]); 246 | goto exit; 247 | } 248 | 249 | size = file_size(fp); 250 | buf = malloc(size); 251 | fread(buf, 1, size, fp); 252 | fclose(fp); 253 | 254 | fp = fopen(argv[3], "rb"); 255 | if(fp == NULL){ 256 | fprintf(stderr, "Failed to open %s\n", argv[3]); 257 | goto exit; 258 | } 259 | 260 | if(fread(xorpad, 1, XORPAD_SIZE, fp) != XORPAD_SIZE){ 261 | fprintf(stderr, "Failed to read in the xor pad\n"); 262 | goto exit; 263 | } 264 | fclose(fp); 265 | 266 | 267 | void* decrypt_ptr = buf + SECTOR_SIZE; 268 | void* write_ptr = buf; 269 | 270 | if(do_wearlevel){ 271 | wearlevel_buf = calloc(1, size - SECTOR_SIZE); 272 | if(wearlevel(buf, size, wearlevel_buf) < 0) 273 | goto exit; 274 | size -= SECTOR_SIZE; 275 | decrypt_ptr = wearlevel_buf; 276 | write_ptr = wearlevel_buf; 277 | } 278 | 279 | if(do_decrypt){ 280 | decrypt(decrypt_ptr, size, xorpad); 281 | 282 | if(do_wearlevel && do_filesys){ 283 | parse_partitions(wearlevel_buf, argv[4]); 284 | } else { 285 | fp = fopen(argv[4], "wb"); 286 | if(fp == NULL){ 287 | fprintf(stderr, "Couldn't open %s for writing.\n", argv[4]); 288 | goto exit; 289 | } 290 | 291 | fwrite(write_ptr, 1, size, fp); 292 | fclose(fp); 293 | } 294 | } 295 | 296 | exit: 297 | free(buf); 298 | free(wearlevel_buf); 299 | 300 | return 0; 301 | } 302 | -------------------------------------------------------------------------------- /decrypt/src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void sha256(void* buf, size_t size, uint8_t* result){ 4 | SHA256_CTX ctx; 5 | SHA256_Init(&ctx); 6 | SHA256_Update(&ctx, buf, size); 7 | SHA256_Final(result, &ctx); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /spi_flash/.cproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 38 | 42 | 43 | 47 | 48 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 744 | 745 | 749 | 750 | 754 | 755 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | 1345 | 1346 | 1347 | 1348 | 1349 | 1350 | 1351 | 1352 | 1353 | 1354 | 1355 | 1356 | 1357 | 1358 | 1359 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1366 | 1367 | 1368 | 1369 | 1370 | 1371 | 1372 | 1373 | 1374 | 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | 1394 | 1395 | 1396 | 1397 | 1398 | 1399 | 1400 | 1401 | 1402 | 1403 | 1404 | 1405 | 1406 | 1407 | 1408 | 1409 | 1410 | 1411 | 1412 | 1413 | 1414 | 1415 | 1416 | 1417 | 1418 | 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 | -------------------------------------------------------------------------------- /spi_flash/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | spi_flash 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.cdt.managedbuilder.core.genmakebuilder 10 | clean,full,incremental, 11 | 12 | 13 | ?name? 14 | 15 | 16 | 17 | org.eclipse.cdt.make.core.append_environment 18 | true 19 | 20 | 21 | org.eclipse.cdt.make.core.autoBuildTarget 22 | all 23 | 24 | 25 | org.eclipse.cdt.make.core.buildArguments 26 | 27 | 28 | 29 | org.eclipse.cdt.make.core.buildCommand 30 | xmake 31 | 32 | 33 | org.eclipse.cdt.make.core.buildLocation 34 | ${workspace_loc:/spi_flash/Debug} 35 | 36 | 37 | org.eclipse.cdt.make.core.cleanBuildTarget 38 | clean 39 | 40 | 41 | org.eclipse.cdt.make.core.contents 42 | org.eclipse.cdt.make.core.activeConfigSettings 43 | 44 | 45 | org.eclipse.cdt.make.core.enableAutoBuild 46 | false 47 | 48 | 49 | org.eclipse.cdt.make.core.enableCleanBuild 50 | true 51 | 52 | 53 | org.eclipse.cdt.make.core.enableFullBuild 54 | true 55 | 56 | 57 | org.eclipse.cdt.make.core.fullBuildTarget 58 | all 59 | 60 | 61 | org.eclipse.cdt.make.core.stopOnError 62 | true 63 | 64 | 65 | org.eclipse.cdt.make.core.useDefaultBuildCmd 66 | true 67 | 68 | 69 | 70 | 71 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 72 | 73 | 74 | 75 | 76 | 77 | org.eclipse.cdt.core.cnature 78 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 79 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 80 | 81 | 82 | -------------------------------------------------------------------------------- /spi_flash/Debug/input: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spi_flash/Debug/makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | -include ../makefile.init 6 | 7 | RM = rm -rf $(1) 8 | 9 | # All of the sources participating in the build are defined here 10 | -include sources.mk 11 | -include subdir.mk 12 | -include src/subdir.mk 13 | -include objects.mk 14 | 15 | ifneq ($(MAKECMDGOALS),clean) 16 | ifneq ($(strip $(C++_DEPS)),) 17 | -include $(C++_DEPS) 18 | endif 19 | ifneq ($(strip $(C_DEPS)),) 20 | -include $(C_DEPS) 21 | endif 22 | ifneq ($(strip $(CC_DEPS)),) 23 | -include $(CC_DEPS) 24 | endif 25 | ifneq ($(strip $(CPP_DEPS)),) 26 | -include $(CPP_DEPS) 27 | endif 28 | ifneq ($(strip $(CXX_DEPS)),) 29 | -include $(CXX_DEPS) 30 | endif 31 | ifneq ($(strip $(C_UPPER_DEPS)),) 32 | -include $(C_UPPER_DEPS) 33 | endif 34 | endif 35 | 36 | -include ../makefile.defs 37 | 38 | # Add inputs and outputs from these tool invocations to the build variables 39 | 40 | # All Target 41 | all: spi_flash.xe 42 | 43 | # Tool invocations 44 | spi_flash.xe: $(OBJS) $(XN_SRCS) $(XTA_SRCS) 45 | @echo 'Building target: $@' 46 | @echo 'Invoking: Mapper/Linker' 47 | xcc -Werror=timing-syntax -o "spi_flash.xe" $(OBJS) $(LIBS) $(XN_SRCS) $(XTA_SRCS) 48 | @echo 'Finished building target: $@' 49 | @echo ' ' 50 | 51 | # Other Targets 52 | clean: 53 | -$(call RM, $(C++_DEPS)$(OBJS)$(C_DEPS)$(CC_DEPS)$(CPP_DEPS)$(EXECUTABLES)$(CXX_DEPS)$(C_UPPER_DEPS) spi_flash.xe) 54 | -@echo ' ' 55 | 56 | .PHONY: all clean dependents 57 | .SECONDARY: 58 | 59 | -include ../makefile.targets 60 | -------------------------------------------------------------------------------- /spi_flash/Debug/objects.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | USER_OBJS := 6 | 7 | LIBS := 8 | -------------------------------------------------------------------------------- /spi_flash/Debug/sources.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | O_SRCS := 6 | CPP_SRCS := 7 | C_UPPER_SRCS := 8 | C_SRCS := 9 | S_UPPER_SRCS := 10 | S_SRCS := 11 | XC_SRCS := 12 | XTA_SRCS := 13 | CXX_SRCS := 14 | XN_SRCS := 15 | C++_SRCS := 16 | CC_SRCS := 17 | C++_DEPS := 18 | OBJS := 19 | C_DEPS := 20 | CC_DEPS := 21 | CPP_DEPS := 22 | EXECUTABLES := 23 | CXX_DEPS := 24 | C_UPPER_DEPS := 25 | 26 | # Every subdirectory with source files must be described here 27 | SUBDIRS := \ 28 | src \ 29 | . \ 30 | 31 | -------------------------------------------------------------------------------- /spi_flash/Debug/spi_read.xe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3dshax/3ds/ee1f17e20b5287643c4cc62dec417661f81d7683/spi_flash/Debug/spi_read.xe -------------------------------------------------------------------------------- /spi_flash/Debug/spi_write.xe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3dshax/3ds/ee1f17e20b5287643c4cc62dec417661f81d7683/spi_flash/Debug/spi_write.xe -------------------------------------------------------------------------------- /spi_flash/Debug/src/subdir.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | # Add inputs and outputs from these tool invocations to the build variables 6 | C_SRCS += \ 7 | ../src/flash.c \ 8 | ../src/io.c 9 | 10 | XC_SRCS += \ 11 | ../src/main.xc \ 12 | ../src/master.xc 13 | 14 | OBJS += \ 15 | ./src/flash.o \ 16 | ./src/io.o \ 17 | ./src/main.o \ 18 | ./src/master.o 19 | 20 | 21 | # Each subdirectory must supply rules for building sources it contributes 22 | src/%.o: ../src/%.c 23 | @echo 'Building file: $<' 24 | @echo 'Invoking: C Compiler' 25 | xcc -O0 -g -Wall -c -std=gnu89 -o "$@" "$<" 26 | @echo 'Finished building: $<' 27 | @echo ' ' 28 | 29 | src/%.o: ../src/%.xc 30 | @echo 'Building file: $<' 31 | @echo 'Invoking: XC Compiler' 32 | xcc -O0 -g -Wall -c -o "$@" "$<" "../XK-1.xn" 33 | @echo 'Finished building: $<' 34 | @echo ' ' 35 | 36 | 37 | -------------------------------------------------------------------------------- /spi_flash/Debug/subdir.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | # Add inputs and outputs from these tool invocations to the build variables 6 | XN_SRCS += \ 7 | ../XK-1.xn 8 | 9 | 10 | # Each subdirectory must supply rules for building sources it contributes 11 | 12 | -------------------------------------------------------------------------------- /spi_flash/XK-1.xn: -------------------------------------------------------------------------------- 1 | 2 | 5 | Board 6 | XK-1 Development Board 7 | 8 | 9 | core stdcore[1] 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /spi_flash/src/flash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * flash.c 3 | * 4 | * Created on: Apr 7, 2011 5 | * Author: erant 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include "master.h" 12 | 13 | #define FLASH_READ_ID 0x9F 14 | #define FLASH_ERASE_CHIP 0x60 15 | #define FLASH_ERASE_SECTOR 0x10 16 | #define FLASH_READ 0x03 17 | #define FLASH_WRITE_ENABLE 0x06 18 | #define FLASH_WRITE_DISABLE 0x04 19 | #define FLASH_READ_STATUS 0x05 20 | #define FLASH_PROG_PAGE 0x02 21 | 22 | #define PAGE_SIZE 32 23 | #define NUMBER_PAGES 4096 24 | 25 | size_t flash_size = 0; 26 | uint32_t flash_id = 0; 27 | 28 | void flash_init(){ 29 | spi_init(); 30 | spi_select(); 31 | spi_out_byte(FLASH_READ_ID); 32 | 33 | flash_id = spi_in_byte() << 16; 34 | flash_id |= spi_in_byte() << 8; 35 | flash_id |= spi_in_byte(); 36 | 37 | flash_size = 1 << (flash_id & 0xFF); 38 | printf("JEDEC ID: %06X, size: %u bytes\n", (unsigned int)flash_id, flash_size); 39 | spi_deselect(); 40 | } 41 | 42 | void flash_protect(){ 43 | spi_select(); 44 | spi_out_byte(FLASH_WRITE_DISABLE); 45 | spi_deselect(); 46 | } 47 | 48 | uint8_t flash_read_status(){ 49 | uint8_t status; 50 | spi_select(); 51 | spi_out_byte(FLASH_READ_STATUS); 52 | status = spi_in_byte(); 53 | spi_deselect(); 54 | return status; 55 | } 56 | 57 | void flash_unprotect(){ 58 | spi_select(); 59 | spi_out_byte(FLASH_WRITE_ENABLE); 60 | spi_deselect(); 61 | } 62 | 63 | 64 | 65 | void flash_wait_complete(){ 66 | while(flash_read_status() & 0x1); 67 | } 68 | 69 | void flash_send_address(uint32_t addr){ 70 | addr = ((flash_size - 1) & addr) | (-1 - (flash_size - 1)); 71 | spi_out_byte((addr >> 16) & 0xFF); 72 | spi_out_byte((addr >> 8) & 0xFF); 73 | spi_out_byte((addr >> 0) & 0xFF); 74 | } 75 | 76 | void flash_erase_sector(int sector){ 77 | flash_unprotect(sector); 78 | 79 | spi_select(); 80 | spi_out_byte(FLASH_ERASE_SECTOR); 81 | flash_send_address((sector * PAGE_SIZE) + 1); 82 | spi_deselect(); 83 | 84 | flash_wait_complete(); 85 | flash_protect(sector); 86 | } 87 | 88 | void flash_erase_chip(){ 89 | flash_unprotect(); 90 | 91 | spi_select(); 92 | spi_out_byte(FLASH_ERASE_CHIP); 93 | spi_deselect(); 94 | 95 | flash_wait_complete(); 96 | flash_protect(); 97 | } 98 | 99 | void flash_read(uint8_t* buf, uint32_t addr, size_t size){ 100 | spi_select(); 101 | spi_out_byte(FLASH_READ); 102 | flash_send_address(addr); 103 | 104 | int i; 105 | for(i = 0; i < size; i++){ 106 | buf[i] = spi_in_byte(); 107 | } 108 | spi_deselect(); 109 | } 110 | 111 | size_t flash_get_size(){ 112 | return flash_size; 113 | } 114 | 115 | void flash_write_page(uint8_t* buf, int page){ 116 | flash_unprotect(); 117 | spi_select(); 118 | spi_out_byte(FLASH_PROG_PAGE); 119 | flash_send_address(page * PAGE_SIZE); 120 | int i; 121 | for(i = 0; i < PAGE_SIZE; i++){ 122 | spi_out_byte(buf[i]); 123 | } 124 | spi_deselect(); 125 | flash_wait_complete(); 126 | } 127 | 128 | void flash_write(uint8_t* buf, uint32_t addr, size_t size){ 129 | int i = 0; 130 | 131 | for(; i < size / PAGE_SIZE; i++){ 132 | flash_write_page(buf + (i * PAGE_SIZE), i + (addr / PAGE_SIZE)); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /spi_flash/src/flash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * flash.h 3 | * 4 | * Created on: Apr 7, 2011 5 | * Author: erant 6 | */ 7 | 8 | #ifndef FLASH_H_ 9 | #define FLASH_H_ 10 | 11 | #include 12 | 13 | void flash_init(void); 14 | 15 | #ifdef __XC__ 16 | void flash_read(uint8_t buf[], uint32_t addr, size_t sz); 17 | void flash_write(uint8_t buf[], uint32_t addr, size_t sz); 18 | void flash_erase_chip(); 19 | size_t flash_get_size(); 20 | #else 21 | void flash_read(uint8_t* buf, uint32_t addr, size_t size); 22 | void flash_write(uint8_t* buf, uint32_t addr, size_t size); 23 | void flash_erase_chip(); 24 | #endif 25 | 26 | #endif /* FLASH_H_ */ 27 | -------------------------------------------------------------------------------- /spi_flash/src/io.c: -------------------------------------------------------------------------------- 1 | /* 2 | * io.c 3 | * 4 | * Created on: Apr 7, 2011 5 | * Author: erant 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include "io.h" 11 | 12 | FILE* fp = NULL; 13 | 14 | #define FILE_BUF_SIZE 8192 15 | char vbuf[FILE_BUF_SIZE]; 16 | 17 | void io_init(void){ 18 | #ifdef READ 19 | fp = fopen("output", "wb"); 20 | #else 21 | fp = fopen("input", "rb"); 22 | #endif 23 | setvbuf(fp, vbuf, _IOFBF, FILE_BUF_SIZE); 24 | } 25 | 26 | void io_write(uint8_t* data, size_t sz){ 27 | fwrite(data, 1, sz, fp); 28 | } 29 | 30 | int io_read(uint8_t* data, size_t sz){ 31 | return fread(data, 1, sz, fp); 32 | } 33 | 34 | void io_close(void){ 35 | fclose(fp); 36 | } 37 | -------------------------------------------------------------------------------- /spi_flash/src/io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * io.h 3 | * 4 | * Created on: Apr 7, 2011 5 | * Author: erant 6 | */ 7 | 8 | #ifndef IO_H_ 9 | #define IO_H_ 10 | 11 | #include 12 | #include 13 | 14 | #define WRITE 15 | 16 | void io_init(void); 17 | void io_write(uint8_t data[], size_t sz); 18 | int io_read(uint8_t data[], size_t sz); 19 | void io_close(void); 20 | 21 | #endif /* IO_H_ */ 22 | -------------------------------------------------------------------------------- /spi_flash/src/main.xc: -------------------------------------------------------------------------------- 1 | /* 2 | * main.xc 3 | * 4 | * Created on: Apr 7, 2011 5 | * Author: erant 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "flash.h" 12 | #include "io.h" 13 | 14 | uint8_t array[4096]; 15 | 16 | #define ERASE 17 | 18 | int main(void){ 19 | uint32_t addr; 20 | io_init(); 21 | flash_init(); 22 | 23 | #ifdef ERASE 24 | flash_erase_chip(); 25 | return 0; 26 | #endif 27 | 28 | #ifdef READ 29 | for(int i = 0; i < flash_get_size() / sizeof(array); i++){ 30 | flash_read(array, i * sizeof(array), sizeof(array)); 31 | io_write(array, sizeof(array)); 32 | if(!((i * sizeof(array)) % 0x4000)) 33 | printf("@%06X\n", i * sizeof(array)); 34 | } 35 | #else 36 | 37 | flash_erase_chip(); 38 | addr = 0; 39 | while(io_read(array, sizeof(array)) == sizeof(array)){ 40 | flash_write(array, addr, sizeof(array)); 41 | if(!(addr % 0x4000)) 42 | printf("@%06X\n", addr); 43 | addr += sizeof(array); 44 | } 45 | #endif 46 | 47 | io_close(); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /spi_flash/src/master.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // SPI Master (mode 3) 4 | // Version 1.0 5 | // 25 Nov 2009 6 | // 7 | // master.h 8 | // 9 | // Select lines are intentionally not part of API 10 | // They are simple port outputs 11 | // They depend on how many slaves there are and how they're connected 12 | // 13 | // Copyright (C) 2009, XMOS Ltd 14 | // All rights reserved. 15 | // 16 | // Redistribution and use in source and binary forms, with or without 17 | // modification, are permitted provided that the following conditions are met: 18 | // * Redistributions of source code must retain the above copyright 19 | // notice, this list of conditions and the following disclaimer. 20 | // * Redistributions in binary form must reproduce the above copyright 21 | // notice, this list of conditions and the following disclaimer in the 22 | // documentation and/or other materials provided with the distribution. 23 | // * Neither the name of the XMOS Ltd nor the names of its contributors may 24 | // be used to endorse or promote products derived from this software 25 | // without specific prior written permission. 26 | // 27 | // THIS SOFTWARE IS PROVIDED BY XMOS LTD ''AS IS'' AND ANY EXPRESS OR 28 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 29 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 30 | // DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 31 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 35 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 36 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | // POSSIBILITY OF SUCH DAMAGE. 38 | // 39 | 40 | #ifndef _master_h_ 41 | #define _master_h_ 42 | 43 | void spi_init(); 44 | void spi_shutdown(); 45 | 46 | // SPI master output 47 | // big endian byte order 48 | void spi_select(); 49 | void spi_deselect(); 50 | void spi_out_word(unsigned int data); 51 | void spi_out_short(unsigned short data); 52 | void spi_out_byte(unsigned char data); 53 | 54 | // SPI master input 55 | // big endian byte order 56 | unsigned int spi_in_word(); 57 | unsigned short spi_in_short(); 58 | unsigned char spi_in_byte(); 59 | 60 | // SPI ports 61 | #ifndef SPI_MISO 62 | #define SPI_MISO XS1_PORT_1A 63 | #endif 64 | #ifndef SPI_CLK 65 | #define SPI_CLK XS1_PORT_1C 66 | #endif 67 | #ifndef SPI_MOSI 68 | #define SPI_MOSI XS1_PORT_1D 69 | #endif 70 | 71 | // SPI clock frequency is fref/(2*SPI_CLOCK_DIV) 72 | // where fref defaults to 100MHz 73 | //#ifndef SPI_CLOCK_DIV 74 | #define SPI_CLOCK_DIV 4 75 | //#endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /spi_flash/src/master.xc: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // SPI Master (mode 3) 4 | // Version 1.0 5 | // 25 Nov 2009 6 | // 7 | // master.xc 8 | // 9 | // Select lines are intentionally not part of API 10 | // They are simple port outputs 11 | // They depend on how many slaves there are and how they're connected 12 | // 13 | // Copyright (C) 2009, XMOS Ltd 14 | // All rights reserved. 15 | // 16 | // Redistribution and use in source and binary forms, with or without 17 | // modification, are permitted provided that the following conditions are met: 18 | // * Redistributions of source code must retain the above copyright 19 | // notice, this list of conditions and the following disclaimer. 20 | // * Redistributions in binary form must reproduce the above copyright 21 | // notice, this list of conditions and the following disclaimer in the 22 | // documentation and/or other materials provided with the distribution. 23 | // * Neither the name of the XMOS Ltd nor the names of its contributors may 24 | // be used to endorse or promote products derived from this software 25 | // without specific prior written permission. 26 | // 27 | // THIS SOFTWARE IS PROVIDED BY XMOS LTD ''AS IS'' AND ANY EXPRESS OR 28 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 29 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 30 | // DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 31 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 35 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 36 | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 | // POSSIBILITY OF SUCH DAMAGE. 38 | // 39 | 40 | #include 41 | #include 42 | #include 43 | #include "master.h" 44 | #include 45 | 46 | //out buffered port:8 spi_mosi = SPI_MOSI; 47 | //out buffered port:8 spi_sclk = SPI_CLK; 48 | //in buffered port:8 spi_miso = SPI_MISO; 49 | 50 | in buffered port:8 spi_miso = PORT_TGT_MISO; 51 | out buffered port:8 spi_sclk = PORT_TGT_CLK; 52 | out buffered port:1 spi_ss = PORT_TGT_SS; 53 | out buffered port:8 spi_mosi = PORT_TGT_MOSI; 54 | 55 | //#define spi_miso PORT_TGT_MISO 56 | //#define spi_mosi PORT_TGT_MOSI 57 | //#define spi_sclk PORT_TGT_CLK 58 | //#define spi_ss PORT_TGT_CS 59 | 60 | // need two clock blocks: 61 | // (1) continuous clock 62 | // (2) software initiated clock ticks aligned to (1) 63 | clock blk1 = XS1_CLKBLK_5; 64 | clock blk2 = XS1_CLKBLK_4; 65 | 66 | void spi_select() 67 | { 68 | spi_ss <: 0; 69 | } 70 | 71 | void spi_deselect() 72 | { 73 | spi_ss <: 1; 74 | } 75 | 76 | 77 | void spi_init() 78 | { 79 | // configure ports and clock blocks 80 | set_port_use_on(spi_sclk); 81 | set_port_use_on(spi_mosi); 82 | set_port_use_on(spi_miso); 83 | set_port_use_on(spi_ss); 84 | 85 | spi_deselect(); 86 | 87 | configure_clock_rate(blk1, 100, 8); 88 | configure_out_port(spi_sclk, blk1, 0); 89 | configure_clock_src(blk2, spi_sclk); 90 | configure_out_port(spi_mosi, blk2, 0); 91 | configure_in_port(spi_miso, blk2); 92 | clearbuf(spi_mosi); 93 | clearbuf(spi_sclk); 94 | start_clock(blk1); 95 | start_clock(blk2); 96 | spi_sclk <: 0xFF; 97 | } 98 | 99 | void spi_shutdown() 100 | { 101 | // need clock ticks in order to stop clock blocks 102 | spi_sclk <: 0xAA; 103 | spi_sclk <: 0xAA; 104 | stop_clock(blk2); 105 | stop_clock(blk1); 106 | } 107 | 108 | unsigned char spi_in_byte() 109 | { 110 | // MSb-first bit order - SPI standard 111 | unsigned x; 112 | clearbuf(spi_miso); 113 | spi_sclk <: 0xAA; 114 | spi_sclk <: 0xAA; 115 | sync(spi_sclk); 116 | spi_miso :> x;\ 117 | return bitrev(x) >> 24; 118 | } 119 | 120 | unsigned short spi_in_short() 121 | { 122 | // big endian byte order 123 | unsigned short data = 0; 124 | data |= (spi_in_byte() << 8); 125 | data |= spi_in_byte(); 126 | return data; 127 | } 128 | 129 | unsigned int spi_in_word() 130 | { 131 | // big endian byte order 132 | unsigned int data = 0; 133 | data |= (spi_in_byte() << 24); 134 | data |= (spi_in_byte() << 16); 135 | data |= (spi_in_byte() << 8); 136 | data |= spi_in_byte(); 137 | return data; 138 | } 139 | 140 | void spi_out_byte(unsigned char data) 141 | { 142 | // MSb-first bit order - SPI standard 143 | unsigned x = bitrev(data) >> 24; 144 | spi_mosi <: x; 145 | spi_sclk <: 0xAA; 146 | spi_sclk <: 0xAA; 147 | sync(spi_sclk); 148 | spi_miso :> void; 149 | } 150 | 151 | void spi_out_short(unsigned short data) 152 | { 153 | // big endian byte order 154 | spi_out_byte((data >> 8) & 0xFF); 155 | spi_out_byte(data & 0xFF); 156 | } 157 | 158 | void spi_out_word(unsigned int data) 159 | { 160 | // big endian byte order 161 | spi_out_byte((data >> 24) & 0xFF); 162 | spi_out_byte((data >> 16) & 0xFF); 163 | spi_out_byte((data >> 8) & 0xFF); 164 | spi_out_byte(data & 0xFF); 165 | } 166 | --------------------------------------------------------------------------------