diff --git a/Makefile b/Makefile index 7894d05..5496190 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,9 @@ include $(DEVKITARM)/ds_rules #--------------------------------------------------------------------------------- export TARGET := GodMode9 BUILD := build -SOURCES := source source/fatfs source/fatfs/crypto source/abstraction +SOURCES := source source/fatfs source/decryptor source/abstraction DATA := data -INCLUDES := include source source/fatfs source/fatfs/crypto +INCLUDES := include source source/fatfs source/decryptor #--------------------------------------------------------------------------------- # options for code generation diff --git a/source/fatfs/3dsnand.c b/source/fatfs/3dsnand.c new file mode 100644 index 0000000..3e2395d --- /dev/null +++ b/source/fatfs/3dsnand.c @@ -0,0 +1,220 @@ +#include "platform.h" +#include "fatfs/aes.h" +#include "fatfs/3dsnand.h" +#include "fatfs/sdmmc.h" + +// see: http://3dbrew.org/wiki/Flash_Filesystem +static PartitionInfo partitions[] = { + { "TWLN", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x00012E00, 0x08FB5200, 0x3, AES_CNT_TWLNAND_MODE }, + { "TWLP", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x09011A00, 0x020B6600, 0x3, AES_CNT_TWLNAND_MODE }, + { "AGBSAVE", {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 0x0B100000, 0x00030000, 0x7, AES_CNT_CTRNAND_MODE }, + { "FIRM0", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B130000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE }, + { "FIRM1", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B530000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE }, + { "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95CA00, 0x2F3E3600, 0x4, AES_CNT_CTRNAND_MODE }, // O3DS + { "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95AE00, 0x41D2D200, 0x5, AES_CNT_CTRNAND_MODE } // N3DS +}; + +static u32 emunand_header = 0; +static u32 emunand_offset = 0; + + +u32 CheckEmuNand(void) +{ + u8* buffer = BUFFER_ADDRESS; + u32 nand_size_sectors = getMMCDevice(0)->total_size; + u32 multi_sectors = (GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS; + u32 ret = EMUNAND_NOT_READY; + + // check the MBR for presence of a hidden partition + sdmmc_sdcard_readsectors(0, 1, buffer); + u32 hidden_sectors = getle32(buffer + 0x1BE + 0x8); + + for (u32 offset_sector = 0; offset_sector + nand_size_sectors < hidden_sectors; offset_sector += multi_sectors) { + // check for Gateway type EmuNAND + sdmmc_sdcard_readsectors(offset_sector + nand_size_sectors, 1, buffer); + if (memcmp(buffer + 0x100, "NCSD", 4) == 0) { + ret |= EMUNAND_GATEWAY << (2 * (offset_sector / multi_sectors)); + continue; + } + // check for RedNAND type EmuNAND + sdmmc_sdcard_readsectors(offset_sector + 1, 1, buffer); + if (memcmp(buffer + 0x100, "NCSD", 4) == 0) { + ret |= EMUNAND_REDNAND << (2 * (offset_sector / multi_sectors)); + continue; + } + // EmuNAND ready but not set up + ret |= EMUNAND_READY << (2 * (offset_sector / multi_sectors)); + } + + return ret; +} + +void SetNand(bool set_emunand, u32 base_sector) +{ + // no checks here AT ALL - be careful!!! + if (set_emunand) { + emunand_header = base_sector + getMMCDevice(0)->total_size; + emunand_offset = base_sector; + } else { + emunand_header = 0; + emunand_offset = 0; + } +} + +int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out) +{ + if (emunand_header) { + if (sector_no == 0) { + int errorcode = sdmmc_sdcard_readsectors(emunand_header, 1, out); + if (errorcode) return errorcode; + sector_no = 1; + numsectors--; + out += 0x200; + } + return sdmmc_sdcard_readsectors(sector_no + emunand_offset, numsectors, out); + } else return sdmmc_nand_readsectors(sector_no, numsectors, out); +} + +int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in) +{ + if (emunand_header) { + if (sector_no == 0) { + int errorcode = sdmmc_sdcard_writesectors(emunand_header, 1, in); + if (errorcode) return errorcode; + sector_no = 1; + numsectors--; + in += 0x200; + } + return sdmmc_sdcard_writesectors(sector_no + emunand_offset, numsectors, in); + } else return sdmmc_nand_writesectors(sector_no, numsectors, in); +} + +u32 GetNandSize(void) +{ + return getMMCDevice(0)->total_size * NAND_SECTOR_SIZE; +} + +PartitionInfo* GetPartitionInfo(u32 partition_id) +{ + u32 partition_num = 0; + + if (partition_id == P_CTRNAND) { + partition_num = (GetUnitPlatform() == PLATFORM_3DS) ? 5 : 6; + } else { + for(; !(partition_id & (1<= 32) ? NULL : &(partitions[partition_num]); +} + +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) { + 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; + break; + } + } + } + + if (ctr_start == NULL) { + 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 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) { + 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; +} + +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 EncryptMemToNand(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; + CryptBuffer(&info); + WriteNandSectors(start_sector, n_sectors, buffer); + + return 0; +} diff --git a/source/fatfs/crypto/nand.h b/source/fatfs/3dsnand.h similarity index 54% rename from source/fatfs/crypto/nand.h rename to source/fatfs/3dsnand.h index 9ba748a..2a018ec 100644 --- a/source/fatfs/crypto/nand.h +++ b/source/fatfs/3dsnand.h @@ -2,6 +2,9 @@ #include "common.h" +#define BUFFER_ADDRESS ((u8*) 0x21000000) // buffer must not be used for anything else! +#define BUFFER_MAX_SIZE (1 * 1024 * 1024) // must be a multiple of 0x40 (64) + #define NAND_SECTOR_SIZE 0x200 #define SECTORS_PER_READ (BUFFER_MAX_SIZE / NAND_SECTOR_SIZE) @@ -12,12 +15,6 @@ #define P_FIRM1 (1<<4) #define P_CTRNAND (1<<5) -// these three are not handled by the feature functions -// they have to be handled by the menu system -#define N_EMUNAND (1<<29) -#define N_FORCENAND (1<<30) -#define N_NANDWRITE (1<<31) - // return values for the CheckEmuNAND() function #define EMUNAND_NOT_READY 0 // must be zero #define EMUNAND_READY 1 @@ -28,6 +25,16 @@ #define EMUNAND_MULTI_OFFSET_O3DS 0x00200000 #define EMUNAND_MULTI_OFFSET_N3DS 0x00400000 +typedef struct { + u32 keyslot; + u32 setKeyY; + u8 ctr[16]; + u8 keyY[16]; + u32 size; + u32 mode; + u8* buffer; +} __attribute__((packed)) CryptBufferInfo; + typedef struct { char name[16]; u8 magic[8]; @@ -38,25 +45,16 @@ typedef struct { } __attribute__((packed)) PartitionInfo; PartitionInfo* GetPartitionInfo(u32 partition_id); -u32 GetNandCtr(u8* ctr, u32 offset); -u32 OutputFileNameSelector(char* filename, const char* basename, char* extension); -u32 InputFileNameSelector(char* filename, const char* basename, char* extension, u8* magic, u32 msize, u32 fsize); +u32 CheckEmuNand(void); +void SetNand(bool set_emunand, u32 base_sector); + +u32 GetNandCtr(u8* ctr, u32 offset); +u32 CryptBuffer(CryptBufferInfo *info); + +int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out); +int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in); +u32 GetNandSize(void); u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition); -u32 DecryptNandToFile(const char* filename, u32 offset, u32 size, PartitionInfo* partition); u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition); -u32 EncryptFileToNand(const char* filename, u32 offset, u32 size, PartitionInfo* partition); - -// --> FEATURE FUNCTIONS <-- -u32 CheckEmuNand(void); -u32 SetNand(bool set_emunand, bool force_emunand); - -u32 CtrNandPadgen(u32 param); -u32 TwlNandPadgen(u32 param); -u32 Firm0Firm1Padgen(u32 param); - -u32 DumpNand(u32 param); -u32 RestoreNand(u32 param); -u32 DecryptNandPartition(u32 param); -u32 InjectNandPartition(u32 param); diff --git a/source/fatfs/crypto/aes.c b/source/fatfs/aes.c similarity index 100% rename from source/fatfs/crypto/aes.c rename to source/fatfs/aes.c diff --git a/source/fatfs/crypto/aes.h b/source/fatfs/aes.h similarity index 100% rename from source/fatfs/crypto/aes.h rename to source/fatfs/aes.h diff --git a/source/fatfs/crypto/decryptor.c b/source/fatfs/crypto/decryptor.c deleted file mode 100644 index e64e9cc..0000000 --- a/source/fatfs/crypto/decryptor.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "fs.h" -#include "draw.h" -#include "decryptor/decryptor.h" -#include "decryptor/aes.h" - - -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) { - 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; -} - -u32 CreatePad(PadInfo *info) -{ - u8* buffer = BUFFER_ADDRESS; - u32 result = 0; - - if (!FileCreate(info->filename, true)) // No DebugFileCreate() here - messages are already given - return 1; - - CryptBufferInfo decryptInfo = {.keyslot = info->keyslot, .setKeyY = info->setKeyY, .mode = info->mode, .buffer = buffer}; - memcpy(decryptInfo.ctr, info->ctr, 16); - memcpy(decryptInfo.keyY, info->keyY, 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); - decryptInfo.size = curr_block_size; - memset(buffer, 0x00, curr_block_size); - ShowProgress(i, size_bytes); - CryptBuffer(&decryptInfo); - if (!DebugFileWrite((void*)buffer, curr_block_size, i)) { - result = 1; - break; - } - } - - ShowProgress(0, 0); - FileClose(); - - return result; -} diff --git a/source/fatfs/crypto/decryptor.h b/source/fatfs/crypto/decryptor.h deleted file mode 100644 index dc914dd..0000000 --- a/source/fatfs/crypto/decryptor.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "common.h" - -#define BUFFER_ADDRESS ((u8*) 0x21000000) -#define BUFFER_MAX_SIZE (1 * 1024 * 1024) // must be a multiple of 0x40 (64) - -typedef struct { - u32 keyslot; - u32 setKeyY; - u8 ctr[16]; - u8 keyY[16]; - u32 size; - u32 mode; - u8* buffer; -} __attribute__((packed)) CryptBufferInfo; - -typedef struct { - u32 keyslot; - u32 setKeyY; - u8 ctr[16]; - u8 keyY[16]; - u32 size_mb; - u32 mode; - char filename[180]; -} __attribute__((packed, aligned(16))) PadInfo; - - -u32 CryptBuffer(CryptBufferInfo *info); -u32 CreatePad(PadInfo *info); diff --git a/source/fatfs/crypto/game.c b/source/fatfs/crypto/game.c deleted file mode 100644 index da5c618..0000000 --- a/source/fatfs/crypto/game.c +++ /dev/null @@ -1,1313 +0,0 @@ -#include "fs.h" -#include "draw.h" -#include "hid.h" -#include "platform.h" -#include "decryptor/aes.h" -#include "decryptor/sha.h" -#include "decryptor/decryptor.h" -#include "decryptor/nand.h" -#include "decryptor/nandfat.h" -#include "decryptor/titlekey.h" -#include "decryptor/game.h" - - -u32 GetSdCtr(u8* ctr, const char* path) -{ - // get AES counter, see: http://www.3dbrew.org/wiki/Extdata#Encryption - // path is the part of the full path after //Nintendo 3DS// - u8 hashstr[256]; - u8 sha256sum[32]; - u32 plen = 0; - // poor man's UTF-8 -> UTF-16 - for (u32 i = 0; i < 128; i++) { - hashstr[2*i] = path[i]; - hashstr[2*i+1] = 0; - if (path[i] == 0) { - plen = i; - break; - } - } - sha_init(SHA256_MODE); - sha_update(hashstr, (plen + 1) * 2); - sha_get(sha256sum); - for (u32 i = 0; i < 16; i++) - ctr[i] = sha256sum[i] ^ sha256sum[i+16]; - - return 0; -} - -u32 GetSd0x34KeyY(u8* movable_keyY, bool from_nand) -{ - u8 movable_sed[0x200]; - - if (from_nand) { // load console 0x34 keyY from movable.sed from NAND - PartitionInfo* p_info = GetPartitionInfo(P_CTRNAND); - u32 offset; - u32 size; - if (DebugSeekFileInNand(&offset, &size, "movable.sed", "PRIVATE MOVABLE SED", p_info) != 0) - return 1; - if (size < 0x120) { - Debug("movable.sed has bad size!"); - return 1; - } - DecryptNandToMem(movable_sed, offset, 0x120, p_info); - } else if (DebugFileOpen("movable.sed")) { // load console 0x34 keyY from movable.sed from SD card - if (!DebugFileRead(movable_sed, 0x120, 0)) { - FileClose(); - return 1; - } - FileClose(); - } else { - return 1; - } - if (memcmp(movable_sed, "SEED", 4) != 0) { - Debug("movable.sed is corrupt!"); - return 1; - } - memcpy(movable_keyY, movable_sed + 0x110, 0x10); - - return 0; -} - -u32 SdFolderSelector(char* path, u8* keyY) -{ - char** dirptr = (char**) 0x20400000; // allow using 0x8000 byte - char* dirlist = (char*) 0x20408000; // allow using 0x80000 byte - u32 n_dirs = 0; - - // the keyY is used to generate a proper base path - // see here: https://www.3dbrew.org/wiki/Nand/private/movable.sed - u32 sha256sum[8]; - char base_path[64]; - sha_init(SHA256_MODE); - sha_update(keyY, 16); - sha_get(sha256sum); - snprintf(base_path, 63, "/Nintendo 3DS/%08X%08X%08X%08X", - (unsigned int) sha256sum[0], (unsigned int) sha256sum[1], - (unsigned int) sha256sum[2], (unsigned int) sha256sum[3]); - Debug(" is %s", base_path + 14); - if (!GetFileList(base_path, dirlist, 0x80000, true, false, true)) { - Debug("Failed retrieving the dirlist"); - return 1; - } - - // parse the dirlist for usable entries - for (char* dir = strtok(dirlist, "\n"); dir != NULL; dir = strtok(NULL, "\n")) { - if (strnlen(dir, 256) <= 13 + 33 + 33) - continue; - if (strchrcount(dir, '/') > 6) - continue; // allow a maximum depth of 6 for the full folder - char* subdir = dir + 13 + 33 + 33; // length of ("/Nintendo 3DS" + "/" + "/"); - if ((strncmp(subdir, "/dbs", 4) != 0) && (strncmp(subdir, "/extdata", 8) != 0) && (strncmp(subdir, "/title", 6) != 0)) - continue; - dirptr[n_dirs++] = dir; - if (n_dirs * sizeof(char**) >= 0x8000) - return 1; - } - if (n_dirs == 0) { - Debug("No valid SD data found"); - return 1; - } - - // let the user choose a directory - u32 index = 0; - strncpy(path, dirptr[0], 128); - Debug("Use arrow keys and to choose a folder"); - while (true) { - Debug("\r%s", path + 13 + 33 + 33); - u32 pad_state = InputWait(); - u32 cur_lvl = strchrcount(path, '/'); - if (pad_state & BUTTON_DOWN) { // find next path of same level - do { - if (++index >= n_dirs) - index = 0; - } while (strchrcount(dirptr[index], '/') != cur_lvl); - } else if (pad_state & BUTTON_UP) { // find prev path of same level - do { - index = (index) ? index - 1 : n_dirs - 1; - } while (strchrcount(dirptr[index], '/') != cur_lvl); - } else if ((pad_state & BUTTON_RIGHT) && (cur_lvl < 6)) { // up one level - if ((index < n_dirs - 1) && (strchrcount(dirptr[index+1], '/') > cur_lvl)) - index++; // this only works because of the sorting of the dir list - } else if ((pad_state & BUTTON_LEFT) && (cur_lvl > 4)) { // down one level - while ((index > 0) && (cur_lvl == strchrcount(dirptr[index], '/'))) - index--; - } else if (pad_state & BUTTON_A) { - Debug("%s", path + 13 + 33 + 33); - break; - } else if (pad_state & BUTTON_B) { - Debug("(cancelled by user)"); - return 2; - } - strncpy(path, dirptr[index], 128); - } - - return 0; -} - -u32 SdInfoGen(SdInfo* info, const char* base_path) -{ - char* filelist = (char*)0x20400000; - - // check the base path for validity - if ((strncmp(base_path, "/Nintendo 3DS", 13) != 0 ) || (strncmp(base_path, "/Nintendo 3DS/Private/", 22) == 0) || - (strnlen(base_path, 255) < 13 + 33 + 33)) { - Debug("Invalid base path given"); - return 1; - } - - Debug("Generating SDinfo.bin in memory..."); - if (!GetFileList(base_path, filelist, 0x100000, true, true, false)) { - Debug("Failed retrieving the filelist"); - return 1; - } - - u32 n_entries = 0; - SdInfoEntry* entries = info->entries; - for (char* path = strtok(filelist, "\n"); path != NULL; path = strtok(NULL, "\n")) { - u32 plen = strnlen(path, 255); - // get size in MB - if (!FileOpen(path)) - continue; - entries[n_entries].size_mb = (FileGetSize() + (1024 * 1024) - 1) / (1024 * 1024); - FileClose(); - // skip to relevant part of path - path += 13 + 33 + 33; // length of ("/Nintendo 3DS" + "/" + "/") - plen -= 13 + 33 + 33; - if ((strncmp(path, "/dbs", 4) != 0) && (strncmp(path, "/extdata", 8) != 0) && (strncmp(path, "/title", 6) != 0)) - continue; - // get filename - char* filename = entries[n_entries].filename; - filename[0] = '/'; - for (u32 i = 1; i < 180 && path[i] != 0; i++) - filename[i] = (path[i] == '/') ? '.' : path[i]; - strncpy(filename + plen, ".xorpad", (180 - 1) - plen); - // get AES counter - GetSdCtr(entries[n_entries].ctr, path); - if (++n_entries >= MAX_ENTRIES) - break; - } - info->n_entries = n_entries; - - return (n_entries > 0) ? 0 : 1; -} - -u32 NcchPadgen(u32 param) -{ - 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("slot0x11key96.bin")) { - u8 slot0x11key96[16] = {0}; - if (FileRead(&slot0x11key96, 16, 0) != 16) { - Debug("slot0x11key96.bin is corrupt!"); - FileClose(); - return 1; - } - FileClose(); - setup_aeskey(0x11, slot0x11key96); - } else { - Debug("Secure4 crypto will fail"); - } - - if (DebugFileOpen("seeddb.bin")) { - if (!DebugFileRead(seedinfo, 16, 0)) { - FileClose(); - return 1; - } - if (!seedinfo->n_entries || seedinfo->n_entries > MAX_ENTRIES) { - FileClose(); - Debug("Bad number of seeddb entries"); - return 1; - } - if (!DebugFileRead(seedinfo->entries, seedinfo->n_entries * sizeof(SeedInfoEntry), 16)) { - FileClose(); - return 1; - } - FileClose(); - } else { - 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) { - FileClose(); - Debug("Bad number of entries in ncchinfo.bin"); - return 1; - } - if (info->ncch_info_version == 0xF0000004) { // ncchinfo v4 - if (!DebugFileRead(info->entries, info->n_entries * sizeof(NcchInfoEntry), 16)) { - FileClose(); - return 1; - } - } else if (info->ncch_info_version == 0xF0000003) { // ncchinfo v3 - // read ncchinfo v3 entry & convert to ncchinfo v4 - for (u32 i = 0; i < info->n_entries; i++) { - u8* entry_data = (u8*) (info->entries + i); - if (!DebugFileRead(entry_data, 160, 16 + (160*i))) { - FileClose(); - return 1; - } - memmove(entry_data + 56, entry_data + 48, 112); - *(u64*) (entry_data + 48) = 0; - } - } else { // unknown file / ncchinfo version - FileClose(); - Debug("Incompatible version ncchinfo.bin"); - return 1; - } - FileClose(); - - Debug("Number of entries: %i", info->n_entries); - - for (u32 i = 0; i < info->n_entries; i++) { // check and fix filenames - char* filename = info->entries[i].filename; - if (filename[1] == 0x00) { // convert UTF-16 -> UTF-8 - for (u32 j = 1; j < (112 / 2); j++) - filename[j] = filename[j*2]; - } - if (memcmp(filename, "sdmc:", 5) == 0) // fix sdmc: prefix - memmove(filename, filename + 5, 112 - 5); - } - - for (u32 i = 0; i < info->n_entries; i++) { - PadInfo padInfo = {.setKeyY = 1, .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, 112); - Debug ("%2i: %s (%iMB)", i, info->entries[i].filename, info->entries[i].size_mb); - - if (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 1; - } - u8 sha256sum[32]; - sha_init(SHA256_MODE); - sha_update(keydata, 32); - sha_get(sha256sum); - memcpy(padInfo.keyY, sha256sum, 16); - } - else - memcpy(padInfo.keyY, info->entries[i].keyY, 16); - - if (info->entries[i].uses7xCrypto == 0xA) { - if (GetUnitPlatform() == PLATFORM_3DS) { // won't work on an Old 3DS - Debug("This can only be generated on N3DS"); - return 1; - } - padInfo.keyslot = 0x18; // Secure3 crypto - } else if (info->entries[i].uses7xCrypto == 0xB) { - padInfo.keyslot = 0x11; // Secure4 crypto, needs slot0x11key96.bin - } 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; // 7.x crypto - else - padInfo.keyslot = 0x2C; // standard crypto - Debug("Using keyslot: %02X", padInfo.keyslot); - - if (CreatePad(&padInfo) != 0) - return 1; // this can't fail anyways - } - - return 0; -} - -u32 SdPadgen(u32 param) -{ - SdInfo *info = (SdInfo*) 0x20316000; - u8 movable_keyY[16]; - - if (GetSd0x34KeyY(movable_keyY, false) == 0) { - Debug("Setting console 0x34 keyY"); - setup_aeskeyY(0x34, movable_keyY); - 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) { - 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) -{ - SdInfo *info = (SdInfo*) 0x20316000; - char basepath[256]; - u8 movable_keyY[16]; - - if (GetSd0x34KeyY(movable_keyY, true) == 0) { - Debug("Setting console 0x34 keyY"); - setup_aeskeyY(0x34, movable_keyY); - use_aeskey(0x34); - } else { - 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 UpdateSeedDb(u32 param) -{ - PartitionInfo* ctrnand_info = GetPartitionInfo(P_CTRNAND); - u8* buffer = BUFFER_ADDRESS; - SeedInfo *seedinfo = (SeedInfo*) 0x20400000; - - u32 nNewSeeds = 0; - u32 offset; - u32 size; - - // load full seedsave to memory - Debug("Searching for seedsave..."); - if (SeekFileInNand(&offset, &size, "DATA ???????????SYSDATA 0001000F 00000000 ", ctrnand_info) != 0) { - Debug("Failed!"); - return 1; - } - Debug("Found at %08X, size %ukB", offset, size / 1024); - if (size != 0xAC000) { - Debug("Expected %ukB, failed!", 0xAC000); - return 1; - } - DecryptNandToMem(buffer, offset, size, ctrnand_info); - - // load / create seeddb.bin - if (DebugFileOpen("seeddb.bin")) { - if (!DebugFileRead(seedinfo, 16, 0)) { - FileClose(); - return 1; - } - if (seedinfo->n_entries > MAX_ENTRIES) { - Debug("seeddb.bin seems to be corrupt!"); - FileClose(); - return 1; - } - if (!DebugFileRead(seedinfo->entries, seedinfo->n_entries * sizeof(SeedInfoEntry), 16)) { - FileClose(); - return 1; - } - } else { - if (!DebugFileCreate("seeddb.bin", true)) - return 1; - memset(seedinfo, 0x00, 16); - } - - // search and extract seeds - for ( int n = 0; n < 2; n++ ) { - // there are two offsets where seeds can be found - 0x07000 & 0x5C000 - static const int seed_offsets[2] = {0x7000, 0x5C000}; - unsigned char* seed_data = buffer + seed_offsets[n]; - for ( size_t i = 0; i < 2000; i++ ) { - static const u8 zeroes[16] = { 0x00 }; - // magic number is the reversed first 4 byte of a title id - static const u8 magic[4] = { 0x00, 0x00, 0x04, 0x00 }; - // 2000 seed entries max, splitted into title id and seed area - u8* titleId = seed_data + (i*8); - u8* seed = seed_data + (2000*8) + (i*16); - if (memcmp(titleId + 4, magic, 4) != 0) - continue; - // Bravely Second demo seed workaround - if (memcmp(seed, zeroes, 16) == 0) - seed = buffer + seed_offsets[(n+1)%2] + (2000 * 8) + (i*16); - if (memcmp(seed, zeroes, 16) == 0) - continue; - // seed found, check if it already exists - u32 entryPos = 0; - for (entryPos = 0; entryPos < seedinfo->n_entries; entryPos++) - if (memcmp(titleId, &(seedinfo->entries[entryPos].titleId), 8) == 0) - break; - if (entryPos < seedinfo->n_entries) { - Debug("Found %08X%08X seed (duplicate)", getle32(titleId + 4), getle32(titleId)); - continue; - } - // seed is new, create a new entry - Debug("Found %08X%08X seed (new)", getle32(titleId + 4), getle32(titleId)); - memset(&(seedinfo->entries[entryPos]), 0x00, sizeof(SeedInfoEntry)); - memcpy(&(seedinfo->entries[entryPos].titleId), titleId, 8); - memcpy(&(seedinfo->entries[entryPos].external_seed), seed, 16); - seedinfo->n_entries++; - nNewSeeds++; - } - } - - if (nNewSeeds == 0) { - Debug("Found no new seeds, %i total", seedinfo->n_entries); - FileClose(); - return 0; - } - - Debug("Found %i new seeds, %i total", nNewSeeds, seedinfo->n_entries); - if (!DebugFileWrite(seedinfo, 16 + seedinfo->n_entries * sizeof(SeedInfoEntry), 0)) - return 1; - FileClose(); - - return 0; -} - -u32 CryptSdToSd(const char* filename, u32 offset, u32 size, CryptBufferInfo* info) -{ - u8* buffer = BUFFER_ADDRESS; - u32 offset_16 = offset % 16; - u32 result = 0; - - // no DebugFileOpen() - at this point the file has already been checked enough - if (!FileOpen(filename)) - return 1; - - info->buffer = buffer; - if (offset_16) { // handle offset alignment / this assumes the data is >= 16 byte - if(!DebugFileRead(buffer + offset_16, 16 - offset_16, offset)) { - result = 1; - } - info->size = 16; - CryptBuffer(info); - if(!DebugFileWrite(buffer + offset_16, 16 - offset_16, offset)) { - result = 1; - } - } - for (u32 i = (offset_16) ? (16 - offset_16) : 0; i < size; i += BUFFER_MAX_SIZE) { - u32 read_bytes = min(BUFFER_MAX_SIZE, (size - i)); - ShowProgress(i, size); - if(!DebugFileRead(buffer, read_bytes, offset + i)) { - result = 1; - break; - } - info->size = read_bytes; - CryptBuffer(info); - if(!DebugFileWrite(buffer, read_bytes, offset + i)) { - result = 1; - break; - } - } - - ShowProgress(0, 0); - FileClose(); - - return result; -} - -u32 GetHashFromFile(const char* filename, u32 offset, u32 size, u8* hash) -{ - // uses the standard buffer, so be careful - u8* buffer = BUFFER_ADDRESS; - - if (!FileOpen(filename)) - return 1; - sha_init(SHA256_MODE); - for (u32 i = 0; i < size; i += BUFFER_MAX_SIZE) { - u32 read_bytes = min(BUFFER_MAX_SIZE, (size - i)); - if (size >= 0x100000) ShowProgress(i, size); - if(!FileRead(buffer, read_bytes, offset + i)) { - FileClose(); - return 1; - } - sha_update(buffer, read_bytes); - } - sha_get(hash); - ShowProgress(0, 0); - FileClose(); - - return 0; -} - -u32 CheckHashFromFile(const char* filename, u32 offset, u32 size, u8* hash) -{ - u8 digest[32]; - - if (GetHashFromFile(filename, offset, size, digest) != 0) - return 1; - - return (memcmp(hash, digest, 32) == 0) ? 0 : 1; -} - -u32 CryptNcch(const char* filename, u32 offset, u32 size, u64 seedId, u8* encrypt_flags) -{ - NcchHeader* ncch = (NcchHeader*) 0x20316200; - u8* buffer = (u8*) 0x20316400; - CryptBufferInfo info0 = {.setKeyY = 1, .keyslot = 0x2C, .mode = AES_CNT_CTRNAND_MODE}; - CryptBufferInfo info1 = {.setKeyY = 1, .mode = AES_CNT_CTRNAND_MODE}; - u8 seedKeyY[16] = { 0x00 }; - u32 result = 0; - - if (!FileOpen(filename)) // already checked this file - return 1; - if (!DebugFileRead((void*) ncch, 0x200, offset)) { - FileClose(); - return 1; - } - FileClose(); - - // check (again) for magic number - if (memcmp(ncch->magic, "NCCH", 4) != 0) { - Debug("Not a NCCH container"); - return 2; // not an actual error - } - - // size plausibility check - u32 size_sum = 0x200 + ((ncch->size_exthdr) ? 0x800 : 0x0) + 0x200 * - (ncch->size_plain + ncch->size_logo + ncch->size_exefs + ncch->size_romfs); - if (ncch->size * 0x200 < size_sum) { - Debug("Probably not a NCCH container"); - return 2; // not an actual error - } - - // check if encrypted - if (!encrypt_flags && (ncch->flags[7] & 0x04)) { - Debug("NCCH is not encrypted"); - return 2; // not an actual error - } else if (encrypt_flags && !(ncch->flags[7] & 0x04)) { - Debug("NCCH is already encrypted"); - return 2; // not an actual error - } else if (encrypt_flags && (encrypt_flags[7] & 0x04)) { - Debug("Nothing to do!"); - return 2; // not an actual error - } - - // check size - if ((size > 0) && (ncch->size * 0x200 > size)) { - Debug("NCCH size is out of bounds"); - return 1; - } - - // select correct title ID for seed crypto - if (seedId == 0) seedId = ncch->partitionId; - - // copy over encryption parameters (if applicable) - if (encrypt_flags) { - ncch->flags[3] = encrypt_flags[3]; - ncch->flags[7] &= (0x01|0x20|0x04)^0xFF; - ncch->flags[7] |= (0x01|0x20)&encrypt_flags[7]; - } - - // check crypto type - bool uses7xCrypto = ncch->flags[3]; - bool usesSeedCrypto = ncch->flags[7] & 0x20; - bool usesSec3Crypto = (ncch->flags[3] == 0x0A); - bool usesSec4Crypto = (ncch->flags[3] == 0x0B); - bool usesFixedKey = ncch->flags[7] & 0x01; - - Debug("Code / Crypto: %s / %s%s%s%s", ncch->productCode, (usesFixedKey) ? "FixedKey " : "", (usesSec4Crypto) ? "Secure4 " : (usesSec3Crypto) ? "Secure3 " : (uses7xCrypto) ? "7x " : "", (usesSeedCrypto) ? "Seed " : "", (!uses7xCrypto && !usesSeedCrypto && !usesFixedKey) ? "Standard" : ""); - - // setup zero key crypto - if (usesFixedKey) { - // from https://github.com/profi200/Project_CTR/blob/master/makerom/pki/dev.h - u8 zeroKey[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - u8 sysKey[16] = {0x52, 0x7C, 0xE6, 0x30, 0xA9, 0xCA, 0x30, 0x5F, 0x36, 0x96, 0xF3, 0xCD, 0xE9, 0x54, 0x19, 0x4B}; - if (uses7xCrypto || usesSeedCrypto) { - Debug("Crypto combination is not allowed!"); - return 1; - } - info1.setKeyY = info0.setKeyY = 0; - info1.keyslot = info0.keyslot = 0x11; - setup_aeskey(0x11, (ncch->programId & ((u64) 0x10 << 32)) ? sysKey : zeroKey); - } - - // check secure4 crypto - if (usesSec4Crypto) { - Debug("Warning: Secure4 support is preliminary!"); - if (FileOpen("slot0x11key96.bin")) { - u8 slot0x11key96[16] = {0}; - if (FileRead(&slot0x11key96, 16, 0) != 16) { - Debug("slot0x11key96.bin is corrupt!"); - FileClose(); - return 1; - } - FileClose(); - setup_aeskey(0x11, slot0x11key96); - } else { - return 1; - } - } - - // check / setup 7x crypto - if (uses7xCrypto && (GetUnitPlatform() == PLATFORM_3DS)) { - if (usesSec3Crypto) { - Debug("Secure3 crypto needs a N3DS!"); - return 1; - } - if (FileOpen("slot0x25KeyX.bin")) { - u8 slot0x25KeyX[16] = {0}; - if (FileRead(&slot0x25KeyX, 16, 0) != 16) { - Debug("slot0x25keyX.bin is corrupt!"); - FileClose(); - return 1; - } - FileClose(); - setup_aeskeyX(0x25, slot0x25KeyX); - } else { - Debug("Warning: Need slot0x25KeyX.bin on O3DS < 7.x"); - } - } - - // check / setup seed crypto - if (usesSeedCrypto) { - if (FileOpen("seeddb.bin")) { - SeedInfoEntry* entry = (SeedInfoEntry*) buffer; - u32 found = 0; - for (u32 i = 0x10;; i += 0x20) { - if (FileRead(entry, 0x20, i) != 0x20) - break; - if (entry->titleId == seedId) { - u8 keydata[32]; - memcpy(keydata, ncch->signature, 16); - memcpy(keydata + 16, entry->external_seed, 16); - u8 sha256sum[32]; - sha_init(SHA256_MODE); - sha_update(keydata, 32); - sha_get(sha256sum); - memcpy(seedKeyY, sha256sum, 16); - found = 1; - } - } - FileClose(); - if (!found) { - Debug("Seed not found in seeddb.bin!"); - return 1; - } - } else { - Debug("Need seeddb.bin for seed crypto!"); - return 1; - } - } - - // basic setup of CryptBufferInfo structs - memset(info0.ctr, 0x00, 16); - if (ncch->version == 1) { - memcpy(info0.ctr, &(ncch->partitionId), 8); - } else { - for (u32 i = 0; i < 8; i++) - info0.ctr[i] = ((u8*) &(ncch->partitionId))[7-i]; - } - memcpy(info1.ctr, info0.ctr, 8); - memcpy(info0.keyY, ncch->signature, 16); - memcpy(info1.keyY, (usesSeedCrypto) ? seedKeyY : ncch->signature, 16); - info1.keyslot = (usesSec4Crypto) ? 0x11 : ((usesSec3Crypto) ? 0x18 : ((uses7xCrypto) ? 0x25 : info0.keyslot)); - - Debug("%s ExHdr/ExeFS/RomFS (%ukB/%ukB/%uMB)", - (encrypt_flags) ? "Encrypt" : "Decrypt", - (ncch->size_exthdr > 0) ? 0x800 / 1024 : 0, - (ncch->size_exefs * 0x200) / 1024, - (ncch->size_romfs * 0x200) / (1024*1024)); - - // process ExHeader - if (ncch->size_exthdr > 0) { - memset(info0.ctr + 12, 0x00, 4); - if (ncch->version == 1) - add_ctr(info0.ctr, 0x200); // exHeader offset - else - info0.ctr[8] = 1; - result |= CryptSdToSd(filename, offset + 0x200, 0x800, &info0); - } - - // process ExeFS - if (ncch->size_exefs > 0) { - u32 offset_byte = ncch->offset_exefs * 0x200; - u32 size_byte = ncch->size_exefs * 0x200; - memset(info0.ctr + 12, 0x00, 4); - if (ncch->version == 1) - add_ctr(info0.ctr, offset_byte); - else - info0.ctr[8] = 2; - if (uses7xCrypto || usesSeedCrypto) { - u32 offset_code = 0; - u32 size_code = 0; - // find .code offset and size - if (!encrypt_flags) // decrypt this first (when decrypting) - result |= CryptSdToSd(filename, offset + offset_byte, 0x200, &info0); - if(!FileOpen(filename)) - return 1; - if(!DebugFileRead(buffer, 0x200, offset + offset_byte)) { - FileClose(); - return 1; - } - FileClose(); - for (u32 i = 0; i < 10; i++) { - if(memcmp(buffer + (i*0x10), ".code", 5) == 0) { - offset_code = getle32(buffer + (i*0x10) + 0x8) + 0x200; - size_code = getle32(buffer + (i*0x10) + 0xC); - break; - } - } - if (encrypt_flags) // encrypt this last (when encrypting) - result |= CryptSdToSd(filename, offset + offset_byte, 0x200, &info0); - // special ExeFS decryption routine (only .code has new encryption) - if (size_code > 0) { - result |= CryptSdToSd(filename, offset + offset_byte + 0x200, offset_code - 0x200, &info0); - memcpy(info1.ctr, info0.ctr, 16); // this depends on the exeFS file offsets being aligned (which they are) - add_ctr(info0.ctr, size_code / 0x10); - info0.setKeyY = info1.setKeyY = 1; - result |= CryptSdToSd(filename, offset + offset_byte + offset_code, size_code, &info1); - result |= CryptSdToSd(filename, - offset + offset_byte + offset_code + size_code, - size_byte - (offset_code + size_code), &info0); - } else { - result |= CryptSdToSd(filename, offset + offset_byte + 0x200, size_byte - 0x200, &info0); - } - } else { - result |= CryptSdToSd(filename, offset + offset_byte, size_byte, &info0); - } - } - - // process RomFS - if (ncch->size_romfs > 0) { - u32 offset_byte = ncch->offset_romfs * 0x200; - u32 size_byte = ncch->size_romfs * 0x200; - memset(info1.ctr + 12, 0x00, 4); - if (ncch->version == 1) - add_ctr(info1.ctr, offset_byte); - else - info1.ctr[8] = 3; - info1.setKeyY = 1; - result |= CryptSdToSd(filename, offset + offset_byte, size_byte, &info1); - } - - // set NCCH header flags - if (!encrypt_flags) { - ncch->flags[3] = 0x00; - ncch->flags[7] &= (0x01|0x20)^0xFF; - ncch->flags[7] |= 0x04; - } - - // write header back - if (!FileOpen(filename)) - return 1; - if (!DebugFileWrite((void*) ncch, 0x200, offset)) { - FileClose(); - return 1; - } - FileClose(); - - // verify decryption - if ((result == 0) && !encrypt_flags) { - char* status_str[3] = { "OK", "Fail", "-" }; - u32 ver_exthdr = 2; - u32 ver_exefs = 2; - u32 ver_romfs = 2; - - if (ncch->size_exthdr > 0) - ver_exthdr = CheckHashFromFile(filename, offset + 0x200, 0x400, ncch->hash_exthdr); - if (ncch->size_exefs_hash > 0) - ver_exefs = CheckHashFromFile(filename, offset + (ncch->offset_exefs * 0x200), ncch->size_exefs_hash * 0x200, ncch->hash_exefs); - if (ncch->size_romfs_hash > 0) - ver_romfs = CheckHashFromFile(filename, offset + (ncch->offset_romfs * 0x200), ncch->size_romfs_hash * 0x200, ncch->hash_romfs); - - Debug("Verify ExHdr/ExeFS/RomFS: %s/%s/%s", status_str[ver_exthdr], status_str[ver_exefs], status_str[ver_romfs]); - result = (((ver_exthdr | ver_exefs | ver_romfs) & 1) == 0) ? 0 : 1; - } - - - return result; -} - -u32 CryptCia(const char* filename, u8* ncch_crypt, bool cia_encrypt, bool cxi_only) -{ - u8* buffer = (u8*) 0x20316600; - __attribute__((aligned(16))) u8 titlekey[16]; - u64 titleId; - u8* content_list; - u8* ticket_data; - u8* tmd_data; - - u32 offset_ticktmd; - u32 offset_content; - u32 size_ticktmd; - u32 size_ticket; - u32 size_tmd; - u32 size_content; - - u32 content_count; - u32 result = 0; - - if (cia_encrypt) // process only one layer when encrypting - ncch_crypt = NULL; - - if (!FileOpen(filename)) // already checked this file - return 1; - if (!DebugFileRead(buffer, 0x20, 0x00)) { - FileClose(); - return 1; - } - - // get offsets for various sections & check - u32 section_size[6]; - u32 section_offset[6]; - section_size[0] = getle32(buffer); - section_offset[0] = 0; - for (u32 i = 1; i < 6; i++) { - section_size[i] = getle32(buffer + 4 + ((i == 4) ? (5*4) : (i == 5) ? (4*4) : (i*4)) ); - section_offset[i] = section_offset[i-1] + align(section_size[i-1], 64); - } - offset_ticktmd = section_offset[2]; - offset_content = section_offset[4]; - size_ticktmd = section_offset[4] - section_offset[2]; - size_ticket = section_size[2]; - size_tmd = section_size[3]; - size_content = section_size[4]; - - if (FileGetSize() != section_offset[5] + align(section_size[5], 64)) { - Debug("Probably not a CIA file"); - FileClose(); - return 1; - } - - if ((size_ticktmd) > 0x10000) { - Debug("Ticket/TMD too big"); - FileClose(); - return 1; - } - - // load ticket & tmd to buffer, close file - if (!DebugFileRead(buffer, size_ticktmd, offset_ticktmd)) { - FileClose(); - return 1; - } - FileClose(); - - u32 signature_size[2] = { 0 }; - u8* section_data[2] = {buffer, buffer + align(size_ticket, 64)}; - for (u32 i = 0; i < 2; i++) { - u32 type = section_data[i][3]; - signature_size[i] = (type == 3) ? 0x240 : (type == 4) ? 0x140 : (type == 5) ? 0x80 : 0; - if ((signature_size[i] == 0) || (memcmp(section_data[i], "\x00\x01\x00", 3) != 0)) { - Debug("Unknown signature type: %08X", getbe32(section_data[i])); - return 1; - } - } - - ticket_data = section_data[0] + signature_size[0]; - size_ticket -= signature_size[0]; - tmd_data = section_data[1] + signature_size[1]; - size_tmd -= signature_size[1]; - - // extract & decrypt titlekey - if (size_ticket < 0x210) { - Debug("Ticket is too small (%i byte)", size_ticket); - return 1; - } - TitleKeyEntry titlekeyEntry; - titleId = getbe64(ticket_data + 0x9C); - memcpy(titlekeyEntry.titleId, ticket_data + 0x9C, 8); - memcpy(titlekeyEntry.encryptedTitleKey, ticket_data + 0x7F, 16); - titlekeyEntry.commonKeyIndex = *(ticket_data + 0xB1); - DecryptTitlekey(&titlekeyEntry); - memcpy(titlekey, titlekeyEntry.encryptedTitleKey, 16); - - // get content data from TMD - content_count = getbe16(tmd_data + 0x9E); - content_list = tmd_data + 0xC4 + (64 * 0x24); - if (content_count * 0x30 != size_tmd - (0xC4 + (64 * 0x24))) { - Debug("TMD content count (%i) / list size mismatch", content_count); - return 1; - } - u32 size_tmd_content = 0; - for (u32 i = 0; i < content_count; i++) - size_tmd_content += getbe32(content_list + (0x30 * i) + 0xC); - if (size_tmd_content != size_content) { - Debug("TMD content size / actual size mismatch"); - return 1; - } - - bool untouched = true; - u32 n_processed = 0; - u32 next_offset = offset_content; - CryptBufferInfo info = {.setKeyY = 0, .keyslot = 0x11, .mode = (cia_encrypt) ? AES_CNT_TITLEKEY_ENCRYPT_MODE : AES_CNT_TITLEKEY_DECRYPT_MODE}; - setup_aeskey(0x11, titlekey); - - if (ncch_crypt) - Debug("Pass #1: CIA decryption..."); - if (cxi_only) content_count = 1; - for (u32 i = 0; i < content_count; i++) { - u32 size = getbe32(content_list + (0x30 * i) + 0xC); - u32 offset = next_offset; - next_offset = offset + size; - if (!(content_list[(0x30 * i) + 0x7] & 0x1) != cia_encrypt) - continue; // depending on 'cia_encrypt' setting: not/already encrypted - untouched = false; - if (cia_encrypt) { - Debug("Verifying unencrypted content..."); - if (CheckHashFromFile(filename, offset, size, content_list + (0x30 * i) + 0x10) != 0) { - Debug("Verification failed!"); - result = 1; - continue; - } - Debug("Verified OK!"); - } - Debug("%scrypting Content %i of %i (%iMB)...", (cia_encrypt) ? "En" : "De", i + 1, content_count, size / (1024*1024)); - memset(info.ctr, 0x00, 16); - memcpy(info.ctr, content_list + (0x30 * i) + 4, 2); - if (CryptSdToSd(filename, offset, size, &info) != 0) { - Debug("%scryption failed!", (cia_encrypt) ? "En" : "De"); - result = 1; - continue; - } - if (!cia_encrypt) { - Debug("Verifying decrypted content..."); - if (CheckHashFromFile(filename, offset, size, content_list + (0x30 * i) + 0x10) != 0) { - Debug("Verification failed!"); - result = 1; - continue; - } - Debug("Verified OK!"); - } - content_list[(0x30 * i) + 0x7] ^= 0x1; - n_processed++; - } - - if (ncch_crypt) { - Debug("Pass #2: NCCH decryption..."); - next_offset = offset_content; - for (u32 i = 0; i < content_count; i++) { - u32 ncch_state; - u32 size = getbe32(content_list + (0x30 * i) + 0xC); - u32 offset = next_offset; - next_offset = offset + size; - Debug("Processing Content %i of %i (%iMB)...", i + 1, content_count, size / (1024*1024)); - ncch_state = CryptNcch(filename, offset, size, titleId, NULL); - if (!(ncch_crypt[7] & 0x04) && (ncch_state != 1)) - ncch_state = CryptNcch(filename, offset, size, titleId, ncch_crypt); - if (ncch_state == 0) { - untouched = false; - Debug("Recalculating hash..."); - if (GetHashFromFile(filename, offset, size, content_list + (0x30 * i) + 0x10) != 0) { - Debug("Recalculation failed!"); - result = 1; - continue; - } - } else if (ncch_state == 1) { - Debug("Failed!"); - result = 1; - continue; - } - n_processed++; - } - if (!untouched) { - // recalculate content info hashes - Debug("Recalculating TMD hashes..."); - for (u32 i = 0, kc = 0; i < 64 && kc < content_count; i++) { - u32 k = getbe16(tmd_data + 0xC4 + (i * 0x24) + 0x02); - u8 chunk_hash[32]; - sha_init(SHA256_MODE); - sha_update(content_list + kc * 0x30, k * 0x30); - sha_get(chunk_hash); - memcpy(tmd_data + 0xC4 + (i * 0x24) + 0x04, chunk_hash, 32); - kc += k; - } - u8 tmd_hash[32]; - sha_init(SHA256_MODE); - sha_update(tmd_data + 0xC4, 64 * 0x24); - sha_get(tmd_hash); - memcpy(tmd_data + 0xA4, tmd_hash, 32); - } - } - - if (untouched) { - Debug((cia_encrypt) ? "CIA is already encrypted" : "CIA is not encrypted"); - } else if (n_processed > 0) { - if (!FileOpen(filename)) // already checked this file - return 1; - if (!DebugFileWrite(buffer, size_ticktmd, offset_ticktmd)) - result = 1; - FileClose(); - } - - return result; -} - - -u32 CryptGameFiles(u32 param) -{ - u8 ncch_crypt_none[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 }; - u8 ncch_crypt_standard[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - const char* ncsd_partition_name[8] = { - "Executable", "Manual", "DPC", "Unknown", "Unknown", "Unknown", "UpdateN3DS", "UpdateO3DS" - }; - char* batch_dir = GAME_DIR; - u8* buffer = (u8*) 0x20316000; - - bool batch_ncch = param & GC_NCCH_PROCESS; - bool batch_cia = param & GC_CIA_PROCESS; - bool cia_encrypt = param & GC_CIA_ENCRYPT; - bool cxi_only = param & GC_CXI_ONLY; - u8* ncch_crypt = (param & GC_NCCH_ENCRYPT) ? ncch_crypt_standard : NULL; - u8* cia_ncch_crypt = (param & GC_CIA_DEEP) ? ncch_crypt_none : ncch_crypt; - - u32 n_processed = 0; - u32 n_failed = 0; - - if (!DebugDirOpen(batch_dir)) { - if (!DebugDirOpen(WORK_DIR)) { - Debug("No working directory found!"); - return 1; - } - batch_dir = WORK_DIR; - } - - char path[256]; - u32 path_len = strnlen(batch_dir, 128); - memcpy(path, batch_dir, path_len); - path[path_len++] = '/'; - - while (DirRead(path + path_len, 256 - path_len)) { - if (!FileOpen(path)) - continue; - if (!FileRead(buffer, 0x200, 0x0)) { - FileClose(); - continue; - } - FileClose(); - - if (batch_ncch && (memcmp(buffer + 0x100, "NCCH", 4) == 0)) { - Debug("Processing NCCH \"%s\"", path + path_len); - if (CryptNcch(path, 0x00, 0, 0, ncch_crypt) != 1) { - Debug("Success!"); - n_processed++; - } else { - Debug("Failed!"); - n_failed++; - } - } else if (batch_ncch && (memcmp(buffer + 0x100, "NCSD", 4) == 0)) { - if (getle64(buffer + 0x110) != 0) - continue; // skip NAND backup NCSDs - Debug("Processing NCSD \"%s\"", path + path_len); - u32 p; - u32 nc = (cxi_only) ? 1 : 8; - for (p = 0; p < nc; p++) { - u64 seedId = (p) ? getle64(buffer + 0x108) : 0; - u32 offset = getle32(buffer + 0x120 + (p*0x8)) * 0x200; - u32 size = getle32(buffer + 0x124 + (p*0x8)) * 0x200; - if (size == 0) - continue; - Debug("Partition %i (%s)", p, ncsd_partition_name[p]); - if (CryptNcch(path, offset, size, seedId, ncch_crypt) == 1) - break; - } - if ( p == nc ) { - Debug("Success!"); - n_processed++; - } else { - Debug("Failed!"); - n_failed++; - } - } else if (batch_cia && (memcmp(buffer, "\x20\x20", 2) == 0)) { - Debug("Processing CIA \"%s\"", path + path_len); - if (CryptCia(path, cia_ncch_crypt, cia_encrypt, cxi_only) == 0) { - Debug("Success!"); - n_processed++; - } else { - Debug("Failed!"); - n_failed++; - } - } - } - - DirClose(); - - if (n_processed) { - Debug(""); - Debug("%ux processed / %ux failed ", n_processed, n_failed); - } else if (!n_failed) { - Debug("Nothing found in %s/!", batch_dir); - } - - return !n_processed; -} - -u32 CryptSdFiles(u32 param) { - const char* subpaths[] = {"dbs", "extdata", "title", NULL}; - u8 movable_keyY[16] = { 0 }; - char* batch_dir = GAME_DIR; - u32 n_processed = 0; - u32 n_failed = 0; - u32 plen = 0; - - if (!DebugDirOpen(batch_dir)) { - if (!DebugDirOpen(WORK_DIR)) { - Debug("No working directory found!"); - return 1; - } - batch_dir = WORK_DIR; - } - DirClose(); - plen = strnlen(batch_dir, 128); - - if (GetSd0x34KeyY(movable_keyY, false) == 0) { - Debug("Setting console 0x34 keyY"); - setup_aeskeyY(0x34, movable_keyY); - use_aeskey(0x34); - } - - // main processing loop - for (u32 s = 0; subpaths[s] != NULL; s++) { - char* filelist = (char*) 0x20400000; - char basepath[128]; - u32 bplen; - Debug("Processing subpath \"%s\"...", subpaths[s]); - sprintf(basepath, "%s/%s", batch_dir, subpaths[s]); - if (!GetFileList(basepath, filelist, 0x100000, true, true, false)) { - Debug("Not found!"); - continue; - } - bplen = strnlen(basepath, 128); - for (char* path = strtok(filelist, "\n"); path != NULL; path = strtok(NULL, "\n")) { - u32 fsize = 0; - CryptBufferInfo info = {.keyslot = 0x34, .setKeyY = 0, .mode = AES_CNT_CTRNAND_MODE}; - GetSdCtr(info.ctr, path + plen); - if (FileOpen(path)) { - fsize = FileGetSize(); - FileClose(); - } else { - Debug("Could not open: %s", path + bplen); - n_failed++; - continue; - } - Debug("%2u: %s", n_processed, path + bplen); - if (CryptSdToSd(path, 0, fsize, &info) == 0) { - n_processed++; - } else { - Debug("Failed!"); - n_failed++; - } - } - } - - return (n_processed) ? 0 : 1; -} - -u32 DecryptSdFilesDirect(u32 param) { - char* filelist = (char*) 0x20400000; - u8 movable_keyY[16] = { 0 }; - char basepath[256]; - char* batch_dir = GAME_DIR; - u32 n_processed = 0; - u32 n_failed = 0; - u32 bplen = 0; - - if (!DebugDirOpen(batch_dir)) { - if (!DebugDirOpen(WORK_DIR)) { - Debug("No working directory found!"); - return 1; - } - batch_dir = WORK_DIR; - } - DirClose(); - - if (GetSd0x34KeyY(movable_keyY, true) == 0) { - Debug("Setting console 0x34 keyY"); - setup_aeskeyY(0x34, movable_keyY); - use_aeskey(0x34); - } else { - return 1; // movable.sed has to be present in NAND - } - - Debug(""); - if (SdFolderSelector(basepath, movable_keyY) != 0) - return 1; - if (!GetFileList(basepath, filelist, 0x100000, true, true, false)) { - Debug("Nothing found in folder"); - return 1; - } - Debug(""); - - Debug("Using base path %s", basepath); - bplen = strnlen(basepath, 256); - - // main processing loop - for (char* srcpath = strtok(filelist, "\n"); srcpath != NULL; srcpath = strtok(NULL, "\n")) { - char* subpath = srcpath + 13 + 33 + 33; // length of ("/Nintendo 3DS" + "/" + "/") - char dstpath[256]; - u32 fsize = 0; - snprintf(dstpath, 256, "%s%s", batch_dir, subpath); - CryptBufferInfo info = {.keyslot = 0x34, .setKeyY = 0, .mode = AES_CNT_CTRNAND_MODE}; - GetSdCtr(info.ctr, subpath); - Debug("%2u: %s", n_processed, srcpath + bplen); - if (FileOpen(srcpath)) { - fsize = FileGetSize(); - FileCopyTo(dstpath, BUFFER_ADDRESS, BUFFER_MAX_SIZE); - FileClose(); - } else { - Debug("Could not open: %s", srcpath + bplen); - n_failed++; - continue; - } - if (CryptSdToSd(dstpath, 0, fsize, &info) == 0) { - n_processed++; - } else { - Debug("Failed!"); - n_failed++; - } - } - - return (n_processed) ? 0 : 1; -} diff --git a/source/fatfs/crypto/game.h b/source/fatfs/crypto/game.h deleted file mode 100644 index 9721751..0000000 --- a/source/fatfs/crypto/game.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "common.h" -#include "decryptor/decryptor.h" - -#define GC_NCCH_PROCESS (1<<0) -#define GC_CIA_PROCESS (1<<1) -#define GC_CIA_DEEP (1<<2) -#define GC_NCCH_ENCRYPT (1<<3) -#define GC_CIA_ENCRYPT (1<<4) -#define GC_CXI_ONLY (1<<5) - -#define MAX_ENTRIES 1024 - -typedef struct { - u64 titleId; - u8 external_seed[16]; - u8 reserved[8]; -} __attribute__((packed)) SeedInfoEntry; - -typedef struct { - u32 n_entries; - u8 padding[12]; - SeedInfoEntry entries[MAX_ENTRIES]; -} __attribute__((packed)) SeedInfo; - -typedef struct { - u8 ctr[16]; - u32 size_mb; - char filename[180]; -} __attribute__((packed)) SdInfoEntry; - -typedef struct { - u32 n_entries; - SdInfoEntry entries[MAX_ENTRIES]; -} __attribute__((packed, aligned(16))) SdInfo; - -typedef struct { - u8 ctr[16]; - u8 keyY[16]; - u32 size_mb; - u8 reserved[4]; - u32 usesSeedCrypto; - u32 uses7xCrypto; - u64 titleId; - char filename[112]; -} __attribute__((packed)) NcchInfoEntry; - -typedef struct { - u32 padding; - u32 ncch_info_version; - u32 n_entries; - u8 reserved[4]; - NcchInfoEntry entries[MAX_ENTRIES]; -} __attribute__((packed, aligned(16))) NcchInfo; - -typedef struct { - u8 signature[0x100]; - u8 magic[0x4]; - u32 size; - u64 partitionId; - u16 makercode; - u16 version; - u8 reserved0[0x4]; - u64 programId; - u8 reserved1[0x10]; - u8 hash_logo[0x20]; - char productCode[0x10]; - u8 hash_exthdr[0x20]; - u32 size_exthdr; - u8 reserved2[0x4]; - u8 flags[0x8]; - u32 offset_plain; - u32 size_plain; - u32 offset_logo; - u32 size_logo; - u32 offset_exefs; - u32 size_exefs; - u32 size_exefs_hash; - u8 reserved3[0x4]; - u32 offset_romfs; - u32 size_romfs; - u32 size_romfs_hash; - u8 reserved4[0x4]; - u8 hash_exefs[0x20]; - u8 hash_romfs[0x20]; -} __attribute__((packed, aligned(16))) NcchHeader; - - -u32 GetSdCtr(u8* ctr, const char* path); -u32 GetSd0x34KeyY(u8* movable_keyY, bool from_nand); -u32 SdFolderSelector(char* path, u8* keyY); -u32 SdInfoGen(SdInfo* info, const char* base_path); -u32 CryptSdToSd(const char* filename, u32 offset, u32 size, CryptBufferInfo* info); -u32 GetHashFromFile(const char* filename, u32 offset, u32 size, u8* hash); -u32 CheckHashFromFile(const char* filename, u32 offset, u32 size, u8* hash); -u32 CryptNcch(const char* filename, u32 offset, u32 size, u64 seedId, u8* encrypt_flags); -u32 CryptCia(const char* filename, u8* ncch_crypt, bool cia_encrypt, bool cxi_only); - -// --> FEATURE FUNCTIONS <-- -u32 NcchPadgen(u32 param); -u32 SdPadgen(u32 param); -u32 SdPadgenDirect(u32 param); -u32 UpdateSeedDb(u32 param); -u32 CryptGameFiles(u32 param); -u32 CryptSdFiles(u32 param); -u32 DecryptSdFilesDirect(u32 param); diff --git a/source/fatfs/crypto/nand.c b/source/fatfs/crypto/nand.c deleted file mode 100644 index 26f11e3..0000000 --- a/source/fatfs/crypto/nand.c +++ /dev/null @@ -1,682 +0,0 @@ -#include "fs.h" -#include "draw.h" -#include "hid.h" -#include "platform.h" -#include "decryptor/aes.h" -#include "decryptor/decryptor.h" -#include "decryptor/nand.h" -#include "fatfs/sdmmc.h" - -// see: http://3dbrew.org/wiki/Flash_Filesystem -static PartitionInfo partitions[] = { - { "TWLN", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x00012E00, 0x08FB5200, 0x3, AES_CNT_TWLNAND_MODE }, - { "TWLP", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x09011A00, 0x020B6600, 0x3, AES_CNT_TWLNAND_MODE }, - { "AGBSAVE", {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 0x0B100000, 0x00030000, 0x7, AES_CNT_CTRNAND_MODE }, - { "FIRM0", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B130000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE }, - { "FIRM1", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B530000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE }, - { "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95CA00, 0x2F3E3600, 0x4, AES_CNT_CTRNAND_MODE }, // O3DS - { "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95AE00, 0x41D2D200, 0x5, AES_CNT_CTRNAND_MODE } // N3DS -}; - -static u32 emunand_header = 0; -static u32 emunand_offset = 0; - - -u32 CheckEmuNand(void) -{ - u8* buffer = BUFFER_ADDRESS; - u32 nand_size_sectors = getMMCDevice(0)->total_size; - u32 multi_sectors = (GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS; - u32 ret = EMUNAND_NOT_READY; - - // check the MBR for presence of a hidden partition - sdmmc_sdcard_readsectors(0, 1, buffer); - u32 hidden_sectors = getle32(buffer + 0x1BE + 0x8); - - for (u32 offset_sector = 0; offset_sector + nand_size_sectors < hidden_sectors; offset_sector += multi_sectors) { - // check for Gateway type EmuNAND - sdmmc_sdcard_readsectors(offset_sector + nand_size_sectors, 1, buffer); - if (memcmp(buffer + 0x100, "NCSD", 4) == 0) { - ret |= EMUNAND_GATEWAY << (2 * (offset_sector / multi_sectors)); - continue; - } - // check for RedNAND type EmuNAND - sdmmc_sdcard_readsectors(offset_sector + 1, 1, buffer); - if (memcmp(buffer + 0x100, "NCSD", 4) == 0) { - ret |= EMUNAND_REDNAND << (2 * (offset_sector / multi_sectors)); - continue; - } - // EmuNAND ready but not set up - ret |= EMUNAND_READY << (2 * (offset_sector / multi_sectors)); - } - - return ret; -} - -u32 SetNand(bool set_emunand, bool force_emunand) -{ - if (set_emunand) { - u32 emunand_state = CheckEmuNand(); - u32 emunand_count = 0; - u32 offset_sector = 0; - - for (emunand_count = 0; (emunand_state >> (2 * emunand_count)) & 0x3; emunand_count++); - if (emunand_count > 1) { // multiple EmuNANDs -> use selector - u32 multi_sectors = (GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS; - u32 emunand_no = 0; - Debug("Use arrow keys and to choose EmuNAND"); - while (true) { - u32 emunandn_state = (emunand_state >> (2 * emunand_no)) & 0x3; - offset_sector = emunand_no * multi_sectors; - Debug("\rEmuNAND #%u: %s", emunand_no, (emunandn_state == EMUNAND_READY) ? "EmuNAND ready" : (emunandn_state == EMUNAND_GATEWAY) ? "GW EmuNAND" : "RedNAND"); - // user input routine - u32 pad_state = InputWait(); - if (pad_state & BUTTON_DOWN) { - emunand_no = (emunand_no + 1) % emunand_count; - } else if (pad_state & BUTTON_UP) { - emunand_no = (emunand_no) ? emunand_no - 1 : emunand_count - 1; - } else if (pad_state & BUTTON_A) { - Debug("EmuNAND #%u", emunand_no); - emunand_state = emunandn_state; - break; - } else if (pad_state & BUTTON_B) { - Debug("(cancelled by user)"); - return 2; - } - } - } - - if ((emunand_state == EMUNAND_READY) && force_emunand) - emunand_state = EMUNAND_GATEWAY; - switch (emunand_state) { - case EMUNAND_NOT_READY: - Debug("SD is not formatted for EmuNAND"); - return 1; - case EMUNAND_GATEWAY: - emunand_header = offset_sector + getMMCDevice(0)->total_size; - emunand_offset = offset_sector; - Debug("Using EmuNAND @ %06X/%06X", emunand_header, emunand_offset); - return 0; - case EMUNAND_REDNAND: - emunand_header = offset_sector + 1; - emunand_offset = offset_sector + 1; - Debug("Using RedNAND @ %06X/%06X", emunand_header, emunand_offset); - return 0; - default: - Debug("EmuNAND is not available"); - return 1; - } - } else { - emunand_header = 0; - emunand_offset = 0; - return 0; - } -} - -static inline int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out) -{ - if (emunand_header) { - if (sector_no == 0) { - int errorcode = sdmmc_sdcard_readsectors(emunand_header, 1, out); - if (errorcode) return errorcode; - sector_no = 1; - numsectors--; - out += 0x200; - } - return sdmmc_sdcard_readsectors(sector_no + emunand_offset, numsectors, out); - } else return sdmmc_nand_readsectors(sector_no, numsectors, out); -} - -static inline int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in) -{ - if (emunand_header) { - if (sector_no == 0) { - int errorcode = sdmmc_sdcard_writesectors(emunand_header, 1, in); - if (errorcode) return errorcode; - sector_no = 1; - numsectors--; - in += 0x200; - } - return sdmmc_sdcard_writesectors(sector_no + emunand_offset, numsectors, in); - } else return sdmmc_nand_writesectors(sector_no, numsectors, in); -} - -u32 OutputFileNameSelector(char* filename, const char* basename, char* extension) { - char bases[3][64] = { 0 }; - char* dotpos = NULL; - - // build first base name and extension - strncpy(bases[0], basename, 63); - dotpos = strrchr(bases[0], '.'); - - if (dotpos) { - *dotpos = '\0'; - if (!extension) - extension = dotpos + 1; - } - - // build other two base names - snprintf(bases[1], 63, "%s_%s", bases[0], (emunand_header) ? "emu" : "sys"); - snprintf(bases[2], 63, "%s%s" , (emunand_header) ? "emu" : "sys", bases[0]); - - u32 fn_id = (emunand_header) ? 1 : 0; - u32 fn_num = (emunand_header) ? (emunand_offset / ((GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS)) : 0; - bool exists = false; - char extstr[16] = { 0 }; - if (extension) - snprintf(extstr, 15, ".%s", extension); - Debug("Use arrow keys and to choose a name"); - while (true) { - char numstr[2] = { 0 }; - // build and output file name (plus "(!)" if existing) - numstr[0] = (fn_num > 0) ? '0' + fn_num : '\0'; - snprintf(filename, 63, "%s%s%s", bases[fn_id], numstr, extstr); - if ((exists = FileOpen(filename))) - FileClose(); - Debug("\r%s%s", filename, (exists) ? " (!)" : ""); - // user input routine - u32 pad_state = InputWait(); - if (pad_state & BUTTON_DOWN) { // increment filename id - fn_id = (fn_id + 1) % 3; - } else if (pad_state & BUTTON_UP) { // decrement filename id - fn_id = (fn_id > 0) ? fn_id - 1 : 2; - } else if ((pad_state & BUTTON_RIGHT) && (fn_num < 9)) { // increment number - fn_num++; - } else if ((pad_state & BUTTON_LEFT) && (fn_num > 0)) { // decrement number - fn_num--; - } else if (pad_state & BUTTON_A) { - Debug("%s%s", filename, (exists) ? " (!)" : ""); - break; - } else if (pad_state & BUTTON_B) { - Debug("(cancelled by user)"); - return 2; - } - } - - // overwrite confirmation - if (exists) { - Debug("Press to overwrite existing file"); - while (true) { - u32 pad_state = InputWait(); - if (pad_state & BUTTON_A) { - break; - } else if (pad_state & BUTTON_B) { - Debug("(cancelled by user)"); - return 2; - } - } - } - - return 0; -} - -u32 InputFileNameSelector(char* filename, const char* basename, char* extension, u8* magic, u32 msize, u32 fsize) { - char** fnptr = (char**) 0x20400000; // allow using 0x8000 byte - char* fnlist = (char*) 0x20408000; // allow using 0x80000 byte - u32 n_names = 0; - - // get the file list - try work directory first - if (!GetFileList(WORK_DIR, fnlist, 0x80000, false, true, false) && !GetFileList("/", fnlist, 0x800000, false, true, false)) { - Debug("Failed retrieving the file names list"); - return 1; - } - - // get base name, extension - char base[64] = { 0 }; - if (basename != NULL) { - // build base name and extension - strncpy(base, basename, 63); - char* dotpos = strrchr(base, '.'); - if (dotpos) { - *dotpos = '\0'; - if (!extension) - extension = dotpos + 1; - } - } - - // limit magic number size - if (msize > 0x200) - msize = 0x200; - - // parse the file names list for usable entries - for (char* fn = strtok(fnlist, "\n"); fn != NULL; fn = strtok(NULL, "\n")) { - u8 data[0x200]; - char* dotpos = strrchr(fn, '.'); - if (strrchr(fn, '/')) - fn = strrchr(fn, '/') + 1; - if (strnlen(fn, 128) > 63) - continue; // file name too long - if ((basename != NULL) && !strstr(fn, base)) - continue; // basename check failed - if ((extension != NULL) && (dotpos != NULL) && (strncmp(dotpos + 1, extension, strnlen(extension, 16)))) - continue; // extension check failed - else if ((extension == NULL) != (dotpos == NULL)) - continue; // extension check failed - if (!FileOpen(fn)) - continue; // file can't be opened - if (fsize && (FileGetSize() != fsize)) { - FileClose(); - continue; // file size check failed - } - if (msize) { - if (FileRead(data, msize, 0) != msize) { - FileClose(); - continue; // can't be read - } - if (memcmp(data, magic, msize) != 0) { - FileClose(); - continue; // magic number does not match - } - } - FileClose(); - // this is a match - keep it - fnptr[n_names++] = fn; - if (n_names * sizeof(char**) >= 0x8000) - return 1; - } - if (n_names == 0) { - Debug("No usable file found"); - return 1; - } - - u32 index = 0; - Debug("Use arrow keys and to choose a file"); - while (true) { - snprintf(filename, 63, "%s", fnptr[index]); - Debug("\r%s", filename); - u32 pad_state = InputWait(); - if (pad_state & BUTTON_DOWN) { // next filename - index = (index + 1) % n_names; - } else if (pad_state & BUTTON_UP) { // previous filename - index = (index > 0) ? index - 1 : n_names - 1; - } else if (pad_state & BUTTON_A) { - Debug("%s", filename); - break; - } else if (pad_state & BUTTON_B) { - Debug("(cancelled by user)"); - return 2; - } - } - - return 0; -} - -PartitionInfo* GetPartitionInfo(u32 partition_id) -{ - u32 partition_num = 0; - - if (partition_id == P_CTRNAND) { - partition_num = (GetUnitPlatform() == PLATFORM_3DS) ? 5 : 6; - } else { - for(; !(partition_id & (1<= 32) ? NULL : &(partitions[partition_num]); -} - -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; -} - -u32 DumpNand(u32 param) -{ - char filename[64]; - u8* buffer = BUFFER_ADDRESS; - u32 nand_size = getMMCDevice(0)->total_size * NAND_SECTOR_SIZE; - u32 result = 0; - - Debug("Dumping %sNAND. Size (MB): %u", (param & N_EMUNAND) ? "Emu" : "Sys", nand_size / (1024 * 1024)); - - if (OutputFileNameSelector(filename, "NAND.bin", NULL) != 0) - return 1; - if (!DebugFileCreate(filename, true)) - return 1; - - u32 n_sectors = nand_size / NAND_SECTOR_SIZE; - for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { - u32 read_sectors = min(SECTORS_PER_READ, (n_sectors - i)); - ShowProgress(i, n_sectors); - ReadNandSectors(i, read_sectors, buffer); - if(!DebugFileWrite(buffer, NAND_SECTOR_SIZE * read_sectors, i * NAND_SECTOR_SIZE)) { - result = 1; - break; - } - } - - ShowProgress(0, 0); - FileClose(); - - return result; -} - -u32 DecryptNandPartition(u32 param) -{ - PartitionInfo* p_info = NULL; - char filename[64]; - u8 magic[NAND_SECTOR_SIZE]; - - for (u32 partition_id = P_TWLN; partition_id <= P_CTRNAND; partition_id = partition_id << 1) { - if (param & partition_id) { - p_info = GetPartitionInfo(partition_id); - break; - } - } - if (p_info == NULL) { - Debug("No partition to dump"); - return 1; - } - - Debug("Dumping & Decrypting %s, size (MB): %u", p_info->name, p_info->size / (1024 * 1024)); - if (DecryptNandToMem(magic, p_info->offset, 16, p_info) != 0) - return 1; - if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) { - Debug("Decryption error, please contact us"); - return 1; - } - if (OutputFileNameSelector(filename, p_info->name, "bin") != 0) - return 1; - - return DecryptNandToFile(filename, p_info->offset, p_info->size, p_info); -} - -u32 EncryptMemToNand(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; - CryptBuffer(&info); - WriteNandSectors(start_sector, n_sectors, buffer); - - return 0; -} - -u32 EncryptFileToNand(const char* filename, u32 offset, u32 size, PartitionInfo* partition) -{ - u8* buffer = BUFFER_ADDRESS; - u32 result = 0; - - if (!DebugFileOpen(filename)) - return 1; - - if (FileGetSize() != size) { - Debug("%s has wrong size", filename); - FileClose(); - 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); - if(!DebugFileRead(buffer, read_bytes, i)) { - result = 1; - break; - } - EncryptMemToNand(buffer, offset + i, read_bytes, partition); - } - - ShowProgress(0, 0); - FileClose(); - - return result; -} - -u32 RestoreNand(u32 param) -{ - char filename[64]; - u8* buffer = BUFFER_ADDRESS; - u32 nand_size = getMMCDevice(0)->total_size * NAND_SECTOR_SIZE; - u32 result = 0; - u8 magic[4]; - - if (!(param & N_NANDWRITE)) // developer screwup protection - return 1; - - // User file select - if (InputFileNameSelector(filename, "NAND.bin", NULL, NULL, 0, nand_size) != 0) - return 1; - - if (!DebugFileOpen(filename)) - return 1; - if (nand_size != FileGetSize()) { - FileClose(); - Debug("NAND backup has the wrong size!"); - return 1; - }; - if(!DebugFileRead(magic, 4, 0x100)) { - FileClose(); - return 1; - } - if (memcmp(magic, "NCSD", 4) != 0) { - FileClose(); - Debug("Not a proper NAND backup!"); - return 1; - } - - Debug("Restoring %sNAND. Size (MB): %u", (param & N_EMUNAND) ? "Emu" : "Sys", nand_size / (1024 * 1024)); - - u32 n_sectors = nand_size / NAND_SECTOR_SIZE; - for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { - u32 read_sectors = min(SECTORS_PER_READ, (n_sectors - i)); - ShowProgress(i, n_sectors); - if(!DebugFileRead(buffer, NAND_SECTOR_SIZE * read_sectors, i * NAND_SECTOR_SIZE)) { - result = 1; - break; - } - WriteNandSectors(i, read_sectors, buffer); - } - - ShowProgress(0, 0); - FileClose(); - - return result; -} - -u32 InjectNandPartition(u32 param) -{ - PartitionInfo* p_info = NULL; - char filename[64]; - u8 magic[NAND_SECTOR_SIZE]; - - if (!(param & N_NANDWRITE)) // developer screwup protection - return 1; - - for (u32 partition_id = P_TWLN; partition_id <= P_CTRNAND; partition_id = partition_id << 1) { - if (param & partition_id) { - p_info = GetPartitionInfo(partition_id); - break; - } - } - if (p_info == NULL) { - Debug("No partition to inject to"); - return 1; - } - - Debug("Encrypting & Injecting %s, size (MB): %u", p_info->name, p_info->size / (1024 * 1024)); - // User file select - if (InputFileNameSelector(filename, p_info->name, "bin", - p_info->magic, (p_info->magic[0] != 0xFF) ? 8 : 0, p_info->size) != 0) - return 1; - - // Encryption check - if (DecryptNandToMem(magic, p_info->offset, 16, p_info) != 0) - return 1; - if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) { - Debug("Decryption error, please contact us"); - return 1; - } - - // File check - if (FileOpen(filename)) { - if(!DebugFileRead(magic, 8, 0)) { - FileClose(); - return 1; - } - if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) { - Debug("Bad file content, won't inject"); - FileClose(); - return 1; - } - FileClose(); - } - - return EncryptFileToNand(filename, p_info->offset, p_info->size, p_info); -} diff --git a/source/fatfs/crypto/sha.c b/source/fatfs/crypto/sha.c deleted file mode 100644 index 0902ddc..0000000 --- a/source/fatfs/crypto/sha.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "sha.h" - -void sha_init(u32 mode) -{ - while(*REG_SHACNT & 1); - *REG_SHACNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND; -} - -void sha_update(const void* src, u32 size) -{ - const u32* src32 = (const u32*)src; - - while(size >= 0x40) { - while(*REG_SHACNT & 1); - for(u32 i = 0; i < 4; i++) { - *REG_SHAINFIFO = *src32++; - *REG_SHAINFIFO = *src32++; - *REG_SHAINFIFO = *src32++; - *REG_SHAINFIFO = *src32++; - } - size -= 0x40; - } - while(*REG_SHACNT & 1); - memcpy((void*)REG_SHAINFIFO, src32, size); -} - -void sha_get(void* res) { - *REG_SHACNT = (*REG_SHACNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND; - while(*REG_SHACNT & SHA_FINAL_ROUND); - while(*REG_SHACNT & 1); - memcpy(res, (void*)REG_SHAHASH, (256 / 8)); -} diff --git a/source/fatfs/crypto/sha.h b/source/fatfs/crypto/sha.h deleted file mode 100644 index cb88d42..0000000 --- a/source/fatfs/crypto/sha.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "common.h" - -#define REG_SHACNT ((volatile uint32_t*)0x1000A000) -#define REG_SHABLKCNT ((volatile uint32_t*)0x1000A004) -#define REG_SHAHASH ((volatile uint32_t*)0x1000A040) -#define REG_SHAINFIFO ((volatile uint32_t*)0x1000A080) - -#define SHA_CNT_STATE 0x00000003 -#define SHA_CNT_OUTPUT_ENDIAN 0x00000008 -#define SHA_CNT_MODE 0x00000030 -#define SHA_CNT_ENABLE 0x00010000 -#define SHA_CNT_ACTIVE 0x00020000 - -#define SHA_HASH_READY 0x00000000 -#define SHA_NORMAL_ROUND 0x00000001 -#define SHA_FINAL_ROUND 0x00000002 - -#define SHA256_MODE 0 -#define SHA224_MODE 0x00000010 -#define SHA1_MODE 0x00000020 - - -void sha_init(u32 mode); -void sha_update(const void* src, u32 size); -void sha_get(void* res); diff --git a/source/fatfs/diskio.c b/source/fatfs/diskio.c index bbcb1a3..1c0eb19 100644 --- a/source/fatfs/diskio.c +++ b/source/fatfs/diskio.c @@ -8,8 +8,52 @@ /*-----------------------------------------------------------------------*/ #include "diskio.h" /* FatFs lower layer API */ +#include "platform.h" #include "sdmmc.h" +#include "decryptor/nand.h" +#define TYPE_SDCARD 0 +#define TYPE_SYSNAND 1 +#define TYPE_EMUNAND 2 + +typedef struct { + DWORD offset; + DWORD subtype; + BYTE type; +} FATpartition; + +FATpartition DriveInfo[28] = { + { 0x000000, TYPE_SCARD, 0 }, // 0 - SDCARD + { 0x000000, TYPE_SYSNAND, P_CTRNAND }, // 1 - SYSNAND CTRNAND + { 0x000000, TYPE_SYSNAND, P_TWLN }, // 2 - SYSNAND TWLN + { 0x000000, TYPE_SYSNAND, P_TWLP }, // 3 - SYSNAND TWLP + { 0x000000, TYPE_EMUNAND, P_CTRNAND }, // 4 - EMUNAND0 O3DS CTRNAND + { 0x000000, TYPE_EMUNAND, P_TWLN }, // 5 - EMUNAND0 O3DS TWLN + { 0x000000, TYPE_EMUNAND, P_TWLP }, // 6 - EMUNAND0 O3DS TWLP + { 0x200000, TYPE_EMUNAND, P_CTRNAND }, // 7 - EMUNAND1 O3DS CTRNAND + { 0x200000, TYPE_EMUNAND, P_TWLN }, // 8 - EMUNAND1 O3DS TWLN + { 0x200000, TYPE_EMUNAND, P_TWLP }, // 9 - EMUNAND1 O3DS TWLP + { 0x400000, TYPE_EMUNAND, P_CTRNAND }, // 10 - EMUNAND2 O3DS CTRNAND + { 0x400000, TYPE_EMUNAND, P_TWLN }, // 11 - EMUNAND2 O3DS TWLN + { 0x400000, TYPE_EMUNAND, P_TWLP }, // 12 - EMUNAND2 O3DS TWLP + { 0x600000, TYPE_EMUNAND, P_CTRNAND }, // 13 - EMUNAND3 O3DS CTRNAND + { 0x600000, TYPE_EMUNAND, P_TWLN }, // 14 - EMUNAND3 O3DS TWLN + { 0x600000, TYPE_EMUNAND, P_TWLP }, // 15 - EMUNAND3 O3DS TWLP + { 0x000000, TYPE_EMUNAND, P_CTRNAND }, // 16 - EMUNAND0 N3DS CTRNAND + { 0x000000, TYPE_EMUNAND, P_TWLN }, // 17 - EMUNAND0 N3DS TWLN + { 0x000000, TYPE_EMUNAND, P_TWLP }, // 18 - EMUNAND0 N3DS TWLP + { 0x400000, TYPE_EMUNAND, P_CTRNAND }, // 19 - EMUNAND1 N3DS CTRNAND + { 0x400000, TYPE_EMUNAND, P_TWLN }, // 20 - EMUNAND1 N3DS TWLN + { 0x400000, TYPE_EMUNAND, P_TWLP }, // 21 - EMUNAND1 N3DS TWLP + { 0x800000, TYPE_EMUNAND, P_CTRNAND }, // 22 - EMUNAND2 N3DS CTRNAND + { 0x800000, TYPE_EMUNAND, P_TWLN }, // 23 - EMUNAND2 N3DS TWLN + { 0x800000, TYPE_EMUNAND, P_TWLP }, // 24 - EMUNAND2 N3DS TWLP + { 0xC00000, TYPE_EMUNAND, P_CTRNAND }, // 25 - EMUNAND3 N3DS CTRNAND + { 0xC00000, TYPE_EMUNAND, P_TWLN }, // 26 - EMUNAND3 N3DS TWLN + { 0xC00000, TYPE_EMUNAND, P_TWLP } // 27 - EMUNAND3 N3DS TWLP +}; + + /*-----------------------------------------------------------------------*/ /* Get Drive Status */ @@ -52,9 +96,18 @@ DRESULT disk_read ( UINT count /* Number of sectors to read */ ) { - if (sdmmc_sdcard_readsectors(sector, count, buff)) { - return RES_PARERR; - } + if (DriveInfo[pdrv].type == TYPE_SCARD) { + if (sdmmc_sdcard_readsectors(sector, count, buff)) { + return RES_PARERR; + } + } else { + PartitionInfo* partition = GetPartitionInfo(DriveInfo[pdrv].subtype); + if (partition == NULL) return RES_PARERR; + u32 offset = (sector * 0x200) + partition->offset; + SetNand(DriveInfo[pdrv].type == TYPE_EMUNAND, DriveInfo[pdrv].offset); + if (DecryptNandToMem(buff, offset, count * 0x200, partition) != 0) + return RES_PARERR; + } return RES_OK; } @@ -74,9 +127,19 @@ DRESULT disk_write ( UINT count /* Number of sectors to write */ ) { - if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) { - return RES_PARERR; - } + if (DriveInfo[pdrv].type == TYPE_SCARD) { + if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) { + return RES_PARERR; + } + } else { + PartitionInfo* partition = GetPartitionInfo(DriveInfo[pdrv].subtype); + if (partition == NULL) return RES_PARERR; + u32 offset = (sector * 0x200) + partition->offset; + SetNand(DriveInfo[pdrv].type == TYPE_EMUNAND, DriveInfo[pdrv].offset); + // if (EncryptMemToNand(buff, offset, count * 0x200, partition) != 0) + return RES_PARERR; + // NO, not yet! + } return RES_OK; } @@ -103,11 +166,11 @@ DRESULT disk_ioctl ( *((DWORD*) buff) = 0x200; return RES_OK; case GET_SECTOR_COUNT: - *((DWORD*) buff) = getMMCDevice(1)->total_size; + *((DWORD*) buff) = getMMCDevice((DriveInfo[pdrv].type == TYPE_SCARD) ? 1 : 0)->total_size; return RES_OK; case GET_BLOCK_SIZE: *((DWORD*) buff) = 0x2000; - return RES_OK; + return (DriveInfo[pdrv].type == TYPE_SCARD) ? RES_OK : RES_PARERR; case CTRL_SYNC: // nothing to do here - the disk_write function handles that return RES_OK; diff --git a/source/fs.h b/source/fs.h index 9d4f7be..baf766e 100644 --- a/source/fs.h +++ b/source/fs.h @@ -2,6 +2,22 @@ #include "common.h" +typedef enum { + T_NAND_BASE, + T_NONFAT_ROOT, + T_FAT_ROOT, + T_FAT_FILE, + T_FAT_DIR +} EntryType; + +typedef struct { + char* name; // should point to the correct portion of the path + char path[256]; + u32 size; + u32 offset; + EntryType entryType; +} DirEntry; + bool InitFS(); void DeinitFS(); diff --git a/source/main.c b/source/main.c index 7436bc0..5649e01 100644 --- a/source/main.c +++ b/source/main.c @@ -5,188 +5,6 @@ #include "i2c.h" #include "decryptor/game.h" #include "decryptor/nand.h" -#include "decryptor/nandfat.h" -#include "decryptor/titlekey.h" - -#define SUBMENU_START 5 - - -MenuInfo menu[] = -{ - { - "XORpad Generator Options", 7, - { - { "NCCH Padgen", &NcchPadgen, 0 }, - { "SD Padgen (SDinfo.bin)", &SdPadgen, 0 }, - { "SD Padgen (SysNAND dir)", &SdPadgenDirect, 0 }, - { "SD Padgen (EmuNAND dir)", &SdPadgenDirect, N_EMUNAND }, - { "CTRNAND Padgen", &CtrNandPadgen, 0 }, - { "TWLNAND Padgen", &TwlNandPadgen, 0 }, - { "FIRM0FIRM1 Padgen", &Firm0Firm1Padgen, 0 } - } - }, - { - "Titlekey Decrypt Options", 3, - { - { "Titlekey Decrypt (file)", &DecryptTitlekeysFile, 0 }, - { "Titlekey Decrypt (SysNAND)", &DecryptTitlekeysNand, 0 }, - { "Titlekey Decrypt (EmuNAND)", &DecryptTitlekeysNand, N_EMUNAND } - } - }, - { - "SysNAND Options", 8, - { - { "NAND Backup", &DumpNand, 0 }, - { "NAND Restore", &RestoreNand, N_NANDWRITE }, - { "Partition Dump...", NULL, SUBMENU_START + 0 }, - { "Partition Inject...", NULL, SUBMENU_START + 2 }, - { "File Dump...", NULL, SUBMENU_START + 4 }, - { "File Inject...", NULL, SUBMENU_START + 6 }, - { "Health&Safety Dump", &DumpHealthAndSafety, 0 }, - { "Health&Safety Inject", &InjectHealthAndSafety, N_NANDWRITE } - } - }, - { - "EmuNAND Options", 9, - { - { "EmuNAND Backup", &DumpNand, N_EMUNAND }, - { "EmuNAND Restore", &RestoreNand, N_NANDWRITE | N_EMUNAND | N_FORCENAND }, - { "Partition Dump...", NULL, SUBMENU_START + 1 }, - { "Partition Inject...", NULL, SUBMENU_START + 3 }, - { "File Dump...", NULL, SUBMENU_START + 5 }, - { "File Inject...", NULL, SUBMENU_START + 7 }, - { "Health&Safety Dump", &DumpHealthAndSafety, N_EMUNAND }, - { "Health&Safety Inject", &InjectHealthAndSafety, N_NANDWRITE | N_EMUNAND }, - { "Update SeedDB", &UpdateSeedDb, N_EMUNAND } - } - }, - { - "Game Decryptor Options", 10, - { - { "NCCH/NCSD Decryptor", &CryptGameFiles, GC_NCCH_PROCESS }, - { "NCCH/NCSD Encryptor", &CryptGameFiles, GC_NCCH_PROCESS | GC_NCCH_ENCRYPT }, - { "CIA Decryptor (shallow)", &CryptGameFiles, GC_CIA_PROCESS }, - { "CIA Decryptor (deep)", &CryptGameFiles, GC_CIA_PROCESS | GC_CIA_DEEP }, - { "CIA Decryptor (CXI only)", &CryptGameFiles, GC_CIA_PROCESS | GC_CIA_DEEP | GC_CXI_ONLY }, - { "CIA Encryptor (NCCH)", &CryptGameFiles, GC_CIA_PROCESS | GC_NCCH_ENCRYPT }, - { "CIA Encryptor (CXI only)", &CryptGameFiles, GC_CIA_PROCESS | GC_NCCH_ENCRYPT | GC_CXI_ONLY }, - { "SD Decryptor/Encryptor", &CryptSdFiles, 0 }, - { "SD Decryptor (SysNAND dir)", &DecryptSdFilesDirect, 0 }, - { "SD Decryptor (EmuNAND dir)", &DecryptSdFilesDirect, N_EMUNAND } - } - }, - // everything below is not contained in the main menu - { - "Partition Dump... (SysNAND)", 6, // ID 0 - { - { "Dump TWLN Partition", &DecryptNandPartition, P_TWLN }, - { "Dump TWLP Partition", &DecryptNandPartition, P_TWLP }, - { "Dump AGBSAVE Partition", &DecryptNandPartition, P_AGBSAVE }, - { "Dump FIRM0 Partition", &DecryptNandPartition, P_FIRM0 }, - { "Dump FIRM1 Partition", &DecryptNandPartition, P_FIRM1 }, - { "Dump CTRNAND Partition", &DecryptNandPartition, P_CTRNAND } - } - }, - { - "Partition Dump...(EmuNAND)", 6, // ID 1 - { - { "Dump TWLN Partition", &DecryptNandPartition, N_EMUNAND | P_TWLN }, - { "Dump TWLP Partition", &DecryptNandPartition, N_EMUNAND | P_TWLP }, - { "Dump AGBSAVE Partition", &DecryptNandPartition, N_EMUNAND | P_AGBSAVE }, - { "Dump FIRM0 Partition", &DecryptNandPartition, N_EMUNAND | P_FIRM0 }, - { "Dump FIRM1 Partition", &DecryptNandPartition, N_EMUNAND | P_FIRM1 }, - { "Dump CTRNAND Partition", &DecryptNandPartition, N_EMUNAND | P_CTRNAND } - } - }, - { - "Partition Inject... (SysNAND)", 6, // ID 2 - { - { "Inject TWLN Partition", &InjectNandPartition, N_NANDWRITE | P_TWLN }, - { "Inject TWLP Partition", &InjectNandPartition, N_NANDWRITE | P_TWLP }, - { "Inject AGBSAVE Partition", &InjectNandPartition, N_NANDWRITE | P_AGBSAVE }, - { "Inject FIRM0 Partition", &InjectNandPartition, N_NANDWRITE | P_FIRM0 }, - { "Inject FIRM1 Partition", &InjectNandPartition, N_NANDWRITE | P_FIRM1 }, - { "Inject CTRNAND Partition", &InjectNandPartition, N_NANDWRITE | P_CTRNAND } - } - }, - { - "Partition Inject... (EmuNAND)", 6, // ID 3 - { - { "Inject TWLN Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_TWLN }, - { "Inject TWLP Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_TWLP }, - { "Inject AGBSAVE Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_AGBSAVE }, - { "Inject FIRM0 Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_FIRM0 }, - { "Inject FIRM1 Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_FIRM1 }, - { "Inject CTRNAND Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_CTRNAND } - } - }, - { - "File Dump... (SysNAND)", 10, // ID 4 - { - { "Dump ticket.db", &DumpFile, F_TICKET }, - { "Dump title.db", &DumpFile, F_TITLE }, - { "Dump import.db", &DumpFile, F_IMPORT }, - { "Dump certs.db", &DumpFile, F_CERTS }, - { "Dump SecureInfo_A", &DumpFile, F_SECUREINFO }, - { "Dump LocalFriendCodeSeed_B", &DumpFile, F_LOCALFRIEND }, - { "Dump rand_seed", &DumpFile, F_RANDSEED }, - { "Dump movable.sed", &DumpFile, F_MOVABLE }, - { "Dump nagsave.bin", &DumpFile, F_NAGSAVE }, - { "Dump nnidsave.bin", &DumpFile, F_NNIDSAVE } - } - }, - { - "File Dump... (EmuNAND)", 11, // ID 5 - { - { "Dump ticket.db", &DumpFile, N_EMUNAND | F_TICKET }, - { "Dump title.db", &DumpFile, N_EMUNAND | F_TITLE }, - { "Dump import.db", &DumpFile, N_EMUNAND | F_IMPORT }, - { "Dump certs.db", &DumpFile, N_EMUNAND | F_CERTS }, - { "Dump SecureInfo_A", &DumpFile, N_EMUNAND | F_SECUREINFO }, - { "Dump LocalFriendCodeSeed_B", &DumpFile, N_EMUNAND | F_LOCALFRIEND }, - { "Dump rand_seed", &DumpFile, N_EMUNAND | F_RANDSEED }, - { "Dump movable.sed", &DumpFile, N_EMUNAND | F_MOVABLE }, - { "Dump seedsave.bin", &DumpFile, N_EMUNAND | F_SEEDSAVE }, - { "Dump nagsave.bin", &DumpFile, N_EMUNAND | F_NAGSAVE }, - { "Dump nnidsave.bin", &DumpFile, N_EMUNAND | F_NNIDSAVE } - } - }, - { - "File Inject... (SysNAND)", 10, // ID 6 - { - { "Inject ticket.db", &InjectFile, N_NANDWRITE | F_TICKET }, - { "Inject title.db", &InjectFile, N_NANDWRITE | F_TITLE }, - { "Inject import.db", &InjectFile, N_NANDWRITE | F_IMPORT }, - { "Inject certs.db", &InjectFile, N_NANDWRITE | F_CERTS }, - { "Inject SecureInfo_A", &InjectFile, N_NANDWRITE | F_SECUREINFO }, - { "Inject LocalFriendCodeSeed_B", &InjectFile, N_NANDWRITE | F_LOCALFRIEND }, - { "Inject rand_seed", &InjectFile, N_NANDWRITE | F_RANDSEED }, - { "Inject movable.sed", &InjectFile, N_NANDWRITE | F_MOVABLE }, - { "Inject nagsave.bin", &InjectFile, N_NANDWRITE | F_NAGSAVE }, - { "Inject nnidsave.bin", &InjectFile, N_NANDWRITE | F_NNIDSAVE } - } - }, - { - "File Inject... (EmuNAND)", 11, // ID 7 - { - { "Inject ticket.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_TICKET }, - { "Inject title.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_TITLE }, - { "Inject import.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_IMPORT }, - { "Inject certs.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_CERTS }, - { "Inject SecureInfo_A", &InjectFile, N_NANDWRITE | N_EMUNAND | F_SECUREINFO }, - { "Inject LocalFriendCodeSeed_B", &InjectFile, N_NANDWRITE | N_EMUNAND | F_LOCALFRIEND }, - { "Inject rand_seed", &InjectFile, N_NANDWRITE | N_EMUNAND | F_RANDSEED }, - { "Inject movable.sed", &InjectFile, N_NANDWRITE | N_EMUNAND | F_MOVABLE }, - { "Inject seedsave.bin", &InjectFile, N_NANDWRITE | N_EMUNAND | F_SEEDSAVE }, - { "Inject nagsave.bin", &InjectFile, N_NANDWRITE | N_EMUNAND | F_NAGSAVE }, - { "Inject nnidsave.bin", &InjectFile, N_NANDWRITE | N_EMUNAND | F_NNIDSAVE } - } - }, - { - NULL, 0, {}, // empty menu to signal end - } -}; - void Reboot() { @@ -207,9 +25,9 @@ int main() ClearScreenFull(true, true); InitFS(); - u32 menu_exit = ProcessMenu(menu, SUBMENU_START); + u32 godmode_exit = 0; DeinitFS(); - (menu_exit == MENU_EXIT_REBOOT) ? Reboot() : PowerOff(); + (godmode_exit == 0) ? Reboot() : PowerOff(); return 0; }