mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-29 23:32:23 +00:00
Rewrite package2.c
This commit is contained in:
parent
a39ad22841
commit
77faa00a57
3 changed files with 102 additions and 88 deletions
|
@ -60,7 +60,7 @@ void nxboot_main(void) {
|
||||||
derive_nx_keydata(MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware);
|
derive_nx_keydata(MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware);
|
||||||
|
|
||||||
/* Patch package2, adding thermosphere + custom KIPs. */
|
/* 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. */
|
/* Boot up Exosphere. */
|
||||||
MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE = 0;
|
MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE = 0;
|
||||||
|
|
|
@ -7,56 +7,76 @@
|
||||||
#include "kip.h"
|
#include "kip.h"
|
||||||
#include "se.h"
|
#include "se.h"
|
||||||
|
|
||||||
/* Stage 2 executes from DRAM, so we have tons of space. */
|
static void package2_decrypt(package2_header_t *package2);
|
||||||
/* This *greatly* simplifies logic. */
|
static size_t package2_get_thermosphere(void **thermosphere);
|
||||||
static uint8_t *g_patched_package2;
|
static void package2_patch_kernel(void *kernel, size_t kernel_size);
|
||||||
static uint8_t *g_package2_sections[PACKAGE2_SECTION_MAX];
|
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);
|
/* First things first: Decrypt Package2 in place. */
|
||||||
void package2_add_thermosphere_section(void);
|
package2_decrypt(package2);
|
||||||
void package2_patch_kernel(void);
|
kernel = (void *)(pk2da + package2->metadata.section_offsets[PACKAGE2_SECTION_KERNEL]);
|
||||||
void package2_patch_ini1(void);
|
kernel_size = package2->metadata.section_sizes[PACKAGE2_SECTION_KERNEL];
|
||||||
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);
|
|
||||||
|
|
||||||
/* Modify Package2 to add an additional thermosphere section. */
|
/* 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. */
|
/* 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.) */
|
/* Perform any patches to the INI1, rebuilding it (This is where our built-in sysmodules will be added.) */
|
||||||
package2_patch_ini1();
|
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. */
|
/* 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. */
|
/* 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) {
|
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. */
|
/* 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};
|
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);
|
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) {
|
if (metadata->magic != MAGIC_PK21) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -153,7 +173,7 @@ bool validate_package2_metadata(package2_meta_t *metadata) {
|
||||||
return false;
|
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;
|
package2_meta_t metadata;
|
||||||
|
|
||||||
/* TODO: Also accept plaintext package2 based on bootconfig. */
|
/* 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. */
|
/* Copy the ctr (which stores information) into the decrypted metadata. */
|
||||||
memcpy(metadata.ctr, header->metadata.ctr, sizeof(header->metadata.ctr));
|
memcpy(metadata.ctr, header->metadata.ctr, sizeof(header->metadata.ctr));
|
||||||
/* See if this is the correct key. */
|
/* See if this is the correct key. */
|
||||||
if (validate_package2_metadata(&metadata)) {
|
if (package2_validate_metadata(&metadata)) {
|
||||||
header->metadata = metadata;
|
header->metadata = metadata;
|
||||||
return mkey_rev;
|
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()) {
|
if (mkey_rev > mkey_get_revision()) {
|
||||||
panic(0xFAF00003);
|
panic(0xFAF00003);
|
||||||
}
|
}
|
||||||
} else if (!validate_package2_metadata(&header->metadata)) {
|
} else if (!package2_validate_metadata(&header->metadata)) {
|
||||||
panic(0xFAF0003);
|
panic(0xFAF0003);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void package2_decrypt(void *package2_address) {
|
static void package2_decrypt(package2_header_t *package2) {
|
||||||
/* TODO: Actually decrypt, and copy sections into the relevant g_package2_sections[n] */
|
bool is_package2_plaintext = package2->signature[0];
|
||||||
memcpy(g_patched_package2, NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, PACKAGE2_SIZE_MAX);
|
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];
|
uint32_t pk21_mkey_revision = package2_decrypt_and_validate_header(package2, is_package2_plaintext);
|
||||||
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);
|
|
||||||
|
|
||||||
size_t cur_section_offset = 0;
|
size_t cur_section_offset = 0;
|
||||||
/* Copy each section to its appropriate location, decrypting if necessary. */
|
/* Copy each section to its appropriate location, decrypting if necessary. */
|
||||||
for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *src_start = g_patched_package2 + sizeof(package2_header_t) + cur_section_offset;
|
void *src_start = (uint8_t *)package2 + sizeof(package2_header_t) + cur_section_offset;
|
||||||
void *dst_start = g_package2_sections[section];
|
void *dst_start = src_start;
|
||||||
size_t size = (size_t)g_patched_package2_header->metadata.section_sizes[section];
|
size_t size = (size_t)package2->metadata.section_sizes[section];
|
||||||
|
|
||||||
if (is_package2_plaintext&& size != 0) {
|
if (is_package2_plaintext&& size != 0) {
|
||||||
memcpy(dst_start, src_start, size);
|
memcpy(dst_start, src_start, size);
|
||||||
} else if (size != 0) {
|
} 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;
|
cur_section_offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear the signature, to signal that this is a plaintext, unsigned package2. */
|
/* 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) {
|
static size_t package2_get_thermosphere(void **thermosphere) {
|
||||||
if (g_patched_package2_header->metadata.section_sizes[PACKAGE2_SECTION_UNUSED] != 0) {
|
(*thermosphere) = NULL;
|
||||||
printf(u8"Error: Package2 has no unused section for Thermosphère!\n");
|
return 0;
|
||||||
generic_panic();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Copy thermosphere to g_package2_sections[PACKAGE2_SECTION_UNUSED], update header size. */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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? */
|
/* 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? */
|
/* 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 *inis_to_merge[STRATOSPHERE_INI1_MAX] = {0};
|
||||||
ini1_header_t *merged;
|
ini1_header_t *merged;
|
||||||
|
|
||||||
inis_to_merge[STRATOSPHERE_INI1_EMBEDDED] = stratosphere_get_ini1();
|
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. */
|
/* Merge all of the INI1s. */
|
||||||
merged = stratosphere_merge_inis(inis_to_merge, STRATOSPHERE_INI1_MAX);
|
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 temporary buffer. */
|
||||||
free(merged);
|
|
||||||
stratosphere_free_ini1();
|
stratosphere_free_ini1();
|
||||||
|
|
||||||
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
void package2_fixup_header_and_section_hashes(void) {
|
static void package2_append_section(size_t id, package2_header_t *package2, void *data, size_t size) {
|
||||||
size_t cur_section_offset = 0;
|
/* 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. */
|
/* Copy each section to its appropriate location. */
|
||||||
for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size = (size_t)g_patched_package2_header->metadata.section_sizes[section];
|
size_t sz = (size_t)package2->metadata.section_sizes[section];
|
||||||
if (sizeof(package2_header_t) + cur_section_offset + size > PACKAGE2_SIZE_MAX) {
|
size_t off = sizeof(package2_header_t) + package2->metadata.section_offsets[section];
|
||||||
printf("Error: Patched Package2 is too big!\n");
|
|
||||||
generic_panic();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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. */
|
/* Fix up the hash. */
|
||||||
se_calculate_sha256(g_patched_package2_header->metadata.section_hashes[section], g_package2_sections[section], size);
|
se_calculate_sha256(package2->metadata.section_hashes[section],(uint8_t *)package2 + off, sz);
|
||||||
|
|
||||||
cur_section_offset += size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix up the size in XOR'd CTR. */
|
/* 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 package_size = package2->metadata.ctr_dwords[0] ^ package2->metadata.ctr_dwords[2] ^ package2->metadata.ctr_dwords[3];
|
||||||
uint32_t new_package_size = sizeof(package2_header_t) + cur_section_offset;
|
package2->metadata.ctr_dwords[3] ^= (package_size ^ size);
|
||||||
g_patched_package2_header->metadata.ctr_dwords[3] ^= (package_size ^ new_package_size);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,6 @@ typedef struct {
|
||||||
};
|
};
|
||||||
} package2_header_t;
|
} package2_header_t;
|
||||||
|
|
||||||
void package2_patch(void *package2_address);
|
void package2_rebuild_and_copy(void *package2_address);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue