static FRESULT initN3DSKeys() { uint8_t buff[AES_BLOCK_SIZE]; UINT br; FRESULT r; FIL f; r = f_open(&f, _T("key_0x16.bin"), FA_READ); if (r != FR_OK) return r; r = f_read(&f, buff, sizeof(buff), &br); if (br < sizeof(buff)) return r == FR_OK ? EOF : r; f_close(&f); setup_aeskeyX(0x16, buff); r = f_open(&f, _T("key_0x1B.bin"), FA_READ); if (r != FR_OK) return r; r = f_read(&f, buff, sizeof(buff), &br); if (br < sizeof(buff)) return r == FR_OK ? EOF : r; f_close(&f); setup_aeskeyX(0x1B, buff); return 0; }
static int decryptFirmKtrArm9(void *p) { uint8_t key[AES_BLOCK_SIZE]; PartitionInfo info; Arm9Hdr *hdr; FirmSeg *seg, *btm; seg = ((FirmHdr *)p)->segs; for (btm = seg + FIRM_SEG_NUM; seg->isArm11; seg++) if (seg == btm) return -1; hdr = (void *)(p + seg->offset); info.ctr = hdr->ctr; info.buffer = (uint8_t *)hdr + 0x800; info.keyY = hdr->keyY; info.size = atoi(hdr->size); use_aeskey(0x11); if (hdr->ext.pad[0] == 0xFFFFFFFF) { info.keyslot = 0x15; aes_decrypt(hdr->keyX, key, NULL, 1, AES_ECB_DECRYPT_MODE); } else { info.keyslot = 0x16; aes_decrypt(hdr->ext.s.keyX_0x16, key, NULL, 1, AES_ECB_DECRYPT_MODE); } setup_aeskeyX(info.keyslot, key); return DecryptPartition(&info); }
void ctr_twl_keyslot_setup(void) { //Only a9lh really needs to bother with this, and it really only needs to happen once, before ITCM gets messed up. static bool setup = false; if (!setup && ctr_detect_a9lh_entry()) { uint32_t* TwlCustId = (uint32_t*) (0x01FFB808); uint32_t TwlKeyX[4]; alignas(32) uint8_t TwlKeyY[16]; // thanks b1l1s & Normmatt // see source from https://gbatemp.net/threads/release-twltool-dsi-downgrading-save-injection-etc-multitool.393488/ const char* nintendo = "NINTENDO"; TwlKeyX[0] = (TwlCustId[0] ^ 0xB358A6AF) | 0x80000000; TwlKeyX[3] = TwlCustId[1] ^ 0x08C267B7; memcpy(TwlKeyX + 1, nintendo, 8); // see: https://www.3dbrew.org/wiki/Memory_layout#ARM9_ITCM uint32_t TwlKeyYW3 = 0xE1A00005; memcpy(TwlKeyY, (uint8_t*) 0x01FFD3C8, 12); memcpy(TwlKeyY + 12, &TwlKeyYW3, 4); setup_aeskeyX(0x03, (uint8_t*)TwlKeyX); setup_aeskeyY(0x03, TwlKeyY); use_aeskey(0x03); } setup = true; }
void Key7X(void) { size_t bytesRead; if (FSFileOpen("/slot0x25KeyX.bin")) { u8 slot0x25KeyX[16] = { 0 }; bytesRead = FSFileRead(&slot0x25KeyX, 16, 0); FSFileClose(); if (bytesRead != 16) { DrawDebug(0, 1, "slot0x25KeyX.bin is too small!"); } DrawDebug(0, 1, "slot0x25KeyX.bin Found!"); DrawDebug(0, 1, ""); u8 zero[16] = { 0x00 }; memcpy((u32*)0x01FFCD00, zero, 16); setup_aeskeyX(0x25, slot0x25KeyX); } else { DrawDebug(0, 1, "You can't perform firmlaunch without slot0x25KeyX.bin"); int a; while (1) { a += 1; a -= 1; } } }
// @breif Initialize N3DS keys void KeyInit(void* source) { int i; u8 firm_key[16] = { 0 }; u8 * _source = (u8*)source; u8 firm_key_xored[16] = { 0xDE, 0x0E, 0x42, 0x0F, 0xE5, 0x75, 0x2C, 0xF0, 0x77, 0x4D, 0xA1, 0x87, 0x42, 0x33, 0xB9, 0xAA }; for (i = 0; i<16; i++) { firm_key[i] = _source[i] ^ firm_key_xored[i]; } setup_aeskeyX(0x16, firm_key); DrawDebug(0, 1, "N3DS Key Initialized!"); }
int main(){ if (Initialize()) while (1); //7.X Keys stuff File KeyFile; const char *keyfile = "/slot0x25KeyX.bin"; if(FileOpen(&KeyFile, keyfile, 0)){ uint8_t keyX[16]; FileRead(&KeyFile, keyX, 16, 0); FileClose(&KeyFile); setup_aeskeyX(0x25, keyX); }else{ if (sysver < 7) { ConsoleInit(); ConsoleSetTitle(strings[STR_WARNING]); print(strings[STR_ERROR_OPENING], keyfile); print(strings[STR_WARNING_KEYFILE]); print(strings[STR_PRESS_BUTTON_ACTION], strings[STR_BUTTON_A], strings[STR_CONTINUE]); ConsoleShow(); WaitForButton(BUTTON_A); } } //That's the Main Menu initialization, easy and cool OpenAnimation(); MenuInit(&MainMenu); MenuShow(); while (true) { uint32_t pad_state = InputWait(); if (pad_state & (BUTTON_DOWN | BUTTON_RIGHT | BUTTON_R1)) MenuNextSelection(); //I try to support every theme style if (pad_state & (BUTTON_UP | BUTTON_LEFT | BUTTON_L1)) MenuPrevSelection(); if (pad_state & BUTTON_A) { OpenAnimation(); MenuSelect(); } if (pad_state & BUTTON_SELECT) { fadeOut(); ShutDown(); } if (pad_state & BUTTON_START) { fadeOut(); returnHomeMenu(); } TryScreenShot(); MenuShow(); } FSDeInit(); return 0; }
int main(){ Initialize(); DrawString(TOP_SCREEN, "SUPPORT THE ORIGINAL, NOT THE IMITATION!", 75, 240-10, GREY, BLACK); //7.X Keys stuff File KeyFile; if(FileOpen(&KeyFile, "/slot0x25KeyX.bin", 0)){ u8 keyX[16]; FileRead(&KeyFile, keyX, 16, 0); FileClose(&KeyFile); setup_aeskeyX(0x25, keyX); DrawString(TOP_SCREEN, " NewKeyX ", 0, 240-8, GREEN, BLACK); }else{ if(GetSystemVersion() < 3){ ConsoleInit(); print("WARNING:\n\nCannot find slot0x25KeyX.bin.\nSome titles decryption will fail,\nand some some EmuNANDs will not boot.\n\nPress A to continue...\n"); ConsoleShow(); WaitForButton(BUTTON_A); } DrawString(TOP_SCREEN, " NewKeyX ", 0, 240-8, RED, BLACK); } DrawString(TOP_SCREEN, " EmuNAND ", 0, 240-16, checkEmuNAND() ? GREEN : RED, BLACK); //That's the Main Menu initialization, easy and cool MenuInit(&MainMenu); MenuShow(); while (true) { DrawString(TOP_SCREEN, "[SELECT] Reboot", 349-18*8, 181-24-8, RED, BLACK); DrawString(TOP_SCREEN, "[START] Shutdown", 349-18*8, 181-24, RED, BLACK); u32 pad_state = InputWait(); if(pad_state & BUTTON_DOWN) MenuNextSelection(); if(pad_state & BUTTON_UP) MenuPrevSelection(); if(pad_state & BUTTON_A) MenuSelect(); if(pad_state & BUTTON_SELECT) returnHomeMenu(); if(pad_state & BUTTON_START) ShutDown(); TryScreenShot(); MenuShow(); } FSDeInit(); return 0; }
u32 LoadKeyXFromFile(u32 keyslot) { char filename[32]; u8 keyX[16] = {0}; snprintf(filename, 31, "%s/%02X" SUFFIX, dir, (unsigned int) keyslot); if (!FileOpen(filename)) { Debug("Loading %s: not found", filename); return 1; } if (FileRead(keyX, 16, 0) != 16) { Debug("Loading %s: bad file", filename); FileClose(); return 1; } FileClose(); setup_aeskeyX(keyslot, keyX); Debug("Loading %s: ok", filename); return 0; }
int main(){ Initialize(); //7.X Keys stuff File KeyFile; if(FileOpen(&KeyFile, "/slot0x25KeyX.bin", 0)){ u8 keyX[16]; FileRead(&KeyFile, keyX, 16, 0); FileClose(&KeyFile); setup_aeskeyX(0x25, keyX); }else{ if(GetSystemVersion() < 3){ ConsoleInit(); ConsoleSetTitle(" WARNING"); print("WARNING:\n\nCannot find slot0x25KeyX.bin. If\nyour firmware version is less than\n7.X, some titles decryption will\nfail, and some EmuNANDs will not\nboot.\n\nPress A to continue...\n"); ConsoleShow(); WaitForButton(BUTTON_A); } } //That's the Main Menu initialization, easy and cool MenuInit(&MainMenu); MenuShow(); while (true) { u32 pad_state = InputWait(); if (pad_state & (BUTTON_DOWN | BUTTON_RIGHT | BUTTON_R1)) MenuNextSelection(); //I try to support every theme style if (pad_state & (BUTTON_UP | BUTTON_LEFT | BUTTON_L1)) MenuPrevSelection(); if(pad_state & BUTTON_A) MenuSelect(); if(pad_state & BUTTON_SELECT) ShutDown(); if(pad_state & BUTTON_START) returnHomeMenu(); TryScreenShot(); MenuShow(); } FSDeInit(); return 0; }
u32 NcchPadgen() { u32 result; NcchInfo *info = (NcchInfo*)0x20316000; SeedInfo *seedinfo = (SeedInfo*)0x20400000; if (DebugFileOpen("/slot0x25KeyX.bin")) { u8 slot0x25KeyX[16] = {0}; if (!DebugFileRead(&slot0x25KeyX, 16, 0)) { FileClose(); return 1; } FileClose(); setup_aeskeyX(0x25, slot0x25KeyX); } else { Debug("7.x game decryption will fail on less than 7.x!"); } if (DebugFileOpen("/seeddb.bin")) { if (!DebugFileRead(seedinfo, 16, 0)) { FileClose(); return 1; } if (!seedinfo->n_entries || seedinfo->n_entries > MAX_ENTRIES) { Debug("Too many/few seeddb entries."); return 1; } if (!DebugFileRead(seedinfo->entries, seedinfo->n_entries * sizeof(SeedInfoEntry), 16)) { FileClose(); return 1; } FileClose(); } else { // Debug("Warning, didn't open seeddb.bin"); Debug("9.x seed crypto game decryption will fail!"); } if (!DebugFileOpen("/ncchinfo.bin")) return 1; if (!DebugFileRead(info, 16, 0)) { FileClose(); return 1; } if (!info->n_entries || info->n_entries > MAX_ENTRIES) { Debug("Too many/few entries in ncchinfo.bin"); return 1; } if (info->ncch_info_version != 0xF0000004) { Debug("Wrong version ncchinfo.bin"); return 1; } if (!DebugFileRead(info->entries, info->n_entries * sizeof(NcchInfoEntry), 16)) { FileClose(); return 1; } FileClose(); Debug("Number of entries: %i", info->n_entries); 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 = {.setKeyY = 1, .size_mb = info->entries[i].size_mb}; memcpy(padInfo.CTR, info->entries[i].CTR, 16); memcpy(padInfo.filename, info->entries[i].filename, 112); if (info->entries[i].uses7xCrypto && info->entries[i].usesSeedCrypto) { u8 keydata[32]; memcpy(keydata, info->entries[i].keyY, 16); u32 found_seed = 0; for (u32 j = 0; j < seedinfo->n_entries; j++) { if (seedinfo->entries[j].titleId == info->entries[i].titleId) { found_seed = 1; memcpy(&keydata[16], seedinfo->entries[j].external_seed, 16); break; } } if (!found_seed) { Debug("Failed to find seed in seeddb.bin"); return 0; } u8 sha256sum[32]; sha256_context shactx; sha256_starts(&shactx); sha256_update(&shactx, keydata, 32); sha256_finish(&shactx, sha256sum); memcpy(padInfo.keyY, sha256sum, 16); } else memcpy(padInfo.keyY, info->entries[i].keyY, 16); if(info->entries[i].uses7xCrypto == 0xA) // won't work on an Old 3DS padInfo.keyslot = 0x18; else if(info->entries[i].uses7xCrypto >> 8 == 0xDEC0DE) // magic value to manually specify keyslot padInfo.keyslot = info->entries[i].uses7xCrypto & 0x3F; else if(info->entries[i].uses7xCrypto) padInfo.keyslot = 0x25; else padInfo.keyslot = 0x2C; Debug("Using keyslot: %02X", padInfo.keyslot); result = CreatePad(&padInfo); if (!result) Debug("Done!"); else return 1; } return 0; }
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); }
FRESULT LoadAllKeyXFromFile() { char fpath[_MAX_LFN + 1]; char *p; long keyslot; u8 key[AES_BLOCK_SIZE]; FILINFO fno; FRESULT r; FIL f; DIR d; UINT br; r = f_opendir(&d, dir); if (r == FR_NO_PATH) return FR_OK; else if (r != FR_OK) return r; fno.lfname = NULL; while (1) { r = f_readdir(&d, &fno); if (r != FR_OK) { Debug("Failed to read directory %s: %d", dir, r); f_closedir(&d); return r; } if (fno.fname[0] == 0) break; keyslot = strtol(fno.fname, &p, 16); if (strcmp(p, SUFFIX)) continue; snprintf(fpath, _MAX_LFN, "%s/%s", dir, fno.fname); r = f_open(&f, fpath, FA_READ); if (r) { Debug("Failed to open %s: %d", fpath, r); f_closedir(&d); return r; } r = f_read(&f, key, sizeof(key), &br); if (r) { Debug("Failed to read %s: %d", fpath, r); f_close(&f); f_closedir(&d); return r; } if (br < sizeof(key)) { Debug("%s is too small. expected: %d, result: %d", fpath, br, sizeof(key)); f_close(&f); f_closedir(&d); return -1; } f_close(&f); setup_aeskeyX(keyslot, key); } f_closedir(&d); return FR_OK; }