diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index f7c08c4f1..7828268a6 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -60,7 +60,7 @@ void nxboot_main(void) { derive_nx_keydata(MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware); /* Patch package2, adding thermosphere + custom KIPs. */ - package2_patch((void *)loader_ctx->package2_loadfile.load_address); + package2_rebuild_and_copy((void *)loader_ctx->package2_loadfile.load_address); /* Boot up Exosphere. */ MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE = 0; diff --git a/fusee/fusee-secondary/src/package2.c b/fusee/fusee-secondary/src/package2.c index 19fef1d81..523538967 100644 --- a/fusee/fusee-secondary/src/package2.c +++ b/fusee/fusee-secondary/src/package2.c @@ -7,56 +7,76 @@ #include "kip.h" #include "se.h" -/* Stage 2 executes from DRAM, so we have tons of space. */ -/* This *greatly* simplifies logic. */ -static uint8_t *g_patched_package2; -static uint8_t *g_package2_sections[PACKAGE2_SECTION_MAX]; +static void package2_decrypt(package2_header_t *package2); +static size_t package2_get_thermosphere(void **thermosphere); +static void package2_patch_kernel(void *kernel, size_t kernel_size); +static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1); +static void package2_append_section(size_t id, package2_header_t *package2, void *data, size_t size); +static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size); -static package2_header_t *g_patched_package2_header; +void package2_rebuild_and_copy(void *package2_address) { + uintptr_t pk2da = (uintptr_t)package2_address + sizeof(package2_header_t); + package2_header_t *package2 = (package2_header_t *)package2_address; + package2_header_t *rebuilt_package2; + size_t rebuilt_package2_size; + void *kernel; + size_t kernel_size; + void *thermosphere; + size_t thermosphere_size; + ini1_header_t *rebuilt_ini1; -void package2_decrypt(void *package2_address); -void package2_add_thermosphere_section(void); -void package2_patch_kernel(void); -void package2_patch_ini1(void); -void package2_fixup_header_and_section_hashes(void); - -void package2_allocate_mem(void) { - /* TODO: call it */ - g_patched_package2 = (uint8_t *)malloc(PACKAGE2_SIZE_MAX); - for(size_t i = 0; i < PACKAGE2_SECTION_MAX; i++) { - g_package2_sections[i] = (uint8_t *)malloc(PACKAGE2_SIZE_MAX); - } - g_patched_package2_header = (package2_header_t *)g_patched_package2; -} - -void package2_free_mem(void) { - free(g_patched_package2); - for(size_t i = 0; i < PACKAGE2_SECTION_MAX; i++) { - free(g_package2_sections[i]); - } - g_patched_package2_header = NULL; -} - -void package2_patch(void *package2_address) { - /* First things first: Decrypt Package2. */ - package2_decrypt(package2_address); + /* First things first: Decrypt Package2 in place. */ + package2_decrypt(package2); + kernel = (void *)(pk2da + package2->metadata.section_offsets[PACKAGE2_SECTION_KERNEL]); + kernel_size = package2->metadata.section_sizes[PACKAGE2_SECTION_KERNEL]; /* Modify Package2 to add an additional thermosphere section. */ - package2_add_thermosphere_section(); + thermosphere_size = package2_get_thermosphere(&thermosphere); + + if (thermosphere_size != 0 && package2->metadata.section_sizes[PACKAGE2_SECTION_UNUSED] != 0) { + printf(u8"Error: Package2 has no unused section for Thermosphère!\n"); + generic_panic(); + } /* Perform any patches we want to the NX kernel. */ - package2_patch_kernel(); + package2_patch_kernel(kernel, kernel_size); - /* Perform any patches we want to the INI1 (This is where our built-in sysmodules will be added.) */ - package2_patch_ini1(); + /* Perform any patches to the INI1, rebuilding it (This is where our built-in sysmodules will be added.) */ + rebuilt_ini1 = package2_rebuild_ini1((ini1_header_t *)(pk2da + package2->metadata.section_offsets[PACKAGE2_SECTION_INI1])); + + /* Allocate the rebuilt package2. */ + rebuilt_package2_size = sizeof(package2_header_t); + rebuilt_package2_size += package2->metadata.section_sizes[PACKAGE2_SECTION_KERNEL] + thermosphere_size + rebuilt_ini1->size; + + if (rebuilt_package2_size > PACKAGE2_SIZE_MAX) { + printf("Error: rebuilt package2 is too big!\n"); + generic_panic(); + } + + rebuilt_package2 = (package2_header_t *)malloc(rebuilt_package2_size); + if (rebuilt_package2 == NULL) { + printf("Error: package2_rebuild: out of memory!\n"); + generic_panic(); + } + + /* Rebuild package2. */ + memcpy(rebuilt_package2, package2, sizeof(package2_header_t)); + package2_append_section(PACKAGE2_SECTION_KERNEL, rebuilt_package2, kernel, kernel_size); + package2_append_section(PACKAGE2_SECTION_INI1, rebuilt_package2, rebuilt_ini1, rebuilt_ini1->size); + package2_append_section(PACKAGE2_SECTION_UNUSED, rebuilt_package2, thermosphere, thermosphere_size); /* Fix all necessary data in the header to accomodate for the new patches. */ - package2_fixup_header_and_section_hashes(); + package2_fixup_header_and_section_hashes(rebuilt_package2, rebuilt_package2_size); /* Relocate Package2. */ - memcpy(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, g_patched_package2, PACKAGE2_SIZE_MAX); + memcpy(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, rebuilt_package2, PACKAGE2_SIZE_MAX); + + /* We're done. */ + free(rebuilt_ini1); + free(rebuilt_package2); } + static void package2_crypt_ctr(unsigned int master_key_rev, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) { /* Derive package2 key. */ const uint8_t package2_key_source[0x10] = {0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7}; @@ -67,7 +87,7 @@ static void package2_crypt_ctr(unsigned int master_key_rev, void *dst, size_t ds se_aes_ctr_crypt(KEYSLOT_SWITCH_PACKAGE2KEY, dst, dst_size, src, src_size, ctr, ctr_size); } -bool validate_package2_metadata(package2_meta_t *metadata) { +static bool package2_validate_metadata(package2_meta_t *metadata) { if (metadata->magic != MAGIC_PK21) { return false; } @@ -153,7 +173,7 @@ bool validate_package2_metadata(package2_meta_t *metadata) { return false; } -uint32_t decrypt_and_validate_header(package2_header_t *header, bool is_plaintext) { +static uint32_t package2_decrypt_and_validate_header(package2_header_t *header, bool is_plaintext) { package2_meta_t metadata; /* TODO: Also accept plaintext package2 based on bootconfig. */ @@ -167,7 +187,7 @@ uint32_t decrypt_and_validate_header(package2_header_t *header, bool is_plaintex /* Copy the ctr (which stores information) into the decrypted metadata. */ memcpy(metadata.ctr, header->metadata.ctr, sizeof(header->metadata.ctr)); /* See if this is the correct key. */ - if (validate_package2_metadata(&metadata)) { + if (package2_validate_metadata(&metadata)) { header->metadata = metadata; return mkey_rev; } @@ -177,102 +197,96 @@ uint32_t decrypt_and_validate_header(package2_header_t *header, bool is_plaintex if (mkey_rev > mkey_get_revision()) { panic(0xFAF00003); } - } else if (!validate_package2_metadata(&header->metadata)) { + } else if (!package2_validate_metadata(&header->metadata)) { panic(0xFAF0003); } return 0; } -void package2_decrypt(void *package2_address) { - /* TODO: Actually decrypt, and copy sections into the relevant g_package2_sections[n] */ - memcpy(g_patched_package2, NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, PACKAGE2_SIZE_MAX); +static void package2_decrypt(package2_header_t *package2) { + bool is_package2_plaintext = package2->signature[0]; + is_package2_plaintext &= memcmp(package2->signature, package2->signature + 1, sizeof(package2->signature) - 1) == 0; + is_package2_plaintext &= package2->metadata.magic == MAGIC_PK21; - bool is_package2_plaintext = g_patched_package2_header->signature[0]; - is_package2_plaintext &= memcmp(g_patched_package2_header->signature, g_patched_package2_header->signature + 1, sizeof(g_patched_package2_header->signature) - 1) == 0; - is_package2_plaintext &= g_patched_package2_header->metadata.magic == MAGIC_PK21; - - uint32_t pk21_mkey_revision = decrypt_and_validate_header(g_patched_package2_header, is_package2_plaintext); + uint32_t pk21_mkey_revision = package2_decrypt_and_validate_header(package2, is_package2_plaintext); size_t cur_section_offset = 0; /* Copy each section to its appropriate location, decrypting if necessary. */ for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - if (g_patched_package2_header->metadata.section_sizes[section] == 0) { + if (package2->metadata.section_sizes[section] == 0) { continue; } - void *src_start = g_patched_package2 + sizeof(package2_header_t) + cur_section_offset; - void *dst_start = g_package2_sections[section]; - size_t size = (size_t)g_patched_package2_header->metadata.section_sizes[section]; + void *src_start = (uint8_t *)package2 + sizeof(package2_header_t) + cur_section_offset; + void *dst_start = src_start; + size_t size = (size_t)package2->metadata.section_sizes[section]; if (is_package2_plaintext&& size != 0) { memcpy(dst_start, src_start, size); } else if (size != 0) { - package2_crypt_ctr(pk21_mkey_revision, dst_start, size, src_start, size, g_patched_package2_header->metadata.section_ctrs[section], 0x10); + package2_crypt_ctr(pk21_mkey_revision, dst_start, size, src_start, size, package2->metadata.section_ctrs[section], 0x10); } cur_section_offset += size; } /* Clear the signature, to signal that this is a plaintext, unsigned package2. */ - memset(g_patched_package2_header->signature, 0, sizeof(g_patched_package2_header->signature)); + memset(package2->signature, 0, sizeof(package2->signature)); } -void package2_add_thermosphere_section(void) { - if (g_patched_package2_header->metadata.section_sizes[PACKAGE2_SECTION_UNUSED] != 0) { - printf(u8"Error: Package2 has no unused section for Thermosphère!\n"); - generic_panic(); - } - - /* TODO: Copy thermosphere to g_package2_sections[PACKAGE2_SECTION_UNUSED], update header size. */ +static size_t package2_get_thermosphere(void **thermosphere) { + (*thermosphere) = NULL; + return 0; } -void package2_patch_kernel(void) { +static void package2_patch_kernel(void *kernel, size_t size) { + (void)kernel; + (void)size; /* TODO: What kind of patching do we want to try to do here? */ } -void package2_patch_ini1(void) { +static ini1_header_t *package2_rebuild_ini1(ini1_header_t *ini1) { /* TODO: Do we want to support loading another INI from sd:/whatever/INI1.bin? */ ini1_header_t *inis_to_merge[STRATOSPHERE_INI1_MAX] = {0}; ini1_header_t *merged; inis_to_merge[STRATOSPHERE_INI1_EMBEDDED] = stratosphere_get_ini1(); - inis_to_merge[STRATOSPHERE_INI1_PACKAGE2] = (ini1_header_t *)g_package2_sections[PACKAGE2_SECTION_INI1]; + inis_to_merge[STRATOSPHERE_INI1_PACKAGE2] = ini1; /* Merge all of the INI1s. */ merged = stratosphere_merge_inis(inis_to_merge, STRATOSPHERE_INI1_MAX); - memcpy(g_package2_sections[PACKAGE2_SECTION_INI1], merged, merged->size); - g_patched_package2_header->metadata.section_sizes[PACKAGE2_SECTION_INI1] = merged->size; - /* Free temporary buffers. */ - free(merged); + /* Free temporary buffer. */ stratosphere_free_ini1(); + + return merged; } -void package2_fixup_header_and_section_hashes(void) { - size_t cur_section_offset = 0; +static void package2_append_section(size_t id, package2_header_t *package2, void *data, size_t size) { + /* This function must be called in ascending order of id */ + uint32_t offset = id == 0 ? 0 : package2->metadata.section_offsets[id - 1] + package2->metadata.section_sizes[id - 1]; + void *dst = (uint8_t *)package2 + sizeof(package2_header_t) + offset; + memcpy(dst, data, size); + package2->metadata.section_offsets[id] = offset; + package2->metadata.section_sizes[id] = ((size + 3) << 2) >> 2; +} + +static void package2_fixup_header_and_section_hashes(package2_header_t *package2, size_t size) { /* Copy each section to its appropriate location. */ for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - if (g_patched_package2_header->metadata.section_sizes[section] == 0) { + if (package2->metadata.section_sizes[section] == 0) { continue; } - size_t size = (size_t)g_patched_package2_header->metadata.section_sizes[section]; - if (sizeof(package2_header_t) + cur_section_offset + size > PACKAGE2_SIZE_MAX) { - printf("Error: Patched Package2 is too big!\n"); - generic_panic(); - } + size_t sz = (size_t)package2->metadata.section_sizes[section]; + size_t off = sizeof(package2_header_t) + package2->metadata.section_offsets[section]; - /* Copy the section into the new package2. */ - memcpy(g_patched_package2 + sizeof(package2_header_t) + cur_section_offset, g_package2_sections[section], size); /* Fix up the hash. */ - se_calculate_sha256(g_patched_package2_header->metadata.section_hashes[section], g_package2_sections[section], size); - - cur_section_offset += size; + se_calculate_sha256(package2->metadata.section_hashes[section],(uint8_t *)package2 + off, sz); } /* Fix up the size in XOR'd CTR. */ - uint32_t package_size = g_patched_package2_header->metadata.ctr_dwords[0] ^ g_patched_package2_header->metadata.ctr_dwords[2] ^ g_patched_package2_header->metadata.ctr_dwords[3]; - uint32_t new_package_size = sizeof(package2_header_t) + cur_section_offset; - g_patched_package2_header->metadata.ctr_dwords[3] ^= (package_size ^ new_package_size); + uint32_t package_size = package2->metadata.ctr_dwords[0] ^ package2->metadata.ctr_dwords[2] ^ package2->metadata.ctr_dwords[3]; + package2->metadata.ctr_dwords[3] ^= (package_size ^ size); } diff --git a/fusee/fusee-secondary/src/package2.h b/fusee/fusee-secondary/src/package2.h index c8ab0cf48..e16d64a4a 100644 --- a/fusee/fusee-secondary/src/package2.h +++ b/fusee/fusee-secondary/src/package2.h @@ -53,6 +53,6 @@ typedef struct { }; } package2_header_t; -void package2_patch(void *package2_address); +void package2_rebuild_and_copy(void *package2_address); -#endif \ No newline at end of file +#endif