int GetNcchInfo(ncch_info *info, ncch_hdr *hdr) { clrmem(info, sizeof(ncch_info)); info->titleId = u8_to_u64(hdr->titleId,LE); info->programId = u8_to_u64(hdr->programId,LE); u32 block_size = GetNcchBlockSize(hdr); info->formatVersion = u8_to_u16(hdr->formatVersion,LE); if(!IsCfa(hdr)){ info->exhdrOffset = sizeof(ncch_hdr); info->exhdrSize = u8_to_u32(hdr->exhdrSize,LE); info->acexOffset = (info->exhdrOffset + info->exhdrSize); info->acexSize = sizeof(access_descriptor); info->plainRegionOffset = (u64)(u8_to_u32(hdr->plainRegionOffset,LE)*block_size); info->plainRegionSize = (u64)(u8_to_u32(hdr->plainRegionSize,LE)*block_size); } info->logoOffset = (u64)(u8_to_u32(hdr->logoOffset,LE)*block_size); info->logoSize = (u64)(u8_to_u32(hdr->logoSize,LE)*block_size); info->exefsOffset = (u64)(u8_to_u32(hdr->exefsOffset,LE)*block_size); info->exefsSize = (u64)(u8_to_u32(hdr->exefsSize,LE)*block_size); info->exefsHashDataSize = (u64)(u8_to_u32(hdr->exefsHashSize,LE)*block_size); info->romfsOffset = (u64) (u8_to_u32(hdr->romfsOffset,LE)*block_size); info->romfsSize = (u64) (u8_to_u32(hdr->romfsSize,LE)*block_size); info->romfsHashDataSize = (u64)(u8_to_u32(hdr->romfsHashSize,LE)*block_size); return 0; }
void PrintNCSDHeaderData(NCSD_STRUCT *ctx, NCSD_HEADER *header, CARD_INFO_HEADER *card_info, DEV_CARD_INFO_HEADER *dev_card_info) { printf("[+] CCI Image Details\n"); u8 CardDevice = header->partition_flags[MEDIA_CARD_DEVICE_OLD] + header->partition_flags[MEDIA_CARD_DEVICE]; switch (header->partition_flags[MEDIA_TYPE_INDEX]){ case INNER_DEVICE: printf(" Media Type: INTERNAL_DEVICE\n"); break; case CARD1: printf(" Media Type: CARD1\n"); break; case CARD2: printf(" Media Type: CARD2\n > Writable Region:\n - Offset: 0x%llx\n - Size: 0x%llx (%lld MB)\n",ctx->WRITABLE_ADDRESS,ctx->CARD2_MAX_SAVEDATA_SIZE,(ctx->CARD2_MAX_SAVEDATA_SIZE/MB)); break; case EXTENDED_DEVICE: printf(" Media Type: EXTENDED_DEVICE\n"); break; } GetCHIPFullSize(ctx->MEDIA_SIZE,ctx->type); GetCCIDataSize(ctx->CCI_IMAGE_SIZE,ctx->type); GetCCIFileStatus(ctx->CCI_FILE_SIZE,ctx->CCI_FILE_STATUS,ctx->type); switch (CardDevice){ case CARD_DEVICE_NOR_FLASH: printf(" Additional Device: EEPROM\n"); break; case CARD_DEVICE_NONE: printf(" Additional Device: None\n"); break; case CARD_DEVICE_BT: printf(" Additional Device: BT\n"); break; } printf(" Partition Count: %d\n",ctx->partition_count); if(u8_to_u64(card_info->cver_title_id,LE) && u8_to_u16(card_info->cver_title_version,LE)){ //printf("CVer Title ID: %016llx\n",u8_to_u64(card_info->cver_title_id,LE)); //printf("CVer Title Ver: v%d\n",u8_to_u16(card_info->cver_title_version,LE)); GetCUPVersion(card_info); } if(!header->partition_flags[MEDIA_6X_SAVE_CRYPTO] && !header->partition_flags[MEDIA_CARD_DEVICE] && !header->partition_flags[MEDIA_CARD_DEVICE_OLD]){ printf(" Save Crypto: Repeating CTR Fail\n"); } else if(!header->partition_flags[MEDIA_6X_SAVE_CRYPTO] && (header->partition_flags[MEDIA_CARD_DEVICE] || header->partition_flags[MEDIA_CARD_DEVICE_OLD])){ printf(" Save Crypto: 2.2.0 KeyY Method\n"); } else if(header->partition_flags[MEDIA_6X_SAVE_CRYPTO] == 1 && !header->partition_flags[MEDIA_CARD_DEVICE_OLD]){ if(!header->partition_flags[MEDIA_CARD_DEVICE]){ printf(" Save Crypto: 2.2.0 KeyY Method\n"); } else if(header->partition_flags[MEDIA_CARD_DEVICE]){ printf(" Save Crypto: 6.0.0 KeyY Method\n"); } } printf("[+] CXI Partition\n"); printf(" Product Code: %.16s\n",card_info->partition_0_header.product_code); printf(" Company Code: %.2s\n",card_info->partition_0_header.maker_code); u32 UniqueID = u8_to_u32(&card_info->partition_0_header.program_id[1],LE)&0xffffff; printf(" Unique ID: %05x\n",UniqueID); printf(" Build Type: %s\n",ctx->BUILD_TYPE? "Debug or Development" : "Release"); printf(" SDK Version: %d.%d.%d %s\n",ctx->SDK_VER[0],ctx->SDK_VER[1],ctx->SDK_VER[2],ctx->SDK_PATCH); if(ctx->FW_VER[0] || ctx->FW_VER[1]) printf(" Req. Kernel Version: %d.%d-%d\n",ctx->FW_VER[0],ctx->FW_VER[1],ctx->FW_VER[2]); printf("[+] CFA Partitions\n"); for(int i = 1; i < 8; i++){ if(i == 1) printf(" E-Manual: %s\n",ctx->partition_data[i].active? "Yes" : "No"); else if(i == 2) printf(" DLP Child: %s\n",ctx->partition_data[i].active? "Yes" : "No"); else if(i == 7) printf(" Update Data: %s\n",ctx->partition_data[i].active? "Yes" : "No"); else{ if(ctx->partition_data[i].active) printf(" CFA %d: %s\n",i,ctx->partition_data[i].active? "Yes" : "No"); } } }
u64 GetDecTitleID(u8 *enc_used, u8 *enc_blank) { u8 xorpad[8]; u8 decTID[8]; xor_file(xorpad,FF_bytes,enc_blank,8); xor_file(decTID,enc_used,xorpad,8); return u8_to_u64(decTID,LE); }
void GetNewNcchIdForCci(u8 *newTid, u8 *srcTid, u8 index, tmd_hdr *tmdHdr) { u64 titleId = u8_to_u64(srcTid,LE) & 0xffffffffffff; if(tmdHdr && index == 7) titleId |= (u64)(GetTmdVersion(tmdHdr)) << 48; else titleId |= (u64)(index+4) << 48; u64_to_u8(newTid,titleId,LE); }
int ProcessNcchForCci(cci_settings *set) { u8 *ncch; ncch_hdr *hdr; u8 titleId[8]; u8 srcId[8]; if(set->options.cverDataPath && set->content.active[7]){ if(ProcessCverDataForCci(set)) return FAILED_TO_IMPORT_FILE; } for(int i = 0; i < CCI_MAX_CONTENT; i++){ if(set->content.active[i]){ ncch = set->content.data + set->content.dOffset[i]; if(!IsNcch(NULL,ncch)){ fprintf(stderr,"[CCI ERROR] NCCH %d is corrupt\n",i); return NCSD_INVALID_NCCH; } hdr = (ncch_hdr*)ncch; if(i > 0 && !set->options.noModTid){ if(set->options.verbose){ printf("[CCI] Modifying NCCH %d IDs\n",i); printf("[Old Ids]\n"); memdump(stdout," > TitleId: 0x",hdr->titleId,8); memdump(stdout," > ProgramId: 0x",hdr->programId,8); } GetNewNcchIdForCci(titleId,srcId,i,set->options.tmdHdr); if(ModifyNcchIds(ncch, titleId, srcId, set->keys)) return -1; if(set->options.verbose){ printf("[New Ids]\n"); memdump(stdout," > TitleId: 0x",hdr->titleId,8); memdump(stdout," > ProgramId: 0x",hdr->programId,8); } } set->content.titleId[i] = u8_to_u64(hdr->titleId,LE); if(i == 0) memcpy(srcId,hdr->titleId,8); } } return 0; }
void InstallTicket(std::string FullPath, std::string TitleId) { Handle hTik; u32 writtenbyte; std::string curr = get_file_contents(FullPath.c_str()); // Remove the ticket incase there was a bad one previously installed char* nTitleId = parse_string(TitleId); u64 titleId = u8_to_u64((u8*)nTitleId, BIG_ENDIAN); free (nTitleId); AM_DeleteTicket(titleId); // Install new ticket AM_InstallTicketBegin(&hTik); FSFILE_Write(hTik, &writtenbyte, 0, curr.c_str(), 0x100000, 0); AM_InstallTicketFinish(hTik); printf("Ticket Installed."); //delete temp ticket, ticket folder still exists... ugly. later stream directly to the handle remove(FullPath.c_str()); }
u64 GetPartitionSize(u8 *ncsd, u8 index) { cci_hdr *hdr = (cci_hdr*)ncsd; return (u64)u8_to_u64(hdr->offset_sizeTable[index].size,LE) * (u64)GetCtrBlockSize(hdr->flags[cciflag_MEDIA_BLOCK_SIZE]); }
void SetAesCtrOffset(u8 *ctr, u64 offset) { u64_to_u8(ctr+8,u8_to_u64(ctr+8,BE)|align(offset,16)/16,BE); }
int GetNCSDData(CCI_CONTEXT *ctx) { // Opening CCI FILE *cci = fopen(ctx->cci_file.argument,"rb"); if(ctx->ncsd_struct == NULL) return Fail; memset(ctx->ncsd_struct,0x0,sizeof(NCSD_STRUCT)); // Getting File Size of CCI File ctx->ncsd_struct->CCI_FILE_SIZE = GetFileSize_u64(ctx->cci_file.argument); NCSD_HEADER header; CARD_INFO_HEADER card_info; DEV_CARD_INFO_HEADER dev_card_info; // Reading CCI Header Sections fseek(cci,0x0,SEEK_SET); fread(&ctx->ncsd_struct->signature,0x100,1,cci); fseek(cci,0x100,SEEK_SET); fread(&header,sizeof(NCSD_HEADER),1,cci); fseek(cci,0x200,SEEK_SET); fread(&card_info,sizeof(CARD_INFO_HEADER),1,cci); fseek(cci,0x1200,SEEK_SET); fread(&dev_card_info,sizeof(DEV_CARD_INFO_HEADER),1,cci); // Checking CCI Magic if(u8_to_u32(header.magic,BE) != NCSD_MAGIC){ printf("[!] NCSD File is corrupt\n"); goto fail; } // Checking Media Type to see if suitable for CCI if(header.partition_flags[MEDIA_TYPE_INDEX] != CARD1 && header.partition_flags[MEDIA_TYPE_INDEX] != CARD2){ printf("[!] NCSD File is not CCI\n"); goto fail; } // Determining Media Unit Size ctx->ncsd_struct->MEDIA_UNIT_SIZE = 0x200*pow(2,header.partition_flags[MEDIA_UNIT_SIZE]); // Media Size ctx->ncsd_struct->MEDIA_SIZE = u8_to_u32(header.media_size,LE)*(ctx->ncsd_struct->MEDIA_UNIT_SIZE); // Get Writable Address ctx->ncsd_struct->WRITABLE_ADDRESS = u8_to_u32(card_info.writable_address,LE)*(ctx->ncsd_struct->MEDIA_UNIT_SIZE); ctx->ncsd_struct->CARD2_MAX_SAVEDATA_SIZE = ctx->ncsd_struct->MEDIA_SIZE - ctx->ncsd_struct->WRITABLE_ADDRESS; // Getting CCI Image Size by summing total size of NCSD Partitions u32 tmp = u8_to_u32(header.offsetsize_table[0].offset,LE); for(int i = 0; i < 8; i++){ tmp += u8_to_u32(header.offsetsize_table[i].size,LE); if (i == 6) ctx->ncsd_struct->CCI_S_TRIM_SIZE = tmp*ctx->ncsd_struct->MEDIA_UNIT_SIZE; // The Update Partition is always in Partition 7, so this size doesn't include Partition 7 } ctx->ncsd_struct->CCI_IMAGE_SIZE = tmp*ctx->ncsd_struct->MEDIA_UNIT_SIZE; // Comparing CCI File Size, with calculated size ctx->ncsd_struct->CCI_FILE_STATUS = 0; if(ctx->ncsd_struct->CCI_FILE_SIZE == ctx->ncsd_struct->MEDIA_SIZE) ctx->ncsd_struct->CCI_FILE_STATUS = IS_FULL; else if(ctx->ncsd_struct->CCI_FILE_SIZE == ctx->ncsd_struct->CCI_IMAGE_SIZE) ctx->ncsd_struct->CCI_FILE_STATUS = IS_TRIM; else if(ctx->ncsd_struct->CCI_FILE_SIZE == ctx->ncsd_struct->CCI_S_TRIM_SIZE) ctx->ncsd_struct->CCI_FILE_STATUS = IS_S_TRIM; else { /* printf("CCI_FILE_SIZE = 0x%llx\n",ctx->ncsd_struct->CCI_FILE_SIZE); printf("MEDIA_SIZE = 0x%llx\n",ctx->ncsd_struct->MEDIA_SIZE); printf("CCI_IMAGE_SIZE = 0x%llx\n",ctx->ncsd_struct->CCI_IMAGE_SIZE); printf("CCI_S_TRIM_SIZE = 0x%llx\n",ctx->ncsd_struct->CCI_S_TRIM_SIZE); */ ctx->ncsd_struct->CCI_FILE_STATUS = IS_MALFORMED; goto fail; } // Storing Partition Offsets ctx->ncsd_struct->partition_count = 0; for(int i = 0; i < 8; i++){ ctx->ncsd_struct->partition_data[i].offset = u8_to_u32(header.offsetsize_table[i].offset,LE)*ctx->ncsd_struct->MEDIA_UNIT_SIZE; ctx->ncsd_struct->partition_data[i].size = u8_to_u32(header.offsetsize_table[i].size,LE)*ctx->ncsd_struct->MEDIA_UNIT_SIZE; if(ctx->ncsd_struct->partition_data[i].offset != 0 && ctx->ncsd_struct->partition_data[i].size != 0) ctx->ncsd_struct->partition_data[i].active = True; ctx->ncsd_struct->partition_data[i].title_id = u8_to_u64(header.partition_id_table[i],LE); ctx->ncsd_struct->partition_data[i].fs_type = header.partitions_fs_type[i]; ctx->ncsd_struct->partition_data[i].crypto_type = header.partitions_crypto_type[i]; // Checking to see if partition actually exists if(ctx->ncsd_struct->partition_data[i].offset >= ctx->ncsd_struct->CCI_FILE_SIZE){ ctx->ncsd_struct->partition_data[i].active = False; break; } if(ctx->ncsd_struct->partition_data[i].active) ctx->ncsd_struct->partition_count++; } // Exit if no CXI found if(!ctx->ncsd_struct->partition_data[0].active){ printf("[!] CXI Not Found\n"); goto fail; } // Getting Data from CXI NCCH_HEADER cxi_header; fseek_64(cci,(ctx->ncsd_struct->partition_data[0].offset + 0x100),SEEK_SET); fread(&cxi_header,sizeof(NCCH_HEADER),1,cci); if(u8_to_u32(cxi_header.magic,BE) != NCCH_MAGIC){ printf("[!] CXI is Corrupt\n"); goto fail; } // Must have ExeFS region to be CXI if((cxi_header.flags[MEDIA_TYPE_INDEX] & ExeFS) != ExeFS){ printf("[!] CXI Not Found\n"); goto fail; } ctx->ncsd_struct->partition_data[0].content_type = CXI; // Getting Product Code fseek_64(cci,(ctx->ncsd_struct->partition_data[0].offset + 0x150),SEEK_SET); fread(ctx->ncsd_struct->partition_data[0].product_code,16,1,cci); // Checking 'other flag' for crypto settings if((cxi_header.flags[OtherFlag] & 1) == 1){ if((cxi_header.flags[OtherFlag] & 4) == 4) ctx->ncsd_struct->partition_data[0].ncch_crypto_key = no_crypto; else if ((cxi_header.program_id[4] && 0x10) == 0x10) ctx->ncsd_struct->partition_data[0].ncch_crypto_key = fixed_system; else ctx->ncsd_struct->partition_data[0].ncch_crypto_key = fixed_zeros; } else if(!cxi_header.flags[OtherFlag]){ ctx->ncsd_struct->partition_data[0].ncch_crypto_key = secure_key; if(cxi_header.flags[SecureCryptoType2Flag]) ctx->ncsd_struct->partition_data[0].ncch_crypto_key = secure_key2; } // Getting SDK Version u32 CXI_Media_Unit_Size = 0x200*pow(2,cxi_header.flags[MEDIA_UNIT_SIZE]); u64 plain_region_offset = ctx->ncsd_struct->partition_data[0].offset + u8_to_u32(cxi_header.plain_region_offset,LE)*CXI_Media_Unit_Size; u64 plain_region_size = u8_to_u32(cxi_header.plain_region_size,LE)*CXI_Media_Unit_Size; int result = GetSDKVersion(cci,plain_region_offset,plain_region_size,ctx->ncsd_struct); // If that failed, attempt to guess SDK version if(result){ strcpy(ctx->ncsd_struct->SDK_PATCH,"Release"); memset(&ctx->ncsd_struct->SDK_VER,0,3); ctx->ncsd_struct->SDK_VER[0] = 1; if(header.partition_flags[MEDIA_CARD_DEVICE_OLD]) ctx->ncsd_struct->SDK_VER[0] = 2; if(header.partition_flags[MEDIA_CARD_DEVICE]) ctx->ncsd_struct->SDK_VER[0] = 3; if(u8_to_u32(cxi_header.logo_region_offset,LE) || u8_to_u32(cxi_header.logo_region_size,LE)) ctx->ncsd_struct->SDK_VER[0] = 5; if(cxi_header.flags[SecureCryptoType2Flag]) ctx->ncsd_struct->SDK_VER[0] = 6; } // Getting Data from remaining CFA partitions for(int i = 1; i < 8; i++){ u8 magic[4]; fseek_64(cci,(ctx->ncsd_struct->partition_data[i].offset + 0x100),SEEK_SET); fread(&magic,4,1,cci); if(u8_to_u32(magic,BE) == NCCH_MAGIC){ u8 flags[8]; u8 ProgramID[8]; fseek_64(cci,(ctx->ncsd_struct->partition_data[i].offset + 0x188),SEEK_SET); fread(&flags,8,1,cci); fseek_64(cci,(ctx->ncsd_struct->partition_data[i].offset + 0x118),SEEK_SET); fread(&ProgramID,8,1,cci); if( (flags[NCCH_Type] & ExeFS) != ExeFS && (flags[NCCH_Type] & RomFS) == RomFS ){ if((flags[NCCH_Type] & Manual) == Manual && (flags[NCCH_Type] & SystemUpdate) != SystemUpdate) ctx->ncsd_struct->partition_data[i].content_type = CFA_Manual; else if((flags[NCCH_Type] & Child) == Child) ctx->ncsd_struct->partition_data[i].content_type = CFA_DLPChild; else if((flags[NCCH_Type] & SystemUpdate) == SystemUpdate && (flags[NCCH_Type] & Manual) != Manual) ctx->ncsd_struct->partition_data[i].content_type = CFA_Update; else ctx->ncsd_struct->partition_data[i].content_type = CFA_Simple; } else if((flags[NCCH_Type] & ExeFS) == ExeFS) ctx->ncsd_struct->partition_data[i].content_type = CXI; else ctx->ncsd_struct->partition_data[i].content_type = _unknown; // Checking 'other flag' for crypto settings if((flags[OtherFlag] & 1) == 1){ if((flags[OtherFlag] & 4) == 4) ctx->ncsd_struct->partition_data[i].ncch_crypto_key = no_crypto; else if ((ProgramID[4] && 0x10) == 0x10) ctx->ncsd_struct->partition_data[i].ncch_crypto_key = fixed_system; else ctx->ncsd_struct->partition_data[i].ncch_crypto_key = fixed_zeros; } else if(!flags[OtherFlag]){ ctx->ncsd_struct->partition_data[i].ncch_crypto_key = secure_key; if(flags[SecureCryptoType2Flag]) ctx->ncsd_struct->partition_data[i].ncch_crypto_key = secure_key2; } fseek_64(cci,(ctx->ncsd_struct->partition_data[i].offset + 0x150),SEEK_SET); fread(ctx->ncsd_struct->partition_data[i].product_code,16,1,cci); } else ctx->ncsd_struct->partition_data[i].content_type = _unknown; } if(ctx->flags[info]) PrintNCSDHeaderData(ctx->ncsd_struct,&header,&card_info,&dev_card_info); if(ctx->flags[part_info]) PrintCCIPartitionData(ctx->ncsd_struct,&header,&card_info,&dev_card_info); fclose(cci); return 0; fail: fclose(cci); return Fail; }
Result DownloadTitle(std::string titleId, std::string encTitleKey, std::string titleName, std::string region) { // Convert the titleid to a u64 for later use char* nTitleId = parse_string(titleId); u64 uTitleId = u8_to_u64((u8*)nTitleId, BIG_ENDIAN); free (nTitleId); // Wait for wifi to be available u32 wifi = 0; Result ret = 0; Result res = 0; while(R_SUCCEEDED(ret = ACU_GetWifiStatus(&wifi)) && wifi == 0) { hidScanInput(); if (hidKeysDown() & KEY_B) { ret = -1; break; } } if (R_FAILED(ret)) { printf("Unable to access internet.\n"); return ret; } std::string outputDir = "/CIAngel"; if (titleName.length() == 0) { titleName = titleId; } // Include region in filename if (region.length() > 0) { titleName = titleName + " (" + region + ")"; } std::string mode_text; if(config.GetMode() == CConfig::Mode::DOWNLOAD_CIA) { mode_text = "create"; } else if(config.GetMode() == CConfig::Mode::INSTALL_CIA) { mode_text = "install"; } printf("Starting - %s\n", titleName.c_str()); // If in install mode, download/install the SEED entry if (config.GetMode() == CConfig::Mode::INSTALL_CIA) { // Download and install the SEEDDB entry if install mode // Code based on code from FBI: https://github.com/Steveice10/FBI/blob/master/source/core/util.c#L254 // Copyright (C) 2015 Steveice10 u8 seed[16]; static const char* regionStrings[] = {"JP", "US", "GB", "GB", "HK", "KR", "TW"}; u8 region = CFG_REGION_USA; CFGU_GetSystemLanguage(®ion); if(region <= CFG_REGION_TWN) { char url[128]; snprintf(url, 128, SEED_URL "0x%016llX/ext_key?country=%s", uTitleId, regionStrings[region]); httpcContext context; if(R_SUCCEEDED(res = httpcOpenContext(&context, HTTPC_METHOD_GET, url, 1))) { httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); u32 responseCode = 0; if(R_SUCCEEDED(res = httpcBeginRequest(&context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(&context, &responseCode, 0))) { if(responseCode == 200) { u32 pos = 0; u32 bytesRead = 0; while(pos < sizeof(seed) && (R_SUCCEEDED(res = httpcDownloadData(&context, &seed[pos], sizeof(seed) - pos, &bytesRead)) || (u32)res == HTTPC_RESULTCODE_DOWNLOADPENDING)) { pos += bytesRead; } } else { res = -1; } } httpcCloseContext(&context); } if (R_SUCCEEDED(res)) { res = InstallSeed(uTitleId, seed); if (R_FAILED(res)) { printf("Error installing SEEDDB entry: 0x%lx\n", res); } } } } // Make sure the CIA doesn't already exist std::string cp = outputDir + "/" + titleName + ".cia"; if (config.GetMode() == CConfig::Mode::DOWNLOAD_CIA && FileExists(cp.c_str())) { printf("%s already exists.\n", cp.c_str()); return 0; } std::ofstream ofs; FILE *oh = fopen((outputDir + "/tmp/tmd").c_str(), "wb"); if (!oh) { printf("Error opening %s/tmp/tmd\n", outputDir.c_str()); return -1; } res = DownloadFile((NUS_URL + titleId + "/tmd").c_str(), oh, false); fclose(oh); if (res != 0) { printf("Could not download TMD. Internet/Title ID is OK?\n"); return res; } // Read version std::ifstream tmdfs; tmdfs.open(outputDir + "/tmp/tmd", std::ofstream::out | std::ofstream::in | std::ofstream::binary); char titleVersion[2]; tmdfs.seekg(top+0x9C, std::ios::beg); tmdfs.read(titleVersion, 0x2); tmdfs.close(); CreateTicket(titleId, encTitleKey, titleVersion, outputDir + "/tmp/ticket"); printf("Now %s the CIA...\n", mode_text.c_str()); res = ProcessCIA(outputDir + "/tmp", titleName); if (res != 0) { printf("Could not %s the CIA.\n", mode_text.c_str()); return res; } if (config.GetMode() == CConfig::Mode::DOWNLOAD_CIA) { rename((outputDir + "/tmp/" + titleName + ".cia").c_str(), (outputDir + "/" + titleName + ".cia").c_str()); } printf(" DONE!\n"); return res; }
static inline u64 dispatch_t210(t210 a211, u8 a212) { return u8_to_u64(a212); }
int GetNCSDData(USER_CONTEXT *ctx, NCSD_STRUCT *ncsd_struct, FILE *ncsd) { if(ncsd_struct == NULL) return Fail; memset(ncsd_struct,0x0,sizeof(NCSD_STRUCT)); NCSD_HEADER header; CARD_INFO_HEADER card_info; DEV_CARD_INFO_HEADER dev_card_info; fseek(ncsd,0x0,SEEK_SET); fread(&ncsd_struct->signature,0x100,1,ncsd); fseek(ncsd,0x100,SEEK_SET); fread(&header,sizeof(NCSD_HEADER),1,ncsd); fseek(ncsd,0x200,SEEK_SET); fread(&card_info,sizeof(CARD_INFO_HEADER),1,ncsd); fseek(ncsd,0x1200,SEEK_SET); fread(&dev_card_info,sizeof(DEV_CARD_INFO_HEADER),1,ncsd); ctr_sha(&header,sizeof(NCSD_HEADER),ncsd_struct->ncsd_header_hash,CTR_SHA_256); if(u8_to_u32(header.magic,BE) != NCSD_MAGIC){ printf("[!] ROM is Corrupt\n"); return Fail; } ncsd_struct->sig_valid = ctr_rsa(ncsd_struct->ncsd_header_hash,ncsd_struct->signature,ctx->keys.NcsdCfa.n,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); u32 media_size = ((header.partition_flags[6] + 1)*0x200); ncsd_struct->rom_size = u8_to_u32(header.rom_size,LE)*media_size; ncsd_struct->used_rom_size = 0; if(ncsd_struct->used_rom_size == 0){ u32 tmp = u8_to_u32(header.offsetsize_table[0].offset,LE); for(int i = 0; i < 8; i++){ tmp += u8_to_u32(header.offsetsize_table[i].size,LE); } ncsd_struct->used_rom_size = tmp*media_size; } for(int i = 0; i < 8; i++){ ncsd_struct->partition_data[i].offset = u8_to_u32(header.offsetsize_table[i].offset,LE)*media_size; ncsd_struct->partition_data[i].size = u8_to_u32(header.offsetsize_table[i].size,LE)*media_size; if(ncsd_struct->partition_data[i].offset != 0 && ncsd_struct->partition_data[i].size != 0) ncsd_struct->partition_data[i].active = True; ncsd_struct->partition_data[i].title_id = u8_to_u64(header.partition_id_table[i],LE); ncsd_struct->partition_data[i].fs_type = header.partitions_fs_type[i]; ncsd_struct->partition_data[i].crypto_type = header.partitions_crypto_type[i]; u8 magic[4]; fseek_64(ncsd,(ncsd_struct->partition_data[i].offset + 0x100),SEEK_SET); fread(&magic,4,1,ncsd); if(u8_to_u32(magic,BE) == NCCH_MAGIC){ u8 flags[8]; u8 flag_bool[8]; fseek_64(ncsd,(ncsd_struct->partition_data[i].offset + 0x188),SEEK_SET); fread(&flags,8,1,ncsd); resolve_flag(flags[5],flag_bool); if(flag_bool[1] == False && flag_bool[0] == True){ if(flag_bool[2] == False && flag_bool[3] == True) ncsd_struct->partition_data[i].content_type = CFA_Manual; else if(flag_bool[2] == True && flag_bool[3] == True) ncsd_struct->partition_data[i].content_type = CFA_DLPChild; else if(flag_bool[2] == True && flag_bool[3] == False) ncsd_struct->partition_data[i].content_type = CFA_Update; else ncsd_struct->partition_data[i].content_type = _unknown; } else if(flag_bool[1] == True) ncsd_struct->partition_data[i].content_type = CXI; else ncsd_struct->partition_data[i].content_type = _unknown; } else ncsd_struct->partition_data[i].content_type = _unknown; } if(u8_to_u64(card_info.cver_title_id,LE) == 0){ u8 stock_title_key[0x10] = {0x6E, 0xC7, 0x5F, 0xB2, 0xE2, 0xB4, 0x87, 0x46, 0x1E, 0xDD, 0xCB, 0xB8, 0x97, 0x11, 0x92, 0xBA}; if(memcmp(dev_card_info.TitleKey,stock_title_key,0x10) == 0) ncsd_struct->type = dev_external_SDK; else ncsd_struct->type = dev_internal_SDK; } else ncsd_struct->type = retail; /** if(ncsd_struct->type != retail){ u8 iv[16]; u8 key[16]; memset(&iv,0x0,16); memset(&key,0x0,16); //iv[0] = header.partition_flags[7]; //memcpy(iv+11,header.partition_flags,5); u8 tmp[16] = {0xB2, 0x57, 0xA7, 0xC0, 0x24, 0xC8, 0xC1, 0xB0, 0x75, 0x91, 0xC4, 0xC5, 0x1D, 0x96, 0x67, 0x4F}; memcpy(iv,tmp,16); memcpy(key,common_dpki_aesKey,16); ctr_aes_context aes; memset(&aes,0x0,sizeof(ctr_aes_context)); memdump(stdout,"ENC Title Key: ",dev_card_info.TitleKey,0x10); ctr_init_aes_cbc(&aes,key,iv,DEC); ctr_aes_cbc(&aes,dev_card_info.TitleKey,dev_card_info.TitleKey,0x10,DEC); memdump(stdout,"DEC Title Key: ",dev_card_info.TitleKey,0x10); } **/ /** for(int i = 0; i < 8; i++){ if(ncsd_struct->partition_data[i].active == True){ ncsd_struct->partition_data[i].sig_valid = VerifyNCCHSection(ctx,dev_card_info.TitleKey,ncsd_struct->partition_data[i].offset,ncsd); } } **/ ncsd_struct->valid = True; if(ctx->flags[info]) PrintNCSDData(ncsd_struct,&header,&card_info,&dev_card_info); return 0; }
void PrintNCSDData(NCSD_STRUCT *ctx, NCSD_HEADER *header, CARD_INFO_HEADER *card_info, DEV_CARD_INFO_HEADER *dev_card_info) { if(ctx->valid != True){ printf("[!] NCSD Corrupt\n"); return; } printf("[+] NCSD\n"); switch(ctx->sig_valid){ case Good : memdump(stdout,"Signature(Good): ",ctx->signature,0x100); break; case Fail : memdump(stdout,"Signature(Fail): ",ctx->signature,0x100); break; case NotChecked: memdump(stdout,"Signature: ",ctx->signature,0x100); break; } switch(ctx->type){ case retail : printf("Target: Retail/Production\n"); printf("CVer Title ID: %016llx\n",u8_to_u64(card_info->cver_title_id,LE)); printf("CVer Title Ver: v%d\n",u8_to_u16(card_info->cver_title_version,LE)); char *FW_STRING = malloc(10); memset(FW_STRING,0,10); GetMin3DSFW(FW_STRING,card_info); printf("Min 3DS Firm: %s\n",FW_STRING); _free(FW_STRING); break; case dev_internal_SDK : printf("Target: Debug/Development\n"); printf("SDK Type: Nintendo Internal SDK\n"); memdump(stdout,"Title Key: ",dev_card_info->TitleKey,0x10); break; case dev_external_SDK : printf("Target: Debug/Development\n"); printf("SDK Type: Nintendo 3RD Party SDK\n"); memdump(stdout,"Title Key: ",dev_card_info->TitleKey,0x10); break; } if(ctx->rom_size >= GB){ printf("ROM Cart Size: %lld GB",ctx->rom_size/GB); printf(" (%lld Gbit)\n",(ctx->rom_size/GB)*8); } else{ printf("ROM Cart Size: %lld MB",ctx->rom_size/MB); u32 tmp = (ctx->rom_size/MB)*8; if(tmp >= 1024) printf(" (%d Gbit)\n",tmp/1024); else printf(" (%d Mbit)\n",tmp); } if(ctx->used_rom_size >= MB){ printf("ROM Used Size: %lld MB",ctx->used_rom_size/MB); printf(" (0x%llx bytes)\n",ctx->used_rom_size); } else if(ctx->used_rom_size >= KB){ printf("ROM Used Size: %lld KB",ctx->used_rom_size/KB); printf(" (0x%llx bytes)\n",ctx->used_rom_size); } printf("NCSD Title ID: %016llx\n",u8_to_u64(header->title_id,LE)); memdump(stdout,"ExHeader Hash: ",header->exheader_hash,0x20); printf("AddHeader Size: 0x%x\n",u8_to_u32(header->additional_header_size,LE)); printf("Sector 0 Offset: 0x%x\n",u8_to_u32(header->sector_zero_offset,LE)); memdump(stdout,"Flags: ",header->partition_flags,8); printf("\n"); for(int i = 0; i < 8; i++){ if(ctx->partition_data[i].active == True){ printf("Partition %d\n",i); printf(" Title ID: %016llx\n",ctx->partition_data[i].title_id); printf(" Content Type: "); switch(ctx->partition_data[i].content_type){ case _unknown : printf("Unknown\n"); break; case CXI : printf("Application\n"); break; case CFA_Manual : printf("Electronic Manual\n"); break; case CFA_DLPChild : printf("Download Play Child\n"); break; case CFA_Update : printf("Software Update Partition\n"); break; } printf(" FS Type: %x\n",ctx->partition_data[i].fs_type); printf(" Crypto Type: %x\n",ctx->partition_data[i].crypto_type); printf(" Offset: 0x%x\n",ctx->partition_data[i].offset); printf(" Size: 0x%x\n",ctx->partition_data[i].size); printf("\n"); } } }