From fe0e743720b62a91c46e8f8915f30e16c283a2bf Mon Sep 17 00:00:00 2001 From: chaoskagami Date: Thu, 1 Sep 2016 18:05:11 -0400 Subject: [PATCH] More refactoring; load_firm now uses a heuristic for unknown FIRMs, and no longer needs to know what it is loading --- include/firm/firm.h | 12 +++ include/firm/headers.h | 2 +- source/firm/firm.c | 105 +++++++++++++------ source/firm/version.c | 13 ++- source/menu.c | 228 +++++++++++++++++++++++++++-------------- 5 files changed, 249 insertions(+), 111 deletions(-) diff --git a/include/firm/firm.h b/include/firm/firm.h index d657b40..2e0b83c 100644 --- a/include/firm/firm.h +++ b/include/firm/firm.h @@ -11,6 +11,13 @@ enum consoles console_n3ds ///< N3DS }; +enum firm_type +{ + type_native, ///< NATIVE FIRM + type_twl, ///< TWL FIRM + type_agb ///< AGB FIRM +}; + /* Storage struct for version information of FIRMs */ struct firm_signature @@ -19,6 +26,7 @@ struct firm_signature unsigned int version; ///< CDN/contents version of FIRM char version_string[16]; ///< Human readable version number enum consoles console; ///< Console type + enum firm_type type; ///< Type of FIRM. }; extern firm_h *firm_loc; @@ -67,4 +75,8 @@ void boot_firm(); */ void boot_cfw(); +/* Loads a firmware off disk, returning it. The memory should be free()'d when done, unless you plan to boot. + */ +firm_h* load_firm(const char *path, const char *path_firmkey, const char *path_cetk, uint32_t *size); + #endif diff --git a/include/firm/headers.h b/include/firm/headers.h index 11c05d0..09fba3c 100644 --- a/include/firm/headers.h +++ b/include/firm/headers.h @@ -14,7 +14,7 @@ #define NCCH_MAGIC (0x4843434E) ///< Lit. "NCCH" #define NCSD_MAGIC (0x4453434E) ///< Lit. "NCSD" #define FIRM_MAGIC (0x4D524946) ///< Lit. "FIRM" -#define ARM9BIN_MAGIC (0x47704770) +#define ARM9BIN_MAGIC (0x47704770) ///< Lit. "GpGp" #define LGY_ARM9BIN_MAGIC (0xB0862000) typedef struct firm_section_h diff --git a/source/firm/firm.c b/source/firm/firm.c index 4e97f7b..56d3402 100644 --- a/source/firm/firm.c +++ b/source/firm/firm.c @@ -29,7 +29,7 @@ static int update_96_keys = 0; static volatile uint32_t *const a11_entry = (volatile uint32_t *)0x1FFFFFF8; // Fwd decl -int decrypt_arm9bin(arm9bin_h *header, uint64_t firm_title, uint8_t version); +int decrypt_arm9bin(arm9bin_h *header, struct firm_signature *sig); #define SECTOR_SIZE 0x200 @@ -72,10 +72,13 @@ void dump_firm(firm_h** buffer, uint8_t index) { uint8_t detver = 0; + struct firm_signature s; + s.version = 0; + s.type = type_native; if(index == 1) - detver = 0x10; + s.version = 0x10; - if(decrypt_arm9bin((arm9bin_h*)((uint8_t*)firm + firm->section[2].offset), NATIVE_FIRM_TITLEID, detver)) { + if(decrypt_arm9bin((arm9bin_h*)((uint8_t*)firm + firm->section[2].offset), &s)) { abort(" Failed to decrypt FIRM%u arm9loader.\n", index); } @@ -261,11 +264,11 @@ decrypt_firm_title(firm_h *dest, ncch_h *ncch, uint32_t *size, void *key) } int -decrypt_arm9bin(arm9bin_h *header, uint64_t firm_title, uint8_t version) +decrypt_arm9bin(arm9bin_h *header, struct firm_signature *sig) { uint8_t slot = 0x15; - if (firm_title == NATIVE_FIRM_TITLEID && version > 0x0F) { + if (sig->type == type_native && sig->version > 0x0F) { uint8_t decrypted_keyx[AES_BLOCK_SIZE]; slot0x11key96_init(); @@ -285,10 +288,10 @@ decrypt_arm9bin(arm9bin_h *header, uint64_t firm_title, uint8_t version) use_aeskey(slot); ctr_decrypt(arm9bin, arm9bin, (size_t)size / AES_BLOCK_SIZE, AES_CNT_CTRNAND_MODE, header->ctr); - if (firm_title == NATIVE_FIRM_TITLEID) + if (sig->type == type_native) return *(uint32_t *)arm9bin != ARM9BIN_MAGIC; - else if (firm_title == AGB_FIRM_TITLEID || firm_title == TWL_FIRM_TITLEID) + else if (sig->type == type_twl || sig->type == type_agb) return *(uint32_t *)arm9bin != LGY_ARM9BIN_MAGIC; return 1; @@ -325,25 +328,34 @@ decrypt_firm(firm_h *dest, const char *path_firmkey, const char *path_cetk, uint extern int patch_services(); -int -load_firm(firm_h *dest, const char *path, const char *path_firmkey, const char *path_cetk, uint32_t *size, uint64_t firm_title) +firm_h* +load_firm(const char *path, const char *path_firmkey, const char *path_cetk, uint32_t *size) { + firm_h *dest; + int status = 0; int firmware_changed = 0; - if (read_file(dest, path, *size) == 0) { + FILE* f = fopen(path, "r"); + if (!f) { fprintf(stderr, " FIRM file is missing.\n"); - return 1; - } else { - fprintf(stderr, " Loaded FIRM off filesystem\n"); + return NULL; } + *size = fsize(f); + + dest = (firm_h*)malloc(*size); + + fread(dest, 1, *size, f); + + fclose(f); + fprintf(stderr, " Loaded FIRM off filesystem\n"); // Check and decrypt FIRM if it is encrypted. if (dest->magic != FIRM_MAGIC) { status = decrypt_firm(dest, path_firmkey, path_cetk, size); if (status != 0) { fprintf(stderr, " Decryption seems to have failed\n"); - return 1; + return NULL; } firmware_changed = 1; // Decryption performed. } else { @@ -351,6 +363,43 @@ load_firm(firm_h *dest, const char *path, const char *path_firmkey, const char * } 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) { @@ -360,21 +409,21 @@ load_firm(firm_h *dest, const char *path, const char *path_firmkey, const char * // Check whether the arm9bin is encrypted. int arm9bin_iscrypt = 0; uint32_t magic = *(uint32_t *)((uintptr_t)dest + section->offset + 0x800); - if (firm_title == NATIVE_FIRM_TITLEID) + if (fsig->type == type_native) arm9bin_iscrypt = (magic != ARM9BIN_MAGIC); - else if (firm_title == AGB_FIRM_TITLEID || firm_title == TWL_FIRM_TITLEID) + else if (fsig->type == type_twl || fsig->type == type_twl) arm9bin_iscrypt = (magic != LGY_ARM9BIN_MAGIC); if (arm9bin_iscrypt) { // Decrypt the arm9bin. - if (decrypt_arm9bin((arm9bin_h *)((uintptr_t)dest + section->offset), firm_title, fsig->version)) { + if (decrypt_arm9bin((arm9bin_h *)((uintptr_t)dest + section->offset), fsig)) { fprintf(stderr, " ARM9 segment is encrypted\n"); - return 1; + return NULL; } firmware_changed = 1; // Decryption of arm9bin performed. } else { fprintf(stderr, " ARM9 segment is decrypted\n"); - if (firm_title == NATIVE_FIRM_TITLEID && fsig->version > 0x0F) { + if (fsig->type == type_native && fsig->version > 0x0F) { slot0x11key96_init(); // This has to be loaded // regardless, otherwise boot will // fail. @@ -397,16 +446,16 @@ load_firm(firm_h *dest, const char *path, const char *path_firmkey, const char * fprintf(stderr, " Patching arm9 entrypoint\n"); // Patch the entrypoint to skip arm9loader - if (firm_title == NATIVE_FIRM_TITLEID) { + if (fsig->type == type_native) { dest->a9Entry = 0x0801B01C; - } else if (firm_title == AGB_FIRM_TITLEID || firm_title == TWL_FIRM_TITLEID) { + } else if (fsig->type == type_twl || fsig->type == type_agb) { dest->a9Entry = 0x0801301C; } // The entrypoints seem to be the same across different FIRM versions, // so we don't change them. } - return 0; + return dest; } __attribute__ ((noreturn)) @@ -516,19 +565,15 @@ load_firms() fprintf(stderr, "FIRM load triggered.\n"); - firm_loc = malloc(firm_size); - fprintf(stderr, "Loading NATIVE_FIRM\n"); - if (load_firm(firm_loc, get_opt((void*)OPTION_NFIRM_PATH), PATH_NATIVE_FIRMKEY, PATH_NATIVE_CETK, &firm_size, NATIVE_FIRM_TITLEID) != 0) { + 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 ); - twl_firm_loc = malloc(twl_firm_size); - fprintf(stderr, "TWL_FIRM\n"); - if (load_firm(twl_firm_loc, get_opt((void*)OPTION_TFIRM_PATH), PATH_TWL_FIRMKEY, PATH_TWL_CETK, &twl_firm_size, TWL_FIRM_TITLEID) != 0) { + 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 { @@ -536,10 +581,8 @@ load_firms() fprintf(stderr, " Ver: %x, %u\n", get_firm_info(twl_firm_loc)->version, get_firm_info(twl_firm_loc)->console ); } - agb_firm_loc = malloc(agb_firm_size); - fprintf(stderr, "AGB_FIRM\n"); - if (load_firm(agb_firm_loc, get_opt((void*)OPTION_AFIRM_PATH), PATH_AGB_FIRMKEY, PATH_AGB_CETK, &agb_firm_size, AGB_FIRM_TITLEID) != 0) { + 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 { diff --git a/source/firm/version.c b/source/firm/version.c index a666339..7a63e32 100644 --- a/source/firm/version.c +++ b/source/firm/version.c @@ -2,6 +2,8 @@ // 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", @@ -34,6 +36,9 @@ struct firm_signature firm_signatures[] = { .version = 0x52, .version_string = "11.0.0", .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", @@ -54,6 +59,9 @@ struct firm_signature firm_signatures[] = { .version = 0x21, .version_string = "11.0.0", .console = console_n3ds }, + + // TWL + {.sig = { 0xE8, 0xB8, 0x82, 0xF5, 0x8C, 0xC4, 0x1B, 0x24, 0x05, 0x60, 0x6D, 0xB8, 0x74, 0xF5, 0xE5, 0xDD }, .version = 0x16, .version_string = "6.2.0", @@ -62,6 +70,9 @@ struct firm_signature firm_signatures[] = { .version = 0x00, .version_string = "9.0.0", .console = console_n3ds }, + + // AGB + {.sig = { 0x65, 0xB7, 0x55, 0x78, 0x97, 0xE6, 0x5C, 0xD6, 0x11, 0x74, 0x95, 0xDD, 0x61, 0xE8, 0x08, 0x40 }, .version = 0x0B, .version_string = "6.0.0", @@ -78,7 +89,7 @@ struct firm_signature firm_signatures[] = { .version_string = "Dev 11.4", .console = console_o3ds }, - {.version = 0xFF, .version_string = "Not found" } // Terminate list + {.version = 0xFF, .version_string = "Unknown" } // Terminate list }; struct firm_signature * diff --git a/source/menu.c b/source/menu.c index 953cc6e..d38b73c 100644 --- a/source/menu.c +++ b/source/menu.c @@ -6,6 +6,25 @@ struct options_s *patches = NULL; uint8_t *enable_list; +#define MAX_FIRMS 50 +struct options_s *firm = NULL; + +static int current_menu_index_patches = 0; +static int desc_is_fname_sto = 0; + +#if defined(CHAINLOADER) && CHAINLOADER == 1 +void chainload_menu(); +#endif + +#ifndef REL +#define REL "master" +#endif + +#define ln(s) { s, "", unselectable, 0, NULL, NULL, 0, 0 } +#define lnh(s) { s, "", unselectable, 0, NULL, NULL, 0, 1 } + +void config_main_menu(); + static struct options_s options[] = { // Patches. { "General Options", "", unselectable, 0, NULL, NULL, 0, 1 }, @@ -93,9 +112,75 @@ static struct options_s options[] = { { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two. }; -static int current_menu_index_patches = 0; +static struct options_s config_opts[] = { + { "Options", + "Internal options for the CFW.\nThese are part of " FW_NAME " itself.", + option, options, (void(*)(void*))show_menu, NULL, 0, 0 }, + { "Patches", + "External bytecode patches found in `" PATH_PATCHES "`.\nYou can choose which to enable.", + option, NULL, (void(*)(void*))show_menu, NULL, 0, 0 }, -static int desc_is_fname_sto = 0; + // Sentinel. + { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two. +}; + +static struct options_s help_d[] = { + lnh("About"), + ln(" This is another 3DS CFW for power users."), + ln(" It seeks to address some faults in other"), + ln(" CFWs and is generally just another choice"), + ln(" for users - but primarily is intended for"), + ln(" developers. It is not for the faint of heart."), + ln(""), + lnh("Usage"), + ln(" A -> Select/Toggle/Increment"), + ln(" B -> Back/Boot"), + ln(" X -> Decrement"), + ln(" Select -> Help/Information"), + ln(" Down -> Down"), + ln(" Right -> Down five"), + ln(" Up -> Up"), + ln(" Left -> Up five"), + ln(" L+R+Start -> Menu Screenshot"), + ln(""), + lnh("Credits"), + ln(" @mid-kid, @Wolfvak, @Reisyukaku, @AuroraWright"), + ln(" @d0k3, @TuxSH, @Steveice10, @delebile,"), + ln(" @Normmatt, @b1l1s, @dark-samus, @TiniVi,"), + ln(" @gemarcano, and anyone else I may have"), + ln(" forgotten (yell at me, please!)"), + ln(""), + lnh(" "), + { NULL, NULL, unselectable, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two. +}; + +static struct options_s main_s[] = { + { "Configuration", + "Configuration options for the CFW.", + option, 0, config_main_menu, NULL, 0, 0 }, + { "Readme", + "Mini-readme.\nWhy are you opening help on this, though?\nThat's kind of silly.", + option, help_d, (void(*)(void*))show_menu, NULL, 0, 0 }, + { "Reboot", + "Reboots the console.", + option, 0, reset, NULL, 0, 0 }, + { "Power off", + "Powers off the console.", + option, 0, poweroff, NULL, 0, 0 }, +#if defined(CHAINLOADER) && CHAINLOADER == 1 + { "Chainload", + "Boot another ARM9 payload file.", + option, 0, chainload_menu, NULL, 0, 0 }, +#endif + { "Boot Firmware", + "Patches the firmware, and boots it.", + break_menu, 0, NULL, NULL, 0, 0 }, + + // Sentinel. + { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two. +}; + +//=================================================================================================== char* patch_get_enable(void* val) { uint32_t opt = (uint32_t)val; @@ -147,42 +232,71 @@ void patch_func(char* fpath) { } } -#ifndef REL -#define REL "master" -#endif +//=================================================================================================== +#if 0 +char* get_nfirm(void* val) { + return config->firms[0]; +} -#define ln(s) { s, "", unselectable, 0, NULL, NULL, 0, 0 } -#define lnh(s) { s, "", unselectable, 0, NULL, NULL, 0, 1 } +char* get_tfirm(void* val) { + return config->firms[1]; +} -static struct options_s help_d[] = { - lnh("About"), - ln(" This is another 3DS CFW for power users."), - ln(" It seeks to address some faults in other"), - ln(" CFWs and is generally just another choice"), - ln(" for users - but primarily is intended for"), - ln(" developers. It is not for the faint of heart."), - ln(""), - lnh("Usage"), - ln(" A -> Select/Toggle/Increment"), - ln(" B -> Back/Boot"), - ln(" X -> Decrement"), - ln(" Select -> Help/Information"), - ln(" Down -> Down"), - ln(" Right -> Down five"), - ln(" Up -> Up"), - ln(" Left -> Up five"), - ln(" L+R+Start -> Menu Screenshot"), - ln(""), - lnh("Credits"), - ln(" @mid-kid, @Wolfvak, @Reisyukaku, @AuroraWright"), - ln(" @d0k3, @TuxSH, @Steveice10, @delebile,"), - ln(" @Normmatt, @b1l1s, @dark-samus, @TiniVi,"), - ln(" @gemarcano, and anyone else I may have"), - ln(" forgotten (yell at me, please!)"), - ln(""), - lnh(" "), - { NULL, NULL, unselectable, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two. -}; +char* get_afirm(void* val) { + return config->firms[2]; +} + +char* patch_get_enable(void* val) { + uint32_t opt = (uint32_t)val; + uint32_t raw = enable_list[opt]; + static char ret[2] = " "; + ret[0] = ' '; + if (raw == 1) + ret[0] = '*'; + return ret; +} + +void patch_set_enable(void* val) { + uint32_t opt = (uint32_t)val; + enable_list[opt] = !enable_list[opt]; +} + +void patch_func(char* fpath) { + FILINFO f2; + if (f_stat(fpath, &f2) != FR_OK) + return; + + if (!(f2.fattrib & AM_DIR)) { + struct system_patch p; + read_file(&p, fpath, sizeof(struct system_patch)); + + if (memcmp(p.magic, "AIDA", 4)) + return; + + patches[current_menu_index_patches].name = strdup_self(p.name); + if (desc_is_fname_sto) + patches[current_menu_index_patches].param = strdup_self(fpath); + else + patches[current_menu_index_patches].desc = strdup_self(p.desc); + + uint32_t val = p.uuid; + + patches[current_menu_index_patches].param = (void*)val; + + patches[current_menu_index_patches].handle = option; + patches[current_menu_index_patches].indent = 0; + + patches[current_menu_index_patches].func = patch_set_enable; + patches[current_menu_index_patches].value = patch_get_enable; + + if (desc_is_fname_sto) + enable_list[p.uuid] = 0; + + current_menu_index_patches++; + } +} +#endif +//=================================================================================================== void reset() @@ -210,22 +324,6 @@ poweroff() ctr_system_poweroff(); } -#if defined(CHAINLOADER) && CHAINLOADER == 1 -void chainload_menu(); -#endif - -static struct options_s config_opts[] = { - { "Options", - "Internal options for the CFW.\nThese are part of " FW_NAME " itself.", - option, options, (void(*)(void*))show_menu, NULL, 0, 0 }, - { "Patches", - "External bytecode patches found in `" PATH_PATCHES "`.\nYou can choose which to enable.", - option, NULL, (void(*)(void*))show_menu, NULL, 0, 0 }, - - // Sentinel. - { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two. -}; - // This is dual purpose. When we actually list // patches to build the cache - desc_is_fname // will be set to 1. @@ -265,32 +363,6 @@ void config_main_menu() { generate_patch_cache(); } -static struct options_s main_s[] = { - { "Configuration", - "Configuration options for the CFW.", - option, 0, config_main_menu, NULL, 0, 0 }, - { "Readme", - "Mini-readme.\nWhy are you opening help on this, though?\nThat's kind of silly.", - option, help_d, (void(*)(void*))show_menu, NULL, 0, 0 }, - { "Reboot", - "Reboots the console.", - option, 0, reset, NULL, 0, 0 }, - { "Power off", - "Powers off the console.", - option, 0, poweroff, NULL, 0, 0 }, -#if defined(CHAINLOADER) && CHAINLOADER == 1 - { "Chainload", - "Boot another ARM9 payload file.", - option, 0, chainload_menu, NULL, 0, 0 }, -#endif - { "Boot Firmware", - "Patches the firmware, and boots it.", - break_menu, 0, NULL, NULL, 0, 0 }, - - // Sentinel. - { NULL, NULL, 0, 0, NULL, NULL, 0, 0 }, // cursor_min and cursor_max are stored in the last two. -}; - void menu_handler() { -- 2.39.5