forked from Mirror/GodMode9
FIRM chainloader: offer on-the-fly decryption of encrypted FIRMs
This commit is contained in:
parent
5f1814b599
commit
b6409ec045
@ -264,3 +264,24 @@ u32 DecryptFirmSequential(void* data, u32 offset, u32 size) {
|
||||
|
||||
return (a9lptr) ? DecryptFirm(data, offset, size, firmptr, a9lptr) : 0;
|
||||
}
|
||||
|
||||
u32 DecryptFirmFull(void* data, u32 size) {
|
||||
// this expects the full FIRM being in memory
|
||||
FirmHeader* firm = (FirmHeader*) data;
|
||||
FirmSectionHeader* arm9s = FindFirmArm9Section(firm);
|
||||
if (ValidateFirmHeader(firm, size) != 0) return 1; // not a proper firm
|
||||
if (!arm9s) return 0; // no ARM9 section -> not encrypted -> done
|
||||
|
||||
FirmA9LHeader* a9l = (FirmA9LHeader*)(void*) ((u8*) data + arm9s->offset);
|
||||
if (ValidateFirmA9LHeader(a9l) != 0) return 0; // no ARM9bin -> not encrypted -> done
|
||||
|
||||
// decrypt FIRM and ARM9loader header
|
||||
if ((DecryptFirm(data, 0, size, firm, a9l) != 0) || (DecryptA9LHeader(a9l) != 0))
|
||||
return 1;
|
||||
|
||||
// fix ARM9 section SHA and ARM9 entrypoint
|
||||
sha_quick(arm9s->hash, (u8*) data + arm9s->offset, arm9s->size, SHA256_MODE);
|
||||
firm->entry_arm9 = ARM9ENTRY_FIX(firm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -7,11 +7,20 @@
|
||||
#define FIRM_MAX_SIZE 0x400000 // 4MB, due to FIRM partition size
|
||||
#define ARM11NCCH_OFFSET 0, 0x2A000, 0x2B000, 0x2C000
|
||||
#define ARM9BIN_OFFSET 0x800
|
||||
// ARM9 entrypoint after decryption - TWL/AGB and NATIVE/SAFE_MODE
|
||||
// see: https://github.com/AuroraWright/Luma3DS/blob/master/source/firm.c#L349
|
||||
// and: https://github.com/AuroraWright/Luma3DS/blob/master/source/firm.c#L424
|
||||
// and: https://github.com/AuroraWright/Luma3DS/blob/master/source/firm.c#L463
|
||||
#define ARM9ENTRY_FIX(firm) (((firm)->sections[3].offset) ? 0x801301C : 0x0801B01C)
|
||||
|
||||
#define FIRM_NDMA_CPY 0
|
||||
#define FIRM_XDMA_CPY 1
|
||||
#define FIRM_CPU_MEMCPY 2
|
||||
|
||||
#define IsInstallableFirm(firm, firm_size) (ValidateFirm(firm, firm_size, true) == 0)
|
||||
#define IsBootableFirm(firm, firm_size) (ValidateFirm(firm, firm_size, false) == 0)
|
||||
|
||||
|
||||
// see: https://www.3dbrew.org/wiki/FIRM#Firmware_Section_Headers
|
||||
typedef struct {
|
||||
u32 offset;
|
||||
@ -58,3 +67,4 @@ u32 DecryptA9LHeader(FirmA9LHeader* header);
|
||||
u32 DecryptFirm(void* data, u32 offset, u32 size, FirmHeader* firm, FirmA9LHeader* a9l);
|
||||
u32 DecryptArm9Binary(void* data, u32 offset, u32 size, FirmA9LHeader* a9l);
|
||||
u32 DecryptFirmSequential(void* data, u32 offset, u32 size);
|
||||
u32 DecryptFirmFull(void* data, u32 size);
|
||||
|
@ -768,19 +768,28 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) {
|
||||
if (verbose && !ShowUnlockSequence(3, "%s (%dkB)\nBoot FIRM via chainloader?", pathstr, firm_size / 1024))
|
||||
return 1;
|
||||
|
||||
if ((FileGetData(bootpath, TEMP_BUFFER, firm_size, 0) != firm_size) ||
|
||||
(ValidateFirm(TEMP_BUFFER, firm_size, false) != 0)) {
|
||||
if (verbose) ShowPrompt(false, "%s\nNot a bootable FIRM", pathstr);
|
||||
void* firm = (void*) TEMP_BUFFER;
|
||||
if ((FileGetData(bootpath, firm, firm_size, 0) != firm_size) ||
|
||||
!IsBootableFirm(firm, firm_size)) {
|
||||
if (verbose) ShowPrompt(false, "%s\nNot a bootable FIRM.", pathstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// encrypted firm handling
|
||||
FirmSectionHeader* arm9s = FindFirmArm9Section(firm);
|
||||
FirmA9LHeader* a9l = (FirmA9LHeader*)(void*) ((u8*) firm + arm9s->offset);
|
||||
if (verbose && (ValidateFirmA9LHeader(a9l) == 0) &&
|
||||
ShowPrompt(true, "%s\nFIRM is encrypted.\n \nDecrypt before boot?", pathstr) &&
|
||||
(DecryptFirmFull(firm, firm_size) != 0))
|
||||
return 1;
|
||||
|
||||
// unsupported location handling
|
||||
char fixpath[256] = { 0 };
|
||||
if (verbose && (*bootpath != '0') && (*bootpath != '1')) {
|
||||
const char* optionstr[2] = { "Make a copy at " OUTPUT_PATH "/temp.firm", "Try to boot anyways" };
|
||||
u32 user_select = ShowSelectPrompt(2, optionstr, "%s\nWarning: Trying to boot from\nan unsupported location.", pathstr);
|
||||
u32 user_select = ShowSelectPrompt(2, optionstr, "%s\nWarning: Trying to boot from an\nunsupported location.", pathstr);
|
||||
if (user_select == 1) {
|
||||
FileSetData(OUTPUT_PATH "/temp.firm", TEMP_BUFFER, firm_size, 0, true);
|
||||
FileSetData(OUTPUT_PATH "/temp.firm", firm, firm_size, 0, true);
|
||||
bootpath = OUTPUT_PATH "/temp.firm";
|
||||
} else if (!user_select) bootpath = "";
|
||||
}
|
||||
@ -793,7 +802,7 @@ u32 BootFirmHandler(const char* bootpath, bool verbose, bool delete) {
|
||||
// boot the FIRM (if we got a proper fixpath)
|
||||
if (*fixpath) {
|
||||
if (delete) PathDelete(bootpath);
|
||||
BootFirm((FirmHeader*)(void*)TEMP_BUFFER, fixpath);
|
||||
BootFirm((FirmHeader*) firm, fixpath);
|
||||
while(1);
|
||||
}
|
||||
|
||||
@ -836,8 +845,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
bool xorpadable = (FTYPE_XORPAD(filetype));
|
||||
bool keyinitable = (FTYPE_KEYINIT(filetype));
|
||||
bool scriptable = (FTYPE_SCRIPT(filetype));
|
||||
bool bootable = (FTYPE_BOOTABLE(filetype) && !(drvtype & DRV_VIRTUAL));
|
||||
bool installable = (FTYPE_INSTALLABLE(filetype) && !(drvtype & DRV_VIRTUAL));
|
||||
bool bootable = (FTYPE_BOOTABLE(filetype));
|
||||
bool installable = (FTYPE_INSTALLABLE(filetype));
|
||||
bool agbexportable = (FTPYE_AGBSAVE(filetype));
|
||||
bool agbimportable = (FTPYE_AGBSAVE(filetype) && (drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND));
|
||||
bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || transferable || hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable || bootable || scriptable || installable || agbexportable || agbimportable;
|
||||
@ -1738,7 +1747,7 @@ u32 GodMode(bool is_b9s) {
|
||||
// bootloader handler
|
||||
if (bootloader) {
|
||||
const char* bootfirm_paths[] = { BOOTFIRM_PATHS };
|
||||
if (ValidateFirm(firm_in_mem, FIRM_MAX_SIZE, false) == 0) BootFirm(firm_in_mem, "0:/bootonce.firm");
|
||||
if (IsBootableFirm(firm_in_mem, FIRM_MAX_SIZE)) BootFirm(firm_in_mem, "0:/bootonce.firm");
|
||||
for (u32 i = 0; i < sizeof(bootfirm_paths) / sizeof(char*); i++) {
|
||||
BootFirmHandler(bootfirm_paths[i], false, (BOOTFIRM_TEMPS >> i) & 0x1);
|
||||
}
|
||||
|
@ -908,10 +908,7 @@ u32 DecryptFirmFile(const char* orig, const char* dest) {
|
||||
// write back FIRM header
|
||||
fvx_lseek(&file, 0);
|
||||
memcpy(firm.dec_magic, dec_magic, sizeof(dec_magic));
|
||||
// see: https://github.com/AuroraWright/Luma3DS/blob/master/source/firm.c#L349
|
||||
// and: https://github.com/AuroraWright/Luma3DS/blob/master/source/firm.c#L424
|
||||
// and: https://github.com/AuroraWright/Luma3DS/blob/master/source/firm.c#L463
|
||||
firm.entry_arm9 = (firm.sections[3].offset) ? 0x801301C : 0x0801B01C;
|
||||
firm.entry_arm9 = ARM9ENTRY_FIX(&firm);
|
||||
if (fvx_write(&file, &firm, sizeof(FirmHeader), &btr) != FR_OK) {
|
||||
fvx_close(&file);
|
||||
return 1;
|
||||
|
@ -452,8 +452,9 @@ u32 SafeInstallFirm(const char* path, u32 slots) {
|
||||
u8* firm = (u8*) TEMP_BUFFER;
|
||||
UINT firm_size;
|
||||
if ((fvx_qread(path, firm, 0, TEMP_BUFFER_SIZE, &firm_size) != FR_OK) ||
|
||||
!firm_size || (ValidateFirm(firm, firm_size, true) != 0)) {
|
||||
ShowPrompt(false, "%s\nFIRM load/verify error.", pathstr);
|
||||
!firm_size || !IsInstallableFirm(firm, firm_size)) {
|
||||
ShowPrompt(false, IsBootableFirm(firm, firm_size) ?
|
||||
"%s\nNot a installable FIRM." : "%s\nFIRM load/verify error.", pathstr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -723,8 +723,7 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
|
||||
}
|
||||
else if (id == CMD_ID_BOOT) {
|
||||
size_t firm_size = FileGetData(argv[0], TEMP_BUFFER, TEMP_BUFFER_SIZE, 0);
|
||||
ret = firm_size && (firm_size < TEMP_BUFFER_SIZE) &&
|
||||
(ValidateFirm(TEMP_BUFFER, firm_size, false) == 0);
|
||||
ret = firm_size && IsBootableFirm(TEMP_BUFFER, firm_size);
|
||||
if (ret) {
|
||||
char fixpath[256] = { 0 };
|
||||
if ((*argv[0] == '0') || (*argv[0] == '1'))
|
||||
|
Loading…
x
Reference in New Issue
Block a user