mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Added ability to build seeddb.bin
... plus new entry in HOME more... menu
This commit is contained in:
parent
1c14730f8f
commit
89b6946789
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -25,6 +25,12 @@ static const u8 slot0x11Key95_sha256[0x20] = { // slot0x11Key95 hash (first 16 b
|
|||||||
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,
|
||||||
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00,
|
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x03, 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
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user