From: chaoskagami Date: Sat, 3 Sep 2016 14:15:51 +0000 (-0400) Subject: it's a beautiful day outside. X-Git-Tag: v0.3.0~20 X-Git-Url: https://chaos.moe/g/?a=commitdiff_plain;h=38ce3d75990542a5e4433ba58ed35699b2827f66;p=corbenik%2Fcorbenik.git it's a beautiful day outside. birds are chirping, flowers are blooming. on days like these, code like you... s h o u l d b e b u r n i n g i n h e l l --- diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..c21503e --- /dev/null +++ b/TODO.txt @@ -0,0 +1,5 @@ +Note that the branch name here is now a misnomer. It should be called wip/the_great_unclusterfuck now. Really. + +source/firm/firm.c + Has to GO. The time for cakes' decryptor has ended; time for a new one. + There's too many hacks and it has become an unmaintainable mess. diff --git a/external/bits/chain.s b/external/bits/chain.s index 98f823b..05a79ab 100644 --- a/external/bits/chain.s +++ b/external/bits/chain.s @@ -49,6 +49,9 @@ boot: cmp r2, #4 bcc flush_dcache + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 // drain write buffer before jump + // Reload argc and argv. ldr r0, argc ldr r1, argv diff --git a/host/Makefile b/host/Makefile index 1f10798..8cdd2e3 100644 --- a/host/Makefile +++ b/host/Makefile @@ -1,4 +1,8 @@ -all: bdfe font +all: bdfe font misc + +misc: + gcc -o key_char key_char.c -lcrypto -g -O0 + gcc -o error_decoder error_decoder.c -g -O0 bdfe_dir: make -C bdfe @@ -8,4 +12,4 @@ font: bdfe_dir clean: make -C bdfe clean - rm -f termfont.bin + rm -f termfont.bin key_char error_decoder diff --git a/host/error-decoder.c b/host/error_decoder.c similarity index 100% rename from host/error-decoder.c rename to host/error_decoder.c diff --git a/host/generate_keys.sh b/host/generate_keys.sh new file mode 100755 index 0000000..1c7ba5d --- /dev/null +++ b/host/generate_keys.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Okay, so obviously this file would be non-redistributable if I put the keys here. +# You'll need to get them yourself (in plaintext) and put them in the correct place. + +# Optionally, you can call this file with an option +# and the output binary will contain the keys. DO NOT do this unless you know +# you will NOT share the binaries; the resultant output is NOT MY PROBLEM, and +# logs will be marked as "Tainted key loading used". If I have any reason to believe +# you have enabled this option, NO SUPPORT will be provided unless you go and use +# a build with that off. + +if [ "$1" == "--tainted-no-support" ]; then + WELP_USER_IS_A_WEIRDO_BUT_WHATEVER="$1" +fi + +function key() { + if [ -e "keys/$1.txt" ]; then + echo "Generating key metadata..." + mkdir -p ../source/firm/keys + ./key_char $WELP_USER_IS_A_WEIRDO_BUT_WHATEVER $(cat keys/$1.txt | tr -d '\n') > ../source/firm/keys/$1.gen + else + echo "Key not found, generating stub..." + echo -n "{}" > ../source/firm/keys/$1.gen + fi + + echo "$2" >> ../source/firm/keys/$1.gen +} + +key Y11_95 "," +key Y11_96 "" + +key Y05 "" + +key Y3D_0 "," +key Y3D_1 "," +key Y3D_2 "," +key Y3D_3 "," +key Y3D_4 "," +key Y3D_5 "" diff --git a/host/key_char.c b/host/key_char.c new file mode 100644 index 0000000..88e2fda --- /dev/null +++ b/host/key_char.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include + +static const char digits[] = "0123456789abcdef"; +static char ascii_to_digit[256] = {0}; + +#define HASH_LEN 32 // Sha256 +void hexdump(uint8_t* text, uint8_t* bin, int bin_len) +{ + for(int i=0; i < bin_len; text += 2, bin++, i++) { + text[0] = digits[(bin[0] >> 4) & 0xf]; + text[1] = digits[bin[0] & 0xf]; + } +} + +void unhexdump(uint8_t* bin, uint8_t* str, int bin_len) +{ + for(int i=0; i < bin_len; bin++, str += 2, i++) { + bin[0] = (ascii_to_digit[str[0]] << 4) | ascii_to_digit[str[1]]; + } +} + +uint8_t* sha256(uint8_t* data, uint32_t len) { + uint8_t *hash = malloc(SHA256_DIGEST_LENGTH); + + SHA256_CTX sha256; + + SHA256_Init(&sha256); + SHA256_Update(&sha256, data, len); + SHA256_Final(hash, &sha256); + + return hash; +} + +void init() { + ascii_to_digit['0'] = 0; + ascii_to_digit['1'] = 1; + ascii_to_digit['2'] = 2; + ascii_to_digit['3'] = 3; + ascii_to_digit['4'] = 4; + ascii_to_digit['5'] = 5; + ascii_to_digit['6'] = 6; + ascii_to_digit['7'] = 7; + ascii_to_digit['8'] = 8; + ascii_to_digit['9'] = 9; + + ascii_to_digit['A'] = 0xa; + ascii_to_digit['B'] = 0xb; + ascii_to_digit['C'] = 0xc; + ascii_to_digit['D'] = 0xd; + ascii_to_digit['E'] = 0xe; + ascii_to_digit['F'] = 0xf; + + ascii_to_digit['a'] = 0xa; + ascii_to_digit['b'] = 0xb; + ascii_to_digit['c'] = 0xc; + ascii_to_digit['d'] = 0xd; + ascii_to_digit['e'] = 0xe; + ascii_to_digit['f'] = 0xf; +} + +uint16_t get_roll(uint8_t* data) { + uint16_t roll = 0; + for (int i=0; i < 16; i++) { + roll += data[i]; + } + + return roll; +} + +int main(int c, char **v) { + uint8_t data[16]; + char textsha[65] = {0}; + + if (c < 2) { + printf("Usage: %s \n", v[0]); + return 1; + } + + init(); + + unhexdump(data, v[1], 16); + + uint16_t roll = get_roll(data); + + uint8_t* sha = sha256(data, 16); + + printf("{.roll = 0x%03X, .sha = {", v[1], roll); + + for (int i=0; i < 32; i++) { + printf("0x%02X", sha[i]); + if (i != 31) + printf(", "); + } + printf("} }"); + + free(sha); + + return 0; +} diff --git a/include/common.h b/include/common.h index e9d82f2..cae7211 100644 --- a/include/common.h +++ b/include/common.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/include/firm/firm.h b/include/firm/firm.h index 8a1cffc..5a40af6 100644 --- a/include/firm/firm.h +++ b/include/firm/firm.h @@ -27,44 +27,10 @@ struct firm_signature enum firm_type type; ///< Type of FIRM. }; -extern firm_h *firm_loc; -extern uint32_t firm_size; -extern struct firm_signature *current_firm; -extern firm_section_h firm_proc9; -extern exefs_h *firm_p9_exefs; - -extern firm_h *twl_firm_loc; -extern uint32_t twl_firm_size; -extern struct firm_signature *current_twl_firm; -extern firm_section_h twl_firm_proc9; -extern exefs_h *twl_firm_p9_exefs; - -extern firm_h *agb_firm_loc; -extern uint32_t agb_firm_size; -extern struct firm_signature *current_agb_firm; -extern firm_section_h agb_firm_proc9; -extern exefs_h *agb_firm_p9_exefs; - /* Returns a struct describing the version of a decrypted FIRM */ struct firm_signature *get_firm_info(firm_h *firm); -/* Initializes 0x11 KeyY - */ -void slot0x11key96_init(); - -/* Extracts 0x05 KeyY from FIRM0 on NAND - */ -void extract_slot0x05keyY(); - -/* Extracts 0x3D KeyY from FIRM0 on NAND - */ -void extract_slot0x3DkeyY(); - -/* Loads FIRM files - */ -int load_firms(); - /* Boots native FIRM - do not call directly. */ void boot_firm(); @@ -75,6 +41,6 @@ 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); +firm_h* load_firm(const char *path); #endif diff --git a/include/firm/keys.h b/include/firm/keys.h new file mode 100644 index 0000000..b25bea3 --- /dev/null +++ b/include/firm/keys.h @@ -0,0 +1,30 @@ +#ifndef __FIRM_KEYS_H +#define __FIRM_KEYS_H + +// See the D9 source code (by @d0k3) for info on these defines and structs: +// https://github.com/d0k3/Decrypt9WIP/blob/master/source/decryptor/keys.h + +#define KEY_ENCRYPT (1<<0) +#define KEY_DECRYPT (1<<1) + +#define KEYS_UNKNOWN 0 +#define KEYS_RETAIL 1 +#define KEYS_DEVKIT 2 + +typedef struct { + uint8_t slot; // keyslot, 0x00...0x3F + char type; // type 'X' / 'Y' / 'N' for normalKey + char id[10]; // key ID for special keys, all zero for standard keys + uint8_t reserved[2]; // reserved space + uint8_t isDevkitKey; // 0 for retail units / 1 for DevKit units + uint8_t isEncrypted; // 0 if not / anything else if it is + uint8_t key[16]; +} __attribute__((packed)) aeskeydbent_t; + +typedef struct { + uint16_t roll; + uint8_t sha[32]; + uint8_t key[16]; +} key_find_t; + +#endif diff --git a/include/patch/emunand.h b/include/patch/emunand.h index 35b3c56..1c26bcd 100644 --- a/include/patch/emunand.h +++ b/include/patch/emunand.h @@ -3,6 +3,6 @@ #include -void patch_emunand(uint32_t size); +void patch_emunand(firm_h* firm_loc, uint32_t size); #endif diff --git a/include/patch/patch_file.h b/include/patch/patch_file.h index 152ad87..c07e2ef 100644 --- a/include/patch/patch_file.h +++ b/include/patch/patch_file.h @@ -4,6 +4,6 @@ #include // Build patch into CFW instead of as module. -#define PATCH(name) int patch_##name() +#define PATCH(name) int patch_##name(firm_h* firm_loc) #endif diff --git a/include/patch_format.h b/include/patch_format.h index 9c3bd4b..c16b600 100644 --- a/include/patch_format.h +++ b/include/patch_format.h @@ -75,9 +75,8 @@ #define PATH_TWL_FIRMKEY PATH_KEYS "/twl.key" ///< TWL FIRM decrypted titlekey #define PATH_AGB_FIRMKEY PATH_KEYS "/agb.key" ///< AGB FIRM decrypted titlekey -#define PATH_SLOT0X11KEY96 PATH_KEYS "/11Key96.key" ///< 0x11 KeyY (for 9.6 FIRM arm9loader) - -#define PATH_ALT_SLOT0X11KEY96 "/slot0x11key96.bin" ///< Alternate path for 0x11 KeyY +#define PATH_SLOT0X11KEY95 "/slot0x11key95.bin" ///< Alternate path for 0x11 KeyY +#define PATH_SLOT0X11KEY96 "/slot0x11key96.bin" ///< Alternate path for 0x11 KeyY #define PATH_LOG LOCALSTATEDIR "/log" ///< Log directory diff --git a/source/Makefile.am b/source/Makefile.am index 8fac091..f62fb99 100644 --- a/source/Makefile.am +++ b/source/Makefile.am @@ -19,4 +19,4 @@ corbenikdir = $(top_srcdir)/source inc_dir = $(top_srcdir)/include -corbenik_SOURCES = patch/reboot.c patch/svc.c patch/module.c patch/emunand.c main.c std/fs.c std/draw.c std/memory.c std/abort.c std/allocator.c menu.c firm/version.c firm/firm.c firm/decryptor.c interpreter.c input.c patcher.c chainloader.c config-backend-file.c menu-backend.c start.s interrupt.c arm11.c +corbenik_SOURCES = patch/reboot.c patch/svc.c patch/module.c patch/emunand.c main.c std/fs.c std/draw.c std/memory.c std/abort.c std/allocator.c menu.c firm/util.c firm/keys.c firm/firmlaunch.c firm/version.c firm/firm.c firm/decryptor.c interpreter.c input.c patcher.c chainloader.c config-backend-file.c menu-backend.c start.s interrupt.c arm11.c diff --git a/source/firm/decryptor.c b/source/firm/decryptor.c index cd628f6..aaab3a8 100644 --- a/source/firm/decryptor.c +++ b/source/firm/decryptor.c @@ -1,7 +1,3 @@ -/* - This is all fairly minimal and based on @d0k3's decrypt9 code. -*/ - #include #include #include diff --git a/source/firm/firm.c b/source/firm/firm.c index 1a7b1fc..f98e061 100644 --- a/source/firm/firm.c +++ b/source/firm/firm.c @@ -6,591 +6,31 @@ #include #include -firm_h *firm_loc = NULL; -uint32_t firm_size = FCRAM_SPACING; -firm_section_h firm_proc9; -exefs_h *firm_p9_exefs; - -firm_h *twl_firm_loc = NULL; -uint32_t twl_firm_size = FCRAM_SPACING * 2; -firm_section_h twl_firm_proc9; -exefs_h *twl_firm_p9_exefs; - -firm_h *agb_firm_loc = NULL; -uint32_t agb_firm_size = FCRAM_SPACING * 2; -firm_section_h agb_firm_proc9; -exefs_h *agb_firm_p9_exefs; - -firm_h* firm0 = NULL; -firm_h* firm1 = NULL; - -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, struct firm_signature *sig); - -#define SECTOR_SIZE 0x200 - -// 0x0B130000 = start of FIRM0 partition, 0x400000 = size of FIRM partition (4MB) -void dump_firm(firm_h** buffer, uint8_t index) { - if (*buffer != NULL) return; - - // NOTE - Cast, because GCC is making assumptions about 'index'. - uint32_t firm_offset = (uint32_t)(0x0B130000 + (index % 2) * 0x400000), - firm_b_size = 0x00100000; // 1MB, because - - buffer[0] = malloc(firm_b_size); - - uint8_t ctr[0x10], - cid[0x10], - sha_t[0x20]; - - firm_h* firm = buffer[0]; - - if (sdmmc_nand_readsectors(firm_offset / SECTOR_SIZE, firm_b_size / SECTOR_SIZE, (uint8_t*)firm)) - abort(" Failed to read NAND!\n"); - - fprintf(stderr, " Read FIRM%u off NAND.\n", index); - - sdmmc_get_cid(1, (uint32_t*)cid); - sha256sum(sha_t, cid, 0x10); - memcpy(ctr, sha_t, 0x10); - add_ctr(ctr, firm_offset / AES_BLOCK_SIZE); - - use_aeskey(0x06); - set_ctr(ctr); - ctr_decrypt(firm, firm, firm_b_size / AES_BLOCK_SIZE, AES_CNT_CTRNAND_MODE, ctr); - - if (memcmp((char*) & firm->magic, "FIRM", 4)) - abort(" Decryption failed on FIRM.\n"); - - fprintf(stderr, " AES decrypted FIRM%u.\n", index); - - fprintf(stderr, " Magic is intact on FIRM%u.\n", index); - - struct firm_signature* sig = get_firm_info(firm); - - sig->k9l = 0; - if(index == 1) - sig->k9l = 2; - - fprintf(stderr, " FIRM: K9L%u, Console:%u, Type:%u\n", sig->k9l, sig->console, sig->type); - - 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); -} - -void -slot0x11key96_init() -{ - // 9.6 crypto may need us to get the key from somewhere else. - // Unless the console already has the key initialized, that is. - uint8_t key[AES_BLOCK_SIZE]; - if (read_file(key, PATH_SLOT0X11KEY96, AES_BLOCK_SIZE) != 0 || read_file(key, PATH_ALT_SLOT0X11KEY96, AES_BLOCK_SIZE) != 0) { - // Read key successfully. - setup_aeskey(0x11, key); - - // Tell boot_firm it needs to regenerate the keys. - update_96_keys = 1; - } -} - -uint8_t* key_search(uint8_t* mem, uint32_t size, uint8_t* sha256, uint8_t byte) { - uint8_t hash[0x20] = {0}; - - // Search ARM9 for key. - for(uint32_t j = 0; j < size; j ++) { - // Is candidate? - if (mem[j] == byte) { - // Yes. Check hash. - sha256sum(hash, &mem[j], 0x10); - - if(!memcmp(sha256, hash, 0x20)) { - return &mem[j]; - } - } - } - - return NULL; -} - -void extract_slot0x05keyY() { - if (firm0 == NULL) - dump_firm(&firm0, 0); - - uint8_t sha256[] = {0x98, 0x24, 0x27, 0x14, 0x22, 0xB0, 0x6B, 0xF2, 0x10, 0x96, 0x9C, 0x36, 0x42, 0x53, 0x7C, 0x86, - 0x62, 0x22, 0x5C, 0xFD, 0x6F, 0xAE, 0x9B, 0x0A, 0x85, 0xA5, 0xCE, 0x21, 0xAA, 0xB6, 0xC8, 0x4D}; - - uint8_t* key_loc = (uint8_t*)firm0 + firm0->section[2].offset; - uint32_t search_size = firm0->section[2].size; - - uint8_t mem[16] __attribute__((aligned(4))) = {0}; - - uint8_t* key_data = key_search(key_loc, search_size, sha256, 0x4D); - - if (!key_data) - abort(" 0x05 KeyY not found!\n"); - - fprintf(stderr, " 0x05 KeyY at %lx in FIRM1\n", (uint32_t)key_data - (uint32_t)key_loc); - - memcpy(mem, key_data, 16); - - setup_aeskeyY(0x05, mem); -} - -void extract_slot0x3DkeyY() { - if (firm0 == NULL) - dump_firm(&firm0, 0); - - uint8_t sha256[] = {0x21, 0x12, 0xf4, 0x50, 0x78, 0x6d, 0xce, 0x64, 0x39, 0xfd, 0xb8, 0x71, 0x14, 0x74, 0x41, 0xf4, - 0x69, 0xb6, 0xc4, 0x70, 0xa4, 0xb1, 0x5f, 0x7d, 0xfd, 0xe8, 0xcc, 0xe4, 0xc4, 0x62, 0x82, 0x5b}; - - uint8_t* key_loc = (uint8_t*)firm0 + firm0->section[2].offset; - uint32_t search_size = firm0->section[2].size; - - uint8_t mem[16] __attribute__((aligned(4))) = {0}; - - uint8_t* key_data = key_search(key_loc, search_size, sha256, 0x0C); - - if (!key_data) - abort(" 0x3D KeyY #1 not found!\n"); - - fprintf(stderr, " 0x3D KeyY #1 at %lx in FIRM0\n", (uint32_t)key_data - (uint32_t)key_loc); - - memcpy(mem, key_data, 16); - - setup_aeskeyY(0x3D, mem); -} - -void* find_section_key() { - // The key will be dword-aligned (I think? Verify this. May need new NFIRM to check assumption. Go, Nintendo!) - - // The hash of the key. Can't give the key itself out, obviously. - uint8_t sha256[] = {0xb9, 0x4d, 0xb1, 0xb1, 0xc3, 0xe0, 0x11, 0x08, 0x9c, 0x19, 0x46, 0x06, 0x4a, 0xbc, 0x40, 0x2a, - 0x7c, 0x66, 0xf4, 0x4a, 0x74, 0x6f, 0x71, 0x50, 0x32, 0xfd, 0xff, 0x03, 0x74, 0xd7, 0x45, 0x2c}; - uint8_t* key_loc = (uint8_t*)firm_loc + firm_loc->section[2].offset; - uint32_t search_size = firm_loc->section[2].size; - - uint8_t* key_data = key_search(key_loc, search_size, sha256, 0xDD); - - if (!key_data) - abort(" FIRM Section key not found!\n"); - - fprintf(stderr, " FIRM Section key at %lx in FIRM\n", (uint32_t)key_data - (uint32_t)key_loc); - - return key_data; -} - -int -decrypt_cetk_key(void *key, const void *cetk) -{ - static int got_cetk = 0; - uint8_t iv[AES_BLOCK_SIZE] = { 0 }; - uint32_t sigtype = __builtin_bswap32(*(const uint32_t *)cetk); - - if (sigtype != SIG_TYPE_RSA2048_SHA256) - return 1; - - const ticket_h *ticket = (const ticket_h *)((const uint8_t*)cetk + sizeof(sigtype) + 0x13C); - if (ticket->ticketCommonKeyYIndex != 1) - return 1; - - if (got_cetk == 0) { - fprintf(stderr, " Retrieving 0x3D KeyY...\n"); - extract_slot0x3DkeyY(); - got_cetk = 1; - } - - use_aeskey(0x3D); - - memcpy(iv, ticket->titleID, sizeof(ticket->titleID)); - memcpy(key, ticket->titleKey, sizeof(ticket->titleKey)); - - cbc_decrypt(key, key, 1, AES_CNT_TITLEKEY_DECRYPT_MODE, iv); - - fprintf(stderr, " Extracted titlekey from cetk.\n"); - - return 0; -} - -int -decrypt_firm_title(firm_h *dest, ncch_h *ncch, uint32_t *size, void *key) -{ - uint8_t firm_iv[16] = { 0 }; - uint8_t exefs_key[16] = { 0 }; - uint8_t exefs_iv[16] = { 0 }; - - fprintf(stderr, " Decrypting FIRM container (size is %lu blocks)\n", *size / AES_BLOCK_SIZE); - - setup_aeskey(0x16, key); - use_aeskey(0x16); - - cbc_decrypt(ncch, ncch, *size / AES_BLOCK_SIZE, AES_CNT_CBC_DECRYPT_MODE, firm_iv); - - if (ncch->magic != NCCH_MAGIC) - return 1; - - memcpy(exefs_key, ncch, AES_BLOCK_SIZE); - - ncch_getctr(ncch, exefs_iv, NCCHTYPE_EXEFS); - - // Get the exefs offset and size from the NCCH - exefs_h *exefs = (exefs_h *)((uint8_t *)ncch + ncch->exeFSOffset * MEDIA_UNITS); - uint32_t exefs_size = ncch->exeFSSize * MEDIA_UNITS; - - fprintf(stderr, " Decrypting ExeFs for FIRM (size is %lu blocks)\n", exefs_size / AES_BLOCK_SIZE); - - setup_aeskeyY(0x2C, exefs_key); - use_aeskey(0x2C); - ctr_decrypt(exefs, exefs, exefs_size / AES_BLOCK_SIZE, AES_CNT_CTRNAND_MODE, exefs_iv); - - // Get the decrypted FIRM - // We assume the firm.bin is always the first file - firm_h *firm = (firm_h *)&exefs[1]; // The offset right behind the exefs - // header; the first file. - *size = exefs->fileHeaders[0].size; - - if (firm->magic != FIRM_MAGIC) - return 1; - - memcpy(dest, firm, *size); - - return 0; -} - -int -decrypt_arm9bin(arm9bin_h *header, struct firm_signature *sig) -{ - uint8_t slot = 0x15; - - if (sig->type == type_native && sig->k9l >= 2) { - uint8_t decrypted_keyx[AES_BLOCK_SIZE]; - - slot0x11key96_init(); - slot = 0x16; - - use_aeskey(0x11); - ecb_decrypt(header->slot0x16keyX, decrypted_keyx, 1, AES_CNT_ECB_DECRYPT_MODE); - setup_aeskeyX(slot, decrypted_keyx); - } - - setup_aeskeyY(slot, header->keyy); - set_ctr(header->ctr); - - void *arm9bin = (uint8_t *)header + 0x800; - int size = atoi(header->size); - - use_aeskey(slot); - ctr_decrypt(arm9bin, arm9bin, (size_t)size / AES_BLOCK_SIZE, AES_CNT_CTRNAND_MODE, header->ctr); - - if (sig->type == type_native && *(uint32_t *)arm9bin == ARM9BIN_MAGIC) - return 0; - - else if ((sig->type == type_twl || sig->type == type_agb) && *(uint32_t *)arm9bin == LGY_ARM9BIN_MAGIC) - return 0; - - return 1; -} - -int -decrypt_firm(firm_h *dest, const char *path_firmkey, const char *path_cetk, uint32_t *size) -{ - uint8_t firm_key[AES_BLOCK_SIZE]; - - // Firmware is likely encrypted. Decrypt. - if (!read_file(firm_key, path_firmkey, AES_BLOCK_SIZE)) { - uint8_t* temp = malloc(FCRAM_SPACING); - // Missing firmkey. Attempt to get from CETK (only works if system was booted) - if (!read_file(temp, path_cetk, FCRAM_SPACING) || decrypt_cetk_key(firm_key, temp)) { - fprintf(stderr, " No firmkey and failed to extract from cetk\n"); - return 1; - } else { - fprintf(stderr, " Saving firmkey for future use.\n"); - write_file(firm_key, path_firmkey, AES_BLOCK_SIZE); - } - free(temp); - } else { - fprintf(stderr, " Read firmkey from filesystem.\n"); - } - - fprintf(stderr, " Decrypting FIRM\n"); - if (decrypt_firm_title(dest, (void *)dest, size, firm_key) != 0) { - fprintf(stderr, " Failed to decrypt FIRM title.\n"); - return 1; - } - return 0; -} - -extern int patch_services(); - -firm_h* -load_firm(const char *path, const char *path_firmkey, const char *path_cetk, uint32_t *size) -{ - if (path == NULL || path_firmkey == NULL || path_cetk == NULL || size == NULL) - return NULL; - - firm_h *dest; - - int status = 0; - int firmware_changed = 0; - - FILE* f = fopen(path, "r"); - if (!f) { - fprintf(stderr, " FIRM file is missing.\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 NULL; - } - firmware_changed = 1; // Decryption performed. - } else { - fprintf(stderr, " FIRM is decrypted\n"); - } - - struct firm_signature *fsig = get_firm_info(dest); - - fprintf(stderr, " FIRM: K9L%u, Console:%u, Type:%u\n", fsig->k9l, fsig->console, fsig->type); - - // The N3DS firm has an additional encryption layer for ARM9 - if (fsig->console == console_n3ds) { - // Look for the arm9 section - for (firm_section_h *section = dest->section; section < dest->section + 4; section++) { - if (section->type == FIRM_TYPE_ARM9) { - // Check whether the arm9bin is encrypted. - int arm9bin_iscrypt = 0; - 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_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 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->k9l >= 2) { - slot0x11key96_init(); // This has to be loaded - // regardless, otherwise boot will - // fail. - } - } - - // We assume there's only one section to decrypt. - break; - } - } - } - - // Save firmware.bin if decryption was done. - if (firmware_changed) { - fprintf(stderr, " Overwriting FIRM with decrypted FIRM\n"); - write_file(dest, path, *size); - } - - if (fsig->console == console_n3ds) { - fprintf(stderr, " Patching arm9 entrypoint\n"); - - // Patch the entrypoint to skip arm9loader - if (fsig->type == type_native) { - dest->a9Entry = 0x0801B01C; - } 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. - } - - fprintf(stderr, " Loaded.\n"); - - free(fsig); - - return dest; -} - __attribute__ ((noreturn)) void -boot_firm() +boot_firm(firm_h* firm) { - struct firm_signature *fsig = get_firm_info(firm_loc); - - fprintf(stderr, " FIRM: K9L%u, Console:%u, Type:%u\n", fsig->k9l, fsig->console, fsig->type); - - // 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->k9l >= 2) { - uint8_t *keydata = find_section_key(); - if (!keydata) { - abort("Couldn't find section key.\n"); - } - - wait(); - - use_aeskey(0x11); - uint8_t keyx[AES_BLOCK_SIZE]; - for (int slot = 0x19; slot < 0x20; slot++) { - ecb_decrypt(keydata, keyx, 1, AES_CNT_ECB_DECRYPT_MODE); - setup_aeskeyX(slot, keyx); - *(uint8_t *)(keydata + 0xF) += 1; - } - - fprintf(stderr, "Updated keyX keyslots.\n"); - } - - free(fsig); - -#ifdef MALLOC_DEBUG - print_alloc_stats(); - wait(); -#endif - - uint32_t entry11 = firm_loc->a11Entry; - uint32_t entry9 = firm_loc->a9Entry; - - // Beyond this point, using malloc() memory is unsafe, since we're trashing memory possibly. - // free() is also irrelevant from here on. - - for (firm_section_h *section = firm_loc->section; section < firm_loc->section + 4 && section->address != 0; section++) { - memmove((void *)section->address, (void *)((uint8_t*)firm_loc + section->offset), section->size); - } - fprintf(stderr, "Copied FIRM.\n"); - - wait(); - - clear_disp(stderr); - set_cursor(stderr, 0, 0); - - fflush(stderr); // Flush logs if need be before unmount. - - fumount(); // Unmount SD. No longer needed. - - // No fprintf will work from here on out. - - deinitScreens(); - - *a11_entry = (uint32_t)entry11; - - ((void (*)())entry9)(); - + abort("Temporarily not implemented\n"); while(1); } -int -find_proc9(firm_h *firm, firm_section_h *process9, exefs_h **p9exefs) -{ - for (firm_section_h *section = firm->section; section < firm->section + 4; section++) { - if (section->address == 0) - break; - - if (section->type == FIRM_TYPE_ARM9) { - uint8_t *arm9section = (uint8_t *)firm + section->offset; - while (arm9section < arm9section + section->size) { - if (!memcmp(arm9section, "Process9", 8)) { // Process9 - ncch_h *ncch = (ncch_h *)((uint8_t*)arm9section - sizeof(ncch_h)); - if (ncch->magic == NCCH_MAGIC) { - // Found Process9 - ncch_ex_h *p9exheader = (ncch_ex_h *)(ncch + 1); - *p9exefs = (exefs_h *)(p9exheader + 1); - process9->address = p9exheader->sci.textCodeSet.address; - process9->size = (*p9exefs)->fileHeaders[0].size; - process9->offset = (uint32_t)((*p9exefs) + 1) - (uint32_t)firm; - fprintf(stderr, " Found process9 offset\n"); - return 0; - } - } - ++arm9section; - } - } - } - fprintf(stderr, " Couldn't find Process9?\n"); - return 1; -} - int firm_loaded = 0; -int -load_firms() +firm_h* +load_firm(const char *path) { - int state = 0; - - if (firm_loaded) - return 0; - - 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) { - abort("\n Failed to load NATIVE_FIRM.\n"); - } - find_proc9(firm_loc, &firm_proc9, &firm_p9_exefs); - - 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) { - 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, "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) { - fprintf(stderr, "\n AGB_FIRM failed to load.\n"); - state = 1; - } else { - find_proc9(agb_firm_loc, &agb_firm_proc9, &agb_firm_p9_exefs); - } - - firm_loaded = 1; // Loaded. - - return state; + return NULL; } + void -boot_cfw() +boot_cfw(char* firm_path) { - load_firms(); + firm_h* firm = load_firm(firm_path); fprintf(stderr, "Patching firmware...\n"); - if (patch_firm_all() != 0) + if (patch_firm_all(firm) != 0) return; - if (get_opt_u32(OPTION_REBOOT)) { - fprintf(stderr, "Saving FIRM for reboot...\n"); - if (!write_file(firm_loc, PATH_NATIVE_P, firm_size)) - abort("Failed to save prepatched native\n"); - - if (!write_file(twl_firm_loc, PATH_TWL_P, twl_firm_size)) - abort("Failed to save prepatched twl\n"); - - if (!write_file(agb_firm_loc, PATH_AGB_P, agb_firm_size)) - abort("Failed to save prepatched agb\n"); - } - - boot_firm(); + boot_firm(firm); } diff --git a/source/firm/firmlaunch.c b/source/firm/firmlaunch.c new file mode 100644 index 0000000..a0acdcf --- /dev/null +++ b/source/firm/firmlaunch.c @@ -0,0 +1,29 @@ +#include +#include +#include + +typedef void (*void_call)(); + +static volatile uint32_t *const a11_entry = (volatile uint32_t *)0x1FFFFFF8; + +void firmlaunch(firm_h* firm) { + // Get entrypoints + uint32_t entry11 = firm->a11Entry; + void_call entry9 = (void_call)firm->a9Entry; + + // Copy sections from FIRMs to their destination. + for (firm_section_h *section = firm->section; section < firm->section + 4 && section->address != 0; section++) { + memmove((void *)section->address, (void *)((uint8_t*)firm + section->offset), section->size); + } + + fflush(stderr); // Flush logs if need be before unmount. + + fumount(); // Unmount SD. + + deinitScreens(); // Turn off display + + *a11_entry = (uint32_t)entry11; // Start kernel11 + + entry9(); // Start process9 +} + diff --git a/source/firm/keys.c b/source/firm/keys.c new file mode 100644 index 0000000..e7a441f --- /dev/null +++ b/source/firm/keys.c @@ -0,0 +1,190 @@ +#include + +#include +#include + +key_find_t Y11_sec; +key_find_t X11_sec; + +key_find_t Y11[] = { + #include "keys/Y11_95.gen" + #include "keys/Y11_96.gen" +}; + +key_find_t Y3D[] = { + #include "keys/Y3D_0.gen" + #include "keys/Y3D_1.gen" + #include "keys/Y3D_2.gen" + #include "keys/Y3D_3.gen" + #include "keys/Y3D_4.gen" + #include "keys/Y3D_5.gen" +}; + +key_find_t Y05 = +#include "keys/Y05.gen" +; + +#define ROLL_WINDOW AES_BLOCK_SIZE +#define MODULO_WINDOW (ROLL_WINDOW / 2) +uint8_t* slice_roll_search(uint8_t *mem, uint32_t size, key_find_t* find) { + uint16_t roll = 0; + uint32_t i = 0; + uint8_t hash[32]; + + // Initial window. + for(; i < ROLL_WINDOW; i++) { + roll += mem[i]; + } + + // Loop through, moving the window. + for(i = ROLL_WINDOW; i < size; i++) { + if (find->roll == roll) { + // Yes. Check hash. + sha256sum(hash, &mem[i], 0x10); + + if(!memcmp(find->sha, hash, 0x20)) { + return & mem[i]; + } + } + + roll -= mem[i-16]; + roll += mem[i]; + } + return NULL; +} + +int get_Y11_sec() { + // FIXME; this only handles the case of K9LH. Needs more sanity checks. + + uint8_t hash[32]; + + // We know better here than GCC. +#pragma GCC diagnostic push +#pragma GCC diagnostic warning "-Wdiscarded-qualifiers" + memcpy(hash, (volatile void*)REG_SHAHASH, 32); +#pragma GCC diagnostic pop + + memcpy(X11_sec.key, hash, 16); + memcpy(Y11_sec.key, hash + 16, 16); + + return 0; +} + +int get_Y11_K9L(firm_h *firm, int index) { + // A9LH corrupts this one. Can't do much here. + int level = 0; + + uint8_t key[AES_BLOCK_SIZE]; + FILE* f; + + // 9.5 key (K9L1) + f = fopen(PATH_SLOT0X11KEY95, "r"); + if (!f) { + level |= 1; + goto next; + } + + fread(Y11[0].key, 1, AES_BLOCK_SIZE, f); + fclose(f); + +next: + // 9.6 key (K9L2) + f = fopen(PATH_SLOT0X11KEY96, "r"); + if (!f) { + level |= 2; + goto end; + } + + fread(Y11[1].key, 1, AES_BLOCK_SIZE, f); + fclose(f); + +end: + return level; +} + +int get_Y3D(firm_h *firm, int index) { + uint8_t* key_loc = (uint8_t*)firm + firm->section[2].offset; // ARM9 segment + uint32_t search_size = firm->section[2].size; + + uint8_t mem[16] __attribute__((aligned(16))) = {0}; + + uint8_t* key_data = slice_roll_search(key_loc, search_size, & Y3D[0]); + + if (!key_data) + return 1; + + memcpy(mem, key_data, 16); + + return 0; +} + +int get_Y05(firm_h *firm) { + uint8_t* key_loc = (uint8_t*)firm + firm->section[2].offset; // ARM9 segment + uint32_t search_size = firm->section[2].size; + + uint8_t mem[16] __attribute__((aligned(16))) = {0}; + + uint8_t* key_data = slice_roll_search(key_loc, search_size, &Y05); + + if (!key_data) + return 1; + + fprintf(stderr, " 0x05 KeyY at %lx in FIRM1\n", (uint32_t)key_data - (uint32_t)key_loc); + + memcpy(mem, key_data, 16); + + return 0; +} + +int extract_keys() { + int level = 0; + + if (get_Y11_sec()) {// MUST be done first. Otherwise, sha register gets clobbered. + // At best, a warning. + level |= 1; + } + +#if 0 + if (get_Y11_K9L()) { // For decrypting K9L. + // Also a warning, but potentially fatal if an N3DS. + level |= 2; + } + + if (get_Y3D()) { + // No cetk decryption. + level |= 4; + } + + if (get_Y05()) { + // Pretty much a warning and nothing else atm. + level |= 8; + } +#endif + + return level; +} + +int set_Y3D_common(int commonKeyIndex) { + setup_aeskeyY(0x3D, (void*) Y3D[commonKeyIndex].key); + + use_aeskey(0x3D); + + return 0; +} + +int set_Y05() { + // N3DS nand key + setup_aeskeyY(0x05, Y05.key); + + use_aeskey(0x05); + + return 0; +} + +int set_Y11_K9L(int index) { + setup_aeskey(0x11, Y11[index].key); + + use_aeskey(0x11); + + return 0; +} diff --git a/source/firm/keys/Y05.gen b/source/firm/keys/Y05.gen new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/source/firm/keys/Y05.gen @@ -0,0 +1 @@ +{} diff --git a/source/firm/keys/Y11_95.gen b/source/firm/keys/Y11_95.gen new file mode 100644 index 0000000..174e1e6 --- /dev/null +++ b/source/firm/keys/Y11_95.gen @@ -0,0 +1 @@ +{}, diff --git a/source/firm/keys/Y11_96.gen b/source/firm/keys/Y11_96.gen new file mode 100644 index 0000000..9bba4bb --- /dev/null +++ b/source/firm/keys/Y11_96.gen @@ -0,0 +1 @@ +{.roll = 0x5D1D533A, .sha = {0x8B, 0xB9, 0x77, 0x76, 0x86, 0xBD, 0xCC, 0xFF, 0x30, 0xE9, 0x4D, 0xC6, 0x5F, 0x23, 0x43, 0xF7, 0x41, 0x2E, 0x3D, 0x6C, 0x19, 0x12, 0xE3, 0x18, 0xDA, 0x9F, 0x17, 0x35, 0x96, 0xB9, 0xE8, 0x98} } diff --git a/source/firm/keys/Y3D_0.gen b/source/firm/keys/Y3D_0.gen new file mode 100644 index 0000000..7753ffd --- /dev/null +++ b/source/firm/keys/Y3D_0.gen @@ -0,0 +1 @@ +{.roll = 0x9EFCCA41, .sha = {0x0A, 0x1C, 0x7B, 0x55, 0x86, 0x05, 0x89, 0xB0, 0xED, 0xD8, 0x87, 0x4B, 0x50, 0x55, 0xE3, 0x47, 0x16, 0xA2, 0xCD, 0xE2, 0x5B, 0xAD, 0x12, 0x48, 0xBB, 0xBB, 0xEE, 0xD1, 0xB3, 0x40, 0xB1, 0xB8} }, diff --git a/source/firm/keys/Y3D_1.gen b/source/firm/keys/Y3D_1.gen new file mode 100644 index 0000000..b170fd7 --- /dev/null +++ b/source/firm/keys/Y3D_1.gen @@ -0,0 +1 @@ +{.roll = 0x65D5C908, .sha = {0x21, 0x12, 0xF4, 0x50, 0x78, 0x6D, 0xCE, 0x64, 0x39, 0xFD, 0xB8, 0x71, 0x14, 0x74, 0x41, 0xF4, 0x69, 0xB6, 0xC4, 0x70, 0xA4, 0xB1, 0x5F, 0x7D, 0xFD, 0xE8, 0xCC, 0xE4, 0xC4, 0x62, 0x82, 0x5B} }, diff --git a/source/firm/keys/Y3D_2.gen b/source/firm/keys/Y3D_2.gen new file mode 100644 index 0000000..78e28ff --- /dev/null +++ b/source/firm/keys/Y3D_2.gen @@ -0,0 +1 @@ +{.roll = 0xC1F558D6, .sha = {0xF7, 0x12, 0x1A, 0xCA, 0x63, 0x61, 0xC0, 0x9C, 0x10, 0xBB, 0x62, 0x8D, 0x69, 0x85, 0x23, 0x08, 0xCB, 0x81, 0xDB, 0x22, 0x9E, 0xFD, 0xC1, 0xAB, 0xF5, 0x7B, 0xA3, 0x8E, 0xDA, 0x64, 0x56, 0x74} }, diff --git a/source/firm/keys/Y3D_3.gen b/source/firm/keys/Y3D_3.gen new file mode 100644 index 0000000..99c8698 --- /dev/null +++ b/source/firm/keys/Y3D_3.gen @@ -0,0 +1 @@ +{.roll = 0x67D738E, .sha = {0xD8, 0xCF, 0x95, 0x7D, 0x88, 0x46, 0x6C, 0x7C, 0x42, 0x50, 0x7C, 0xA5, 0x53, 0xD2, 0x37, 0x34, 0x65, 0x0E, 0x34, 0x32, 0x3A, 0x58, 0x80, 0x76, 0x7E, 0xB5, 0x3A, 0x07, 0xEB, 0x5E, 0x00, 0xFD} }, diff --git a/source/firm/keys/Y3D_4.gen b/source/firm/keys/Y3D_4.gen new file mode 100644 index 0000000..ae21926 --- /dev/null +++ b/source/firm/keys/Y3D_4.gen @@ -0,0 +1 @@ +{.roll = 0xCC1BE763, .sha = {0x75, 0x71, 0x64, 0x46, 0x3B, 0xDA, 0xEC, 0x71, 0x57, 0x95, 0x85, 0x17, 0xDF, 0x9B, 0x1D, 0xC7, 0xF3, 0x6A, 0x87, 0x22, 0x09, 0x70, 0x60, 0xD9, 0x48, 0xCC, 0x01, 0xFF, 0x72, 0x0F, 0xEE, 0x56} }, diff --git a/source/firm/keys/Y3D_5.gen b/source/firm/keys/Y3D_5.gen new file mode 100644 index 0000000..3999b71 --- /dev/null +++ b/source/firm/keys/Y3D_5.gen @@ -0,0 +1 @@ +{.roll = 0xC7A98491, .sha = {0xA4, 0x5B, 0x06, 0xC3, 0x37, 0xB7, 0x51, 0x6D, 0xF7, 0xA7, 0xCD, 0x87, 0xC2, 0x1D, 0x5F, 0xFC, 0x22, 0xA4, 0xAA, 0xB6, 0x48, 0x10, 0x2B, 0x98, 0x7E, 0x00, 0xD5, 0xC2, 0x48, 0x39, 0x6C, 0xF8} } diff --git a/source/firm/util.c b/source/firm/util.c new file mode 100644 index 0000000..c94bb0f --- /dev/null +++ b/source/firm/util.c @@ -0,0 +1,128 @@ +#include +#include + +#include +#include +#include +#include + +#define SECTOR_SIZE 0x200 + +int decrypt_k9l(arm9bin_h *header, enum firm_type type) { + uint8_t slot = 0x15; + + if (type == type_native) { + uint8_t decrypted_keyx[AES_BLOCK_SIZE]; + + slot = 0x16; + + use_aeskey(0x11); + ecb_decrypt(header->slot0x16keyX, decrypted_keyx, 1, AES_CNT_ECB_DECRYPT_MODE); + setup_aeskeyX(slot, decrypted_keyx); + } + + setup_aeskeyY(slot, header->keyy); + + set_ctr(header->ctr); + + void *arm9bin = (uint8_t *)header + 0x800; + int size = atoi(header->size); // Size is plaintext, don't ask me *shrug* + + use_aeskey(slot); + + ctr_decrypt(arm9bin, arm9bin, (size_t)size / AES_BLOCK_SIZE, AES_CNT_CTRNAND_MODE, header->ctr); + + if (type == type_native && *(uint32_t *)arm9bin == ARM9BIN_MAGIC) + return 0; + else if (*(uint32_t *)arm9bin == LGY_ARM9BIN_MAGIC) + return 0; + + return 1; // Failed. +} + +void fix_entry(firm_h* firm, enum firm_type type) { + // Patch the entrypoint to skip arm9loader + if (type == type_native) + firm->a9Entry = 0x0801B01C; + else + firm->a9Entry = 0x0801301C; + + // The entrypoints seem to be the same across different FIRM versions, + // so we don't change them. +} + +// 0x0B130000 = start of FIRM0 partition, 0x400000 = size of FIRM partition (4MB) +firm_h* dump_firm(firm_h* buffer, uint8_t index) { + firm_h* firm; + + // NOTE - Cast, because GCC is making assumptions about 'index'. + uint32_t firm_offset = (uint32_t)(0x0B130000 + (index % 2) * 0x400000), + firm_b_size = 0x00100000; // 1MB, because + + firm = malloc(firm_b_size); + + uint8_t ctr[0x10], + cid[0x10], + sha_t[0x20]; + + if (sdmmc_nand_readsectors(firm_offset / SECTOR_SIZE, firm_b_size / SECTOR_SIZE, (uint8_t*)firm)) + goto failure; + + sdmmc_get_cid(1, (uint32_t*)cid); + sha256sum(sha_t, cid, 0x10); + memcpy(ctr, sha_t, 0x10); + add_ctr(ctr, firm_offset / AES_BLOCK_SIZE); + + use_aeskey(0x06); + set_ctr(ctr); + ctr_decrypt(firm, firm, firm_b_size / AES_BLOCK_SIZE, AES_CNT_CTRNAND_MODE, ctr); + + if (memcmp((char*) & firm->magic, "FIRM", 4)) + goto failure; + + return firm; + +failure: + free(firm); + + return NULL; +} + +uint8_t* find_section_key(firm_h *firm_loc) { + // The key will be dword-aligned (I think? Verify this. May need new NFIRM to check assumption. Go, Nintendo!) +#if 0 + // The hash of the key. Can't give the key itself out, obviously. + uint8_t sha256[] = {0xb9, 0x4d, 0xb1, 0xb1, 0xc3, 0xe0, 0x11, 0x08, 0x9c, 0x19, 0x46, 0x06, 0x4a, 0xbc, 0x40, 0x2a, + 0x7c, 0x66, 0xf4, 0x4a, 0x74, 0x6f, 0x71, 0x50, 0x32, 0xfd, 0xff, 0x03, 0x74, 0xd7, 0x45, 0x2c}; + + uint8_t* key_loc = (uint8_t*)firm_loc + firm_loc->section[2].offset; + uint32_t search_size = firm_loc->section[2].size; + + uint8_t* key_data = key_search(key_loc, search_size, sha256, 0xDD); + + if (!key_data) + abort(" FIRM Section key not found!\n"); + + fprintf(stderr, " FIRM Section key at %lx in FIRM\n", (uint32_t)key_data - (uint32_t)key_loc); + return key_data; +#endif + return NULL; +} + +int set_section_keys(firm_h* firm_loc) { + // Set up the keys needed to boot a few firmwares, due to them being unset, + // depending on which firmware you're booting from. + uint8_t *keydata = find_section_key(firm_loc); + if (!keydata) + return 1; + + use_aeskey(0x11); + uint8_t keyx[AES_BLOCK_SIZE]; + for (int slot = 0x19; slot < 0x20; slot++) { + ecb_decrypt(keydata, keyx, 1, AES_CNT_ECB_DECRYPT_MODE); + setup_aeskeyX(slot, keyx); + *(uint8_t *)(keydata + 0xF) += 1; + } + + return 0; +} diff --git a/source/interpreter.c b/source/interpreter.c index ae38927..4d1f5dc 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -78,6 +78,7 @@ int is_n3ds = 1; // TODO - We don't really need to care, but it should still wor static uint8_t stack_glob[STACK_SIZE]; #else static uint8_t *stack_glob = NULL; + firm_h* firm_patch; #endif int @@ -85,66 +86,28 @@ exec_bytecode(uint8_t *bytecode, uint32_t len, uint8_t* stack, uint32_t stack_si { if (!init_bytecode) { #ifndef LOADER - modes[0].memory = (uint8_t *)firm_loc; - modes[0].size = firm_loc->section[0].size + firm_loc->section[1].size + sizeof(firm_h) + - firm_loc->section[2].size + firm_loc->section[3].size; // NATIVE_FIRM - - modes[1].memory = (uint8_t *)agb_firm_loc; - modes[1].size = agb_firm_loc->section[0].size + agb_firm_loc->section[1].size + sizeof(firm_h) + - agb_firm_loc->section[2].size + agb_firm_loc->section[3].size; // AGB_FIRM - - modes[2].memory = (uint8_t *)twl_firm_loc; - modes[2].size = twl_firm_loc->section[0].size + twl_firm_loc->section[1].size + sizeof(firm_h) + - twl_firm_loc->section[2].size + twl_firm_loc->section[3].size; // TWL_FIRM - - // NATIVE_FIRM Process9 (This is also the default mode.) - modes[3].memory = (uint8_t *)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset; - modes[3].size = firm_p9_exefs->fileHeaders[0].size; - // AGB_FIRM Process9 - modes[4].memory = (uint8_t *)agb_firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset; - modes[4].size = firm_p9_exefs->fileHeaders[0].size; - // TWL_FIRM Process9 - modes[5].memory = (uint8_t *)twl_firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset; - modes[5].size = firm_p9_exefs->fileHeaders[0].size; - - // NATIVE_FIRM Sect 0 - modes[6].memory = (uint8_t *)firm_loc + firm_loc->section[0].offset; - modes[6].size = firm_loc->section[0].size; - // NATIVE_FIRM Sect 1 - modes[7].memory = (uint8_t *)firm_loc + firm_loc->section[1].offset; - modes[7].size = firm_loc->section[1].size; - // NATIVE_FIRM Sect 2 - modes[8].memory = (uint8_t *)firm_loc + firm_loc->section[2].offset; - modes[8].size = firm_loc->section[2].size; - // NATIVE_FIRM Sect 3 - modes[9].memory = (uint8_t *)firm_loc + firm_loc->section[3].offset; - modes[9].size = firm_loc->section[3].size; - - // AGB_FIRM Sect 0 - modes[10].memory = (uint8_t *)agb_firm_loc + agb_firm_loc->section[0].offset; - modes[10].size = agb_firm_loc->section[0].size; - // AGB_FIRM Sect 1 - modes[11].memory = (uint8_t *)agb_firm_loc + agb_firm_loc->section[1].offset; - modes[11].size = agb_firm_loc->section[1].size; - // AGB_FIRM Sect 2 - modes[12].memory = (uint8_t *)agb_firm_loc + agb_firm_loc->section[2].offset; - modes[12].size = agb_firm_loc->section[2].size; - // AGB_FIRM Sect 3 - modes[13].memory = (uint8_t *)agb_firm_loc + agb_firm_loc->section[3].offset; - modes[13].size = agb_firm_loc->section[3].size; - - // TWL_FIRM Sect 0 - modes[14].memory = (uint8_t *)twl_firm_loc + twl_firm_loc->section[0].offset; - modes[14].size = twl_firm_loc->section[0].size; - // TWL_FIRM Sect 1 - modes[15].memory = (uint8_t *)twl_firm_loc + twl_firm_loc->section[1].offset; - modes[15].size = twl_firm_loc->section[1].size; - // TWL_FIRM Sect 2 - modes[16].memory = (uint8_t *)twl_firm_loc + twl_firm_loc->section[2].offset; - modes[16].size = twl_firm_loc->section[2].size; - // TWL_FIRM Sect 3 - modes[17].memory = (uint8_t *)twl_firm_loc + twl_firm_loc->section[3].offset; - modes[17].size = twl_firm_loc->section[3].size; + modes[0].memory = (uint8_t *)firm_patch; + modes[0].size = firm_patch->section[0].size + firm_patch->section[1].size + sizeof(firm_h) + + firm_patch->section[2].size + firm_patch->section[3].size; // NATIVE_FIRM + +#if 0 + // FIRM Process9 (This is also the default mode.) + modes[1].memory = (uint8_t *)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset; + modes[1].size = firm_p9_exefs->fileHeaders[0].size; +#endif + + // FIRM Sect 0 + modes[2].memory = (uint8_t *)firm_patch + firm_patch->section[0].offset; + modes[2].size = firm_patch->section[0].size; + // FIRM Sect 1 + modes[3].memory = (uint8_t *)firm_patch + firm_patch->section[1].offset; + modes[3].size = firm_patch->section[1].size; + // FIRM Sect 2 + modes[4].memory = (uint8_t *)firm_patch + firm_patch->section[2].offset; + modes[4].size = firm_patch->section[2].size; + // FIRM Sect 3 + modes[5].memory = (uint8_t *)firm_patch + firm_patch->section[3].offset; + modes[5].size = firm_patch->section[3].size; #endif init_bytecode = 1; diff --git a/source/patch/emunand.c b/source/patch/emunand.c index c5e92ab..0659163 100644 --- a/source/patch/emunand.c +++ b/source/patch/emunand.c @@ -114,8 +114,9 @@ patchMPU(uint8_t *pos, uint32_t size) } void -patch_emunand(uint32_t index) +patch_emunand(firm_h* firm_loc, uint32_t index) { +#if 0 // ARM9 section. uint8_t *arm9Section = (uint8_t *)firm_loc + firm_loc->section[2].offset; uint32_t arm9SectionSize = firm_loc->section[2].size; @@ -166,4 +167,5 @@ patch_emunand(uint32_t index) patchMPU(arm9Section, arm9SectionSize); fprintf(stderr, "emunand: patched MPU settings\n"); +#endif } diff --git a/source/patch/module.c b/source/patch/module.c index 1120168..82a76f6 100644 --- a/source/patch/module.c +++ b/source/patch/module.c @@ -41,8 +41,9 @@ inject_module(char* fpath) // FIXME - We're potentially corrupting memory here depending on whether we go over the theoretical maximum size of FIRM + // SUPER FIXME 9000: DO NOT LEAVE AS IS. memmove((uint8_t *)sysmodule + module->contentSize * 0x200, (uint8_t *)sysmodule + sysmodule->contentSize * 0x200, - ((uint32_t)firm_modules + firm_size) - ((uint32_t)sysmodule + (module->contentSize * 0x200))); + ((uint32_t)firm_modules + 0x100000) - ((uint32_t)sysmodule + (module->contentSize * 0x200))); sysmodule_section->size += 0x200 * need_units; for (int i = 1; i < 4; i++) { @@ -86,14 +87,10 @@ end_inj: } int -patch_modules() +patch_modules(firm_h* firm_loc) { firm_modules = firm_loc; recurse_call(PATH_MODULE_NATIVE, inject_module); - firm_modules = twl_firm_loc; - recurse_call(PATH_MODULE_TWL, inject_module); - firm_modules = agb_firm_loc; - recurse_call(PATH_MODULE_AGB, inject_module); return 0; } diff --git a/source/patch/reboot.c b/source/patch/reboot.c index d50237f..f448056 100644 --- a/source/patch/reboot.c +++ b/source/patch/reboot.c @@ -15,7 +15,7 @@ getProcess9(uint8_t *pos, uint32_t size, uint32_t *process9Size, uint32_t *proce } void -patch_reboot() +patch_reboot(firm_h* firm_loc) { // Look for firmlaunch code const uint8_t pattern[] = { 0xDE, 0x1F, 0x8D, 0xE2 }; diff --git a/source/patch/svc.c b/source/patch/svc.c index 5d40e7c..48f13f5 100644 --- a/source/patch/svc.c +++ b/source/patch/svc.c @@ -7,7 +7,7 @@ int svc_offs_init = 0; // This code handles restoration of backdoor int -patch_services() +patch_svc_calls(firm_h* firm_loc) { if (svc_offs_init == 0) { arm11Section1 = (uint8_t *)firm_loc + firm_loc->section[1].offset; diff --git a/source/patcher.c b/source/patcher.c index 50a39f5..f5992a6 100644 --- a/source/patcher.c +++ b/source/patcher.c @@ -3,9 +3,9 @@ // TODO - Basically all this needs to move to patcher programs. -extern int patch_services(); -extern int patch_modules(); -extern int patch_reboot(); +extern int patch_svc_calls(firm_h*); +extern int patch_modules(firm_h*); +extern int patch_reboot(firm_h*); extern int doing_autoboot; @@ -51,7 +51,7 @@ generate_patch_cache() } int -patch_firm_all() +patch_firm_all(firm_h* firm) { execb(PATH_LOADER_CACHE "/BOOT", 0); @@ -59,7 +59,7 @@ patch_firm_all() // Hook firmlaunch? if (get_opt_u32(OPTION_REBOOT)) { - patch_reboot(); + patch_reboot(firm); wait(); } @@ -67,14 +67,14 @@ patch_firm_all() // Use EmuNAND? if (get_opt_u32(OPTION_EMUNAND)) { // Yes. - patch_emunand(get_opt_u32(OPTION_EMUNAND_INDEX)); + patch_emunand(firm, get_opt_u32(OPTION_EMUNAND_INDEX)); wait(); } // Inject services? if (get_opt_u32(OPTION_SVCS)) { - if (patch_services()) { + if (patch_svc_calls(firm)) { abort("Fatal. Svc inject has failed."); } wait(); @@ -82,7 +82,7 @@ patch_firm_all() // Replace loader? if (get_opt_u32(OPTION_LOADER)) { - if (patch_modules()) { + if (patch_modules(firm)) { abort("Fatal. Loader inject has failed."); } // This requires OPTION_SIGPATCH. diff --git a/source/std/memory.c b/source/std/memory.c index c5d1ddc..5a039b0 100644 --- a/source/std/memory.c +++ b/source/std/memory.c @@ -56,3 +56,4 @@ memfind(uint8_t *startPos, uint32_t size, const void *pattern, uint32_t patternS return NULL; } +