From: chaoskagami Date: Fri, 2 Sep 2016 03:43:53 +0000 (-0400) Subject: Autodetect FIRM now only X-Git-Tag: v0.3.0~23 X-Git-Url: https://chaos.moe/g/?a=commitdiff_plain;h=528447bdef4296e0903ffb864047212eadf6026d;p=corbenik%2Fcorbenik.git Autodetect FIRM now only --- diff --git a/include/firm/firm.h b/include/firm/firm.h index 2e0b83c..8a1cffc 100644 --- a/include/firm/firm.h +++ b/include/firm/firm.h @@ -22,9 +22,7 @@ enum firm_type */ struct firm_signature { - uint8_t sig[0x10]; ///< First 0x10 bytes of first section hash (used for identification) - unsigned int version; ///< CDN/contents version of FIRM - char version_string[16]; ///< Human readable version number + unsigned int k9l; ///< CDN/contents version of FIRM enum consoles console; ///< Console type enum firm_type type; ///< Type of FIRM. }; diff --git a/source/firm/firm.c b/source/firm/firm.c index 56d3402..c200ea5 100644 --- a/source/firm/firm.c +++ b/source/firm/firm.c @@ -70,18 +70,14 @@ void dump_firm(firm_h** buffer, uint8_t index) { fprintf(stderr, " Magic is intact on FIRM%u.\n", index); - uint8_t detver = 0; + struct firm_signature* sig = get_firm_info(firm); - struct firm_signature s; - s.version = 0; - s.type = type_native; - if(index == 1) - s.version = 0x10; - - if(decrypt_arm9bin((arm9bin_h*)((uint8_t*)firm + firm->section[2].offset), &s)) { + if(decrypt_arm9bin((arm9bin_h*)((uint8_t*)firm + firm->section[2].offset), sig)) { abort(" Failed to decrypt FIRM%u arm9loader.\n", index); } + free(sig); + fprintf(stderr, " Decrypted FIRM%u arm9loader.\n", index); } @@ -268,7 +264,7 @@ decrypt_arm9bin(arm9bin_h *header, struct firm_signature *sig) { uint8_t slot = 0x15; - if (sig->type == type_native && sig->version > 0x0F) { + if (sig->type == type_native && sig->k9l >= 2) { uint8_t decrypted_keyx[AES_BLOCK_SIZE]; slot0x11key96_init(); @@ -288,11 +284,11 @@ decrypt_arm9bin(arm9bin_h *header, struct firm_signature *sig) use_aeskey(slot); ctr_decrypt(arm9bin, arm9bin, (size_t)size / AES_BLOCK_SIZE, AES_CNT_CTRNAND_MODE, header->ctr); - if (sig->type == type_native) - return *(uint32_t *)arm9bin != ARM9BIN_MAGIC; + if (sig->type == type_native && *(uint32_t *)arm9bin == ARM9BIN_MAGIC) + return 0; - else if (sig->type == type_twl || sig->type == type_agb) - return *(uint32_t *)arm9bin != LGY_ARM9BIN_MAGIC; + else if ((sig->type == type_twl || sig->type == type_agb) && *(uint32_t *)arm9bin == LGY_ARM9BIN_MAGIC) + return 0; return 1; } @@ -363,43 +359,6 @@ load_firm(const char *path, const char *path_firmkey, const char *path_cetk, uin } struct firm_signature *fsig = get_firm_info(dest); - int unknown_firm_loaded = 0; - - if (fsig->version == 0xFF) { - // Oh boy, here's a fun case. We have no clue what the user is loading, so - // a number of additional sanity checks and autodetection have to be done. - - unknown_firm_loaded = 1; - - fsig->type = type_native; - - if (dest->section[3].size != 0) { - // Check for the presence of a TWL/AGB only sysmodule - if( !memcmp((char*)dest + dest->section[0].offset + 0x400, "TwlBg", 5)) - fsig->type = type_twl; - else if( !memcmp((char*)dest + dest->section[0].offset + 0x400, "AgbBg", 5)) - fsig->type = type_agb; - } - - for (firm_section_h *section = dest->section; section < dest->section + 4; section++) { - if (section->type == FIRM_TYPE_ARM9) { - if (fsig->type == type_native) { - char* k9l = (char*)memfind((uint8_t*)dest + section->offset, section->size, "K9L", 3); - if (k9l == NULL) // O3DS. - fsig->console = console_o3ds; - else // N3DS. - fsig->console = console_n3ds; - } else { - if (section->address == 0x08006800) // O3DS entry - fsig->console = console_o3ds; - else if (section->address == 0x08006000) // N3DS entry - fsig->console = console_n3ds; - } - - break; - } - } - } // The N3DS firm has an additional encryption layer for ARM9 if (fsig->console == console_n3ds) { @@ -411,19 +370,20 @@ load_firm(const char *path, const char *path_firmkey, const char *path_cetk, uin uint32_t magic = *(uint32_t *)((uintptr_t)dest + section->offset + 0x800); if (fsig->type == type_native) arm9bin_iscrypt = (magic != ARM9BIN_MAGIC); - else if (fsig->type == type_twl || fsig->type == type_twl) + else if (fsig->type == type_twl || fsig->type == type_agb) arm9bin_iscrypt = (magic != LGY_ARM9BIN_MAGIC); if (arm9bin_iscrypt) { // Decrypt the arm9bin. + fprintf(stderr, " ARM9 segment is encrypted\n"); if (decrypt_arm9bin((arm9bin_h *)((uintptr_t)dest + section->offset), fsig)) { - fprintf(stderr, " ARM9 segment is encrypted\n"); + fprintf(stderr, " ARM9 segment failed to decrypt\n"); return NULL; } firmware_changed = 1; // Decryption of arm9bin performed. } else { fprintf(stderr, " ARM9 segment is decrypted\n"); - if (fsig->type == type_native && fsig->version > 0x0F) { + if (fsig->type == type_native && fsig->k9l >= 2) { slot0x11key96_init(); // This has to be loaded // regardless, otherwise boot will // fail. @@ -455,6 +415,10 @@ load_firm(const char *path, const char *path_firmkey, const char *path_cetk, uin // so we don't change them. } + fprintf(stderr, " Loaded.\n"); + + free(fsig); + return dest; } @@ -467,7 +431,7 @@ boot_firm() // Set up the keys needed to boot a few firmwares, due to them being unset, // depending on which firmware you're booting from. // TODO: Don't use the hardcoded offset. - if (update_96_keys && fsig->console == console_n3ds && fsig->version > 0x0F) { + if (update_96_keys && fsig->console == console_n3ds && fsig->k9l >= 2) { uint8_t *keydata = find_section_key(); if (!keydata) { abort("Couldn't find section key.\n"); @@ -486,6 +450,8 @@ boot_firm() fprintf(stderr, "Updated keyX keyslots.\n"); } + free(fsig); + #ifdef MALLOC_DEBUG print_alloc_stats(); wait(); @@ -566,28 +532,25 @@ load_firms() fprintf(stderr, "FIRM load triggered.\n"); fprintf(stderr, "Loading NATIVE_FIRM\n"); - if ((firm_loc = load_firm(get_opt((void*)OPTION_NFIRM_PATH), PATH_NATIVE_FIRMKEY, PATH_NATIVE_CETK, &firm_size)) != NULL) { + if ((firm_loc = load_firm(get_opt((void*)OPTION_NFIRM_PATH), PATH_NATIVE_FIRMKEY, PATH_NATIVE_CETK, &firm_size)) == NULL) { abort("\n Failed to load NATIVE_FIRM.\n"); } find_proc9(firm_loc, &firm_proc9, &firm_p9_exefs); - fprintf(stderr, " Ver: %x, %u\n", get_firm_info(firm_loc)->version, get_firm_info(firm_loc)->console ); fprintf(stderr, "TWL_FIRM\n"); - if ((twl_firm_loc = load_firm(get_opt((void*)OPTION_TFIRM_PATH), PATH_TWL_FIRMKEY, PATH_TWL_CETK, &twl_firm_size)) != NULL) { + if ((twl_firm_loc = load_firm(get_opt((void*)OPTION_TFIRM_PATH), PATH_TWL_FIRMKEY, PATH_TWL_CETK, &twl_firm_size)) == NULL) { fprintf(stderr, "\n TWL_FIRM failed to load.\n"); state = 1; } else { find_proc9(twl_firm_loc, &twl_firm_proc9, &twl_firm_p9_exefs); - fprintf(stderr, " Ver: %x, %u\n", get_firm_info(twl_firm_loc)->version, get_firm_info(twl_firm_loc)->console ); } fprintf(stderr, "AGB_FIRM\n"); - if ((agb_firm_loc = load_firm(get_opt((void*)OPTION_AFIRM_PATH), PATH_AGB_FIRMKEY, PATH_AGB_CETK, &agb_firm_size)) != NULL) { + if ((agb_firm_loc = load_firm(get_opt((void*)OPTION_AFIRM_PATH), PATH_AGB_FIRMKEY, PATH_AGB_CETK, &agb_firm_size)) == NULL) { fprintf(stderr, "\n AGB_FIRM failed to load.\n"); state = 1; } else { find_proc9(agb_firm_loc, &agb_firm_proc9, &agb_firm_p9_exefs); - fprintf(stderr, " Ver: %x, %u\n", get_firm_info(agb_firm_loc)->version, get_firm_info(agb_firm_loc)->console ); } firm_loaded = 1; // Loaded. diff --git a/source/firm/version.c b/source/firm/version.c index e69e9f5..1401d5e 100644 --- a/source/firm/version.c +++ b/source/firm/version.c @@ -1,125 +1,79 @@ #include -// We use the firm's section 0's hash to identify the version -struct firm_signature firm_signatures[] = { - // NFIRM (old) - - {.sig = { 0xEE, 0xE2, 0x81, 0x2E, 0xB9, 0x10, 0x0D, 0x03, 0xFE, 0xA2, 0x3F, 0x44, 0xB5, 0x1C, 0xB3, 0x5E }, - .version = 0x1F, - .version_string = "4.1.0", - .type = type_native, - .console = console_o3ds }, - {.sig = { 0x8C, 0x29, 0xDA, 0x7B, 0xB5, 0x5F, 0xFE, 0x44, 0x1F, 0x66, 0x79, 0x70, 0x8E, 0xE4, 0x42, 0xE3 }, - .version = 0x2A, - .version_string = "6.1.0", - .type = type_native, - .console = console_o3ds }, - {.sig = { 0x1D, 0x96, 0x80, 0xD9, 0x0A, 0xA9, 0xDB, 0xE8, 0x29, 0x77, 0xCB, 0x7D, 0x90, 0x55, 0xB7, 0xF9 }, - .version = 0x30, - .version_string = "7.2.0", - .type = type_native, - .console = console_o3ds }, - {.sig = { 0x3B, 0x61, 0x2E, 0xBA, 0x42, 0xAE, 0x24, 0x46, 0xAD, 0x60, 0x2F, 0x7B, 0x52, 0x16, 0x82, 0x91 }, - .version = 0x37, - .version_string = "8.0.0", - .type = type_native, - .console = console_o3ds }, - {.sig = { 0x3F, 0xBF, 0x14, 0x06, 0x33, 0x77, 0x82, 0xDE, 0xB2, 0x68, 0x83, 0x01, 0x6B, 0x1A, 0x71, 0x69 }, - .version = 0x38, - .version_string = "9.0.0", - .type = type_native, - .console = console_o3ds }, - {.sig = { 0x5C, 0x6A, 0x51, 0xF3, 0x79, 0x4D, 0x21, 0x91, 0x0B, 0xBB, 0xFD, 0x17, 0x7B, 0x72, 0x6B, 0x59 }, - .version = 0x49, - .version_string = "9.6.0", - .type = type_native, - .console = console_o3ds }, - {.sig = { 0xF5, 0x7E, 0xC3, 0x86, 0x1F, 0x8D, 0x8E, 0xFB, 0x44, 0x61, 0xF3, 0x16, 0x51, 0x0A, 0x57, 0x7D }, - .version = 0x50, - .version_string = "10.4.0", - .type = type_native, - .console = console_o3ds }, - {.sig = { 0xE9, 0xAD, 0x74, 0x9D, 0x46, 0x9C, 0x9C, 0xF4, 0x96, 0x9E, 0x1A, 0x7A, 0xDF, 0x40, 0x2A, 0x82 }, - .version = 0x52, - .version_string = "11.0.0", - .type = type_native, - .console = console_o3ds }, - - // NFIRM (new) - - {.sig = { 0x31, 0xCC, 0x46, 0xCD, 0x61, 0x7A, 0xE7, 0x13, 0x7F, 0xE5, 0xFC, 0x20, 0x46, 0x91, 0x6A, 0xBB }, - .version = 0x04, - .version_string = "9.0.0", - .type = type_native, - .console = console_n3ds }, - {.sig = { 0x40, 0x35, 0x6C, 0x9A, 0x24, 0x36, 0x93, 0x7B, 0x76, 0xFE, 0x5D, 0xB1, 0x4D, 0x05, 0x06, 0x52 }, - .version = 0x0F, - .version_string = "9.5.0", - .type = type_native, - .console = console_n3ds }, - {.sig = { 0x07, 0xFE, 0x9A, 0x62, 0x3F, 0xDE, 0x54, 0xC1, 0x9B, 0x06, 0x91, 0xD8, 0x4F, 0x44, 0x9C, 0x21 }, - .version = 0x1B, - .version_string = "10.2.0", - .type = type_native, - .console = console_n3ds }, - {.sig = { 0x1A, 0x56, 0x5C, 0xFF, 0xC9, 0xCC, 0x62, 0xBB, 0x2B, 0xC2, 0x23, 0xB6, 0x4F, 0x48, 0xD1, 0xCC }, - .version = 0x1F, - .version_string = "10.4.0", - .type = type_native, - .console = console_n3ds }, - {.sig = { 0x52, 0x30, 0x0F, 0x55, 0xA2, 0x64, 0x4E, 0xFF, 0x96, 0x90, 0xF0, 0xE5, 0x6E, 0xC8, 0x2E, 0xB3 }, - .version = 0x21, - .version_string = "11.0.0", - .type = type_native, - .console = console_n3ds }, +struct firm_signature * +get_firm_info(firm_h *firm) +{ + // What follows is a heuristic to detect the firmware's properties. Checks are as follows: - // TWL + struct firm_signature *signature = (struct firm_signature*)malloc(sizeof(struct firm_signature)); - {.sig = { 0xE8, 0xB8, 0x82, 0xF5, 0x8C, 0xC4, 0x1B, 0x24, 0x05, 0x60, 0x6D, 0xB8, 0x74, 0xF5, 0xE5, 0xDD }, - .version = 0x16, - .version_string = "6.2.0", - .type = type_twl, - .console = console_o3ds }, - {.sig = { 0x0F, 0x05, 0xC5, 0xF3, 0x60, 0x83, 0x8B, 0x9D, 0xC8, 0x44, 0x3F, 0xB3, 0x06, 0x4D, 0x30, 0xC7 }, - .version = 0x00, - .version_string = "9.0.0", - .type = type_twl, - .console = console_n3ds }, + signature->type = type_native; - // AGB + // Test: Is section #4 a stub + // True: If true, must be NFIRM. + // False: Continue checking. No conclusions drawn. + // Why: All current existing versions on NFIRM and ONLY NFIRM have a + // non-existent section #4. TWL and AGB both do, so this automatically rules them out. - {.sig = { 0x65, 0xB7, 0x55, 0x78, 0x97, 0xE6, 0x5C, 0xD6, 0x11, 0x74, 0x95, 0xDD, 0x61, 0xE8, 0x08, 0x40 }, - .version = 0x0B, - .version_string = "6.0.0", - .type = type_agb, - .console = console_o3ds }, - {.sig = { 0xAF, 0x81, 0xA1, 0xAB, 0xBA, 0xAC, 0xAC, 0xA7, 0x30, 0xE8, 0xD8, 0x74, 0x7C, 0x47, 0x1C, 0x5D }, - .version = 0x00, - .version_string = "9.0.0", - .type = type_agb, - .console = console_n3ds }, + if (firm->section[3].size != 0) { + // Test: Check for a specific sysModule name + // True: If true, must be the corresponding FIRM + // False: No conclusions unless both fail, which means this is NFIRM (see caveat) + // Why: TWL and AGB both contain a system module that handles various tasks. + // Neither of these can be misdetected and they only occur in the correct firmtype. + // Caveat: NFIRM. If we hit this here, unfortunate fact; either TWL and AGB are no longer + // sane or NFIRM has a 4th segment; this would need investigation immediately. - // I don't actually know what legitimate situation the below firmware would be encountered in. - // Better safe than sorry. - {.sig = { 0x16, 0x3B, 0x88, 0xD1, 0x7D, 0x7C, 0x13, 0x19, 0xB5, 0x8C, 0x7D, 0x59, 0x16, 0x81, 0x02, 0x03 }, - .version = 0x50, // I don't actually know, honestly. Developer 11.4 seems to be 10.X based. - .version_string = "Dev 11.4", - .console = console_o3ds }, + // Check for the presence of a TWL/AGB only sysmodule + if( memfind((uint8_t*)firm + firm->section[0].offset, firm->section[0].size, "TwlBg", 5)) { + signature->type = type_twl; +// fprintf(stderr, " TwlBg module found; probably TWL\n"); + } else if( memfind((uint8_t*)firm + firm->section[0].offset, firm->section[0].size, "AgbBg", 5)) { + signature->type = type_agb; +// fprintf(stderr, " AgbBg module found; probably AGB\n"); + } else { +// fprintf(stderr, " Warning: Native, but there is a 4th section. Report this IMMEDIATELY.\n"); + } + } else { +// fprintf(stderr, " Section #4 is empty; this is probably native\n"); + } - {.version = 0xFF, .version_string = "Unknown" } // Terminate list -}; + for (firm_section_h *section = firm->section; section < firm->section + 4; section++) { + if (section->type == FIRM_TYPE_ARM9) { + if (signature->type == type_native) { + // Test: Can we find the string K9L in the arm9bin? + // True: This is a N3DS FIRM + // False: This is an O3DS FIRM + // Why: New3ds arm9bins all use this fancy thing called K9L or Kernel9Loader. + // You probably know this better by the same Arm9Loader (which isn't correct) + // Only N3DS FIRMs have this property. + uint8_t* k9l = (uint8_t*)memfind((uint8_t*)firm + section->offset, section->size, "K9L", 3); + if (k9l == NULL) { // O3DS. +// fprintf(stderr, " No K9L; this is likely an O3DS FIRM\n"); + signature->console = console_o3ds; + } else { // N3DS. +// fprintf(stderr, " K9L found; this is likely an N3DS FIRM\n"); + signature->console = console_n3ds; + signature->k9l = (unsigned int)(k9l[3] - '0'); // String is "K9LN" where N is the version + } + } else { + // Test: Address of segment + // 0x8006800: O3DS + // 0x8006000: N3DS + // Why: O3DS and N3DS have slightly different load addresses on AGB and TWL + // which allows determining which console it is intended for. + if (section->address == 0x08006800) { // O3DS entry + signature->console = console_o3ds; +// fprintf(stderr, " Entry point seems correct for O3DS\n"); + } else if (section->address == 0x08006000) { // N3DS entry + signature->console = console_n3ds; +// fprintf(stderr, " Entry point seems correct for N3DS\n"); + } + } -struct firm_signature * -get_firm_info(firm_h *firm) -{ - for (struct firm_signature *signature = firm_signatures;; signature++) { - if (memcmp(signature->sig, firm->section[0].hash, 0x10) == 0) { - return signature; - } - if (signature->version == 0xFF) { - return signature; // Error. Not found, invalid, etc. + break; } } - return NULL; + return signature; }