void ProcessExeFS(PartitionInfo* info){ //We expect Exefs to take just a block. Why? No exefs right now reached 8MB. if(info->keyslot == 0x2C){ DecryptPartition(info); }else if(info->keyslot == 0x25){ //The new keyX is a bit tricky, 'couse only .code is encrypted with it PartitionInfo myInfo; memcpy((void*)&myInfo, (void*)info, sizeof(PartitionInfo)); u8 OriginalCTR[16]; memcpy(OriginalCTR, info->ctr, 16); myInfo.keyslot = 0x2C; myInfo.size = 0x200; DecryptPartition(&myInfo); add_ctr(myInfo.ctr, 0x200 / 16); if(myInfo.buffer[0] == '.' && myInfo.buffer[1] == 'c' && myInfo.buffer[2] == 'o' && myInfo.buffer[3] == 'd' && myInfo.buffer[4] == 'e'){ //The 7.xKey encrypted .code partition u32 codeSize = *((unsigned int*)(myInfo.buffer + 0x0C)); u32 nextSection = *((unsigned int*)(myInfo.buffer + 0x18)) + 0x200; myInfo.buffer += 0x200; myInfo.size = codeSize; myInfo.keyslot = 0x25; DecryptPartition(&myInfo); //The rest is normally encrypted memcpy((void*)&myInfo, (void*)info, sizeof(PartitionInfo)); myInfo.buffer += nextSection; myInfo.size -= nextSection; myInfo.keyslot = 0x2C; myInfo.ctr = OriginalCTR; add_ctr(myInfo.ctr, nextSection/16); DecryptPartition(&myInfo); }else{ myInfo.size = info->size-0x200; myInfo.buffer += 0x200; DecryptPartition(&myInfo); } } }
void nand_readsectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out, unsigned int partition) { PartitionInfo info; u8 myCtr[16]; for (int i = 0; i < 16; i++) { myCtr[i] = NANDCTR[i]; } info.ctr = myCtr; info.buffer = out; info.size = numsectors * 0x200; info.keyY = NULL; add_ctr(info.ctr, partition / 16); switch (partition) { case TWLN : info.keyslot = 0x3; break; case TWLP : info.keyslot = 0x3; break; case AGB_SAVE : info.keyslot = 0x7; break; case FIRM0 : info.keyslot = 0x6; break; case FIRM1 : info.keyslot = 0x6; break; case CTRNAND : info.keyslot = 0x4; break; } add_ctr(info.ctr, sector_no * 0x20); sdmmc_nand_readsectors(sector_no + partition / 0x200, numsectors, out); DecryptPartition(&info); }
u32 CryptBuffer(CryptBufferInfo *info) { u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->ctr, 16); u8* buffer = info->buffer; u32 size = info->size; u32 mode = info->mode; if (info->setKeyY) { u8 keyY[16] __attribute__((aligned(32))); memcpy(keyY, info->keyY, 16); setup_aeskeyY(info->keyslot, keyY); info->setKeyY = 0; } use_aeskey(info->keyslot); for (u32 i = 0; i < size; i += 0x10, buffer += 0x10) { if (((mode & (0x7 << 27)) != AES_ECB_DECRYPT_MODE) && ((mode & (0x7 << 27)) != AES_ECB_ENCRYPT_MODE)) set_ctr(ctr); if ((mode & (0x7 << 27)) == AES_CBC_DECRYPT_MODE) memcpy(ctr, buffer, 0x10); aes_decrypt((void*) buffer, (void*) buffer, 1, mode); if ((mode & (0x7 << 27)) == AES_CBC_ENCRYPT_MODE) memcpy(ctr, buffer, 0x10); else if ((mode & (0x7 << 27)) == AES_CTR_MODE) add_ctr(ctr, 0x1); } memcpy(info->ctr, ctr, 16); return 0; }
void emunand_writesectors(uint32_t sector_no, uint32_t numsectors, uint8_t *out, unsigned int partition) { PartitionInfo info; uint8_t myCtr[16]; if (partition == CTRNAND && getMpInfo() == MPINFO_KTR) partition = KTR_CTRNAND; //SWITCH TO KTR_CTRNAND IF ON N3DS for (int i = 0; i < 16; i++) { myCtr[i] = NANDCTR[i]; } info.ctr = myCtr; info.buffer = out; info.size = numsectors * 0x200; info.keyY = NULL; add_ctr(info.ctr, partition / 16); switch (partition) { case TWLN: info.keyslot = 0x3; break; case TWLP: info.keyslot = 0x3; break; case AGB_SAVE: info.keyslot = 0x7; break; case FIRM0: info.keyslot = 0x6; break; case FIRM1: info.keyslot = 0x6; break; case CTRNAND: info.keyslot = 0x4; break; case KTR_CTRNAND: info.keyslot = 0x5; break; } add_ctr(info.ctr, sector_no * 0x20); DecryptPartition(&info); sdmmc_sdcard_writesectors(sector_no + partition / 0x200, numsectors, out); //Stubbed, i don't wanna risk }
u32 DecryptPartition(PartitionInfo* info){ size_t bytesWritten; if (info->keyY != NULL) setup_aeskey(info->keyslot, AES_BIG_INPUT | AES_NORMAL_INPUT, info->keyY); use_aeskey(info->keyslot); u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->ctr, 16); u32 size_bytes = info->size; for (u32 i = 0; i < size_bytes; i += BLOCK_SIZE) { u32 j; for (j = 0; (j < BLOCK_SIZE) && (i + j < size_bytes); j += 16) { set_ctr(AES_BIG_INPUT | AES_NORMAL_INPUT, ctr); aes_decrypt((void*)info->buffer + j, (void*)info->buffer + j, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 1); } } return 0; }
u32 DecryptPartition(PartitionInfo* info){ size_t bytesWritten; if(info->keyY != NULL) setup_aeskey(info->keyslot, AES_BIG_INPUT|AES_NORMAL_INPUT, info->keyY); use_aeskey(info->keyslot); u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->ctr, 16); u32 size_bytes = info->size; for (u32 i = 0; i < size_bytes; i += BLOCK_SIZE) { u32 j; for (j = 0; (j < BLOCK_SIZE) && (i+j < size_bytes); j+= 16) { set_ctr(AES_BIG_INPUT|AES_NORMAL_INPUT, ctr); aes_decrypt((void*)info->buffer+j, (void*)info->buffer+j, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 1); TryScreenShot(); //Putting it here allows us to take screenshots at any decryption point, since everyting loops in this } } return 0; }
void BD_Factory::add_gaol(double f_RES) { Array<const ExprNode> new_args(sip.n_arg+sip.p_arg); int I=0; int J=0; for (int K=0; K<sip.n_arg+sip.p_arg; K++) { if (!sip.is_param[K]) new_args.set_ref(K,new_vars[I++]); else // necessary for applying goal function but ignored at the end // (there is no parameter in the goal function): new_args.set_ref(K,ExprConstant::new_(sip.p_domain[J++],true)); } const ExprNode* goal_node=&((*sip.sys.goal)(new_args)); if (problem==ORA) { const ExprConstant& f_RES_node=ExprConstant::new_scalar(f_RES); goal_node = &((*goal_node) - f_RES); add_ctr(ExprCtr(*goal_node,LEQ)); const ExprNode& minus_eta=-new_vars[sip.n_arg]; add_goal(minus_eta); delete &minus_eta; } else { add_goal(*goal_node); } // cleanup for (int K=0; K<sip.n_arg+sip.p_arg; K++) { if (sip.is_param[K]) { if (!new_args[K].fathers.is_empty()) ibex_error("parameters in the objective"); delete &new_args[K]; } } cleanup(*goal_node,false); f_ctrs_copy.clone.clean(); }
u32 DecryptBuffer(DecryptBufferInfo *info) { u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->CTR, 16); u8* buffer = info->buffer; u32 size = info->size; if (info->setKeyY) { setup_aeskey(info->keyslot, AES_BIG_INPUT | AES_NORMAL_INPUT, info->keyY); info->setKeyY = 0; } use_aeskey(info->keyslot); for (u32 i = 0; i < size; i += 0x10, buffer += 0x10) { set_ctr(AES_BIG_INPUT | AES_NORMAL_INPUT, ctr); aes_decrypt((void*) buffer, (void*) buffer, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 0x1); } memcpy(info->CTR, ctr, 16); return 0; }
void BD_Factory::add_discretized_ctr(double eps_g) { std::vector<double>* samples; switch (problem) { case LBD : samples = sip.LBD_samples; break; case UBD : samples = sip.UBD_samples; break; case ORA : samples = sip.ORA_samples; break; } // For each constraint (even non-quantified) for (int c=0; c<sip.sys.nb_ctr; c++) { //cout << "Constraint: " << sip.sys.ctrs[c] << endl; // build an initial set of (all) parameters values. // (includes those not involved in the constraint but these // values will be ignored anyway) Vector p_box(sip.p); for (int j=0; j<sip.p; j++) p_box[j]=samples[j].front(); // push this initial combination in a list list<Vector> p_boxes; //cout << " add p_box:" << p_box << endl; p_boxes.push_back(p_box); // We then create all combinations of LBD/UBD_samples for the parameters // involved in the constraint c, via a recursive Cartesian product // with all previously existing combinations. for (int j=0; j<sip.p; j++) { //cout << " param n°" << j << "=" << sip.varset.param(j); if (sip.sys.ctrs[c].f.used(sip.varset.param(j))) { //cout << " **used**" << endl; // for each box already inside the list... for (list<Vector>::iterator it2=p_boxes.begin(); it2!=p_boxes.end(); it2=p_boxes.erase(it2)) { p_box=*it2; // ... we instantiate the domain of the parameter to each of its current // sampled values for (vector<double>::iterator it=samples[j].begin(); it!=samples[j].end(); it++) { p_box[j]=*it; p_boxes.push_front(p_box); // pushed at the beginning -> no impact for the enclosing loop //cout << " add p_box:" << p_box << endl; } } } else { //cout << " (unused)" << endl; } } // all the constraints generated with constraint n°c // (stored in an array for cleanup) Array<const ExprNode> exprs_ctr(p_boxes.size()); int i=0; // We generate one constraint for each p_box for (list<Vector>::iterator it=p_boxes.begin(); it!=p_boxes.end(); it++) { // transform the "flat" box into a list of domains // (which is necessary for composition of functions expression) load(sip.p_domain, *it); Array<const ExprNode> new_args(sip.n_arg+sip.p_arg); int I=0; int J=0; for (int K=0; K<sip.n_arg+sip.p_arg; K++) { if (!sip.is_param[K]) // a variable x in the original system is a variable in // the LBD/UBD problem new_args.set_ref(K,new_vars[I++]); else // a parameter becomes a constant // (note: can have a reference because the expression is copied afterward) new_args.set_ref(K,ExprConstant::new_(sip.p_domain[J++],true)); } const ExprNode* expr_ctr_tmp=&sip.sys.ctrs[c].f(new_args); if (problem==UBD) { // In the UBD problem, shift the constraint with eps_g const ExprConstant& eps_node=ExprConstant::new_scalar(sip.sys.ctrs[c].op==LEQ ? eps_g : -eps_g); expr_ctr_tmp = & (*expr_ctr_tmp + eps_node); } else if (problem==ORA) { // in the ORA problem, shift the constraint with eta const ExprNode& eta=new_vars[sip.n_arg]; const ExprNode& eta_node=sip.sys.ctrs[c].op==LEQ ? eta : -eta; expr_ctr_tmp = & (*expr_ctr_tmp + eta_node); } // cleanup the constants created which // do not appear in the constraint expression for (int K=0; K<sip.n_arg+sip.p_arg; K++) { if (sip.is_param[K] && new_args[K].fathers.is_empty()) delete &new_args[K]; } const ExprNode& expr_ctr=expr_ctr_tmp->simplify(); ExprCtr ctr(expr_ctr, sip.sys.ctrs[c].op); //cout << " generated constraint:" << ctr << endl; add_ctr(ctr); exprs_ctr.set_ref(i++,expr_ctr); } cleanup(exprs_ctr,false); f_ctrs_copy.clone.clean(); //cout << endl; } }
u32 NandPadgen() { u8* ctrStart = FindNandCtr(); if (ctrStart == NULL) return 1; u8 ctr[16] = {0x0}; u32 i = 0; for(i = 0; i < 16; i++) ctr[i] = *(ctrStart + (15 - i)); //The CTR is stored backwards in memory. add_ctr(ctr, 0xB93000); //The CTR stored in memory would theoretically be for NAND block 0, so we need to increment it some. u32 keyslot = 0x0; u32 nand_size = 0; switch (GetUnitPlatform()) { case PLATFORM_3DS: keyslot = 0x4; nand_size = 758; break; case PLATFORM_N3DS: keyslot = 0x5; nand_size = 1055; break; } Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size); Debug("Filename: nand.fat16.xorpad"); PadInfo padInfo = {.keyslot = keyslot, .setKeyY = 0, .size_mb = nand_size , .filename = "/nand.fat16.xorpad"}; memcpy(padInfo.CTR, ctr, 16); u32 result = CreatePad(&padInfo); if(result == 0) { Debug("Done!"); return 0; } else { return 1; } } u32 CreatePad(PadInfo *info) { static const uint8_t zero_buf[16] __attribute__((aligned(16))) = {0}; u8* buffer = BUFFER_ADDRESS; u32 result = 0; if (!FileCreate(info->filename, true)) // No DebugFileCreate() here - messages are already given return 1; if(info->setKeyY) setup_aeskey(info->keyslot, AES_BIG_INPUT | AES_NORMAL_INPUT, info->keyY); use_aeskey(info->keyslot); u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->CTR, 16); u32 size_bytes = info->size_mb * 1024*1024; for (u32 i = 0; i < size_bytes; i += BUFFER_MAX_SIZE) { u32 curr_block_size = min(BUFFER_MAX_SIZE, size_bytes - i); for (u32 j = 0; j < curr_block_size; j+= 16) { set_ctr(AES_BIG_INPUT | AES_NORMAL_INPUT, ctr); aes_decrypt((void*)zero_buf, (void*)buffer + j, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 1); } ShowProgress(i, size_bytes); if (!DebugFileWrite((void*)buffer, curr_block_size, i)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; } u32 NandDumper() { u8* buffer = BUFFER_ADDRESS; u32 nand_size = (GetUnitPlatform() == PLATFORM_3DS) ? 0x3AF00000 : 0x4D800000; u32 result = 0; Debug("Dumping System NAND. Size (MB): %u", nand_size / (1024 * 1024)); if (!DebugFileCreate("/NAND.bin", true)) return 1; u32 n_sectors = nand_size / NAND_SECTOR_SIZE; for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { ShowProgress(i, n_sectors); sdmmc_nand_readsectors(i, SECTORS_PER_READ, buffer); if(!DebugFileWrite(buffer, NAND_SECTOR_SIZE * SECTORS_PER_READ, i * NAND_SECTOR_SIZE)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; } u32 NandPartitionsDumper() { u32 ctrnand_offset; u32 ctrnand_size; u32 keyslot; switch (GetUnitPlatform()) { case PLATFORM_3DS: ctrnand_offset = 0x0B95CA00; ctrnand_size = 0x2F3E3600; keyslot = 0x4; break; case PLATFORM_N3DS: ctrnand_offset = 0x0B95AE00; ctrnand_size = 0x41D2D200; keyslot = 0x5; break; } // see: http://3dbrew.org/wiki/Flash_Filesystem Debug("Dumping firm0.bin: %s!", DumpPartition("firm0.bin", 0x0B130000, 0x00400000, 0x6) == 0 ? "succeeded" : "failed"); Debug("Dumping firm1.bin: %s!", DumpPartition("firm1.bin", 0x0B530000, 0x00400000, 0x6) == 0 ? "succeeded" : "failed"); Debug("Dumping ctrnand.bin: %s!", DumpPartition("ctrnand.bin", ctrnand_offset, ctrnand_size, keyslot) == 0 ? "succeeded" : "failed"); return 0; }
u32 SdPadgen() { u32 result; SdInfo *info = (SdInfo*)0x20316000; u8 movable_seed[0x120] = {0}; // Load console 0x34 keyY from movable.sed if present on SD card if (DebugFileOpen("/movable.sed")) { if (!DebugFileRead(&movable_seed, 0x120, 0)) { FileClose(); return 1; } FileClose(); if (memcmp(movable_seed, "SEED", 4) != 0) { Debug("movable.sed is too corrupt!"); return 1; } setup_aeskey(0x34, AES_BIG_INPUT|AES_NORMAL_INPUT, &movable_seed[0x110]); use_aeskey(0x34); } if (!DebugFileOpen("/SDinfo.bin")) return 1; if (!DebugFileRead(info, 4, 0)) { FileClose(); return 1; } if (!info->n_entries || info->n_entries > MAX_ENTRIES) { Debug("Too many/few entries!"); return 1; } Debug("Number of entries: %i", info->n_entries); if (!DebugFileRead(info->entries, info->n_entries * sizeof(SdInfoEntry), 4)) { FileClose(); return 1; } FileClose(); for(u32 i = 0; i < info->n_entries; i++) { Debug ("Creating pad number: %i. Size (MB): %i", i+1, info->entries[i].size_mb); PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb}; memcpy(padInfo.CTR, info->entries[i].CTR, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); result = CreatePad(&padInfo); if (!result) Debug("Done!"); else return 1; } return 0; } static u8* FindNandCtr() { static const char* versions[] = {"4.x", "5.x", "6.x", "7.x", "8.x", "9.x"}; static const u8* version_ctrs[] = { (u8*)0x080D7CAC, (u8*)0x080D858C, (u8*)0x080D748C, (u8*)0x080D740C, (u8*)0x080D74CC, (u8*)0x080D794C }; static const u32 version_ctrs_len = sizeof(version_ctrs) / sizeof(u32); for (u32 i = 0; i < version_ctrs_len; i++) { if (*(u32*)version_ctrs[i] == 0x5C980) { Debug("System version %s", versions[i]); return (u8*)(version_ctrs[i] + 0x30); } } // If value not in previous list start memory scanning (test range) for (u8* c = (u8*)0x080D8FFF; c > (u8*)0x08000000; c--) { if (*(u32*)c == 0x5C980 && *(u32*)(c + 1) == 0x800005C9) { Debug("CTR Start 0x%08X", c + 0x30); return c + 0x30; } } return NULL; } u32 DumpPartition(char* filename, u32 offset, u32 size, u32 keyslot) { DecryptBufferInfo info; u8* buffer = BUFFER_ADDRESS; u8* ctrStart = FindNandCtr(); u32 result = 0; Debug("Dumping System NAND Partition. Size (MB): %u", size / (1024 * 1024)); Debug("Filename: %s", filename); if (ctrStart == NULL) return 1; info.keyslot = keyslot; info.setKeyY = 0; info.size = SECTORS_PER_READ * NAND_SECTOR_SIZE; info.buffer = buffer; for (u32 i = 0; i < 16; i++) { info.CTR[i] = *(ctrStart + (0xF - i)); // The CTR is stored backwards in memory. } add_ctr(info.CTR, offset / 0x10); if (!DebugFileCreate(filename, true)) return 1; u32 n_sectors = size / NAND_SECTOR_SIZE; u32 start_sector = offset / NAND_SECTOR_SIZE; for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { ShowProgress(i, n_sectors); sdmmc_nand_readsectors(start_sector + i, SECTORS_PER_READ, buffer); DecryptBuffer(&info); if (!DebugFileWrite(buffer, NAND_SECTOR_SIZE * SECTORS_PER_READ, i * NAND_SECTOR_SIZE)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; }
u32 SdPadgen(u32 param) { (void) (param); // param is unused here SdInfo *info = (SdInfo*) 0x20316000; // setup AES key from SD SetupSdKeyY0x34(false, NULL); if (!DebugFileOpen("SDinfo.bin")) return 1; if (!DebugFileRead(info, 4, 0)) { FileClose(); return 1; } if (!info->n_entries || info->n_entries > MAX_ENTRIES) { FileClose(); Debug("Bad number of entries!"); return 1; } if (!DebugFileRead(info->entries, info->n_entries * sizeof(SdInfoEntry), 4)) { FileClose(); return 1; } FileClose(); Debug("Number of entries: %i", info->n_entries); for(u32 i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb, .mode = AES_CNT_CTRNAND_MODE}; memcpy(padInfo.ctr, info->entries[i].ctr, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); Debug ("%2i: %s (%iMB)", i, info->entries[i].filename, info->entries[i].size_mb); if (CreatePad(&padInfo) != 0) return 1; // this can't fail anyways } return 0; } u32 SdPadgenDirect(u32 param) { (void) (param); // param is unused here SdInfo *info = (SdInfo*) 0x20316000; char basepath[256]; u8 movable_keyY[16]; if (SetupSdKeyY0x34(true, movable_keyY) != 0) return 1; // movable.sed has to be present in NAND Debug(""); if (SdFolderSelector(basepath, movable_keyY) != 0) return 1; Debug(""); if (SdInfoGen(info, basepath) != 0) return 1; if (!info->n_entries) { Debug("Nothing found in folder"); return 1; } Debug("Number of entries: %i", info->n_entries); for(u32 i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb, .mode = AES_CNT_CTRNAND_MODE}; memcpy(padInfo.ctr, info->entries[i].ctr, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); Debug ("%2i: %s (%iMB)", i, info->entries[i].filename, info->entries[i].size_mb); if (CreatePad(&padInfo) != 0) return 1; // this can't fail anyways } return 0; } u32 AnyPadgen(u32 param) { (void) (param); // param is unused here AnyPadInfo *info = (AnyPadInfo*) 0x20316000; // get header if ((FileGetData("anypad.bin", info, 16, 0) != 16) || !info->n_entries || info->n_entries > MAX_ENTRIES) { Debug("Corrupt or not existing: anypad.bin"); return 1; } // get data u32 data_size = info->n_entries * sizeof(AnyPadInfoEntry); if (FileGetData("anypad.bin", (u8*) info + 16, data_size, 16) != data_size) { Debug("File is missing data: anypad.bin"); return 1; } Debug("Processing anypad.bin..."); Debug("Number of entries: %i", info->n_entries); for (u32 i = 0; i < info->n_entries; i++) { // this translates all entries to a standard padInfo struct AnyPadInfoEntry* entry = &(info->entries[i]); PadInfo padInfo = {.keyslot = entry->keyslot, .setKeyY = 0, .size_mb = 0, .size_b = entry->size_b, .mode = entry->mode}; memcpy(padInfo.filename, entry->filename, 80); memcpy(padInfo.ctr, entry->ctr, 16); // process keys if (entry->setNormalKey) setup_aeskey(entry->keyslot, entry->normalKey); if (entry->setKeyX) setup_aeskeyX(entry->keyslot, entry->keyX); if (entry->setKeyY) setup_aeskeyY(entry->keyslot, entry->keyY); use_aeskey(entry->keyslot); // process flags if (entry->flags & (AP_USE_NAND_CTR|AP_USE_SD_CTR)) { u32 ctr_add = getbe32(padInfo.ctr + 12); u8 shasum[32]; u8 cid[16]; sdmmc_get_cid((entry->flags & AP_USE_NAND_CTR) ? 1 : 0, (uint32_t*) cid); if (entry->mode == AES_CNT_TWLNAND_MODE) { sha_quick(shasum, cid, 16, SHA1_MODE); for (u32 i = 0; i < 16; i++) padInfo.ctr[i] = shasum[15-i]; } else { sha_quick(shasum, cid, 16, SHA256_MODE); memcpy(padInfo.ctr, shasum, 16); } add_ctr(padInfo.ctr, ctr_add); } // create the pad Debug ("%2i: %s (%ikB)", i, entry->filename, entry->size_b / 1024); if (CreatePad(&padInfo) != 0) return 1; // this can't fail anyways } return 0; } u32 CtrNandPadgen(u32 param) { char* filename = (param & PG_FORCESLOT4) ? "nand.fat16.slot0x04.xorpad" : "nand.fat16.xorpad"; u32 keyslot; u32 nand_size; // legacy sizes & offset, to work with Python 3DSFAT16Tool if (GetUnitPlatform() == PLATFORM_3DS) { if (param & PG_FORCESLOT4) { Debug("This is a N3DS only feature"); return 1; } keyslot = 0x4; nand_size = 758; } else { keyslot = (param & PG_FORCESLOT4) ? 0x4 : 0x5; nand_size = 1055; } Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size); Debug("Filename: %s", filename); PadInfo padInfo = { .keyslot = keyslot, .setKeyY = 0, .size_mb = nand_size, .mode = AES_CNT_CTRNAND_MODE }; strncpy(padInfo.filename, filename, 64); if(GetNandCtr(padInfo.ctr, 0xB930000) != 0) return 1; return CreatePad(&padInfo); } u32 TwlNandPadgen(u32 param) { (void) (param); // param is unused here PartitionInfo* twln_info = GetPartitionInfo(P_TWLN); u32 size_mb = (twln_info->size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating TWLN FAT16 xorpad. Size (MB): %u", size_mb); Debug("Filename: twlnand.fat16.xorpad"); PadInfo padInfo = { .keyslot = twln_info->keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "twlnand.fat16.xorpad", .mode = AES_CNT_TWLNAND_MODE }; if(GetNandCtr(padInfo.ctr, twln_info->offset) != 0) return 1; return CreatePad(&padInfo); } u32 Firm0Firm1Padgen(u32 param) { (void) (param); // param is unused here PartitionInfo* firm0_info = GetPartitionInfo(P_FIRM0); PartitionInfo* firm1_info = GetPartitionInfo(P_FIRM1); u32 size_mb = (firm0_info->size + firm1_info->size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating FIRM0FIRM1 xorpad. Size (MB): %u", size_mb); Debug("Filename: firm0firm1.xorpad"); PadInfo padInfo = { .keyslot = firm0_info->keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "firm0firm1.xorpad", .mode = AES_CNT_CTRNAND_MODE }; if(GetNandCtr(padInfo.ctr, firm0_info->offset) != 0) return 1; return CreatePad(&padInfo); }
int ProcessCTR(char* path){ PartitionInfo myInfo; File myFile; char myString[256]; //In case it is needed... if(FileOpen(&myFile, path, 0)){ ConsoleInit(); ConsoleAddText(TITLE); unsigned int ncch_base = 0x100; unsigned char magic[] = { 0, 0, 0, 0, 0}; FileRead(&myFile, magic, 4, ncch_base); if(magic[0] == 'N' && magic[1] == 'C' && magic[2] == 'S' && magic[3] == 'D'){ ncch_base = 0x4000; FileRead(&myFile, magic, 4, ncch_base+0x100); if(!(magic[0] == 'N' && magic[1] == 'C' && magic[2] == 'C' && magic[3] == 'H')){ FileClose(&myFile); return 2; } }else if(magic[0] == 'N' && magic[1] == 'C' && magic[2] == 'C' && magic[3] == 'H'){ ncch_base = 0x0; }else{ FileClose(&myFile); return 2; } ctr_ncchheader NCCH; unsigned int mediaunitsize = 0x200; FileRead(&myFile, &NCCH, 0x200, ncch_base); //ConsoleAddText(path); ConsoleAddText(NCCH.productcode); unsigned int NEWCRYPTO = 0, CRYPTO = 1; if(NCCH.flags[3] != 0) NEWCRYPTO = 1; if(NCCH.flags[7] & 4) CRYPTO = 0; if(NEWCRYPTO){ ConsoleAddText("\nCryptoType : 7.X Key security"); }else if(CRYPTO){ ConsoleAddText("\nCryptoType : Secure"); }else{ ConsoleAddText("\nCryptoType : None"); ConsoleAddText("Decryption completed!"); FileClose(&myFile); ConsoleShow(); return 3; } ConsoleShow(); u8 CTR[16]; if(getle32(NCCH.extendedheadersize) > 0){ ConsoleAddText("Decrypting ExHeader..."); ConsoleShow(); ncch_get_counter(NCCH, CTR, 1); FileRead(&myFile, BUFFER_ADDR, 0x800, ncch_base + 0x200); myInfo.buffer = BUFFER_ADDR; myInfo.size = 0x800; myInfo.keyslot = 0x2C; myInfo.ctr = CTR; myInfo.keyY = NCCH.signature; DecryptPartition(&myInfo); FileWrite(&myFile, BUFFER_ADDR, 0x800, ncch_base + 0x200); } if(getle32(NCCH.exefssize) > 0){ ConsoleAddText("Decrypting ExeFS..."); ConsoleShow(); ncch_get_counter(NCCH, CTR, 2); myInfo.buffer = BUFFER_ADDR; myInfo.keyslot = NEWCRYPTO ? 0x25 : 0x2C; myInfo.ctr = CTR; myInfo.keyY = NCCH.signature; size_t bytesRead = FileRead(&myFile, BUFFER_ADDR, getle32(NCCH.exefssize) * mediaunitsize, ncch_base + getle32(NCCH.exefsoffset) * mediaunitsize); myInfo.size = bytesRead; ProcessExeFS(&myInfo); //Explanation at function definition FileWrite(&myFile, BUFFER_ADDR, getle32(NCCH.exefssize) * mediaunitsize, ncch_base + getle32(NCCH.exefsoffset) * mediaunitsize); } if(getle32(NCCH.romfssize) > 0){ ConsoleAddText("Decrypting RomFS..."); ConsoleShow(); ncch_get_counter(NCCH, CTR, 3); myInfo.buffer = BUFFER_ADDR; myInfo.keyslot = NEWCRYPTO ? 0x25 : 0x2C; myInfo.ctr = CTR; myInfo.keyY = NCCH.signature; for(int i = 0; i < getle32(NCCH.romfssize) * mediaunitsize / BLOCK_SIZE; i++){ sprintf(myString, "%i%%", (int)((i*BLOCK_SIZE)/(getle32(NCCH.romfssize) * mediaunitsize/ 100))); int x, y; ConsoleGetXY(&x, &y); y += CHAR_WIDTH * 4; x += CHAR_WIDTH*22; DrawString(TOP_SCREEN, myString, x, y, ConsoleGetTextColor(), ConsoleGetBackgroundColor()); size_t bytesRead = FileRead(&myFile, BUFFER_ADDR, BLOCK_SIZE, ncch_base + getle32(NCCH.romfsoffset) * mediaunitsize + i*BLOCK_SIZE); myInfo.size = bytesRead; DecryptPartition(&myInfo); add_ctr(myInfo.ctr, bytesRead/16); FileWrite(&myFile, BUFFER_ADDR, BLOCK_SIZE, ncch_base + getle32(NCCH.romfsoffset) * mediaunitsize + i*BLOCK_SIZE); } } NCCH.flags[7] |= 4; //Disable encryption NCCH.flags[3] = 0; //Disable 7.XKey usage FileWrite(&myFile, &NCCH, 0x200, ncch_base); if(ncch_base == 0x4000) FileWrite(&myFile, ((u8*)&NCCH) + 0x100, 0x100, 0x1100); //Only for NCSD FileClose(&myFile); ConsoleAddText("Decryption completed!"); ConsoleShow(); return 0; }else return 1; }
u32 CtrNandPadgen(u32 param) { u32 keyslot; u32 nand_size; // legacy sizes & offset, to work with 3DSFAT16Tool if (GetUnitPlatform() == PLATFORM_3DS) { keyslot = 0x4; nand_size = 758; } else { keyslot = 0x5; nand_size = 1055; } Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size); Debug("Filename: nand.fat16.xorpad"); PadInfo padInfo = { .keyslot = keyslot, .setKeyY = 0, .size_mb = nand_size, .filename = "nand.fat16.xorpad", .mode = AES_CNT_CTRNAND_MODE }; if(GetNandCtr(padInfo.ctr, 0xB930000) != 0) return 1; return CreatePad(&padInfo); } u32 TwlNandPadgen(u32 param) { u32 size_mb = (partitions[0].size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating TWLN FAT16 xorpad. Size (MB): %u", size_mb); Debug("Filename: twlnand.fat16.xorpad"); PadInfo padInfo = { .keyslot = partitions[0].keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "twlnand.fat16.xorpad", .mode = AES_CNT_TWLNAND_MODE }; if(GetNandCtr(padInfo.ctr, partitions[0].offset) != 0) return 1; return CreatePad(&padInfo); } u32 Firm0Firm1Padgen(u32 param) { u32 size_mb = (partitions[3].size + partitions[4].size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating FIRM0FIRM1 xorpad. Size (MB): %u", size_mb); Debug("Filename: firm0firm1.xorpad"); PadInfo padInfo = { .keyslot = partitions[3].keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "firm0firm1.xorpad", .mode = AES_CNT_CTRNAND_MODE }; if(GetNandCtr(padInfo.ctr, partitions[3].offset) != 0) return 1; return CreatePad(&padInfo); } u32 GetNandCtr(u8* ctr, u32 offset) { static const char* versions[] = {"4.x", "5.x", "6.x", "7.x", "8.x", "9.x"}; static const u8* version_ctrs[] = { (u8*)0x080D7CAC, (u8*)0x080D858C, (u8*)0x080D748C, (u8*)0x080D740C, (u8*)0x080D74CC, (u8*)0x080D794C }; static const u32 version_ctrs_len = sizeof(version_ctrs) / sizeof(u32); static u8* ctr_start = NULL; if (ctr_start == NULL) { for (u32 i = 0; i < version_ctrs_len; i++) { if (*(u32*)version_ctrs[i] == 0x5C980) { Debug("System version %s", versions[i]); ctr_start = (u8*) version_ctrs[i] + 0x30; } } // If value not in previous list start memory scanning (test range) if (ctr_start == NULL) { for (u8* c = (u8*) 0x080D8FFF; c > (u8*) 0x08000000; c--) { if (*(u32*)c == 0x5C980 && *(u32*)(c + 1) == 0x800005C9) { ctr_start = c + 0x30; Debug("CTR start 0x%08X", ctr_start); break; } } } if (ctr_start == NULL) { Debug("CTR start not found!"); return 1; } } // the ctr is stored backwards in memory if (offset >= 0x0B100000) { // CTRNAND/AGBSAVE region for (u32 i = 0; i < 16; i++) ctr[i] = *(ctr_start + (0xF - i)); } else { // TWL region for (u32 i = 0; i < 16; i++) ctr[i] = *(ctr_start + 0x88 + (0xF - i)); } // increment counter add_ctr(ctr, offset / 0x10); return 0; } u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition) { CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode}; if(GetNandCtr(info.ctr, offset) != 0) return 1; u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE; u32 start_sector = offset / NAND_SECTOR_SIZE; ReadNandSectors(start_sector, n_sectors, buffer); CryptBuffer(&info); return 0; } u32 DecryptNandToFile(const char* filename, u32 offset, u32 size, PartitionInfo* partition) { u8* buffer = BUFFER_ADDRESS; u32 result = 0; if (!DebugFileCreate(filename, true)) return 1; for (u32 i = 0; i < size; i += NAND_SECTOR_SIZE * SECTORS_PER_READ) { u32 read_bytes = min(NAND_SECTOR_SIZE * SECTORS_PER_READ, (size - i)); ShowProgress(i, size); DecryptNandToMem(buffer, offset + i, read_bytes, partition); if(!DebugFileWrite(buffer, read_bytes, i)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; }
static inline void ctr_advance_ctr(ctr_crypto_interface *io, uint8_t *buffer, size_t buffer_size, size_t block, uint8_t *ctr) { add_ctr(ctr, block); }
uint32_t NcchPadgen() { uint32_t result; File pf; NcchInfo *info = (NcchInfo*)0x20316000; const char *filename = "/ncchinfo.bin"; wchar_t wfilename[14]; mbstowcs(wfilename, filename, 14); if (!FileOpen(&pf, filename, 0)) { print(strings[STR_ERROR_OPENING], filename+1); return 1; } FileRead(&pf, info, 16, 0); if (info->ncch_info_version != 0xF0000003) { print(strings[STR_WRONG], filename+1, strings[STR_VERSION]); return 0; } if (!info->n_entries || info->n_entries > MAXENTRIES) { print(strings[STR_WRONG], filename+1, strings[STR_ENTRIES_COUNT]); return 0; } FileRead(&pf, info->entries, info->n_entries * sizeof(NcchInfoEntry), 16); FileClose(&pf); print(strings[STR_PROCESSING], wfilename+1); ConsoleShow(); for(uint32_t i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.setKeyY = 1, .size_mb = info->entries[i].size_mb}; memcpy(padInfo.CTR, info->entries[i].CTR, 16); memcpy(padInfo.keyY, info->entries[i].keyY, 16); memcpy(padInfo.filename, info->entries[i].filename, 112); if(info->entries[i].uses7xCrypto) padInfo.keyslot = 0x25; else padInfo.keyslot = 0x2C; result = CreatePad(&padInfo, i); if (result) return 1; } return 0; } uint32_t SdPadgen() { size_t bytesRead; uint32_t result; File fp; SdInfo *info = (SdInfo*)0x20316000; uint8_t movable_seed[0x120] = {0}; const char *filename = "/movable.sed"; wchar_t wfilename[13]; mbstowcs(wfilename, filename, 13); // Load console 0x34 keyY from movable.sed if present on SD card if (FileOpen(&fp, filename, 0)) { bytesRead = FileRead(&fp, &movable_seed, 0x120, 0); FileClose(&fp); if (bytesRead != 0x120) { print(strings[STR_WRONG], filename+1, strings[STR_SIZE]); return 1; } if (memcmp(movable_seed, "SEED", 4) != 0) { print(strings[STR_WRONG], filename+1, strings[STR_CONTENT]); return 1; } setup_aeskey(0x34, AES_BIG_INPUT|AES_NORMAL_INPUT, &movable_seed[0x110]); use_aeskey(0x34); } filename = "/SDinfo.bin"; if (!FileOpen(&fp, filename, 0)) { print(strings[STR_ERROR_OPENING], filename+1); return 1; } bytesRead = FileRead(&fp, info, 4, 0); if (!info->n_entries || info->n_entries > MAXENTRIES) { print(strings[STR_WRONG], filename+1, strings[STR_ENTRIES_COUNT]); return 1; } print(strings[STR_PROCESSING], wfilename+1); ConsoleShow(); bytesRead = FileRead(&fp, info->entries, info->n_entries * sizeof(SdInfoEntry), 4); FileClose(&fp); for(uint32_t i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb}; memcpy(padInfo.CTR, info->entries[i].CTR, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); result = CreatePad(&padInfo, i); if (result) return 1; } return 0; } static const uint8_t zero_buf[16] __attribute__((aligned(16))) = {0}; uint32_t CreatePad(PadInfo *info, int index) { File pf; #define BUFFER_ADDR ((volatile uint8_t*)0x21000000) #define BLOCK_SIZE (4*1024*1024) if (!FileOpen(&pf, info->filename, 1)) return 1; if(info->setKeyY != 0) setup_aeskey(info->keyslot, AES_BIG_INPUT|AES_NORMAL_INPUT, info->keyY); use_aeskey(info->keyslot); uint8_t ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->CTR, 16); uint32_t size_bytes = info->size_mb*1024*1024; uint32_t size_100 = size_bytes/100; uint32_t seekpos = 0; for (uint32_t i = 0; i < size_bytes; i += BLOCK_SIZE) { uint32_t j; for (j = 0; (j < BLOCK_SIZE) && (i+j < size_bytes); j+= 16) { set_ctr(AES_BIG_INPUT|AES_NORMAL_INPUT, ctr); aes_decrypt((void*)zero_buf, (void*)BUFFER_ADDR+j, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 1); } print(strings[STR_GENERATING], strings[STR_PAD]); print(L"%i : %i%%", index, (i+j)/size_100); ConsolePrevLine(); ConsolePrevLine(); ConsoleShow(); FileWrite(&pf, (void*)BUFFER_ADDR, j, seekpos); seekpos += j; } FileClose(&pf); return 0; }