Added ability to build seeddb.bin

... plus new entry in HOME more... menu
This commit is contained in:
d0k3 2017-04-12 00:27:02 +02:00
parent 1c14730f8f
commit 89b6946789
9 changed files with 207 additions and 62 deletions

View File

@ -53,51 +53,13 @@ u32 GetArm9BinarySize(FirmA9LHeader* a9l) {
} }
u32 SetupSecretKey(u32 keynum) { u32 SetupSecretKey(u32 keynum) {
const char* base[] = { INPUT_PATHS };
// from: https://github.com/AuroraWright/SafeA9LHInstaller/blob/master/source/installer.c#L9-L17
const u8 sectorHash[0x20] = {
0x82, 0xF2, 0x73, 0x0D, 0x2C, 0x2D, 0xA3, 0xF3, 0x01, 0x65, 0xF9, 0x87, 0xFD, 0xCC, 0xAC, 0x5C,
0xBA, 0xB2, 0x4B, 0x4E, 0x5F, 0x65, 0xC9, 0x81, 0xCD, 0x7B, 0xE6, 0xF4, 0x38, 0xE6, 0xD9, 0xD3
};
static u8 __attribute__((aligned(32))) sector[0x200]; static u8 __attribute__((aligned(32))) sector[0x200];
u8 hash[0x20];
// safety check // safety check
if (keynum >= 0x200/0x10) return 1; if (keynum >= 0x200/0x10) return 1;
// secret sector already loaded? // seach for secret sector data...
sha_quick(hash, sector, 0x200, SHA256_MODE); if (GetLegitSector0x96(sector) == 0) {
if (memcmp(hash, sectorHash, 0x20) == 0) {
setup_aeskey(0x11, sector + (keynum*0x10));
use_aeskey(0x11);
return 0;
}
// search for valid secret sector in SysNAND / EmuNAND
const u32 nand_src[] = { NAND_SYSNAND, NAND_EMUNAND };
for (u32 i = 0; i < sizeof(nand_src) / sizeof(u32); i++) {
ReadNandSectors(sector, 0x96, 1, 0x11, nand_src[i]);
sha_quick(hash, sector, 0x200, SHA256_MODE);
if (memcmp(hash, sectorHash, 0x20) != 0) continue;
setup_aeskey(0x11, sector + (keynum*0x10));
use_aeskey(0x11);
return 0;
}
// no luck? try searching for a file
for (u32 i = 0; i < (sizeof(base)/sizeof(char*)); i++) {
char path[64];
FIL fp;
UINT btr;
snprintf(path, 64, "%s/%s", base[i], SECTOR_NAME);
if (f_open(&fp, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) {
snprintf(path, 64, "%s/%s", base[i], SECRET_NAME);
if (f_open(&fp, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) continue;
}
f_read(&fp, sector, 0x200, &btr);
f_close(&fp);
sha_quick(hash, sector, 0x200, SHA256_MODE);
if (memcmp(hash, sectorHash, 0x20) != 0) continue;
setup_aeskey(0x11, sector + (keynum*0x10)); setup_aeskey(0x11, sector + (keynum*0x10));
use_aeskey(0x11); use_aeskey(0x11);
return 0; return 0;

View File

@ -4,9 +4,6 @@
#define FIRM_MAGIC 'F', 'I', 'R', 'M' #define FIRM_MAGIC 'F', 'I', 'R', 'M'
#define SECTOR_NAME "sector0x96.bin"
#define SECRET_NAME "secret_sector.bin"
#define FIRM_MAX_SIZE 0x400000 // 4MB, due to FIRM partition size #define FIRM_MAX_SIZE 0x400000 // 4MB, due to FIRM partition size
#define ARM11NCCH_OFFSET 0, 0x2A000, 0x2B000, 0x2C000 #define ARM11NCCH_OFFSET 0, 0x2A000, 0x2B000, 0x2C000
#define ARM9BIN_OFFSET 0x800 #define ARM9BIN_OFFSET 0x800

View File

@ -1729,3 +1729,78 @@ u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump) {
return 0; return 0;
} }
u32 BuildSeedInfo(const char* path, bool dump) {
SeedInfo* seed_info = (SeedInfo*) MAIN_BUFFER;
const char* path_out = OUTPUT_PATH "/" SEEDDB_NAME;
const char* path_in = path;
u32 inputtype = 0; // 0 -> none, 1 -> seeddb.bin, 2 -> seed system save
UINT br;
if (!path_in && !dump) { // no input path given - initialize
memset(seed_info, 0, 16);
if ((fvx_stat(path_out, NULL) == FR_OK) &&
(ShowPrompt(true, "%s\nOutput file already exists.\nUpdate this?", path_out))) {
path_in = path_out;
inputtype = 1;
} else return 0;
}
char path_str[128];
if (path_in && (strnlen(path_in, 16) == 2)) { // when only a drive is given...
// grab the key Y from movable.sed
u8 movable_keyy[16];
snprintf(path_str, 128, "%s/private/movable.sed", path_in);
if ((fvx_qread(path_str, movable_keyy, 0x110, 0x10, &br) != FR_OK) || (br != 0x10))
return 1;
// build the seed save path
u32 sha256sum[8];
sha_quick(sha256sum, movable_keyy, 0x10, SHA256_MODE);
snprintf(path_str, 128, "%s/data/%08lX%08lX%08lX%08lX/sysdata/0001000F/00000000",
path_in, sha256sum[0], sha256sum[1], sha256sum[2], sha256sum[3]);
path_in = path_str;
inputtype = 2;
}
if (inputtype == 1) { // seeddb.bin input
SeedInfo* seed_info_merge = (SeedInfo*) TEMP_BUFFER;
if ((fvx_qread(path_in, seed_info_merge, 0, TEMP_BUFFER_SIZE, &br) != FR_OK) ||
(SEEDDB_SIZE(seed_info_merge) != br)) return 1;
// merge and rebuild SeedInfo
u32 n_entries = seed_info_merge->n_entries;
SeedInfoEntry* seed = seed_info_merge->entries;
for (u32 i = 0; i < n_entries; i++, seed++) {
if (SEEDDB_SIZE(seed_info) + 32 > MAIN_BUFFER_SIZE) return 1;
AddSeedToDb(seed_info, seed); // ignore result
}
} else if (inputtype == 2) { // seed system save input
static const u32 seed_offset[2] = {SEEDSAVE_AREA_OFFSETS};
u8* seedsave = (u8*) TEMP_BUFFER;
if ((fvx_qread(path_in, seedsave, 0, 0x200, &br) != FR_OK) || (br != 0x200))
return 1;
u32 p_active = (getle32(seedsave + 0x168)) ? 1 : 0;
for (u32 p = 0; p < 2; p++) {
SeedInfoEntry seed = { 0 };
if ((fvx_qread(path_in, seedsave, seed_offset[(p + p_active) % 2], SEEDSAVE_MAX_ENTRIES*(8+16), &br) != FR_OK) ||
(br != SEEDSAVE_MAX_ENTRIES*(8+16)))
return 1;
for (u32 s = 0; s < SEEDSAVE_MAX_ENTRIES; s++) {
seed.titleId = getle64(seedsave + (s*8));
memcpy(seed.seed, seedsave + (SEEDSAVE_MAX_ENTRIES*8) + (s*16), 16);
if (((seed.titleId >> 32) != 0x00040000) ||
(!getle64(seed.seed) && !getle64(seed.seed + 8))) continue;
if (SEEDDB_SIZE(seed_info) + 32 > MAIN_BUFFER_SIZE) return 1;
AddSeedToDb(seed_info, &seed); // ignore result
}
}
}
if (dump) {
u32 dump_size = SEEDDB_SIZE(seed_info);
f_unlink(path_out);
if ((dump_size <= 16) || (fvx_qwrite(path_out, seed_info, 0, dump_size, &br) != FR_OK) || (br != dump_size))
return 1;
}
return 0;
}

View File

@ -11,3 +11,4 @@ u32 BuildNcchInfoXorpads(const char* destdir, const char* path);
u32 CheckHealthAndSafetyInject(const char* hsdrv); u32 CheckHealthAndSafetyInject(const char* hsdrv);
u32 InjectHealthAndSafety(const char* path, const char* destdrv); u32 InjectHealthAndSafety(const char* path, const char* destdrv);
u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump); u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump);
u32 BuildSeedInfo(const char* path, bool dump);

View File

@ -4,21 +4,8 @@
#include "sha.h" #include "sha.h"
#include "ff.h" #include "ff.h"
#define SEEDDB_NAME "seeddb.bin"
#define EXEFS_KEYID(name) (((strncmp(name, "banner", 8) == 0) || (strncmp(name, "icon", 8) == 0)) ? 0 : 1) #define EXEFS_KEYID(name) (((strncmp(name, "banner", 8) == 0) || (strncmp(name, "icon", 8) == 0)) ? 0 : 1)
typedef struct {
u64 titleId;
u8 seed[16];
u8 reserved[8];
} __attribute__((packed)) SeedInfoEntry;
typedef struct {
u32 n_entries;
u8 padding[12];
SeedInfoEntry entries[256]; // this number is only a placeholder
} __attribute__((packed)) SeedInfo;
u32 ValidateNcchHeader(NcchHeader* header) { u32 ValidateNcchHeader(NcchHeader* header) {
if (memcmp(header->magic, "NCCH", 4) != 0) // check magic number if (memcmp(header->magic, "NCCH", 4) != 0) // check magic number
return 1; return 1;
@ -102,14 +89,13 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
continue; continue;
f_read(&file, seedsave, 0x200, &btr); f_read(&file, seedsave, 0x200, &btr);
u32 p_active = (getle32(seedsave + 0x168)) ? 1 : 0; u32 p_active = (getle32(seedsave + 0x168)) ? 1 : 0;
static const u32 seed_offset[2] = {0x7000, 0x5C000}; static const u32 seed_offset[2] = {SEEDSAVE_AREA_OFFSETS};
for (u32 p = 0; p < 2; p++) { for (u32 p = 0; p < 2; p++) {
f_lseek(&file, seed_offset[(p + p_active) % 2]); f_lseek(&file, seed_offset[(p + p_active) % 2]);
f_read(&file, seedsave, 2000*(8+16), &btr); f_read(&file, seedsave, SEEDSAVE_MAX_ENTRIES*(8+16), &btr);
for (u32 s = 0; s < 2000; s++) { for (u32 s = 0; s < SEEDSAVE_MAX_ENTRIES; s++) {
if (titleId != getle64(seedsave + (s*8))) if (titleId != getle64(seedsave + (s*8))) continue;
continue; memcpy(lseed, seedsave + (SEEDSAVE_MAX_ENTRIES*8) + (s*16), 16);
memcpy(lseed, seedsave + (2000*8) + (s*16), 16);
sha_quick(sha256sum, lseed, 16 + 8, SHA256_MODE); sha_quick(sha256sum, lseed, 16 + 8, SHA256_MODE);
if (hash_seed == sha256sum[0]) { if (hash_seed == sha256sum[0]) {
memcpy(seed, lseed, 16); memcpy(seed, lseed, 16);
@ -148,6 +134,22 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
return 1; return 1;
} }
u32 AddSeedToDb(SeedInfo* seed_info, SeedInfoEntry* seed_entry) {
if (!seed_entry) { // no seed entry -> reset database
memset(seed_info, 0, 16);
return 0;
}
// check if entry already in DB
u32 n_entries = seed_info->n_entries;
SeedInfoEntry* seed = seed_info->entries;
for (u32 i = 0; i < n_entries; i++, seed++)
if (seed->titleId == seed_entry->titleId) return 0;
// actually a new seed entry
memcpy(seed, seed_entry, sizeof(SeedInfoEntry));
seed_info->n_entries++;
return 0;
}
u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid) { u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid) {
u8 flags3 = (crypto >> 8) & 0xFF; u8 flags3 = (crypto >> 8) & 0xFF;
u8 flags7 = crypto & 0xFF; u8 flags7 = crypto & 0xFF;

View File

@ -15,6 +15,12 @@
#define NCCH_STDCRYPTO 0x0000 #define NCCH_STDCRYPTO 0x0000
#define NCCH_GET_CRYPTO(ncch) (!NCCH_ENCRYPTED(ncch) ? NCCH_NOCRYPTO : (((ncch)->flags[3] << 8) | ((ncch)->flags[7]&(0x01|0x20)))) #define NCCH_GET_CRYPTO(ncch) (!NCCH_ENCRYPTED(ncch) ? NCCH_NOCRYPTO : (((ncch)->flags[3] << 8) | ((ncch)->flags[7]&(0x01|0x20))))
#define SEEDDB_NAME "seeddb.bin"
#define SEEDDB_SIZE(sdb) (16 + ((sdb)->n_entries * sizeof(SeedInfoEntry)))
#define SEEDSAVE_AREA_OFFSETS 0x7000, 0x5C000
#define SEEDSAVE_MAX_ENTRIES 2000
// wrapper defines // wrapper defines
#define DecryptNcch(data, offset, size, ncch, exefs) CryptNcch(data, offset, size, ncch, exefs, NCCH_NOCRYPTO) #define DecryptNcch(data, offset, size, ncch, exefs) CryptNcch(data, offset, size, ncch, exefs, NCCH_NOCRYPTO)
#define EncryptNcch(data, offset, size, ncch, exefs, crypto) CryptNcch(data, offset, size, ncch, exefs, crypto) #define EncryptNcch(data, offset, size, ncch, exefs, crypto) CryptNcch(data, offset, size, ncch, exefs, crypto)
@ -70,9 +76,23 @@ typedef struct {
u8 hash_romfs[0x20]; u8 hash_romfs[0x20];
} __attribute__((packed, aligned(16))) NcchHeader; } __attribute__((packed, aligned(16))) NcchHeader;
typedef struct {
u64 titleId;
u8 seed[16];
u8 reserved[8];
} __attribute__((packed)) SeedInfoEntry;
typedef struct {
u32 n_entries;
u8 padding[12];
SeedInfoEntry entries[256]; // this number is only a placeholder
} __attribute__((packed)) SeedInfo;
u32 ValidateNcchHeader(NcchHeader* header); u32 ValidateNcchHeader(NcchHeader* header);
u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid); u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid);
u32 SetupNcchCrypto(NcchHeader* ncch, u16 crypt_to); u32 SetupNcchCrypto(NcchHeader* ncch, u16 crypt_to);
u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypto); u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypto);
u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto); u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto);
u32 SetNcchSdFlag(u8* data); u32 SetNcchSdFlag(u8* data);
u32 AddSeedToDb(SeedInfo* seed_info, SeedInfoEntry* seed_entry);

View File

@ -1120,12 +1120,14 @@ u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboar
int sdformat = ++n_opt; int sdformat = ++n_opt;
int bonus = (GetNandUnusedSectors(NAND_SYSNAND) > 0x2000) ? (int) ++n_opt : -1; // 4MB minsize int bonus = (GetNandUnusedSectors(NAND_SYSNAND) > 0x2000) ? (int) ++n_opt : -1; // 4MB minsize
int multi = (CheckMultiEmuNand()) ? (int) ++n_opt : -1; int multi = (CheckMultiEmuNand()) ? (int) ++n_opt : -1;
int bsupport = ++n_opt;
int hsrestore = ((CheckHealthAndSafetyInject("1:") == 0) || (CheckHealthAndSafetyInject("4:") == 0)) ? (int) ++n_opt : -1; int hsrestore = ((CheckHealthAndSafetyInject("1:") == 0) || (CheckHealthAndSafetyInject("4:") == 0)) ? (int) ++n_opt : -1;
int nandbak = ++n_opt; int nandbak = ++n_opt;
if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu"; if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu";
if (bonus > 0) optionstr[bonus - 1] = "Bonus drive setup"; if (bonus > 0) optionstr[bonus - 1] = "Bonus drive setup";
if (multi > 0) optionstr[multi - 1] = "Switch EmuNAND"; if (multi > 0) optionstr[multi - 1] = "Switch EmuNAND";
if (bsupport > 0) optionstr[bsupport - 1] = "Build support files";
if (hsrestore > 0) optionstr[hsrestore - 1] = "Restore H&S"; if (hsrestore > 0) optionstr[hsrestore - 1] = "Restore H&S";
if (nandbak > 0) optionstr[nandbak - 1] = "Backup NAND"; if (nandbak > 0) optionstr[nandbak - 1] = "Backup NAND";
@ -1162,6 +1164,48 @@ u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboar
} }
GetDirContents(current_dir, current_path); GetDirContents(current_dir, current_path);
return 0; return 0;
} else if (user_select == bsupport) { // build support files
bool tik_enc_sys = false;
bool tik_enc_emu = false;
if (BuildTitleKeyInfo(NULL, false, false) == 0) {
ShowString("Building " TIKDB_NAME_ENC "...");
tik_enc_sys = (BuildTitleKeyInfo("1:/dbs/ticket.db", false, false) == 0);
tik_enc_emu = (BuildTitleKeyInfo("4:/dbs/ticket.db", false, false) == 0);
if (BuildTitleKeyInfo(NULL, false, true) != 0)
tik_enc_sys = tik_enc_emu = false;
}
bool tik_dec_sys = false;
bool tik_dec_emu = false;
if (BuildTitleKeyInfo(NULL, true, false) == 0) {
ShowString("Building " TIKDB_NAME_DEC "...");
tik_dec_sys = (BuildTitleKeyInfo("1:/dbs/ticket.db", true, false) == 0);
tik_dec_emu = (BuildTitleKeyInfo("4:/dbs/ticket.db", true, false) == 0);
if (!tik_dec_sys || BuildTitleKeyInfo(NULL, true, true) != 0)
tik_dec_sys = tik_dec_emu = false;
}
bool seed_sys = false;
bool seed_emu = false;
if (BuildSeedInfo(NULL, false) == 0) {
ShowString("Building " SEEDDB_NAME "...");
seed_sys = (BuildSeedInfo("1:", false) == 0);
seed_emu = (BuildSeedInfo("4:", false) == 0);
if (!seed_sys || BuildSeedInfo(NULL, true) != 0)
seed_sys = seed_emu = false;
}
bool lsector = false;
u8 legit_sector[0x200];
if (GetLegitSector0x96(legit_sector) == 0) {
ShowString("Searching secret sector...");
const char* path_sector = OUTPUT_PATH "/" SECRET_NAME;
lsector = FileSetData(path_sector, legit_sector, 0x200, 0, true);
}
ShowPrompt(false, "Built in " OUTPUT_PATH ":\n \n%18.18-s %s\n%18.18-s %s\n%18.18-s %s\n%18.18-s %s",
TIKDB_NAME_ENC, tik_enc_sys ? tik_enc_emu ? "OK (Sys&Emu)" : "OK (Sys)" : "Failed",
TIKDB_NAME_DEC, tik_dec_sys ? tik_dec_emu ? "OK (Sys&Emu)" : "OK (Sys)" : "Failed",
SEEDDB_NAME, seed_sys ? seed_emu ? "OK (Sys&Emu)" : "OK (Sys)" : "Failed",
SECRET_NAME, lsector ? "OK (legit)" : "Failed");
GetDirContents(current_dir, current_path);
return 0;
} else if (user_select == hsrestore) { // restore Health & Safety } else if (user_select == hsrestore) { // restore Health & Safety
n_opt = 0; n_opt = 0;
int sys = (CheckHealthAndSafetyInject("1:") == 0) ? (int) ++n_opt : -1; int sys = (CheckHealthAndSafetyInject("1:") == 0) ? (int) ++n_opt : -1;

View File

@ -24,6 +24,12 @@ static const u8 slot0x11Key95_sha256[0x20] = { // slot0x11Key95 hash (first 16 b
0xBA, 0xC1, 0x40, 0x9C, 0x6E, 0xE4, 0x1F, 0x04, 0xAA, 0xC4, 0xE2, 0x09, 0x5C, 0xE9, 0x4F, 0x78, 0xBA, 0xC1, 0x40, 0x9C, 0x6E, 0xE4, 0x1F, 0x04, 0xAA, 0xC4, 0xE2, 0x09, 0x5C, 0xE9, 0x4F, 0x78,
0x6C, 0x78, 0x5F, 0xAC, 0xEC, 0x7E, 0xC0, 0x11, 0x26, 0x9D, 0x4E, 0x47, 0xB3, 0x64, 0xC4, 0xA5 0x6C, 0x78, 0x5F, 0xAC, 0xEC, 0x7E, 0xC0, 0x11, 0x26, 0x9D, 0x4E, 0x47, 0xB3, 0x64, 0xC4, 0xA5
}; };
// from: https://github.com/AuroraWright/SafeA9LHInstaller/blob/master/source/installer.c#L9-L17
static const u8 sector0x96_sha256[0x20] = { // hash for legit sector 0x96 (different on A9LH)
0x82, 0xF2, 0x73, 0x0D, 0x2C, 0x2D, 0xA3, 0xF3, 0x01, 0x65, 0xF9, 0x87, 0xFD, 0xCC, 0xAC, 0x5C,
0xBA, 0xB2, 0x4B, 0x4E, 0x5F, 0x65, 0xC9, 0x81, 0xCD, 0x7B, 0xE6, 0xF4, 0x38, 0xE6, 0xD9, 0xD3
};
static const u8 nand_magic_n3ds[0x60] = { // NCSD NAND header N3DS magic static const u8 nand_magic_n3ds[0x60] = { // NCSD NAND header N3DS magic
0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -438,6 +444,38 @@ u64 GetNandUnusedSectors(u32 nand_src)
return GetNandSizeSectors(nand_src) - NAND_MIN_SECTORS; return GetNandSizeSectors(nand_src) - NAND_MIN_SECTORS;
} }
u32 GetLegitSector0x96(u8* sector)
{
// secret sector already in buffer?
if (sha_cmp(sector0x96_sha256, sector, 0x200, SHA256_MODE) == 0)
return 0;
// search for valid secret sector in SysNAND / EmuNAND
const u32 nand_src[] = { NAND_SYSNAND, NAND_EMUNAND };
for (u32 i = 0; i < sizeof(nand_src) / sizeof(u32); i++) {
ReadNandSectors(sector, 0x96, 1, 0x11, nand_src[i]);
if (sha_cmp(sector0x96_sha256, sector, 0x200, SHA256_MODE) == 0)
return 0;
}
// no luck? try searching for a file
const char* base[] = { INPUT_PATHS };
for (u32 i = 0; i < (sizeof(base)/sizeof(char*)); i++) {
char path[64];
snprintf(path, 64, "%s/%s", base[i], SECTOR_NAME);
if ((FileGetData(path, sector, 0x200, 0) == 0x200) &&
(sha_cmp(sector0x96_sha256, sector, 0x200, SHA256_MODE) == 0))
return 0;
snprintf(path, 64, "%s/%s", base[i], SECRET_NAME);
if ((FileGetData(path, sector, 0x200, 0) == 0x200) &&
(sha_cmp(sector0x96_sha256, sector, 0x200, SHA256_MODE) == 0))
return 0;
}
// failed if we arrive here
return 1;
}
bool CheckMultiEmuNand(void) bool CheckMultiEmuNand(void)
{ {
// this only checks for the theoretical possibility // this only checks for the theoretical possibility

View File

@ -35,6 +35,10 @@
#define SIZE_CTR_O3DS 0x17AE80 #define SIZE_CTR_O3DS 0x17AE80
#define SIZE_CTR_N3DS 0x20F680 #define SIZE_CTR_N3DS 0x20F680
// filenames for sector 0x96
#define SECTOR_NAME "sector0x96.bin"
#define SECRET_NAME "secret_sector.bin"
bool InitNandCrypto(void); bool InitNandCrypto(void);
bool CheckSlot0x05Crypto(void); bool CheckSlot0x05Crypto(void);
bool CheckSector0x96Crypto(void); bool CheckSector0x96Crypto(void);
@ -52,6 +56,8 @@ u32 CheckNandMbr(u8* mbr);
u32 CheckNandHeader(u8* header); u32 CheckNandHeader(u8* header);
u32 CheckNandType(u32 src); u32 CheckNandType(u32 src);
u32 GetLegitSector0x96(u8* sector);
bool CheckMultiEmuNand(void); bool CheckMultiEmuNand(void);
u32 InitEmuNandBase(bool reset); u32 InitEmuNandBase(bool reset);
u32 GetEmuNandBase(void); u32 GetEmuNandBase(void);