u32 IdentifyImage(const char* path) { u8 header[0x200]; FIL file; if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) return 0; f_lseek(&file, 0); f_sync(&file); UINT fsize = f_size(&file); UINT bytes_read; if ((f_read(&file, header, 0x200, &bytes_read) != FR_OK) || (bytes_read != 0x200)) { f_close(&file); return 0; } f_close(&file); if ((getbe32(header + 0x100) == 0x4E435344) && (getbe64(header + 0x110) == (u64) 0x0104030301000000) && (getbe64(header + 0x108) == (u64) 0) && (fsize >= 0x8FC8000)) { return IMG_NAND; } else if (getbe16(header + 0x1FE) == 0x55AA) { // migt be FAT or MBR if ((strncmp((char*) header + 0x36, "FAT12 ", 8) == 0) || (strncmp((char*) header + 0x36, "FAT16 ", 8) == 0) || (strncmp((char*) header + 0x36, "FAT ", 8) == 0) || (strncmp((char*) header + 0x52, "FAT32 ", 8) == 0)) { return IMG_FAT; // this is an actual FAT header } else if (((getle32(header + 0x1BE + 0x8) + getle32(header + 0x1BE + 0xC)) < (fsize / 0x200)) && // check file size (getle32(header + 0x1BE + 0x8) > 0) && (getle32(header + 0x1BE + 0xC) >= 0x800) && // check first partition sanity ((header[0x1BE + 0x4] == 0x1) || (header[0x1BE + 0x4] == 0x4) || (header[0x1BE + 0x4] == 0x6) || // filesystem type (header[0x1BE + 0x4] == 0xB) || (header[0x1BE + 0x4] == 0xC) || (header[0x1BE + 0x4] == 0xE))) { return IMG_FAT; // this might be an MBR -> give it the benefit of doubt } } return 0; }
void cia_verify_contents(cia_context *ctx, u32 actions) { u16 contentflags; ctr_tmd_body *body; ctr_tmd_contentchunk *chunk; u8 *verify_buf; u32 content_size=0; int i; // verify TMD content hashes, requires decryption .. body = tmd_get_body(&ctx->tmd); chunk = (ctr_tmd_contentchunk*)(body->contentinfo + (sizeof(ctr_tmd_contentinfo) * TMD_MAX_CONTENTS)); fseek(ctx->file, ctx->offset + ctx->offsetcontent, SEEK_SET); for(i = 0; i < getbe16(body->contentcount); i++) { content_size = getbe64(chunk->size) & 0xffffffff; contentflags = getbe16(chunk->type); verify_buf = malloc(content_size); fread(verify_buf, content_size, 1, ctx->file); if(contentflags & 1 && !(actions & PlainFlag)) // Decrypt if needed { ctx->iv[0] = (getbe16(chunk->index) >> 8) & 0xff; ctx->iv[1] = getbe16(chunk->index) & 0xff; ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); ctr_decrypt_cbc(&ctx->aes, verify_buf, verify_buf, content_size); } if (ctr_sha_256_verify(verify_buf, content_size, chunk->hash) == Good) ctx->tmd.content_hash_stat[i] = 1; else ctx->tmd.content_hash_stat[i] = 2; free(verify_buf); chunk++; }
void tmd_print(tmd_context* ctx) { unsigned int type = getbe32(ctx->buffer); ctr_tmd_header_4096* header4096 = 0; ctr_tmd_header_2048* header2048 = 0; ctr_tmd_body* body = 0; unsigned int contentcount = 0; unsigned int savesize = 0; unsigned int titlever = 0; unsigned int i; if (type == TMD_RSA_2048_SHA256 || type == TMD_RSA_2048_SHA1) { header2048 = (ctr_tmd_header_2048*)ctx->buffer; } else if (type == TMD_RSA_4096_SHA256 || type == TMD_RSA_4096_SHA1) { header4096 = (ctr_tmd_header_4096*)ctx->buffer; } else { return; } body = tmd_get_body(ctx); contentcount = getbe16(body->contentcount); savesize = getle32(body->savedatasize); titlever = getbe16(body->titleversion); fprintf(stdout, "\nTMD header:\n"); fprintf(stdout, "Signature type: %s\n", tmd_get_type_string(type)); fprintf(stdout, "Issuer: %s\n", body->issuer); fprintf(stdout, "Version: %d\n", body->version); fprintf(stdout, "CA CRL version: %d\n", body->ca_crl_version); fprintf(stdout, "Signer CRL version: %d\n", body->signer_crl_version); memdump(stdout, "System version: ", body->systemversion, 8); memdump(stdout, "Title id: ", body->titleid, 8); fprintf(stdout, "Title type: %08x\n", getbe32(body->titletype)); fprintf(stdout, "Group id: %04x\n", getbe16(body->groupid)); if(savesize < sizeKB) fprintf(stdout, "Save Size: %08x\n", savesize); else if(savesize < sizeMB) fprintf(stdout, "Save Size: %dKB (%08x)\n", savesize/sizeKB, savesize); else fprintf(stdout, "Save Size: %dMB (%08x)\n", savesize/sizeMB, savesize); fprintf(stdout, "Access rights: %08x\n", getbe32(body->accessrights)); fprintf(stdout, "Title version: %d.%d.%d (v%d)\n", (titlever >> 10) & 0x3F, (titlever >> 4) & 0x3F, titlever & 0xF, titlever); fprintf(stdout, "Content count: %04x\n", getbe16(body->contentcount)); fprintf(stdout, "Boot content: %04x\n", getbe16(body->bootcontent)); memdump(stdout, "Hash: ", body->hash, 32); fprintf(stdout, "\nTMD content info:\n"); for(i = 0; i < TMD_MAX_CONTENTS; i++) { ctr_tmd_contentinfo* info = (ctr_tmd_contentinfo*)(body->contentinfo + sizeof(ctr_tmd_contentinfo)*i); if (getbe16(info->commandcount) == 0) continue; fprintf(stdout, "Content index: %04x\n", getbe16(info->index)); fprintf(stdout, "Command count: %04x\n", getbe16(info->commandcount)); memdump(stdout, "Unknown: ", info->unk, 32); } fprintf(stdout, "\nTMD contents:\n"); for(i = 0; i < contentcount; i++) { ctr_tmd_contentchunk* chunk = (ctr_tmd_contentchunk*)(body->contentinfo + 36*64 + i*48); unsigned short type = getbe16(chunk->type); fprintf(stdout, "Content id: %08x\n", getbe32(chunk->id)); fprintf(stdout, "Content index: %04x\n", getbe16(chunk->index)); fprintf(stdout, "Content type: %04x", getbe16(chunk->type)); if (type) { fprintf(stdout, " "); if (type & 1) fprintf(stdout, "[encrypted]"); if (type & 2) fprintf(stdout, "[disc]"); if (type & 4) fprintf(stdout, "[cfm]"); if (type & 0x4000) fprintf(stdout, "[optional]"); if (type & 0x8000) fprintf(stdout, "[shared]"); } fprintf(stdout, "\n"); fprintf(stdout, "Content size: %016"PRIx64"\n", getbe64(chunk->size)); switch(ctx->content_hash_stat[i]) { case 1: memdump(stdout, "Content hash [OK]: ", chunk->hash, 32); break; case 2: memdump(stdout, "Content hash [FAIL]: ", chunk->hash, 32); break; default: memdump(stdout, "Content hash: ", chunk->hash, 32); break; } fprintf(stdout, "\n"); } }
void cia_save(cia_context* ctx, u32 type, u32 flags) { u32 offset; u32 size; u16 contentflags; u8 docrypto; filepath* path = 0; ctr_tmd_body *body; ctr_tmd_contentchunk *chunk; int i; char tmpname[255]; switch(type) { case CIATYPE_CERTS: offset = ctx->offsetcerts; size = ctx->sizecert; path = settings_get_certs_path(ctx->usersettings); break; case CIATYPE_TIK: offset = ctx->offsettik; size = ctx->sizetik; path = settings_get_tik_path(ctx->usersettings); break; case CIATYPE_TMD: offset = ctx->offsettmd; size = ctx->sizetmd; path = settings_get_tmd_path(ctx->usersettings); break; case CIATYPE_CONTENT: offset = ctx->offsetcontent; size = ctx->sizecontent; path = settings_get_content_path(ctx->usersettings); break; case CIATYPE_META: offset = ctx->offsetmeta; size = ctx->sizemeta; path = settings_get_meta_path(ctx->usersettings);; break; default: fprintf(stderr, "Error, unknown CIA type specified\n"); return; break; } if (path == 0 || path->valid == 0) return; switch(type) { case CIATYPE_CERTS: fprintf(stdout, "Saving certs to %s\n", path->pathname); break; case CIATYPE_TIK: fprintf(stdout, "Saving tik to %s\n", path->pathname); break; case CIATYPE_TMD: fprintf(stdout, "Saving tmd to %s\n", path->pathname); break; case CIATYPE_CONTENT: body = tmd_get_body(&ctx->tmd); chunk = (ctr_tmd_contentchunk*)(body->contentinfo + (sizeof(ctr_tmd_contentinfo) * TMD_MAX_CONTENTS)); for(i = 0; i < getbe16(body->contentcount); i++) { sprintf(tmpname, "%s.%04x.%08x", path->pathname, getbe16(chunk->index), getbe32(chunk->id)); fprintf(stdout, "Saving content #%04x to %s\n", getbe16(chunk->index), tmpname); contentflags = getbe16(chunk->type); docrypto = contentflags & 1 && !(flags & PlainFlag); if(docrypto) // Decrypt if needed { ctx->iv[0] = (getbe16(chunk->index) >> 8) & 0xff; ctx->iv[1] = getbe16(chunk->index) & 0xff; ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); } cia_save_blob(ctx, tmpname, offset, getbe64(chunk->size) & 0xffffffff, docrypto); offset += getbe64(chunk->size) & 0xffffffff; chunk++; } memset(ctx->iv, 0, 16); return; break; case CIATYPE_META: fprintf(stdout, "Saving meta to %s\n", path->pathname); break; } cia_save_blob(ctx, path->pathname, offset, size, 0); }