Better checks for installable FIRMs

This commit is contained in:
d0k3 2017-08-20 15:31:30 +02:00
parent 7d8f6bb368
commit 8b8a8695d9
4 changed files with 34 additions and 18 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}