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);
}
{
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();
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;
}
}
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) {
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.
// so we don't change them.
}
+ fprintf(stderr, " Loaded.\n");
+
+ free(fsig);
+
return dest;
}
// 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");
fprintf(stderr, "Updated keyX keyslots.\n");
}
+ free(fsig);
+
#ifdef MALLOC_DEBUG
print_alloc_stats();
wait();
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.
#include <common.h>
-// 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;
}