# This is just a helper script I use to copy shit to my own unit.
# You don't and shouldn't need to use it.
-mnt=/media/sd
-dev=/dev/sdc
-
-mnt=/mnt/ext1
+mnt=/media
+dev=/dev/mmcblk0p
mount -t vfat ${dev}1 $mnt || exit 0
cp out/arm9loaderhax.bin $mnt/arm9loaderhax_si.bin || exit 0
umount $mnt || exit 0
sync || exit 0
-eject ${dev} || exit 0
firm_h*
load_firm(const char *path, size_t *size_out)
{
+ uint8_t* mem;
+ struct firm_signature* sig;
int success = 0;
+ FILE* firm_file;
+ size_t size;
+ int save_dec = 0;
- FILE *firm_file = fopen(path, "r");
+ char* decpath = strdupcat(path, ".dec");
+ firm_file = fopen(decpath, "r");
if (!firm_file) {
- return NULL;
+ firm_file = fopen(path, "r");
+ if (!firm_file) {
+ return NULL;
+ }
}
- size_t size = fsize(firm_file);
+ size = fsize(firm_file);
if (size_out)
*size_out = size;
- uint8_t* mem = malloc(size);
+ mem = malloc(size);
firm_h *firm = (firm_h*)mem;
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;
+ if (memcmp(firm->magic, "FIRM", 4)) {
+ char *key_path = strdupcat(path, ".key");
- struct firm_signature* sig = get_firm_info(firm);
+ // Attempt to open keyfile.
+ uint8_t* firmkey = malloc(16);
+ if (read_file(firmkey, key_path, 16) != 16) {
+ // Keyfile couldn't be opened, try the cetk.
+ free(firmkey);
- patch_entry(firm, sig->type);
- if (patch_section_keys(firm, sig->k9l)) {
- free(mem);
- return NULL;
+ // Encrypted. Open CETK.
+ char *cetk_path = strdupcat(path, ".cetk");
+ firmkey = get_titlekey(cetk_path);
+ free(cetk_path);
+
+ // Save firmkey.
+ FILE* keyfile = fopen(key_path, "w");
+ fwrite(firmkey, 1, 16, keyfile);
+ fclose(keyfile);
}
- free(sig);
- } else if (!memcmp(firm->magic, "FIRM", 4)) {
- // O3DS fully decrypted FIRM
+ free(key_path);
- firm = (firm_h*)mem;
- } else {
- // Encrypted.
- char *cetk_path = strdupcat(path, ".cetk");
+ if (firmkey) {
+ firm = extract_firm_from_ncch((ncch_h*)mem, firmkey, size);
- uint8_t* firmkey = get_titlekey(cetk_path);
+ free(firmkey);
- free(cetk_path);
+ if (!firm) {
+ free(mem);
+ return NULL;
+ }
+ } else {
+ free(mem);
+ return NULL;
+ }
- if (firmkey) {
- firm = extract_firm_from_ncch((ncch_h*)mem, firmkey, size);
+ save_dec = 1;
+ }
- if (firm) {
- struct firm_signature* sig = get_firm_info(firm);
+ sig = get_firm_info(firm);
- if (sig->console == console_n3ds) {
- if(dec_k9l(firm)) {
- free(firm);
- free(mem);
- return NULL;
- }
+ if (memcmp(firm->magic, "FIRM", 4)) {
+ // Error. Abort.
+ free(mem);
+ return NULL;
+ }
- patch_entry(firm, sig->type);
+ // If this is a FIRM and not a k9l decrypted FIRM...
+ if (!memcmp(firm->magic, "FIRM", 4) && memcmp(firm->magic + 4, "DEC", 3)) {
+ if (sig->console == console_n3ds) {
+ if (dec_k9l(firm)) {
+ free(firm);
+ free(mem);
+ return NULL;
+ }
+ }
+ }
- if (sig->type == type_native && patch_section_keys(firm, sig->k9l)) {
- free(firm);
- free(mem);
- return NULL;
- }
- }
+ // Save decrypted FIRM.
+ if (save_dec == 1) {
+ firm_file = fopen(decpath, "w");
+ fwrite(firm, 1, size, firm_file);
+ fclose(firm_file);
+ }
+
+ free(decpath);
+
+ // Arm9 decrypted firmware (n3ds)?
+ if (!memcmp(firm->magic, "FIRMDEC", 7)) {
+ if (sig->console == console_n3ds) {
+ patch_entry(firm, sig->type);
- free(sig);
+ if (sig->type == type_native && patch_section_keys(firm, sig->k9l)) {
+ free(firm);
+ free(mem);
+ return NULL;
}
}
- free(mem);
}
return firm;
int
prepatch_firm(const char* firm_path, const char* prepatch_path, const char* module_path)
{
- size_t size = 0;
+ size_t size = 0;
firm_h* firm = load_firm(firm_path, &size);
if (firm == NULL)
free(sig);
if (patch_firm_all(tid, firm, module_path)) {
- free(firm);
+ free(firm);
return 1;
- }
+ }
- FILE* f = fopen(prepatch_path, "w");
- fwrite(firm, 1, size, f);
- fclose(f);
+ FILE* f = fopen(prepatch_path, "w");
+ fwrite(firm, 1, size, f);
+ fclose(f);
- free(firm);
+ free(firm);
- return 0;
+ return 0;
}
int
boot_firm(const char* firm_path, const char* prepatch_path, const char* module_path)
{
- size_t size = 0;
+ size_t size = 0;
firm_h* firm = load_firm(firm_path, &size);
if (firm == NULL)
free(sig);
if (patch_firm_all(tid, firm, module_path)) {
- free(firm);
+ free(firm);
return 1;
- }
+ }
- FILE* f = fopen(prepatch_path, "w");
- fwrite(firm, 1, size, f);
- fclose(f);
+ FILE* f = fopen(prepatch_path, "w");
+ fwrite(firm, 1, size, f);
+ fclose(f);
firmlaunch(firm); // <- should NOT return if all is well
#include <firm/internal.h>
int set_N11_K9L(uint32_t index) {
- static uint8_t ss_keyn[2][16] = {
+ static uint8_t ss_keyn[2][16] = {
{ 0 }, // 9.5
{ 0 }, // 9.6+
};
setup_aeskey(0x11, (void*) ss_keyn[index]);
- use_aeskey(0x11);
+ use_aeskey(0x11);
- return 0;
+ return 0;
}
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
- };
+ // 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
+ };
- setup_aeskeyY(0x3D, (void*) common_keyy[index]);
+ setup_aeskeyY(0x3D, (void*) common_keyy[index]);
- use_aeskey(0x3D);
+ use_aeskey(0x3D);
- return 0;
+ return 0;
}
fclose(f);
- uint8_t iv[AES_BLOCK_SIZE] = { 0 };
- uint32_t sigtype = __builtin_bswap32(*(const uint32_t *)cetk);
+ uint8_t iv[AES_BLOCK_SIZE] = { 0 };
+ uint32_t sigtype = __builtin_bswap32(*(const uint32_t *)cetk);
- if (sigtype != SIG_TYPE_RSA2048_SHA256) {
+ if (sigtype != SIG_TYPE_RSA2048_SHA256) {
free(cetk);
- return NULL;
+ return NULL;
}
- const ticket_h *ticket = (const ticket_h *)((const uint8_t*)cetk + sizeof(sigtype) + 0x13C);
+ const ticket_h *ticket = (const ticket_h *)((const uint8_t*)cetk + sizeof(sigtype) + 0x13C);
set_Y3D_cetk(1);
uint8_t *key = malloc(AES_BLOCK_SIZE);
- memcpy(iv, ticket->titleID, sizeof(ticket->titleID));
- memcpy(key, ticket->titleKey, sizeof(ticket->titleKey));
+ 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);
+ cbc_decrypt(key, key, 1, AES_CNT_TITLEKEY_DECRYPT_MODE, iv);
free(cetk);
- return key;
+ return key;
}
int dec_k9l(firm_h* firm) {
sha256sum(arm9->hash, (uint8_t*)firm + arm9->offset, arm9->size);
// Magic like D9.
- memcpy(firm->magic, "DECFIRM", 7);
+ memcpy(firm->magic, "FIRMDEC", 7);
free(sig);
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 };
+ 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);
+ 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;
+ if (ncch->magic != NCCH_MAGIC) {
+ return NULL;
}
- memcpy(exefs_key, ncch, AES_BLOCK_SIZE);
- ncch_getctr(ncch, exefs_iv, NCCHTYPE_EXEFS);
+ 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;
+ // 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);
+ 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
+ // 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;
+ // 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);
+ memcpy(dest, firm, size);
- return dest;
+ 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;
+ 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 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_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);
+ uint8_t* key_data = key_search(key_loc, search_size, sha256, 0xDD);
- if (!key_data)
- return NULL;
+ if (!key_data)
+ return NULL;
- return key_data;
+ return key_data;
}
int patch_section_keys(firm_h* firm_loc, uint32_t k9l) {
exefs_h*
find_proc9(firm_h *firm)
{
- 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 = memfind((uint8_t *)firm + section->offset, section->size, "Process9", 8);
- if (!arm9section)
- return NULL;
-
- 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);
- return (exefs_h *)(p9exheader + 1);
- }
- }
- }
- return NULL;
+ 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 = memfind((uint8_t *)firm + section->offset, section->size, "Process9", 8);
+ if (!arm9section)
+ return NULL;
+
+ 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);
+ return (exefs_h *)(p9exheader + 1);
+ }
+ }
+ }
+ return NULL;
}
+
#include <ctr9/ctr_hid.h>
#include <ctr9/io.h>
#include <ctr9/ctr_screen.h>
clear_disp(TOP_SCREEN);
clear_disp(BOTTOM_SCREEN);
- if (get_opt_u32(OPTION_AUTOBOOT) && !r_held) {
- doing_autoboot = 1;
+ while (1) {
+ if (get_opt_u32(OPTION_AUTOBOOT) && !r_held && !doing_autoboot) {
+ doing_autoboot = 1;
- if (get_opt_u32(OPTION_SILENCE))
- shut_up(); // This does exactly what it sounds like.
- } else {
- menu_handler();
- }
+ if (get_opt_u32(OPTION_SILENCE))
+ shut_up(); // This does exactly what it sounds like.
+ } else {
+ menu_handler();
+ }
- if (changed_consoles) {
- fprintf(stderr, "Console changed, regenerating caches\n");
- save_config();
- generate_patch_cache();
- }
+ if (changed_consoles) {
+ fprintf(stderr, "Console changed, regenerating caches\n");
+ save_config();
+ generate_patch_cache();
+ }
- if (prepatch_firm(config->firm[1], PATH_TWL_P, PATH_MODULE_TWL)) {
- fprintf(stderr, "WARNING: Failed to load/patch TWL.\n");
- wait();
- }
+ if (prepatch_firm(config->firm[1], PATH_TWL_P, PATH_MODULE_TWL)) {
+ fprintf(stderr, "WARNING: Failed to load/patch TWL.\n");
+ wait();
+ }
- if(prepatch_firm(config->firm[2], PATH_AGB_P, PATH_MODULE_AGB)) {
- fprintf(stderr, "WARNING: Failed to load/patch AGB.\n");
- wait();
- }
+ if(prepatch_firm(config->firm[2], PATH_AGB_P, PATH_MODULE_AGB)) {
+ fprintf(stderr, "WARNING: Failed to load/patch AGB.\n");
+ wait();
+ }
- boot_firm(config->firm[0], PATH_NATIVE_P, PATH_MODULE_NATIVE);
+ boot_firm(config->firm[0], PATH_NATIVE_P, PATH_MODULE_NATIVE);
- panic("Firmlaunch failed!\n");
+ fprintf(stderr, "Firmlaunch failed, returning to menu\n");
+ }
}