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++; }
int main(int argc, char* argv[]) { int result = 0; fprintf(stdout, "stripiosplugin - based on IOS ELF stripper - by neimod\n"); if (argc < 3 ) { fprintf(stderr,"Usage: %s <in.elf> <out.bin> [strip addr]\n", argv[0]); return -1; } FILE* fin = fopen(argv[1], "rb"); FILE* fout = fopen(argv[2], "wb"); if (fin == 0 || fout == 0) { if (fin == 0) fprintf(stderr,"ERROR opening file %s\n", argv[1]); if (fout == 0) fprintf(stderr,"ERROR opening file %s\n", argv[2]); return 1; } elfheader header; if (fread(&header, sizeof(elfheader), 1, fin) != 1) { fprintf(stderr,"ERROR reading ELF header\n"); return 1; } unsigned long elfmagicword = getbe32(&header.ident0); if (elfmagicword != 0x7F454C46) { fprintf(stderr,"ERROR not a valid ELF\n"); return 1; } unsigned long shoff = getbe32(&header.shoff); unsigned short shentsize = getbe16(&header.shentsize); unsigned short shnum = getbe16(&header.shnum); unsigned long memsz = 0, filesz = 0; unsigned long vaddr = 0, paddr = 0; //printf("shoff %lx\n", shoff); //printf("size %x\n", shentsize); //printf("entries %x\n", shnum); elfshentry* entry = new elfshentry[shnum]; fseek(fin, shoff, SEEK_SET); if (fread(entry, sizeof(elfshentry), shnum, fin) != shnum) { fprintf(stderr,"ERROR reading sections header\n"); return 1; } unsigned short i = 0; while (i < shnum) { unsigned long addr = getbe32(&entry[i].shaddr); unsigned long offset = getbe32(&entry[i].shoffset); unsigned long size = getbe32(&entry[i].shsize); if ((addr & ~0xfff) == 0x1377E000) { printf("addr:%lx - ",addr); printf("offset:%lx - ",offset); printf("size: %lx\n", size); fseek(fin, offset, SEEK_SET); char *buf = new char[size]; if (!buf) { fprintf(stderr, "Error allocating memory\n"); return 2; } fread(buf, sizeof(char), size, fin); if (fwrite(buf, sizeof(char), size, fout) != size) { fprintf(stderr, "Error writing output file\n"); return 2; } delete [] buf; } i++; } if (entry) delete[] entry; if (fout) fclose(fout); if (fin) fclose(fin); return result; }
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); }
u32 InjectHealthAndSafety(u32 param) { u8* buffer = BUFFER_ADDRESS; PartitionInfo* ctrnand_info = GetPartitionInfo(P_CTRNAND); TitleListInfo* health = titleList + ((GetUnitPlatform() == PLATFORM_3DS) ? 3 : 4); TitleListInfo* health_alt = (GetUnitPlatform() == PLATFORM_N3DS) ? titleList + 3 : NULL; NcchHeader* ncch = (NcchHeader*) 0x20316000; char filename[64]; u32 offset_app[4]; u32 size_app[4]; u32 offset_tmd; u32 size_tmd; u32 size_hs; if (!(param & N_NANDWRITE)) // developer screwup protection return 1; if ((DebugSeekTitleInNand(&offset_tmd, &size_tmd, offset_app, size_app, health, 4) != 0) && (!health_alt || (DebugSeekTitleInNand(&offset_tmd, &size_tmd, offset_app, size_app, health_alt, 4) != 0))) return 1; if (size_app[0] > 0x400000) { Debug("H&S system app is too big!"); return 1; } if (DecryptNandToMem((void*) ncch, offset_app[0], 0x200, ctrnand_info) != 0) return 1; if (InputFileNameSelector(filename, NULL, "app", ncch->signature, 0x100, 0, false) != 0) return 1; if (!DebugFileOpen(filename)) return 1; size_hs = FileGetSize(); memset(buffer, 0, size_app[0]); if (size_hs > size_app[0]) { Debug("H&S inject app is too big!"); return 1; } if (!DebugFileRead(buffer, size_hs, 0)) { FileClose(); return 1; } FileClose(); if (!DebugFileCreate("hs.enc", true)) return 1; if (!DebugFileWrite(buffer, size_app[0], 0)) { FileClose(); return 1; } FileClose(); if (CryptNcch("hs.enc", 0, 0, 0, ncch->flags) != 0) return 1; Debug("Injecting H&S app..."); if (EncryptFileToNand("hs.enc", offset_app[0], size_app[0], ctrnand_info) != 0) return 1; Debug("Fixing TMD..."); u8* tmd_data = (u8*) 0x20316000; if (DecryptNandToMem(tmd_data, offset_tmd, size_tmd, ctrnand_info) != 0) return 1; tmd_data += (tmd_data[3] == 3) ? 0x240 : (tmd_data[3] == 4) ? 0x140 : 0x80; u8* content_list = tmd_data + 0xC4 + (64 * 0x24); u32 cnt_count = getbe16(tmd_data + 0x9E); if (GetHashFromFile("hs.enc", 0, size_app[0], content_list + 0x10) != 0) { Debug("Failed!"); return 1; } for (u32 i = 0, kc = 0; i < 64 && kc < cnt_count; i++) { u32 k = getbe16(tmd_data + 0xC4 + (i * 0x24) + 0x02); u8* chunk_hash = tmd_data + 0xC4 + (i * 0x24) + 0x04; sha_quick(chunk_hash, content_list + kc * 0x30, k * 0x30, SHA256_MODE); kc += k; } u8* tmd_hash = tmd_data + 0xA4; sha_quick(tmd_hash, tmd_data + 0xC4, 64 * 0x24, SHA256_MODE); tmd_data = (u8*) 0x20316000; if (EncryptMemToNand(tmd_data, offset_tmd, size_tmd, ctrnand_info) != 0) return 1; return 0; }
u32 DebugSeekTitleInNand(u32* offset_tmd, u32* size_tmd, u32* offset_app, u32* size_app, TitleListInfo* title_info, u32 max_cnt) { PartitionInfo* ctrnand_info = GetPartitionInfo(P_CTRNAND); u8* buffer = (u8*) 0x20316000; u32 cnt_count = 0; u32 tid_low = 0; u32 tmd_id = 0; Debug("Searching title \"%s\"...", title_info->name); Debug("Method 1: Search in title.db..."); if (SeekTitleInNandDb(&tid_low, &tmd_id, title_info) == 0) { char path[64]; sprintf(path, "TITLE %08X %08X CONTENT %08XTMD", (unsigned int) title_info->tid_high, (unsigned int) tid_low, (unsigned int) tmd_id); if (SeekFileInNand(offset_tmd, size_tmd, path, ctrnand_info) != 0) tid_low = 0; } if (!tid_low) { Debug("Method 2: Search in file system..."); for (u32 i = 0; i < 6; i++) { char path[64]; if (title_info->tid_low[i] == 0) continue; sprintf(path, "TITLE %08X %08X CONTENT ????????TMD", (unsigned int) title_info->tid_high, (unsigned int) title_info->tid_low[i]); if (SeekFileInNand(offset_tmd, size_tmd, path, ctrnand_info) == 0) { tid_low = title_info->tid_low[i]; break; } } } if (!tid_low) { Debug("Failed!"); return 1; } Debug("Found title %08X%08X", title_info->tid_high, tid_low); Debug("TMD0 found at %08X, size %ub", *offset_tmd, *size_tmd); if ((*size_tmd < 0xC4 + (0x40 * 0x24)) || (*size_tmd > 0x4000)) { Debug("TMD has bad size!"); return 1; } if (DecryptNandToMem(buffer, *offset_tmd, *size_tmd, ctrnand_info) != 0) return 1; u32 size_sig = (buffer[3] == 3) ? 0x240 : (buffer[3] == 4) ? 0x140 : (buffer[3] == 5) ? 0x80 : 0; if ((size_sig == 0) || (memcmp(buffer, "\x00\x01\x00", 3) != 0)) { Debug("Unknown signature type: %08X", getbe32(buffer)); return 1; } cnt_count = getbe16(buffer + size_sig + 0x9E); u32 size_tmd_expected = size_sig + 0xC4 + (0x40 * 0x24) + (cnt_count * 0x30); if (*size_tmd < size_tmd_expected) { Debug("TMD bad size (expected %ub)!", size_tmd_expected ); return 1; } buffer += size_sig + 0xC4 + (0x40 * 0x24); for (u32 i = 0; i < cnt_count && i < max_cnt; i++) { char path[64]; u32 cnt_id = getbe32(buffer + (0x30 * i)); if (i >= max_cnt) { Debug("APP%i was skipped", i); continue; } sprintf(path, "TITLE %08X %08X CONTENT %08XAPP", (unsigned int) title_info->tid_high, (unsigned int) tid_low, (unsigned int) cnt_id); if (SeekFileInNand(offset_app + i, size_app + i, path, ctrnand_info) != 0) { Debug("APP%i not found or fragmented!", i); return 1; } Debug("APP%i found at %08X, size %ukB", i, offset_app[i], size_app[i] / 1024); } return 0; }