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
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);
}
}
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();
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;
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 {
}
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) {
// 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.
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))
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 {
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 {
// 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",
.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",
.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",
.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",
.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 *
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 },
{ 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(" <https://github.com/chaoskagami/corbenik>"),
+ { 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;
}
}
-#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(" <https://github.com/chaoskagami/corbenik>"),
- { 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()
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.
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()
{