diff --git a/source/game/firm.c b/source/game/firm.c index 878fb55..397ad73 100644 --- a/source/game/firm.c +++ b/source/game/firm.c @@ -9,13 +9,22 @@ // 0 -> pre 9.5 / 1 -> 9.5 / 2 -> post 9.5 #define A9L_CRYPTO_TYPE(hdr) ((hdr->k9l[3] == 0xFF) ? 0 : (hdr->k9l[3] == '1') ? 1 : 2) -// valid addresses for FIRM section loading, pairs of start / end address, provided by Wolfvak +// valid addresses for FIRM section loading +// pairs of start / end address, provided by Wolfvak #define FIRM_VALID_ADDRESS \ 0x08000040, 0x08100000, \ 0x18000000, 0x18600000, \ - 0x1FF80000, 0x1FFFFFFC, \ - 0x20000000, 0x23FFFE00, \ - 0x24000000, 0x27FFFB00 + 0x1FF80000, 0x1FFFFFFC + +// valid addresses (installable) for FIRM section loading +#define FIRM_VALID_ADDRESS_INSTALL \ + FIRM_VALID_ADDRESS, \ + 0x10000000, 0x10200000 + +// valid addresses (bootable) for FIRM section loading +#define FIRM_VALID_ADDRESS_BOOT \ + FIRM_VALID_ADDRESS, \ + 0x20000000, 0x27FFFA00 u32 ValidateFirmHeader(FirmHeader* header, u32 data_size) { u8 magic[] = { FIRM_MAGIC }; @@ -59,7 +68,7 @@ u32 ValidateFirmA9LHeader(FirmA9LHeader* header) { return sha_cmp((IS_DEVKIT) ? enckeyX0x15devhash : enckeyX0x15hash, header->keyX0x15, 0x10, SHA256_MODE); } -u32 ValidateFirm(void* firm, u32 firm_size) { +u32 ValidateFirm(void* firm, u32 firm_size, bool installable) { FirmHeader* header = (FirmHeader*) firm; // validate firm header @@ -68,21 +77,28 @@ u32 ValidateFirm(void* firm, u32 firm_size) { // hash verify all available sections and check load address for (u32 i = 0; i < 4; i++) { - u32 valid_address[] = { FIRM_VALID_ADDRESS }; + u32 whitelist_boot[] = { FIRM_VALID_ADDRESS_BOOT }; + u32 whitelist_install[] = { FIRM_VALID_ADDRESS_INSTALL }; + u32* whitelist = (installable) ? whitelist_install : whitelist_boot; + u32 whitelist_size = ((installable) ? sizeof(whitelist_install) : sizeof(whitelist_boot)) / (2*sizeof(u32)); FirmSectionHeader* section = header->sections + i; if (!section->size) continue; if (sha_cmp(section->hash, ((u8*) firm) + section->offset, section->size, SHA256_MODE) != 0) return 1; - bool is_valid_address = false; - for (u32 a = 0; a < sizeof(valid_address) / (2*sizeof(u32)); a++) { - if ((section->address >= valid_address[2*a]) && (section->address + section->size <= valid_address[(2*a)+1])) - is_valid_address = true; + bool is_whitelisted = false; + for (u32 a = 0; a < whitelist_size; a++) { + if ((section->address >= whitelist[2*a]) && (section->address + section->size <= whitelist[(2*a)+1])) + is_whitelisted = true; } - if (!is_valid_address) return 1; + if (!is_whitelisted) return 1; } - // section for arm9 entrypoint not found? - if (!FindFirmArm9Section(header)) + // ARM9 / ARM11 entrypoints available? + if (!header->entry_arm9 || (installable && !header->entry_arm11)) + return 1; + + // B9S screeninit flag? + if (installable && (header->reserved0[0]&0x1)) return 1; return 0; diff --git a/source/game/firm.h b/source/game/firm.h index 8d812be..9fcb2af 100644 --- a/source/game/firm.h +++ b/source/game/firm.h @@ -23,7 +23,7 @@ typedef struct { u8 dec_magic[4]; u32 entry_arm11; u32 entry_arm9; - u8 reserved1[0x30]; + u8 reserved0[0x30]; FirmSectionHeader sections[4]; u8 signature[0x100]; } __attribute__((packed, aligned(16))) FirmHeader; @@ -43,7 +43,7 @@ typedef struct { u32 ValidateFirmHeader(FirmHeader* header, u32 data_size); u32 ValidateFirmA9LHeader(FirmA9LHeader* header); -u32 ValidateFirm(void* firm, u32 firm_size); +u32 ValidateFirm(void* firm, u32 firm_size, bool installable); FirmSectionHeader* FindFirmArm9Section(FirmHeader* firm); u32 GetArm9BinarySize(FirmA9LHeader* a9l); diff --git a/source/godmode.c b/source/godmode.c index 16ae903..d02f53c 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -1447,7 +1447,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur ShowPrompt(false, "FIRM too big, can't launch"); // unlikely } else if (ShowUnlockSequence(3, "%s (%dkB)\nBoot FIRM via chainloader?", pathstr, firm_size / 1024)) { if ((FileGetData(curr_entry->path, TEMP_BUFFER, firm_size, 0) == firm_size) && - (ValidateFirm(TEMP_BUFFER, firm_size) == 0)) { + (ValidateFirm(TEMP_BUFFER, firm_size, false) == 0)) { // fix the boot path first ("sdmc"/"nand" for Luma et al, hacky af) const char* bootpath = curr_entry->path; char fixpath[256] = { 0 }; @@ -2028,7 +2028,7 @@ u32 GodMode(bool is_b9s) { } else if ((user_select == payloads) && (FileSelector(loadpath, "HOME payloads... menu.\nSelect payload:", PAYLOAD_PATH, "*.firm", true, false))) { size_t firm_size = FileGetData(loadpath, TEMP_BUFFER, TEMP_BUFFER_SIZE, 0); if (firm_size && (firm_size < TEMP_BUFFER_SIZE) && - (ValidateFirm(TEMP_BUFFER, firm_size) == 0)) { + (ValidateFirm(TEMP_BUFFER, firm_size, false) == 0)) { char fixpath[256] = { 0 }; if ((*loadpath == '0') || (*loadpath == '1')) snprintf(fixpath, 256, "%s%s", (*loadpath == '0') ? "sdmc" : "nand", loadpath + 1); diff --git a/source/nand/nandutil.c b/source/nand/nandutil.c index e92316f..478e318 100644 --- a/source/nand/nandutil.c +++ b/source/nand/nandutil.c @@ -308,7 +308,7 @@ 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 || (firm_size >= TEMP_BUFFER_SIZE) || (ValidateFirm(firm, firm_size) != 0)) { + !firm_size || (firm_size >= TEMP_BUFFER_SIZE) || (ValidateFirm(firm, firm_size, true) != 0)) { ShowPrompt(false, "%s\nFIRM load/verify error.", pathstr); return 1; }