chmod +x out/generate_localeemu.sh
echo "#!/bin/bash" > out/o3ds_firm.sh
echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013800000002/00000052 -O .@libdir@/firmware/native" >> out/o3ds_firm.sh
- echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013800000002/cetk -O .@datarootdir@/keys/native.cetk" >> out/o3ds_firm.sh
+ echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013800000002/cetk -O .@libdir@/firmware/native.cetk" >> out/o3ds_firm.sh
echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013800000102/00000016 -O .@libdir@/firmware/twl" >> out/o3ds_firm.sh
- echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013800000102/cetk -O .@datarootdir@/keys/twl.cetk" >> out/o3ds_firm.sh
+ echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013800000102/cetk -O .@libdir@/firmware/twl.cetk" >> out/o3ds_firm.sh
echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013800000202/0000000B -O .@libdir@/firmware/agb" >> out/o3ds_firm.sh
- echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013800000202/cetk -O .@datarootdir@/keys/agb.cetk" >> out/o3ds_firm.sh
+ echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013800000202/cetk -O .@libdir@/firmware/agb.cetk" >> out/o3ds_firm.sh
echo "#!/bin/bash" > out/n3ds_firm.sh
echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013820000002/00000021 -O .@libdir@/firmware/native" >> out/n3ds_firm.sh
- echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013820000002/cetk -O .@datarootdir@/keys/native.cetk" >> out/n3ds_firm.sh
+ echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013820000002/cetk -O .@libdir@/firmware/native.cetk" >> out/n3ds_firm.sh
echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013820000102/00000000 -O .@libdir@/firmware/twl" >> out/n3ds_firm.sh
- echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013820000102/cetk -O .@datarootdir@/keys/twl.cetk" >> out/n3ds_firm.sh
+ echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013820000102/cetk -O .@libdir@/firmware/twl.cetk" >> out/n3ds_firm.sh
echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013820000202/00000000 -O .@libdir@/firmware/agb" >> out/n3ds_firm.sh
- echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013820000202/cetk -O .@datarootdir@/keys/agb.cetk" >> out/n3ds_firm.sh
+ echo "wget http://nus.cdn.c.shop.nintendowifi.net/ccs/download/0004013820000202/cetk -O .@libdir@/firmware/agb.cetk" >> out/n3ds_firm.sh
chmod 755 out/*.sh
cp README.md LICENSE.txt out/
-fno-builtin -std=gnu11 -DREVISION=\"$(REVISION)\" \
-DFW_NAME=\"corbenik\" $(PATHARGS) -DMALLOC_DEBUG=1
+# -fsanitize=undefined
-AM_LDFLAGS=-Wl,--use-blx,--pic-veneer,-q -nostdlib -nodefaultlibs -Wl,-z,defs -lgcc \
- -lc -L$(top_srcdir)/external/libctr9/src
+AM_LDFLAGS=-Wl,--use-blx,--pic-veneer,-q -Wl,-z,defs \
+ -L$(top_srcdir)/external/libctr9/src
OCFLAGS=--set-section-flags .bss=alloc,load,contents
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:
+++ /dev/null
-#!/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 ""
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <openssl/sha.h>
-
-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 <key>\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;
-}
*/
struct firm_signature *get_firm_info(firm_h *firm);
-/* Boots native FIRM - do not call directly.
+/* Boots the CFW, generating caches and applying patches as-needed to the specified FIRM
*/
-void boot_firm();
-
-/* Boots the CFW, generating caches and applying patches as-needed
- */
-void boot_cfw();
+int boot_cfw(char *firm_path);
/* Loads a firmware off disk, returning it. The memory should be free()'d when done, unless you plan to boot.
*/
typedef struct firm_h
{
- uint32_t magic; // FIRM
- uint32_t reserved1;
+ char magic[8]; // "FIRM" normally, but D9 has this as "DECFIRM"
uint32_t a11Entry; // ARM11 entry
uint32_t a9Entry; // ARM9 entry
uint8_t reserved2[0x30];
char size[8];
uint8_t pad[8];
uint8_t ctl_block[0x10];
- uint8_t unk[0x10];
+ uint8_t unk[0x10]; // 3dbrew: Added with 9.5.0-X. Only used for hardware debugging: a nop instruction is executed with r0=0 and r1=<address of this data>.
uint8_t slot0x16keyX[0x10];
} arm9bin_h;
--- /dev/null
+#ifdef FIRM_INTERNAL_CODE
+
+#ifndef __FIRM_INTENAL
+#define __FIRM_INTERNAL
+
+#define SECTOR_SIZE 0x200
+
+#define ROLL_WINDOW AES_BLOCK_SIZE
+#define MODULO_WINDOW (ROLL_WINDOW / 2)
+
+typedef void (*void_call)();
+
+void firmlaunch(firm_h* firm);
+uint8_t* slice_roll_search(uint8_t *mem, uint32_t size, key_find_t* find);
+
+int set_Y3D_cetk(uint32_t commonKeyIndex);
+int set_N11_K9L(uint32_t index);
+
+int decrypt_k9l(arm9bin_h *header, enum firm_type type, uint32_t k9l);
+
+int patch_entry(firm_h *firm, enum firm_type type);
+
+void* find_section_key(firm_h *firm_loc);
+int patch_section_keys(firm_h* firm_loc, uint32_t k9l);
+
+int dec_k9l(firm_h* firm);
+
+uint8_t* get_titlekey(char *cetk_filename);
+
+//int decrypt_ncch(ncch_h *ncch, uint8_t* titlekey, size_t size);
+//exefs_h *get_exefs(ncch_h *ncch);
+//int decrypt_exefs(ncch_h *ncch);
+
+firm_h *extract_firm_from_ncch(ncch_h *ncch, uint8_t* titlekey, size_t size);
+
+#endif
+#else
+ #error "Do not include internal headers directly."
+#endif
* \param ... Format operands, see printf manpage
*/
-void abort(const char* x, ...) __attribute__ ((format (printf, 1, 2)));
+void panic(const char* x, ...) __attribute__ ((format (printf, 1, 2)));
#endif
*/
char* strdup_self(const char* str);
+char* strdupcat(const char* str, const char *cat);
+
#endif
FILE* f = fopen(code_file, "r");
if (!f) {
// File missing.
- abort("Missing chainloader.\n");
+ panic("Missing chainloader.\n");
}
b_size = fsize(f);
f = fopen(chain_file, "r");
if (!f) {
// File missing.
- abort("Missing program to chainload?\n");
+ panic("Missing program to chainload?\n");
}
size = fsize(f);
#include <ctr9/aes.h>
#include <ctr9/sha.h>
+#define FIRM_INTERNAL_CODE
+#include <firm/internal.h>
+
void
ncch_getctr(const ncch_h *ncch, uint8_t *ctr, uint8_t type)
{
#include <ctr9/sha.h>
#include <common.h>
-__attribute__ ((noreturn))
-void
-boot_firm(firm_h* firm)
-{
- abort("Temporarily not implemented\n");
- while(1);
-}
-
-int firm_loaded = 0;
+#define FIRM_INTERNAL_CODE
+#include <firm/internal.h>
firm_h*
load_firm(const char *path)
{
- return NULL;
+ int success = 0;
+
+ FILE *firm_file = fopen(path, "r");
+ if (!firm_file) {
+ return NULL;
+ }
+
+ size_t size = fsize(firm_file);
+
+ uint8_t* mem = malloc(size);
+
+ firm_h *firm = (firm_h*)mem;
+
+ fread(mem, 1, size, firm_file);
+
+ fclose(firm_file);
+
+ if (!memcmp(firm->magic, "DECFIRM", 7)) {
+ // Fully decrypted FIRM, courtesy D9. Fix the entrypoint and we're good.
+ firm = (firm_h*)mem;
+
+ struct firm_signature* sig = get_firm_info(firm);
+
+ patch_entry(firm, sig->type);
+ if (patch_section_keys(firm, sig->k9l)) {
+ free(mem);
+ return NULL;
+ }
+
+ free(sig);
+ } else if (!memcmp(firm->magic, "FIRM", 4)) {
+ // O3DS fully decrypted FIRM
+
+ firm = (firm_h*)mem;
+ } else {
+ // Encrypted.
+ char *cetk_path = strdupcat(path, ".cetk");
+
+ uint8_t* firmkey = get_titlekey(cetk_path);
+
+ free(cetk_path);
+
+ if (firmkey) {
+ firm = extract_firm_from_ncch((ncch_h*)mem, firmkey, size);
+
+ if (firm) {
+ struct firm_signature* sig = get_firm_info(firm);
+
+ if (sig->console == console_n3ds) {
+ if(dec_k9l(firm)) {
+ panic("K9L?");
+ free(firm);
+ free(mem);
+ return NULL;
+ }
+
+ patch_entry(firm, sig->type);
+
+ if (patch_section_keys(firm, sig->k9l)) {
+ panic("Section keys?");
+ free(firm);
+ free(mem);
+ return NULL;
+ }
+ }
+
+ free(sig);
+ }
+ }
+ free(mem);
+ }
+
+ return firm;
}
-void
+int
boot_cfw(char* firm_path)
{
firm_h* firm = load_firm(firm_path);
- fprintf(stderr, "Patching firmware...\n");
- if (patch_firm_all(firm) != 0)
- return;
+ if (firm == NULL)
+ panic("Invalid FIRM?");
+
+// if (patch_firm_all(firm) != 0)
+// return 1;
+
+ firmlaunch(firm);
- boot_firm(firm);
+ return 0;
}
#include <ctr9/aes.h>
#include <ctr9/sha.h>
-typedef void (*void_call)();
+#define FIRM_INTERNAL_CODE
+#include <firm/internal.h>
static volatile uint32_t *const a11_entry = (volatile uint32_t *)0x1FFFFFF8;
#include <ctr9/aes.h>
#include <ctr9/sha.h>
-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];
- }
+#define FIRM_INTERNAL_CODE
+#include <firm/internal.h>
+
+int set_N11_K9L(uint32_t index) {
+ static uint8_t ss_keyn[2][16] = {
+ { 0 }, // 9.5
+ { 0 }, // 9.6+
+ };
+
+ if (ss_keyn[0][0] == 0) {
+ ss_keyn[0][0] = 1;
+ FILE* key = fopen(PATH_SLOT0X11KEY95, "r");
+ if (key) {
+ fread(ss_keyn[0], 1, AES_BLOCK_SIZE, key);
+ fclose(key);
}
-
- 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 (ss_keyn[1][0] == 0) {
+ ss_keyn[1][0] = 1;
+ FILE* key = fopen(PATH_SLOT0X11KEY96, "r");
+ if (key) {
+ fread(ss_keyn[1], 1, AES_BLOCK_SIZE, key);
+ fclose(key);
+ }
}
- if (get_Y05()) {
- // Pretty much a warning and nothing else atm.
- level |= 8;
- }
-#endif
+ setup_aeskey(0x11, (void*) ss_keyn[index]);
- return level;
-}
-
-int set_Y3D_common(int commonKeyIndex) {
- setup_aeskeyY(0x3D, (void*) Y3D[commonKeyIndex].key);
-
- use_aeskey(0x3D);
+ use_aeskey(0x11);
return 0;
}
-int set_Y05() {
- // N3DS nand key
- setup_aeskeyY(0x05, Y05.key);
-
- use_aeskey(0x05);
+int set_Y3D_cetk(uint32_t index) {
+ // From https://github.com/profi200/Project_CTR/blob/master/makerom/pki/prod.h#L19
+ static uint8_t common_keyy[6][16] = {
+ {0xD0, 0x7B, 0x33, 0x7F, 0x9C, 0xA4, 0x38, 0x59, 0x32, 0xA2, 0xE2, 0x57, 0x23, 0x23, 0x2E, 0xB9} , // 0 - eShop Titles
+ {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C} , // 1 - System Titles
+ {0xC4, 0x75, 0xCB, 0x3A, 0xB8, 0xC7, 0x88, 0xBB, 0x57, 0x5E, 0x12, 0xA1, 0x09, 0x07, 0xB8, 0xA4} , // 2
+ {0xE4, 0x86, 0xEE, 0xE3, 0xD0, 0xC0, 0x9C, 0x90, 0x2F, 0x66, 0x86, 0xD4, 0xC0, 0x6F, 0x64, 0x9F} , // 3
+ {0xED, 0x31, 0xBA, 0x9C, 0x04, 0xB0, 0x67, 0x50, 0x6C, 0x44, 0x97, 0xA3, 0x5B, 0x78, 0x04, 0xFC} , // 4
+ {0x5E, 0x66, 0x99, 0x8A, 0xB4, 0xE8, 0x93, 0x16, 0x06, 0x85, 0x0F, 0xD7, 0xA1, 0x6D, 0xD7, 0x55} , // 5
+ };
- return 0;
-}
+ setup_aeskeyY(0x3D, (void*) common_keyy[index]);
-int set_Y11_K9L(int index) {
- setup_aeskey(0x11, Y11[index].key);
-
- use_aeskey(0x11);
+ use_aeskey(0x3D);
return 0;
}
+++ /dev/null
-{.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} }
+++ /dev/null
-{.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} },
+++ /dev/null
-{.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} },
+++ /dev/null
-{.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} },
+++ /dev/null
-{.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} },
+++ /dev/null
-{.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} },
+++ /dev/null
-{.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} }
#include <ctr9/sha.h>
#include <common.h>
-#define SECTOR_SIZE 0x200
+#define FIRM_INTERNAL_CODE
+#include <firm/internal.h>
-int decrypt_k9l(arm9bin_h *header, enum firm_type type) {
+int decrypt_k9l(arm9bin_h *header, enum firm_type type, uint32_t k9l) {
uint8_t slot = 0x15;
- if (type == type_native) {
+ if (type == type_native && k9l) {
uint8_t decrypted_keyx[AES_BLOCK_SIZE];
slot = 0x16;
- use_aeskey(0x11);
+ set_N11_K9L(k9l - 1);
+
ecb_decrypt(header->slot0x16keyX, decrypted_keyx, 1, AES_CNT_ECB_DECRYPT_MODE);
setup_aeskeyX(slot, decrypted_keyx);
}
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;
+ if (type == type_native)
+ return *(uint32_t *)arm9bin != ARM9BIN_MAGIC;
- return 1; // Failed.
+ return *(uint32_t *)arm9bin != LGY_ARM9BIN_MAGIC;
}
-void fix_entry(firm_h* firm, enum firm_type type) {
- // Patch the entrypoint to skip arm9loader
+int patch_entry(firm_h *firm, enum firm_type type) {
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.
+ return 0;
}
-// 0x0B130000 = start of FIRM0 partition, 0x400000 = size of FIRM partition (4MB)
-firm_h* dump_firm(firm_h* buffer, uint8_t index) {
- firm_h* firm;
+uint8_t*
+get_titlekey(char *cetk_filename)
+{
+ FILE* f = fopen(cetk_filename, "r");
+ size_t size = fsize(f);
+
+ uint8_t* cetk = malloc(size);
+
+ fread(cetk, 1, size, f);
- // 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
+ fclose(f);
- firm = malloc(firm_b_size);
+ uint8_t iv[AES_BLOCK_SIZE] = { 0 };
+ uint32_t sigtype = __builtin_bswap32(*(const uint32_t *)cetk);
- uint8_t ctr[0x10],
- cid[0x10],
- sha_t[0x20];
+ if (sigtype != SIG_TYPE_RSA2048_SHA256) {
+ free(cetk);
+ return NULL;
+ }
- if (sdmmc_nand_readsectors(firm_offset / SECTOR_SIZE, firm_b_size / SECTOR_SIZE, (uint8_t*)firm))
- goto failure;
+ const ticket_h *ticket = (const ticket_h *)((const uint8_t*)cetk + sizeof(sigtype) + 0x13C);
- 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);
+ set_Y3D_cetk(1);
- use_aeskey(0x06);
- set_ctr(ctr);
- ctr_decrypt(firm, firm, firm_b_size / AES_BLOCK_SIZE, AES_CNT_CTRNAND_MODE, ctr);
+ uint8_t *key = malloc(AES_BLOCK_SIZE);
- if (memcmp((char*) & firm->magic, "FIRM", 4))
- goto failure;
+ memcpy(iv, ticket->titleID, sizeof(ticket->titleID));
+ memcpy(key, ticket->titleKey, sizeof(ticket->titleKey));
- return firm;
+ cbc_decrypt(key, key, 1, AES_CNT_TITLEKEY_DECRYPT_MODE, iv);
-failure:
- free(firm);
+ free(cetk);
- return NULL;
+ return key;
}
-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};
+int dec_k9l(firm_h* firm) {
+ firm_section_h *arm9 = NULL;
+ for (firm_section_h* section = firm->section; section < firm->section + 4; section++) {
+ if (section->type == FIRM_TYPE_ARM9) { // ARM9
+ arm9 = section;
+ break;
+ }
+ }
- uint8_t* key_loc = (uint8_t*)firm_loc + firm_loc->section[2].offset;
- uint32_t search_size = firm_loc->section[2].size;
+ if (arm9 == NULL)
+ return 1;
- uint8_t* key_data = key_search(key_loc, search_size, sha256, 0xDD);
+ struct firm_signature* sig = get_firm_info(firm);
+
+ if (decrypt_k9l((arm9bin_h*)((uint8_t*)firm + arm9->offset), sig->type, sig->k9l))
+ return 1;
- if (!key_data)
- abort(" FIRM Section key not found!\n");
+ // Recalc section hash.
+ sha256sum(arm9->hash, (uint8_t*)firm + arm9->offset, arm9->size);
- fprintf(stderr, " FIRM Section key at %lx in FIRM\n", (uint32_t)key_data - (uint32_t)key_loc);
- return key_data;
-#endif
- return NULL;
+ // Magic like D9.
+ memcpy(firm->magic, "DECFIRM", 7);
+
+ free(sig);
+
+ return 0;
}
-int set_section_keys(firm_h* firm_loc) {
+firm_h*
+extract_firm_from_ncch(ncch_h *ncch, uint8_t *titlekey, size_t size)
+{
+ uint8_t firm_iv[16] = { 0 };
+ uint8_t exefs_key[16] = { 0 };
+ uint8_t exefs_iv[16] = { 0 };
+
+ setup_aeskey(0x16, titlekey);
+ use_aeskey(0x16);
+ cbc_decrypt(ncch, ncch, size / AES_BLOCK_SIZE, AES_CNT_CBC_DECRYPT_MODE, firm_iv);
+
+ if (ncch->magic != NCCH_MAGIC) {
+ return NULL;
+ }
+
+ 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;
+
+ 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 (memcmp(firm->magic, "FIRM", 4)) {
+ return NULL;
+ }
+
+ firm_h* dest = malloc(size);
+
+ memcpy(dest, firm, size);
+
+ return dest;
+}
+
+uint8_t* key_search(uint8_t* mem, uint32_t size, uint8_t* sha256, uint8_t byte) {
+ uint8_t hash[0x20] = {0};
+ // Search 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* find_section_key(firm_h* firm_loc) {
+ 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)
+ return NULL;
+
+ return key_data;
+}
+
+int patch_section_keys(firm_h* firm_loc, uint32_t k9l) {
// 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);
+ set_N11_K9L(k9l - 1);
+
uint8_t keyx[AES_BLOCK_SIZE];
for (int slot = 0x19; slot < 0x20; slot++) {
ecb_decrypt(keydata, keyx, 1, AES_CNT_ECB_DECRYPT_MODE);
#include <common.h>
+#define FIRM_INTERNAL_CODE
+#include <firm/internal.h>
+
struct firm_signature *
get_firm_info(firm_h *firm)
{
// 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");
}
for (firm_section_h *section = firm->section; section < firm->section + 4; section++) {
// 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
}
// 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");
}
}
#ifdef LOADER
#define log(a) logstr(a)
- #define abort(a) \
+ #define panic(a) \
{ \
logstr(a); \
svcBreak(USERBREAK_ASSERT); \
if (debug)
log("abort\n");
- abort("abort triggered, halting VM!\n");
+ panic("abort triggered, halting VM!\n");
break;
case OP_ABORTEQ:
code++;
if (debug)
log("aborteq\n");
if (eq)
- abort("eq flag not set, halting VM!\n");
+ panic("eq flag not set, halting VM!\n");
break;
case OP_ABORTNE:
code++;
if (debug)
log("abortlt\n");
if (!eq)
- abort("eq flag not set, halting VM!\n");
+ panic("eq flag not set, halting VM!\n");
break;
case OP_ABORTLT:
code++;
if (debug)
log("abortlt\n");
if (lt)
- abort("lt flag set, halting VM!\n");
+ panic("lt flag set, halting VM!\n");
break;
case OP_ABORTGT:
code++;
if (debug)
log("abortgt\n");
if (gt)
- abort("gt flag set, halting VM!\n");
+ panic("gt flag set, halting VM!\n");
break;
case OP_ABORTF:
code++;
if (debug)
log("abortf\n");
if (found)
- abort("f flag set, halting VM!\n");
+ panic("f flag set, halting VM!\n");
break;
case OP_ABORTNF:
code++;
if (debug)
log("abortnf\n");
if (!found)
- abort("f flag is not set, halting VM!\n");
+ panic("f flag is not set, halting VM!\n");
break;
case OP_NEXT:
if (debug) {
(uint32_t)code,
*code);
#endif
- abort("Halting startup.\n");
+ panic("Halting startup.\n");
break;
}
#ifndef LOADER
fprintf(stderr, " -> %lx", offset);
#endif
- abort("seeked out of bounds\n");
+ panic("seeked out of bounds\n");
}
#ifndef LOADER
#include <ctr9/ctr_irq.h>
void dump_state_printf(uint32_t* regs) {
- fprintf(stderr, " cpsr:%x sp:%x lr:%x\n"
+ fprintf(stderr, " cpsr:%x sp:%x pc:%x\n"
" r0:%x r1:%x r2:%x r3:%x\n"
" r4:%x r5:%x r6:%x r7:%x\n"
" r8:%x r9:%x r10:%x r11:%x\n"
void undef_INT(uint32_t* regs) {
fprintf(stderr, "Undefined instruction.\n");
dump_state_printf(regs);
- abort("Cannot continue. Halting.\n");
+ panic("Cannot continue. Halting.\n");
}
void swi_INT(_UNUSED uint32_t* regs) {
}
void preabrt_INT(uint32_t* regs) {
- fprintf(stderr, "Prefetch Abort.\n");
+ fprintf(stderr, "Prefetch abort.\n");
dump_state_printf(regs);
- abort("Cannot continue. Halting.\n");
+ panic("Cannot continue. Halting.\n");
}
void databrt_INT(uint32_t* regs) {
fprintf(stderr, "Data abort.\n");
dump_state_printf(regs);
- abort("Cannot continue. Halting.\n");
+ panic("Cannot continue. Halting.\n");
}
void fiq_INT(_UNUSED uint32_t* regs) {
fprintf(stderr, "FIQ called. Returning.\n");
}
-
void install_interrupts() {
ctr_interrupt_prepare();
ctr_irq_initialize();
generate_patch_cache();
}
- boot_cfw();
+ boot_cfw(config->firm[0]);
+
+ panic("Firmlaunch failed!\n");
}
fprintf(stderr, "emunand: found NCSD magic for %lu\n", index);
fprintf(stderr, "emunand: layout is gateway\n");
} else {
- abort("emunand: selected NAND image is not valid.\n");
+ panic("emunand: selected NAND image is not valid.\n");
}
free(emunand_temp);
uint16_t *writeOffset = (uint16_t *)memfind((uint8_t*)(readOffset + 5), 0x100, pattern, 4) - 3;
if (!readOffset || !writeOffset)
- abort("emunand: pattern for r/w missing!\n");
+ panic("emunand: pattern for r/w missing!\n");
readOffset[0] = nandRedir[0];
readOffset[1] = nandRedir[1];
// Copy emuNAND code
void *emuCodeOffset = getEmuCode(arm9Section, arm9SectionSize);
if (!emuCodeOffset)
- abort("emunand: code missing from arm9?\n");
+ panic("emunand: code missing from arm9?\n");
FILE *f = fopen(PATH_EMUNAND_CODE, "r");
if (!f)
- abort("emunand: code not found on SD.\n");
+ panic("emunand: code not found on SD.\n");
uint32_t emunand_size = fsize(f);
fread(emuCodeOffset, 1, emunand_size, f);
*pos_sdmmc = (uint32_t *)memfind(emuCodeOffset, emunand_size, "SDMC", 4);
if (!pos_offset || !pos_head || !pos_sdmmc)
- abort("emunand: couldn't find pattern in hook?\n");
+ panic("emunand: couldn't find pattern in hook?\n");
verify_emunand(index, pos_offset, pos_head);
// Copy firmlaunch code
FILE *f = fopen(PATH_REBOOT_HOOK, "r");
if (!f)
- abort("reboot: hook not found on SD\n");
+ panic("reboot: hook not found on SD\n");
uint32_t size = fsize(f);
fread(off, 1, size, f);
// Put the fOpen offset in the right location
uint32_t *pos_fopen = (uint32_t *)memfind(off, size, "open", 4);
if (!pos_fopen)
- abort("reboot: fopen location missing\n");
+ panic("reboot: fopen location missing\n");
*pos_fopen = fOpenOffset;
uint32_t *pos_agb = (uint32_t *)memfind(off, size, "AGBF", 4);
if (!pos_native && !pos_twl && !pos_agb)
- abort("reboot: missing string placeholder?\n");
+ panic("reboot: missing string placeholder?\n");
fprintf(stderr, "reboot: NATF @ %lx\n", (uint32_t)pos_native);
fprintf(stderr, "reboot: TWLF @ %lx\n", (uint32_t)pos_twl);
f = fopen(PATH_REBOOT_CODE, "r");
if (!f)
- abort("reboot: boot not found on SD\n");
+ panic("reboot: boot not found on SD\n");
fread(mem, 1, fsize(f), f);
fclose(f);
if (enable_list[p.uuid]) {
// Patch is enabled. Cache it.
if (execb(fpath, 1)) {
- abort("Failed to cache:\n %s\n", fpath);
+ panic("Failed to cache:\n %s\n", fpath);
}
wait();
// Inject services?
if (get_opt_u32(OPTION_SVCS)) {
if (patch_svc_calls(firm)) {
- abort("Fatal. Svc inject has failed.");
+ panic("Fatal. Svc inject has failed.");
}
wait();
}
// Replace loader?
if (get_opt_u32(OPTION_LOADER)) {
if (patch_modules(firm)) {
- abort("Fatal. Loader inject has failed.");
+ panic("Fatal. Loader inject has failed.");
}
// This requires OPTION_SIGPATCH.
wait();
#include <stdarg.h>
#include <common.h>
-void abort(char* x, ...) {
+void panic(char* x, ...) {
va_list ap;
va_start(ap, x);
#ifdef MALLOC_DEBUG
if (block->canary != 0x1337d00d) {
- abort("%s: Attempt free non-pointer.\n", info);
+ panic("%s: Attempt free non-pointer.\n", info);
}
++free_count;
FILE* f = fopen(filename, "r");
- if (!f) abort("Failed to load font file!\n");
+ if (!f) panic("Failed to load font file!\n");
unsigned int new_w, new_h;
fread(&new_h, 1, 4, f);
if (new_w == 0 || new_h == 0) {
- abort("Invalid font file: w/h is 0 - not loaded\n");
+ panic("Invalid font file: w/h is 0 - not loaded\n");
}
unsigned int c_font_w = (new_w / 8) + ((new_w % 8) ? 1 : 0);
{
if ((channel == stdout || channel == stderr) && kill_output)
return;
-
- char *copy = strdup_self(format);
- char *ref = copy;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wdiscarded-qualifiers"
+ char *ref = format;
+#pragma GCC diagnostic pop
unsigned char *color = NULL;
if (channel == TOP_SCREEN)
}
++ref;
}
-
- free(copy);
}
void
return new_st;
}
+char*
+strdupcat(const char* str, const char *cat)
+{
+ size_t l_str = strlen(str);
+ size_t l_cat = strlen(cat);
+ char *out = malloc(l_str + l_cat + 1);
+ memcpy(out, str, l_str);
+ memcpy(out + l_str, cat, l_cat + 1);
+ return out;
+}
+
int
atoi(const char *str)
{