Fix trailing spaces

This commit is contained in:
Michael Scire 2020-01-15 02:07:22 -08:00
parent 55e625d67d
commit e37d4d2da2
19 changed files with 264 additions and 264 deletions

30
aes.c
View file

@ -8,24 +8,24 @@
/* Allocate a new context. */ /* Allocate a new context. */
aes_ctx_t *new_aes_ctx(const void *key, unsigned int key_size, aes_mode_t mode) { aes_ctx_t *new_aes_ctx(const void *key, unsigned int key_size, aes_mode_t mode) {
aes_ctx_t *ctx; aes_ctx_t *ctx;
if ((ctx = malloc(sizeof(*ctx))) == NULL) { if ((ctx = malloc(sizeof(*ctx))) == NULL) {
FATAL_ERROR("Failed to allocate aes_ctx_t!"); FATAL_ERROR("Failed to allocate aes_ctx_t!");
} }
mbedtls_cipher_init(&ctx->cipher_dec); mbedtls_cipher_init(&ctx->cipher_dec);
mbedtls_cipher_init(&ctx->cipher_enc); mbedtls_cipher_init(&ctx->cipher_enc);
if (mbedtls_cipher_setup(&ctx->cipher_dec, mbedtls_cipher_info_from_type(mode)) if (mbedtls_cipher_setup(&ctx->cipher_dec, mbedtls_cipher_info_from_type(mode))
|| mbedtls_cipher_setup(&ctx->cipher_enc, mbedtls_cipher_info_from_type(mode))) { || mbedtls_cipher_setup(&ctx->cipher_enc, mbedtls_cipher_info_from_type(mode))) {
FATAL_ERROR("Failed to set up AES context!"); FATAL_ERROR("Failed to set up AES context!");
} }
if (mbedtls_cipher_setkey(&ctx->cipher_dec, key, key_size * 8, AES_DECRYPT) if (mbedtls_cipher_setkey(&ctx->cipher_dec, key, key_size * 8, AES_DECRYPT)
|| mbedtls_cipher_setkey(&ctx->cipher_enc, key, key_size * 8, AES_ENCRYPT)) { || mbedtls_cipher_setkey(&ctx->cipher_enc, key, key_size * 8, AES_ENCRYPT)) {
FATAL_ERROR("Failed to set key for AES context!"); FATAL_ERROR("Failed to set key for AES context!");
} }
return ctx; return ctx;
} }
@ -35,7 +35,7 @@ void free_aes_ctx(aes_ctx_t *ctx) {
if (ctx == NULL) { if (ctx == NULL) {
return; return;
} }
mbedtls_cipher_free(&ctx->cipher_dec); mbedtls_cipher_free(&ctx->cipher_dec);
mbedtls_cipher_free(&ctx->cipher_enc); mbedtls_cipher_free(&ctx->cipher_enc);
free(ctx); free(ctx);
@ -65,17 +65,17 @@ void aes_calculate_cmac(void *dst, void *src, size_t size, const void *key) {
/* Encrypt with context. */ /* Encrypt with context. */
void aes_encrypt(aes_ctx_t *ctx, void *dst, const void *src, size_t l) { void aes_encrypt(aes_ctx_t *ctx, void *dst, const void *src, size_t l) {
size_t out_len = 0; size_t out_len = 0;
/* Prepare context */ /* Prepare context */
mbedtls_cipher_reset(&ctx->cipher_enc); mbedtls_cipher_reset(&ctx->cipher_enc);
/* XTS doesn't need per-block updating */ /* XTS doesn't need per-block updating */
if (mbedtls_cipher_get_cipher_mode(&ctx->cipher_enc) == MBEDTLS_MODE_XTS) if (mbedtls_cipher_get_cipher_mode(&ctx->cipher_enc) == MBEDTLS_MODE_XTS)
mbedtls_cipher_update(&ctx->cipher_enc, (const unsigned char * )src, l, (unsigned char *)dst, &out_len); mbedtls_cipher_update(&ctx->cipher_enc, (const unsigned char * )src, l, (unsigned char *)dst, &out_len);
else else
{ {
unsigned int blk_size = mbedtls_cipher_get_block_size(&ctx->cipher_enc); unsigned int blk_size = mbedtls_cipher_get_block_size(&ctx->cipher_enc);
/* Do per-block updating */ /* Do per-block updating */
for (int offset = 0; (unsigned int)offset < l; offset += blk_size) for (int offset = 0; (unsigned int)offset < l; offset += blk_size)
{ {
@ -83,15 +83,15 @@ void aes_encrypt(aes_ctx_t *ctx, void *dst, const void *src, size_t l) {
mbedtls_cipher_update(&ctx->cipher_enc, (const unsigned char * )src + offset, len, (unsigned char *)dst + offset, &out_len); mbedtls_cipher_update(&ctx->cipher_enc, (const unsigned char * )src + offset, len, (unsigned char *)dst + offset, &out_len);
} }
} }
/* Flush all data */ /* Flush all data */
mbedtls_cipher_finish(&ctx->cipher_enc, NULL, NULL); mbedtls_cipher_finish(&ctx->cipher_enc, NULL, NULL);
} }
/* Decrypt with context. */ /* Decrypt with context. */
void aes_decrypt(aes_ctx_t *ctx, void *dst, const void *src, size_t l) void aes_decrypt(aes_ctx_t *ctx, void *dst, const void *src, size_t l)
{ {
bool src_equals_dst = false; bool src_equals_dst = false;
if (src == dst) if (src == dst)
{ {
@ -105,17 +105,17 @@ void aes_decrypt(aes_ctx_t *ctx, void *dst, const void *src, size_t l)
} }
size_t out_len = 0; size_t out_len = 0;
/* Prepare context */ /* Prepare context */
mbedtls_cipher_reset(&ctx->cipher_dec); mbedtls_cipher_reset(&ctx->cipher_dec);
/* XTS doesn't need per-block updating */ /* XTS doesn't need per-block updating */
if (mbedtls_cipher_get_cipher_mode(&ctx->cipher_dec) == MBEDTLS_MODE_XTS) if (mbedtls_cipher_get_cipher_mode(&ctx->cipher_dec) == MBEDTLS_MODE_XTS)
mbedtls_cipher_update(&ctx->cipher_dec, (const unsigned char * )src, l, (unsigned char *)dst, &out_len); mbedtls_cipher_update(&ctx->cipher_dec, (const unsigned char * )src, l, (unsigned char *)dst, &out_len);
else else
{ {
unsigned int blk_size = mbedtls_cipher_get_block_size(&ctx->cipher_dec); unsigned int blk_size = mbedtls_cipher_get_block_size(&ctx->cipher_dec);
/* Do per-block updating */ /* Do per-block updating */
for (int offset = 0; (unsigned int)offset < l; offset += blk_size) for (int offset = 0; (unsigned int)offset < l; offset += blk_size)
{ {
@ -123,7 +123,7 @@ void aes_decrypt(aes_ctx_t *ctx, void *dst, const void *src, size_t l)
mbedtls_cipher_update(&ctx->cipher_dec, (const unsigned char * )src + offset, len, (unsigned char *)dst + offset, &out_len); mbedtls_cipher_update(&ctx->cipher_dec, (const unsigned char * )src + offset, len, (unsigned char *)dst + offset, &out_len);
} }
} }
/* Flush all data */ /* Flush all data */
mbedtls_cipher_finish(&ctx->cipher_dec, NULL, NULL); mbedtls_cipher_finish(&ctx->cipher_dec, NULL, NULL);

16
bktr.c
View file

@ -12,20 +12,20 @@ bktr_relocation_entry_t *bktr_get_relocation(bktr_relocation_block_t *block, uin
fprintf(stderr, "Too big offset looked up in BKTR relocation table!\n"); fprintf(stderr, "Too big offset looked up in BKTR relocation table!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
uint32_t bucket_num = 0; uint32_t bucket_num = 0;
for (unsigned int i = 1; i < block->num_buckets; i++) { for (unsigned int i = 1; i < block->num_buckets; i++) {
if (block->bucket_virtual_offsets[i] <= offset) { if (block->bucket_virtual_offsets[i] <= offset) {
bucket_num++; bucket_num++;
} }
} }
bktr_relocation_bucket_t *bucket = bktr_get_relocation_bucket(block, bucket_num); bktr_relocation_bucket_t *bucket = bktr_get_relocation_bucket(block, bucket_num);
if (bucket->num_entries == 1) { /* Check for edge case, short circuit. */ if (bucket->num_entries == 1) { /* Check for edge case, short circuit. */
return &bucket->entries[0]; return &bucket->entries[0];
} }
/* Binary search. */ /* Binary search. */
uint32_t low = 0, high = bucket->num_entries - 1; uint32_t low = 0, high = bucket->num_entries - 1;
while (low <= high) { while (low <= high) {
@ -55,20 +55,20 @@ bktr_subsection_entry_t *bktr_get_subsection(bktr_subsection_block_t *block, uin
if (offset >= last_bucket->entries[last_bucket->num_entries].offset) { if (offset >= last_bucket->entries[last_bucket->num_entries].offset) {
return &last_bucket->entries[last_bucket->num_entries]; return &last_bucket->entries[last_bucket->num_entries];
} }
uint32_t bucket_num = 0; uint32_t bucket_num = 0;
for (unsigned int i = 1; i < block->num_buckets; i++) { for (unsigned int i = 1; i < block->num_buckets; i++) {
if (block->bucket_physical_offsets[i] <= offset) { if (block->bucket_physical_offsets[i] <= offset) {
bucket_num++; bucket_num++;
} }
} }
bktr_subsection_bucket_t *bucket = bktr_get_subsection_bucket(block, bucket_num); bktr_subsection_bucket_t *bucket = bktr_get_subsection_bucket(block, bucket_num);
if (bucket->num_entries == 1) { /* Check for edge case, short circuit. */ if (bucket->num_entries == 1) { /* Check for edge case, short circuit. */
return &bucket->entries[0]; return &bucket->entries[0];
} }
/* Binary search. */ /* Binary search. */
uint32_t low = 0, high = bucket->num_entries - 1; uint32_t low = 0, high = bucket->num_entries - 1;
while (low <= high) { while (low <= high) {

View file

@ -178,7 +178,7 @@ void parse_hex_key(unsigned char *key, const char *hex, unsigned int len) {
void extkeys_parse_titlekeys(hactool_settings_t *settings, FILE *f) { void extkeys_parse_titlekeys(hactool_settings_t *settings, FILE *f) {
char *key, *value; char *key, *value;
int ret; int ret;
while ((ret = get_kv(f, &key, &value)) != 1 && ret != -2) { while ((ret = get_kv(f, &key, &value)) != 1 && ret != -2) {
if (ret == 0) { if (ret == 0) {
if (key == NULL || value == NULL) { if (key == NULL || value == NULL) {
@ -186,7 +186,7 @@ void extkeys_parse_titlekeys(hactool_settings_t *settings, FILE *f) {
} }
unsigned char rights_id[0x10]; unsigned char rights_id[0x10];
unsigned char titlekey[0x10]; unsigned char titlekey[0x10];
bool should_ignore_key = false; bool should_ignore_key = false;
if (strlen(key) != 0x20) { if (strlen(key) != 0x20) {
should_ignore_key = true; should_ignore_key = true;
@ -214,7 +214,7 @@ void extkeys_initialize_settings(hactool_settings_t *settings, FILE *f) {
char *key, *value; char *key, *value;
int ret; int ret;
nca_keyset_t *keyset = &settings->keyset; nca_keyset_t *keyset = &settings->keyset;
while ((ret = get_kv(f, &key, &value)) != 1 && ret != -2) { while ((ret = get_kv(f, &key, &value)) != 1 && ret != -2) {
if (ret == 0) { if (ret == 0) {
if (key == NULL || value == NULL) { if (key == NULL || value == NULL) {
@ -225,7 +225,7 @@ void extkeys_initialize_settings(hactool_settings_t *settings, FILE *f) {
parse_hex_key(keyset->aes_kek_generation_source, value, sizeof(keyset->aes_kek_generation_source)); parse_hex_key(keyset->aes_kek_generation_source, value, sizeof(keyset->aes_kek_generation_source));
matched_key = 1; matched_key = 1;
} else if (strcmp(key, "aes_key_generation_source") == 0) { } else if (strcmp(key, "aes_key_generation_source") == 0) {
parse_hex_key(keyset->aes_key_generation_source, value, sizeof(keyset->aes_key_generation_source)); parse_hex_key(keyset->aes_key_generation_source, value, sizeof(keyset->aes_key_generation_source));
matched_key = 1; matched_key = 1;
} else if (strcmp(key, "key_area_key_application_source") == 0) { } else if (strcmp(key, "key_area_key_application_source") == 0) {
parse_hex_key(keyset->key_area_key_application_source, value, sizeof(keyset->key_area_key_application_source)); parse_hex_key(keyset->key_area_key_application_source, value, sizeof(keyset->key_area_key_application_source));
@ -307,28 +307,28 @@ void extkeys_initialize_settings(hactool_settings_t *settings, FILE *f) {
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "keyblob_key_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "keyblob_key_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->keyblob_keys[i], value, sizeof(keyset->keyblob_keys[i])); parse_hex_key(keyset->keyblob_keys[i], value, sizeof(keyset->keyblob_keys[i]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "keyblob_mac_key_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "keyblob_mac_key_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->keyblob_mac_keys[i], value, sizeof(keyset->keyblob_mac_keys[i])); parse_hex_key(keyset->keyblob_mac_keys[i], value, sizeof(keyset->keyblob_mac_keys[i]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "encrypted_keyblob_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "encrypted_keyblob_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->encrypted_keyblobs[i], value, sizeof(keyset->encrypted_keyblobs[i])); parse_hex_key(keyset->encrypted_keyblobs[i], value, sizeof(keyset->encrypted_keyblobs[i]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "keyblob_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "keyblob_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->keyblobs[i], value, sizeof(keyset->keyblobs[i])); parse_hex_key(keyset->keyblobs[i], value, sizeof(keyset->keyblobs[i]));
@ -343,21 +343,21 @@ void extkeys_initialize_settings(hactool_settings_t *settings, FILE *f) {
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "tsec_root_key_%02"PRIx32, i - 6); snprintf(test_name, sizeof(test_name), "tsec_root_key_%02"PRIx32, i - 6);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->tsec_root_keys[i - 6], value, sizeof(keyset->tsec_root_keys[i - 6])); parse_hex_key(keyset->tsec_root_keys[i - 6], value, sizeof(keyset->tsec_root_keys[i - 6]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "master_kek_source_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "master_kek_source_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->master_kek_sources[i], value, sizeof(keyset->master_kek_sources[i])); parse_hex_key(keyset->master_kek_sources[i], value, sizeof(keyset->master_kek_sources[i]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "package1_mac_key_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "package1_mac_key_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->package1_mac_keys[i], value, sizeof(keyset->package1_mac_keys[i])); parse_hex_key(keyset->package1_mac_keys[i], value, sizeof(keyset->package1_mac_keys[i]));
@ -365,56 +365,56 @@ void extkeys_initialize_settings(hactool_settings_t *settings, FILE *f) {
break; break;
} }
} }
for (unsigned int i = 0; i < 0x20 && !matched_key; i++) { for (unsigned int i = 0; i < 0x20 && !matched_key; i++) {
snprintf(test_name, sizeof(test_name), "master_kek_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "master_kek_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->master_keks[i], value, sizeof(keyset->master_keks[i])); parse_hex_key(keyset->master_keks[i], value, sizeof(keyset->master_keks[i]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "master_key_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "master_key_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->master_keys[i], value, sizeof(keyset->master_keys[i])); parse_hex_key(keyset->master_keys[i], value, sizeof(keyset->master_keys[i]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "package1_key_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "package1_key_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->package1_keys[i], value, sizeof(keyset->package1_keys[i])); parse_hex_key(keyset->package1_keys[i], value, sizeof(keyset->package1_keys[i]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "package2_key_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "package2_key_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->package2_keys[i], value, sizeof(keyset->package2_keys[i])); parse_hex_key(keyset->package2_keys[i], value, sizeof(keyset->package2_keys[i]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "titlekek_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "titlekek_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->titlekeks[i], value, sizeof(keyset->titlekeks[i])); parse_hex_key(keyset->titlekeks[i], value, sizeof(keyset->titlekeks[i]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "key_area_key_application_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "key_area_key_application_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->key_area_keys[i][0], value, sizeof(keyset->key_area_keys[i][0])); parse_hex_key(keyset->key_area_keys[i][0], value, sizeof(keyset->key_area_keys[i][0]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "key_area_key_ocean_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "key_area_key_ocean_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->key_area_keys[i][1], value, sizeof(keyset->key_area_keys[i][1])); parse_hex_key(keyset->key_area_keys[i][1], value, sizeof(keyset->key_area_keys[i][1]));
matched_key = 1; matched_key = 1;
break; break;
} }
snprintf(test_name, sizeof(test_name), "key_area_key_system_%02"PRIx32, i); snprintf(test_name, sizeof(test_name), "key_area_key_system_%02"PRIx32, i);
if (strcmp(key, test_name) == 0) { if (strcmp(key, test_name) == 0) {
parse_hex_key(keyset->key_area_keys[i][2], value, sizeof(keyset->key_area_keys[i][2])); parse_hex_key(keyset->key_area_keys[i][2], value, sizeof(keyset->key_area_keys[i][2]));
@ -444,7 +444,7 @@ void settings_add_titlekey(hactool_settings_t *settings, const unsigned char *ri
fprintf(stderr, " already has a corresponding titlekey!\n"); fprintf(stderr, " already has a corresponding titlekey!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* Ensure enough space for keys. */ /* Ensure enough space for keys. */
if (settings->known_titlekeys.count == 0) { if (settings->known_titlekeys.count == 0) {
settings->known_titlekeys.titlekeys = malloc(1 * sizeof(titlekey_entry_t)); settings->known_titlekeys.titlekeys = malloc(1 * sizeof(titlekey_entry_t));
@ -456,9 +456,9 @@ void settings_add_titlekey(hactool_settings_t *settings, const unsigned char *ri
fprintf(stderr, "Failed to allocate titlekey list!\n"); fprintf(stderr, "Failed to allocate titlekey list!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
titlekey_entry_t *new_key = &settings->known_titlekeys.titlekeys[settings->known_titlekeys.count++]; titlekey_entry_t *new_key = &settings->known_titlekeys.titlekeys[settings->known_titlekeys.count++];
memcpy(new_key->rights_id, rights_id, 0x10); memcpy(new_key->rights_id, rights_id, 0x10);
memcpy(new_key->titlekey, titlekey, 0x10); memcpy(new_key->titlekey, titlekey, 0x10);
} }
@ -469,7 +469,7 @@ titlekey_entry_t *settings_get_titlekey(hactool_settings_t *settings, const unsi
return &settings->known_titlekeys.titlekeys[i]; return &settings->known_titlekeys.titlekeys[i];
} }
} }
return NULL; return NULL;
} }

14
hfs0.c
View file

@ -3,13 +3,13 @@
void hfs0_process(hfs0_ctx_t *ctx) { void hfs0_process(hfs0_ctx_t *ctx) {
/* Read *just* safe amount. */ /* Read *just* safe amount. */
hfs0_header_t raw_header; hfs0_header_t raw_header;
fseeko64(ctx->file, ctx->offset, SEEK_SET); fseeko64(ctx->file, ctx->offset, SEEK_SET);
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) { if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
fprintf(stderr, "Failed to read HFS0 header!\n"); fprintf(stderr, "Failed to read HFS0 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (raw_header.magic != MAGIC_HFS0) { if (raw_header.magic != MAGIC_HFS0) {
memdump(stdout, "Sanity: ", &raw_header, sizeof(raw_header)); memdump(stdout, "Sanity: ", &raw_header, sizeof(raw_header));
printf("Error: HFS0 is corrupt!\n"); printf("Error: HFS0 is corrupt!\n");
@ -22,16 +22,16 @@ void hfs0_process(hfs0_ctx_t *ctx) {
fprintf(stderr, "Failed to allocate HFS0 header!\n"); fprintf(stderr, "Failed to allocate HFS0 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
fseeko64(ctx->file, ctx->offset, SEEK_SET); fseeko64(ctx->file, ctx->offset, SEEK_SET);
if (fread(ctx->header, 1, header_size, ctx->file) != header_size) { if (fread(ctx->header, 1, header_size, ctx->file) != header_size) {
fprintf(stderr, "Failed to read HFS0 header!\n"); fprintf(stderr, "Failed to read HFS0 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* Weak file validation. */ /* Weak file validation. */
uint64_t max_size = 0x1ULL; uint64_t max_size = 0x1ULL;
max_size <<= 48; /* Switch file sizes are capped at 48 bits. */ max_size <<= 48; /* Switch file sizes are capped at 48 bits. */
uint64_t cur_ofs = 0; uint64_t cur_ofs = 0;
for (unsigned int i = 0; i < ctx->header->num_files; i++) { for (unsigned int i = 0; i < ctx->header->num_files; i++) {
hfs0_file_entry_t *cur_file = hfs0_get_file_entry(ctx->header, i); hfs0_file_entry_t *cur_file = hfs0_get_file_entry(ctx->header, i);
@ -41,11 +41,11 @@ void hfs0_process(hfs0_ctx_t *ctx) {
} }
cur_ofs += cur_file->size; cur_ofs += cur_file->size;
} }
if (ctx->tool_ctx->action & ACTION_INFO) { if (ctx->tool_ctx->action & ACTION_INFO) {
hfs0_print(ctx); hfs0_print(ctx);
} }
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
hfs0_save(ctx); hfs0_save(ctx);
} }

42
kip.c
View file

@ -6,13 +6,13 @@
void ini1_process(ini1_ctx_t *ctx) { void ini1_process(ini1_ctx_t *ctx) {
/* Read *just* safe amount. */ /* Read *just* safe amount. */
ini1_header_t raw_header; ini1_header_t raw_header;
fseeko64(ctx->file, 0, SEEK_SET); fseeko64(ctx->file, 0, SEEK_SET);
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) { if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
fprintf(stderr, "Failed to read INI1 header!\n"); fprintf(stderr, "Failed to read INI1 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (raw_header.magic != MAGIC_INI1 || raw_header.num_processes > INI1_MAX_KIPS) { if (raw_header.magic != MAGIC_INI1 || raw_header.num_processes > INI1_MAX_KIPS) {
printf("Error: INI1 is corrupt!\n"); printf("Error: INI1 is corrupt!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -23,13 +23,13 @@ void ini1_process(ini1_ctx_t *ctx) {
fprintf(stderr, "Failed to allocate INI1 header!\n"); fprintf(stderr, "Failed to allocate INI1 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
fseeko64(ctx->file, 0, SEEK_SET); fseeko64(ctx->file, 0, SEEK_SET);
if (fread(ctx->header, 1, raw_header.size, ctx->file) != raw_header.size) { if (fread(ctx->header, 1, raw_header.size, ctx->file) != raw_header.size) {
fprintf(stderr, "Failed to read INI1!\n"); fprintf(stderr, "Failed to read INI1!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
uint64_t offset = 0; uint64_t offset = 0;
for (unsigned int i = 0; i < ctx->header->num_processes; i++) { for (unsigned int i = 0; i < ctx->header->num_processes; i++) {
ctx->kips[i].tool_ctx = ctx->tool_ctx; ctx->kips[i].tool_ctx = ctx->tool_ctx;
@ -40,11 +40,11 @@ void ini1_process(ini1_ctx_t *ctx) {
} }
offset += kip1_get_size(&ctx->kips[i]); offset += kip1_get_size(&ctx->kips[i]);
} }
if (ctx->tool_ctx->action & ACTION_INFO) { if (ctx->tool_ctx->action & ACTION_INFO) {
ini1_print(ctx); ini1_print(ctx);
} }
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
ini1_save(ctx); ini1_save(ctx);
} }
@ -109,7 +109,7 @@ char *kip1_get_json(kip1_ctx_t *ctx) {
cJSON *kip_json = cJSON_CreateObject(); cJSON *kip_json = cJSON_CreateObject();
char *output_str = NULL; char *output_str = NULL;
char work_buffer[0x300] = {0}; char work_buffer[0x300] = {0};
/* Add KIP1 header fields. */ /* Add KIP1 header fields. */
strcpy(work_buffer, ctx->header->name); strcpy(work_buffer, ctx->header->name);
cJSON_AddStringToObject(kip_json, "name", work_buffer); cJSON_AddStringToObject(kip_json, "name", work_buffer);
@ -118,13 +118,13 @@ char *kip1_get_json(kip1_ctx_t *ctx) {
cJSON_AddNumberToObject(kip_json, "main_thread_priority", ctx->header->main_thread_priority); cJSON_AddNumberToObject(kip_json, "main_thread_priority", ctx->header->main_thread_priority);
cJSON_AddNumberToObject(kip_json, "default_cpu_id", ctx->header->default_core); cJSON_AddNumberToObject(kip_json, "default_cpu_id", ctx->header->default_core);
cJSON_AddNumberToObject(kip_json, "process_category", ctx->header->process_category); cJSON_AddNumberToObject(kip_json, "process_category", ctx->header->process_category);
/* Add KAC. */ /* Add KAC. */
cJSON *kac_json = kac_get_json(ctx->header->capabilities, sizeof(ctx->header->capabilities) / sizeof(uint32_t)); cJSON *kac_json = kac_get_json(ctx->header->capabilities, sizeof(ctx->header->capabilities) / sizeof(uint32_t));
cJSON_AddItemToObject(kip_json, "kernel_capabilities", kac_json); cJSON_AddItemToObject(kip_json, "kernel_capabilities", kac_json);
output_str = cJSON_Print(kip_json); output_str = cJSON_Print(kip_json);
cJSON_Delete(kip_json); cJSON_Delete(kip_json);
return output_str; return output_str;
} }
@ -133,11 +133,11 @@ static void kip1_blz_uncompress(void *hdr_end) {
uint32_t addl_size = ((uint32_t *)hdr_end)[-1]; uint32_t addl_size = ((uint32_t *)hdr_end)[-1];
uint32_t header_size = ((uint32_t *)hdr_end)[-2]; uint32_t header_size = ((uint32_t *)hdr_end)[-2];
uint32_t cmp_and_hdr_size = ((uint32_t *)hdr_end)[-3]; uint32_t cmp_and_hdr_size = ((uint32_t *)hdr_end)[-3];
unsigned char *cmp_start = (unsigned char *)(((uintptr_t)hdr_end) - cmp_and_hdr_size); unsigned char *cmp_start = (unsigned char *)(((uintptr_t)hdr_end) - cmp_and_hdr_size);
uint32_t cmp_ofs = cmp_and_hdr_size - header_size; uint32_t cmp_ofs = cmp_and_hdr_size - header_size;
uint32_t out_ofs = cmp_and_hdr_size + addl_size; uint32_t out_ofs = cmp_and_hdr_size + addl_size;
while (out_ofs) { while (out_ofs) {
unsigned char control = cmp_start[--cmp_ofs]; unsigned char control = cmp_start[--cmp_ofs];
for (unsigned int i = 0; i < 8; i++) { for (unsigned int i = 0; i < 8; i++) {
@ -155,7 +155,7 @@ static void kip1_blz_uncompress(void *hdr_end) {
seg_size = out_ofs; seg_size = out_ofs;
} }
out_ofs -= seg_size; out_ofs -= seg_size;
for (unsigned int j = 0; j < seg_size; j++) { for (unsigned int j = 0; j < seg_size; j++) {
cmp_start[out_ofs + j] = cmp_start[out_ofs + j + seg_ofs]; cmp_start[out_ofs + j] = cmp_start[out_ofs + j + seg_ofs];
} }
@ -182,7 +182,7 @@ static void *kip1_uncompress(kip1_ctx_t *ctx, uint64_t *size) {
new_header.section_headers[i].compressed_size = new_header.section_headers[i].out_size; new_header.section_headers[i].compressed_size = new_header.section_headers[i].out_size;
} }
new_header.flags &= 0xF8; new_header.flags &= 0xF8;
*size = kip1_get_size_from_header(&new_header); *size = kip1_get_size_from_header(&new_header);
unsigned char *new_kip = calloc(1, *size); unsigned char *new_kip = calloc(1, *size);
if (new_kip == NULL) { if (new_kip == NULL) {
@ -190,7 +190,7 @@ static void *kip1_uncompress(kip1_ctx_t *ctx, uint64_t *size) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
*((kip1_header_t *)new_kip) = new_header; *((kip1_header_t *)new_kip) = new_header;
uint64_t new_offset = 0x100; uint64_t new_offset = 0x100;
uint64_t old_offset = 0x100; uint64_t old_offset = 0x100;
for (unsigned int i = 0; i < 3; i++) { for (unsigned int i = 0; i < 3; i++) {
@ -200,19 +200,19 @@ static void *kip1_uncompress(kip1_ctx_t *ctx, uint64_t *size) {
new_offset += ctx->header->section_headers[i].out_size; new_offset += ctx->header->section_headers[i].out_size;
old_offset += ctx->header->section_headers[i].compressed_size; old_offset += ctx->header->section_headers[i].compressed_size;
} }
return new_kip; return new_kip;
} }
void kip1_process(kip1_ctx_t *ctx) { void kip1_process(kip1_ctx_t *ctx) {
/* Read *just* safe amount. */ /* Read *just* safe amount. */
kip1_header_t raw_header; kip1_header_t raw_header;
fseeko64(ctx->file, 0, SEEK_SET); fseeko64(ctx->file, 0, SEEK_SET);
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) { if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
fprintf(stderr, "Failed to read KIP1 header!\n"); fprintf(stderr, "Failed to read KIP1 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (raw_header.magic != MAGIC_KIP1) { if (raw_header.magic != MAGIC_KIP1) {
printf("Error: KIP1 is corrupt!\n"); printf("Error: KIP1 is corrupt!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -224,17 +224,17 @@ void kip1_process(kip1_ctx_t *ctx) {
fprintf(stderr, "Failed to allocate KIP1!\n"); fprintf(stderr, "Failed to allocate KIP1!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
fseeko64(ctx->file, 0, SEEK_SET); fseeko64(ctx->file, 0, SEEK_SET);
if (fread(ctx->header, 1, size, ctx->file) != size) { if (fread(ctx->header, 1, size, ctx->file) != size) {
fprintf(stderr, "Failed to read KIP1!\n"); fprintf(stderr, "Failed to read KIP1!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (ctx->tool_ctx->action & ACTION_INFO) { if (ctx->tool_ctx->action & ACTION_INFO) {
kip1_print(ctx, 0); kip1_print(ctx, 0);
} }
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
kip1_save(ctx); kip1_save(ctx);
} }

76
main.c
View file

@ -19,7 +19,7 @@ static const char *prog_name = "hactool";
/* Print usage. Taken largely from ctrtool. */ /* Print usage. Taken largely from ctrtool. */
static void usage(void) { static void usage(void) {
fprintf(stderr, fprintf(stderr,
"hactool (c) SciresM.\n" "hactool (c) SciresM.\n"
"Built: %s %s\n" "Built: %s %s\n"
"\n" "\n"
@ -56,7 +56,7 @@ static void usage(void) {
" --baseromfs Set Base RomFS to use with update partitions.\n" " --baseromfs Set Base RomFS to use with update partitions.\n"
" --basenca Set Base NCA to use with update partitions.\n" " --basenca Set Base NCA to use with update partitions.\n"
" --basefake Use a fake Base RomFS with update partitions (all reads will return 0xCC).\n" " --basefake Use a fake Base RomFS with update partitions (all reads will return 0xCC).\n"
" --onlyupdated Ignore non-updated files in update partitions.\n" " --onlyupdated Ignore non-updated files in update partitions.\n"
"NPDM options:\n" "NPDM options:\n"
" --json=file Specify file path for saving JSON representation of program permissions to.\n" " --json=file Specify file path for saving JSON representation of program permissions to.\n"
"KIP1 options:\n" "KIP1 options:\n"
@ -124,7 +124,7 @@ int main(int argc, char **argv) {
filepath_init(&keypath); filepath_init(&keypath);
nca_ctx.tool_ctx = &tool_ctx; nca_ctx.tool_ctx = &tool_ctx;
nca_ctx.is_cli_target = true; nca_ctx.is_cli_target = true;
nca_ctx.tool_ctx->file_type = FILETYPE_NCA; nca_ctx.tool_ctx->file_type = FILETYPE_NCA;
base_ctx.file_type = FILETYPE_NCA; base_ctx.file_type = FILETYPE_NCA;
@ -134,7 +134,7 @@ int main(int argc, char **argv) {
while (1) { while (1) {
int option_index; int option_index;
int c; int c;
static struct option long_options[] = static struct option long_options[] =
{ {
{"extract", 0, NULL, 'x'}, {"extract", 0, NULL, 'x'},
{"info", 0, NULL, 'i'}, {"info", 0, NULL, 'i'},
@ -192,7 +192,7 @@ int main(int argc, char **argv) {
if (c == -1) if (c == -1)
break; break;
switch (c) switch (c)
{ {
case 'i': case 'i':
nca_ctx.tool_ctx->action |= ACTION_INFO; nca_ctx.tool_ctx->action |= ACTION_INFO;
@ -219,9 +219,9 @@ int main(int argc, char **argv) {
} else if (!strcmp(optarg, "pfs0") || !strcmp(optarg, "exefs")) { } else if (!strcmp(optarg, "pfs0") || !strcmp(optarg, "exefs")) {
nca_ctx.tool_ctx->file_type = FILETYPE_PFS0; nca_ctx.tool_ctx->file_type = FILETYPE_PFS0;
} else if (!strcmp(optarg, "romfs")) { } else if (!strcmp(optarg, "romfs")) {
nca_ctx.tool_ctx->file_type = FILETYPE_ROMFS; nca_ctx.tool_ctx->file_type = FILETYPE_ROMFS;
} else if (!strcmp(optarg, "nca0_romfs") || !strcmp(optarg, "nca0romfs") || !strcmp(optarg, "betaromfs") || !strcmp(optarg, "beta_romfs")) { } else if (!strcmp(optarg, "nca0_romfs") || !strcmp(optarg, "nca0romfs") || !strcmp(optarg, "betaromfs") || !strcmp(optarg, "beta_romfs")) {
nca_ctx.tool_ctx->file_type = FILETYPE_NCA0_ROMFS; nca_ctx.tool_ctx->file_type = FILETYPE_NCA0_ROMFS;
} else if (!strcmp(optarg, "hfs0")) { } else if (!strcmp(optarg, "hfs0")) {
nca_ctx.tool_ctx->file_type = FILETYPE_HFS0; nca_ctx.tool_ctx->file_type = FILETYPE_HFS0;
} else if (!strcmp(optarg, "xci") || !strcmp(optarg, "gamecard") || !strcmp(optarg, "gc")) { } else if (!strcmp(optarg, "xci") || !strcmp(optarg, "gamecard") || !strcmp(optarg, "gc")) {
@ -256,19 +256,19 @@ int main(int argc, char **argv) {
case 7: filepath_set(&nca_ctx.tool_ctx->settings.section_dir_paths[3], optarg); break; case 7: filepath_set(&nca_ctx.tool_ctx->settings.section_dir_paths[3], optarg); break;
case 8: case 8:
nca_ctx.tool_ctx->settings.exefs_path.enabled = 1; nca_ctx.tool_ctx->settings.exefs_path.enabled = 1;
filepath_set(&nca_ctx.tool_ctx->settings.exefs_path.path, optarg); filepath_set(&nca_ctx.tool_ctx->settings.exefs_path.path, optarg);
break; break;
case 9: case 9:
nca_ctx.tool_ctx->settings.romfs_path.enabled = 1; nca_ctx.tool_ctx->settings.romfs_path.enabled = 1;
filepath_set(&nca_ctx.tool_ctx->settings.romfs_path.path, optarg); filepath_set(&nca_ctx.tool_ctx->settings.romfs_path.path, optarg);
break; break;
case 10: case 10:
nca_ctx.tool_ctx->settings.exefs_dir_path.enabled = 1; nca_ctx.tool_ctx->settings.exefs_dir_path.enabled = 1;
filepath_set(&nca_ctx.tool_ctx->settings.exefs_dir_path.path, optarg); filepath_set(&nca_ctx.tool_ctx->settings.exefs_dir_path.path, optarg);
break; break;
case 11: case 11:
nca_ctx.tool_ctx->settings.romfs_dir_path.enabled = 1; nca_ctx.tool_ctx->settings.romfs_dir_path.enabled = 1;
filepath_set(&nca_ctx.tool_ctx->settings.romfs_dir_path.path, optarg); filepath_set(&nca_ctx.tool_ctx->settings.romfs_dir_path.path, optarg);
break; break;
case 12: case 12:
parse_hex_key(nca_ctx.tool_ctx->settings.cli_titlekey, optarg, 16); parse_hex_key(nca_ctx.tool_ctx->settings.cli_titlekey, optarg, 16);
@ -314,43 +314,43 @@ int main(int argc, char **argv) {
break; break;
case 17: case 17:
tool_ctx.settings.out_dir_path.enabled = 1; tool_ctx.settings.out_dir_path.enabled = 1;
filepath_set(&tool_ctx.settings.out_dir_path.path, optarg); filepath_set(&tool_ctx.settings.out_dir_path.path, optarg);
break; break;
case 18: case 18:
filepath_set(&nca_ctx.tool_ctx->settings.plaintext_path, optarg); filepath_set(&nca_ctx.tool_ctx->settings.plaintext_path, optarg);
break; break;
case 19: case 19:
filepath_set(&nca_ctx.tool_ctx->settings.header_path, optarg); filepath_set(&nca_ctx.tool_ctx->settings.header_path, optarg);
break; break;
case 20: case 20:
filepath_set(&tool_ctx.settings.pfs0_dir_path, optarg); filepath_set(&tool_ctx.settings.pfs0_dir_path, optarg);
break; break;
case 21: case 21:
filepath_set(&tool_ctx.settings.hfs0_dir_path, optarg); filepath_set(&tool_ctx.settings.hfs0_dir_path, optarg);
break; break;
case 22: case 22:
filepath_set(&tool_ctx.settings.rootpt_dir_path, optarg); filepath_set(&tool_ctx.settings.rootpt_dir_path, optarg);
break; break;
case 23: case 23:
filepath_set(&tool_ctx.settings.update_dir_path, optarg); filepath_set(&tool_ctx.settings.update_dir_path, optarg);
break; break;
case 24: case 24:
filepath_set(&tool_ctx.settings.normal_dir_path, optarg); filepath_set(&tool_ctx.settings.normal_dir_path, optarg);
break; break;
case 25: case 25:
filepath_set(&tool_ctx.settings.secure_dir_path, optarg); filepath_set(&tool_ctx.settings.secure_dir_path, optarg);
break; break;
case 26: case 26:
filepath_set(&tool_ctx.settings.logo_dir_path, optarg); filepath_set(&tool_ctx.settings.logo_dir_path, optarg);
break; break;
case 27: case 27:
filepath_set(&tool_ctx.settings.pk11_dir_path, optarg); filepath_set(&tool_ctx.settings.pk11_dir_path, optarg);
break; break;
case 28: case 28:
filepath_set(&tool_ctx.settings.pk21_dir_path, optarg); filepath_set(&tool_ctx.settings.pk21_dir_path, optarg);
break; break;
case 29: case 29:
filepath_set(&tool_ctx.settings.ini1_dir_path, optarg); filepath_set(&tool_ctx.settings.ini1_dir_path, optarg);
break; break;
case 30: case 30:
tool_ctx.action |= ACTION_EXTRACTINI1; tool_ctx.action |= ACTION_EXTRACTINI1;
@ -386,13 +386,13 @@ int main(int argc, char **argv) {
parse_hex_key(nca_ctx.tool_ctx->settings.keygen_tsec, optarg, 16); parse_hex_key(nca_ctx.tool_ctx->settings.keygen_tsec, optarg, 16);
break; break;
case 37: case 37:
filepath_set(&tool_ctx.settings.npdm_json_path, optarg); filepath_set(&tool_ctx.settings.npdm_json_path, optarg);
break; break;
case 38: case 38:
tool_ctx.action |= ACTION_SAVEINIJSON; tool_ctx.action |= ACTION_SAVEINIJSON;
break; break;
case 39: case 39:
filepath_set(&nca_ctx.tool_ctx->settings.uncompressed_path, optarg); filepath_set(&nca_ctx.tool_ctx->settings.uncompressed_path, optarg);
break; break;
case 40: case 40:
nca_ctx.tool_ctx->settings.skip_key_warnings = 1; nca_ctx.tool_ctx->settings.skip_key_warnings = 1;
@ -405,7 +405,7 @@ int main(int argc, char **argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
/* Try to populate default keyfile. */ /* Try to populate default keyfile. */
FILE *keyfile = NULL; FILE *keyfile = NULL;
if (keypath.valid == VALIDITY_VALID) { if (keypath.valid == VALIDITY_VALID) {
@ -432,7 +432,7 @@ int main(int argc, char **argv) {
pki_derive_keys(&tool_ctx.settings.keyset); pki_derive_keys(&tool_ctx.settings.keyset);
fclose(keyfile); fclose(keyfile);
} }
/* Try to load titlekeys. */ /* Try to load titlekeys. */
FILE *titlekeyfile = open_key_file("title"); FILE *titlekeyfile = open_key_file("title");
if (titlekeyfile != NULL) { if (titlekeyfile != NULL) {
@ -445,7 +445,7 @@ int main(int argc, char **argv) {
} else if (tool_ctx.file_type != FILETYPE_BOOT0 && ((optind < argc) || (argc == 1))) { } else if (tool_ctx.file_type != FILETYPE_BOOT0 && ((optind < argc) || (argc == 1))) {
usage(); usage();
} }
/* Special case NAX0. */ /* Special case NAX0. */
if (tool_ctx.file_type == FILETYPE_NAX0) { if (tool_ctx.file_type == FILETYPE_NAX0) {
nax0_ctx_t nax_ctx; nax0_ctx_t nax_ctx;
@ -453,10 +453,10 @@ int main(int argc, char **argv) {
filepath_set(&nax_ctx.base_path, input_name); filepath_set(&nax_ctx.base_path, input_name);
nax_ctx.tool_ctx = &tool_ctx; nax_ctx.tool_ctx = &tool_ctx;
nax0_process(&nax_ctx); nax0_process(&nax_ctx);
if (nax_ctx.aes_ctx) { if (nax_ctx.aes_ctx) {
free_aes_ctx(nax_ctx.aes_ctx); free_aes_ctx(nax_ctx.aes_ctx);
} }
if (nax_ctx.num_files) { if (nax_ctx.num_files) {
for (unsigned int i = 0; i < nax_ctx.num_files; i++) { for (unsigned int i = 0; i < nax_ctx.num_files; i++) {
fclose(nax_ctx.files[i]); fclose(nax_ctx.files[i]);
@ -467,13 +467,13 @@ int main(int argc, char **argv) {
} }
printf("Done!\n"); printf("Done!\n");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
if ((tool_ctx.file = fopen(input_name, "rb")) == NULL && tool_ctx.file_type != FILETYPE_BOOT0) { if ((tool_ctx.file = fopen(input_name, "rb")) == NULL && tool_ctx.file_type != FILETYPE_BOOT0) {
fprintf(stderr, "unable to open %s: %s\n", input_name, strerror(errno)); fprintf(stderr, "unable to open %s: %s\n", input_name, strerror(errno));
return EXIT_FAILURE; return EXIT_FAILURE;
} }
switch (tool_ctx.file_type) { switch (tool_ctx.file_type) {
case FILETYPE_NCA: { case FILETYPE_NCA: {
if (nca_ctx.tool_ctx->base_nca_ctx != NULL) { if (nca_ctx.tool_ctx->base_nca_ctx != NULL) {
@ -497,18 +497,18 @@ int main(int argc, char **argv) {
nca_ctx.file = tool_ctx.file; nca_ctx.file = tool_ctx.file;
nca_process(&nca_ctx); nca_process(&nca_ctx);
nca_free_section_contexts(&nca_ctx); nca_free_section_contexts(&nca_ctx);
if (nca_ctx.tool_ctx->base_file_type == BASEFILE_FAKE) { if (nca_ctx.tool_ctx->base_file_type == BASEFILE_FAKE) {
nca_ctx.tool_ctx->base_file = NULL; nca_ctx.tool_ctx->base_file = NULL;
} }
if (nca_ctx.tool_ctx->base_file != NULL) { if (nca_ctx.tool_ctx->base_file != NULL) {
fclose(nca_ctx.tool_ctx->base_file); fclose(nca_ctx.tool_ctx->base_file);
if (nca_ctx.tool_ctx->base_file_type == BASEFILE_NCA) { if (nca_ctx.tool_ctx->base_file_type == BASEFILE_NCA) {
nca_free_section_contexts(nca_ctx.tool_ctx->base_nca_ctx); nca_free_section_contexts(nca_ctx.tool_ctx->base_nca_ctx);
free(nca_ctx.tool_ctx->base_nca_ctx); free(nca_ctx.tool_ctx->base_nca_ctx);
} }
} }
break; break;
} }
case FILETYPE_PFS0: { case FILETYPE_PFS0: {
@ -699,7 +699,7 @@ int main(int argc, char **argv) {
usage(); usage();
} }
} }
if (tool_ctx.settings.known_titlekeys.titlekeys != NULL) { if (tool_ctx.settings.known_titlekeys.titlekeys != NULL) {
free(tool_ctx.settings.known_titlekeys.titlekeys); free(tool_ctx.settings.known_titlekeys.titlekeys);
} }

18
nax0.c
View file

@ -8,7 +8,7 @@ static size_t nax0_read(nax0_ctx_t *ctx, uint64_t offset, void *dst, size_t size
fseeko64(ctx->files[0], offset, SEEK_SET); fseeko64(ctx->files[0], offset, SEEK_SET);
return fread(dst, 1, size, ctx->files[0]); return fread(dst, 1, size, ctx->files[0]);
} }
FILE *which = ctx->files[offset / 0xFFFF0000ULL]; FILE *which = ctx->files[offset / 0xFFFF0000ULL];
uint64_t offset_in_file = offset % 0xFFFF0000ULL; uint64_t offset_in_file = offset % 0xFFFF0000ULL;
fseeko64(which, offset_in_file, SEEK_SET); fseeko64(which, offset_in_file, SEEK_SET);
@ -61,15 +61,15 @@ void nax0_process(nax0_ctx_t *ctx) {
} }
} }
} }
nax0_read(ctx, 0, &ctx->header, sizeof(ctx->header)); nax0_read(ctx, 0, &ctx->header, sizeof(ctx->header));
if (ctx->header.magic != MAGIC_NAX0) { if (ctx->header.magic != MAGIC_NAX0) {
printf("Error: File has invalid NAX0 magic!\n"); printf("Error: File has invalid NAX0 magic!\n");
return; return;
} }
memcpy(ctx->encrypted_keys, ctx->header.keys, sizeof(ctx->header.keys)); memcpy(ctx->encrypted_keys, ctx->header.keys, sizeof(ctx->header.keys));
int found = 0; int found = 0;
for (ctx->k = 0; ctx->k < 2; ctx->k++) { for (ctx->k = 0; ctx->k < 2; ctx->k++) {
unsigned char nax_specific_keys[2][0x10]; unsigned char nax_specific_keys[2][0x10];
@ -79,7 +79,7 @@ void nax0_process(nax0_ctx_t *ctx) {
aes_decrypt(nax_k_ctx, ctx->header.keys[i], ctx->encrypted_keys[i], 0x10); aes_decrypt(nax_k_ctx, ctx->header.keys[i], ctx->encrypted_keys[i], 0x10);
free_aes_ctx(nax_k_ctx); free_aes_ctx(nax_k_ctx);
} }
unsigned char validation_mac[0x20]; unsigned char validation_mac[0x20];
sha256_get_buffer_hmac(validation_mac, &ctx->header.magic, 0x60, ctx->tool_ctx->settings.keyset.sd_card_keys[ctx->k] + 0x10, 0x10); sha256_get_buffer_hmac(validation_mac, &ctx->header.magic, 0x60, ctx->tool_ctx->settings.keyset.sd_card_keys[ctx->k] + 0x10, 0x10);
if (memcmp(ctx->header.hmac_header, validation_mac, 0x20) == 0) { if (memcmp(ctx->header.hmac_header, validation_mac, 0x20) == 0) {
@ -87,18 +87,18 @@ void nax0_process(nax0_ctx_t *ctx) {
break; break;
} }
} }
if (!found) { if (!found) {
printf("Error: NAX0 key derivation failed. Check SD card seed and relative path?\n"); printf("Error: NAX0 key derivation failed. Check SD card seed and relative path?\n");
return; return;
} }
ctx->aes_ctx = new_aes_ctx(ctx->header.keys, 0x20, AES_MODE_XTS); ctx->aes_ctx = new_aes_ctx(ctx->header.keys, 0x20, AES_MODE_XTS);
if (ctx->tool_ctx->action & ACTION_INFO) { if (ctx->tool_ctx->action & ACTION_INFO) {
nax0_print(ctx); nax0_print(ctx);
} }
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
nax0_save(ctx); nax0_save(ctx);
} }

2
nca.h
View file

@ -165,7 +165,7 @@ typedef struct {
typedef struct nca_ctx { typedef struct nca_ctx {
FILE *file; /* File for this NCA. */ FILE *file; /* File for this NCA. */
size_t file_size; size_t file_size;
unsigned char crypto_type; unsigned char crypto_type;
int has_rights_id; int has_rights_id;
int is_decrypted; int is_decrypted;

View file

@ -16,7 +16,7 @@ static void nca0_romfs_visit_file(nca0_romfs_ctx_t *ctx, uint32_t file_offset, f
if (entry->name_size) { if (entry->name_size) {
filepath_append_n(cur_path, entry->name_size, "%s", entry->name); filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
} }
/* If we're extracting... */ /* If we're extracting... */
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) { if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
printf("Saving %s...\n", cur_path->char_path); printf("Saving %s...\n", cur_path->char_path);
@ -99,14 +99,14 @@ void nca0_romfs_process(nca0_romfs_ctx_t *ctx) {
fprintf(stderr, "NCA0 RomFS is corrupt?\n"); fprintf(stderr, "NCA0 RomFS is corrupt?\n");
return; return;
} }
/* If there's ever anything meaningful to print about RomFS, uncomment and implement. /* If there's ever anything meaningful to print about RomFS, uncomment and implement.
* *
* if (ctx->tool_ctx->action & ACTION_INFO) { * if (ctx->tool_ctx->action & ACTION_INFO) {
* nca0_romfs_print(ctx); * nca0_romfs_print(ctx);
* } * }
*/ */
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
nca0_romfs_save(ctx); nca0_romfs_save(ctx);
} }

View file

@ -27,7 +27,7 @@ typedef struct {
uint32_t block_size; /* In bytes. */ uint32_t block_size; /* In bytes. */
uint32_t always_2; uint32_t always_2;
uint64_t hash_table_offset; /* Normally zero. */ uint64_t hash_table_offset; /* Normally zero. */
uint64_t hash_table_size; uint64_t hash_table_size;
uint64_t romfs_offset; uint64_t romfs_offset;
uint64_t romfs_size; uint64_t romfs_size;
uint8_t _0x48[0xF0]; uint8_t _0x48[0xF0];

20
nso.c
View file

@ -15,7 +15,7 @@ static void *nso_uncompress(nso0_ctx_t *ctx) {
new_header.segments[1].align_or_total_size = 0; new_header.segments[1].align_or_total_size = 0;
/* Clear compression flags. */ /* Clear compression flags. */
new_header.flags &= 0xF8; new_header.flags &= 0xF8;
uint64_t size = nso_get_size(&new_header); uint64_t size = nso_get_size(&new_header);
nso0_header_t *new_nso = calloc(1, size); nso0_header_t *new_nso = calloc(1, size);
if (new_nso == NULL) { if (new_nso == NULL) {
@ -23,7 +23,7 @@ static void *nso_uncompress(nso0_ctx_t *ctx) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
*((nso0_header_t *)new_nso) = new_header; *((nso0_header_t *)new_nso) = new_header;
for (unsigned int segment = 0; segment < 3; segment++) { for (unsigned int segment = 0; segment < 3; segment++) {
char *src = (char *)ctx->header + ctx->header->segments[segment].file_off; char *src = (char *)ctx->header + ctx->header->segments[segment].file_off;
char *dst = (char *)new_nso + new_header.segments[segment].file_off; char *dst = (char *)new_nso + new_header.segments[segment].file_off;
@ -45,19 +45,19 @@ static void *nso_uncompress(nso0_ctx_t *ctx) {
} }
} }
} }
return new_nso; return new_nso;
} }
void nso0_process(nso0_ctx_t *ctx) { void nso0_process(nso0_ctx_t *ctx) {
/* Read *just* safe amount. */ /* Read *just* safe amount. */
nso0_header_t raw_header; nso0_header_t raw_header;
fseeko64(ctx->file, 0, SEEK_SET); fseeko64(ctx->file, 0, SEEK_SET);
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) { if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
fprintf(stderr, "Failed to read NSO0 header!\n"); fprintf(stderr, "Failed to read NSO0 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (raw_header.magic != MAGIC_NSO0) { if (raw_header.magic != MAGIC_NSO0) {
printf("Error: NSO0 is corrupt!\n"); printf("Error: NSO0 is corrupt!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -69,19 +69,19 @@ void nso0_process(nso0_ctx_t *ctx) {
fprintf(stderr, "Failed to allocate NSO0!\n"); fprintf(stderr, "Failed to allocate NSO0!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
fseeko64(ctx->file, 0, SEEK_SET); fseeko64(ctx->file, 0, SEEK_SET);
if (fread(ctx->header, 1, size, ctx->file) != size) { if (fread(ctx->header, 1, size, ctx->file) != size) {
fprintf(stderr, "Failed to read NSO0!\n"); fprintf(stderr, "Failed to read NSO0!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
ctx->uncompressed_header = nso_uncompress(ctx); ctx->uncompressed_header = nso_uncompress(ctx);
if (ctx->tool_ctx->action & ACTION_INFO) { if (ctx->tool_ctx->action & ACTION_INFO) {
nso0_print(ctx); nso0_print(ctx);
} }
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
nso0_save(ctx); nso0_save(ctx);
} }
@ -111,7 +111,7 @@ void nso0_print(nso0_ctx_t *ctx) {
void nso0_save(nso0_ctx_t *ctx) { void nso0_save(nso0_ctx_t *ctx) {
filepath_t *uncmp_path = &ctx->tool_ctx->settings.uncompressed_path; filepath_t *uncmp_path = &ctx->tool_ctx->settings.uncompressed_path;
if (ctx->tool_ctx->file_type == FILETYPE_NSO0 && uncmp_path->valid == VALIDITY_VALID) { if (ctx->tool_ctx->file_type == FILETYPE_NSO0 && uncmp_path->valid == VALIDITY_VALID) {
FILE *f_uncmp = os_fopen(uncmp_path->os_path, OS_MODE_WRITE); FILE *f_uncmp = os_fopen(uncmp_path->os_path, OS_MODE_WRITE);
if (f_uncmp == NULL) { if (f_uncmp == NULL) {
fprintf(stderr, "Failed to open %s!\n", uncmp_path->char_path); fprintf(stderr, "Failed to open %s!\n", uncmp_path->char_path);

View file

@ -11,7 +11,7 @@ void pk11_process(pk11_ctx_t *ctx) {
fprintf(stderr, "Failed to read PK11 Stage 1!\n"); fprintf(stderr, "Failed to read PK11 Stage 1!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* Check if PK11 was built in 2016. */ /* Check if PK11 was built in 2016. */
/* This is a heuristic to detect an older layout for the PK11 binary. */ /* This is a heuristic to detect an older layout for the PK11 binary. */
if (ctx->stage1.build_date[0] == '2' && ctx->stage1.build_date[1] == '0' && ctx->stage1.build_date[2] == '1' && ctx->stage1.build_date[3] == '6') { if (ctx->stage1.build_date[0] == '2' && ctx->stage1.build_date[1] == '0' && ctx->stage1.build_date[2] == '1' && ctx->stage1.build_date[3] == '6') {
@ -19,18 +19,18 @@ void pk11_process(pk11_ctx_t *ctx) {
} else { } else {
ctx->is_pilot = 0; ctx->is_pilot = 0;
} }
ctx->pk11 = malloc(ctx->stage1.pk11_size); ctx->pk11 = malloc(ctx->stage1.pk11_size);
if (ctx->pk11 == NULL) { if (ctx->pk11 == NULL) {
fprintf(stderr, "Failed to allocate PK11!\n"); fprintf(stderr, "Failed to allocate PK11!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (fread(ctx->pk11, 1, ctx->stage1.pk11_size, ctx->file) != ctx->stage1.pk11_size) { if (fread(ctx->pk11, 1, ctx->stage1.pk11_size, ctx->file) != ctx->stage1.pk11_size) {
fprintf(stderr, "Failed to read PK11!\n"); fprintf(stderr, "Failed to read PK11!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
aes_ctx_t *crypt_ctx = NULL; aes_ctx_t *crypt_ctx = NULL;
pk11_t dec_header; pk11_t dec_header;
for (unsigned int i = 0; i < 0x20; i++) { for (unsigned int i = 0; i < 0x20; i++) {
@ -44,26 +44,26 @@ void pk11_process(pk11_ctx_t *ctx) {
free_aes_ctx(crypt_ctx); free_aes_ctx(crypt_ctx);
crypt_ctx = NULL; crypt_ctx = NULL;
} }
if (crypt_ctx == NULL) { if (crypt_ctx == NULL) {
fprintf(stderr, "Failed to decrypt PK11! Is correct key present?\n"); fprintf(stderr, "Failed to decrypt PK11! Is correct key present?\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
aes_setiv(crypt_ctx, ctx->stage1.ctr, 0x10); aes_setiv(crypt_ctx, ctx->stage1.ctr, 0x10);
aes_decrypt(crypt_ctx, ctx->pk11, ctx->pk11, ctx->stage1.pk11_size); aes_decrypt(crypt_ctx, ctx->pk11, ctx->pk11, ctx->stage1.pk11_size);
uint64_t pk11_size = 0x20 + ctx->pk11->warmboot_size + ctx->pk11->nx_bootloader_size + ctx->pk11->secmon_size; uint64_t pk11_size = 0x20 + ctx->pk11->warmboot_size + ctx->pk11->nx_bootloader_size + ctx->pk11->secmon_size;
pk11_size = align64(pk11_size, 0x10); pk11_size = align64(pk11_size, 0x10);
if (pk11_size != ctx->stage1.pk11_size) { if (pk11_size != ctx->stage1.pk11_size) {
fprintf(stderr, "PK11 seems corrupt!\n"); fprintf(stderr, "PK11 seems corrupt!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (ctx->tool_ctx->action & ACTION_INFO) { if (ctx->tool_ctx->action & ACTION_INFO) {
pk11_print(ctx); pk11_print(ctx);
} }
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
pk11_save(ctx); pk11_save(ctx);
} }
@ -92,7 +92,7 @@ void pk11_save(pk11_ctx_t *ctx) {
} }
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) { if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
os_makedir(dirpath->os_path); os_makedir(dirpath->os_path);
/* Save Decrypted.bin */ /* Save Decrypted.bin */
printf("Saving decrypted binary to %s/Decrypted.bin\n", dirpath->char_path); printf("Saving decrypted binary to %s/Decrypted.bin\n", dirpath->char_path);
char *decrypted_bin = malloc(sizeof(ctx->stage1) + ctx->stage1.pk11_size); char *decrypted_bin = malloc(sizeof(ctx->stage1) + ctx->stage1.pk11_size);
@ -104,28 +104,28 @@ void pk11_save(pk11_ctx_t *ctx) {
memcpy(decrypted_bin + sizeof(ctx->stage1), ctx->pk11, ctx->stage1.pk11_size); memcpy(decrypted_bin + sizeof(ctx->stage1), ctx->pk11, ctx->stage1.pk11_size);
save_buffer_to_directory_file(decrypted_bin, sizeof(ctx->stage1) + ctx->stage1.pk11_size, dirpath, "Decrypted.bin"); save_buffer_to_directory_file(decrypted_bin, sizeof(ctx->stage1) + ctx->stage1.pk11_size, dirpath, "Decrypted.bin");
free(decrypted_bin); free(decrypted_bin);
/* Save Warmboot.bin */ /* Save Warmboot.bin */
printf("Saving Warmboot.bin to %s/Warmboot.bin...\n", dirpath->char_path); printf("Saving Warmboot.bin to %s/Warmboot.bin...\n", dirpath->char_path);
save_buffer_to_directory_file(pk11_get_warmboot_bin(ctx), ctx->pk11->warmboot_size, dirpath, "Warmboot.bin"); save_buffer_to_directory_file(pk11_get_warmboot_bin(ctx), ctx->pk11->warmboot_size, dirpath, "Warmboot.bin");
/* Save NX_Bootloader.bin */ /* Save NX_Bootloader.bin */
printf("Saving NX_Bootloader.bin to %s/NX_Bootloader.bin...\n", dirpath->char_path); printf("Saving NX_Bootloader.bin to %s/NX_Bootloader.bin...\n", dirpath->char_path);
save_buffer_to_directory_file(pk11_get_nx_bootloader(ctx), ctx->pk11->nx_bootloader_size, dirpath, "NX_Bootloader.bin"); save_buffer_to_directory_file(pk11_get_nx_bootloader(ctx), ctx->pk11->nx_bootloader_size, dirpath, "NX_Bootloader.bin");
/* Save Secure_Monitor.bin */ /* Save Secure_Monitor.bin */
printf("Saving Secure_Monitor.bin to %s/Secure_Monitor.bin...\n", dirpath->char_path); printf("Saving Secure_Monitor.bin to %s/Secure_Monitor.bin...\n", dirpath->char_path);
save_buffer_to_directory_file(pk11_get_secmon(ctx), ctx->pk11->secmon_size, dirpath, "Secure_Monitor.bin"); save_buffer_to_directory_file(pk11_get_secmon(ctx), ctx->pk11->secmon_size, dirpath, "Secure_Monitor.bin");
} }
} }
void pk21_process(pk21_ctx_t *ctx) { void pk21_process(pk21_ctx_t *ctx) {
fseeko64(ctx->file, 0, SEEK_SET); fseeko64(ctx->file, 0, SEEK_SET);
if (fread(&ctx->header, 1, sizeof(ctx->header), ctx->file) != sizeof(ctx->header)) { if (fread(&ctx->header, 1, sizeof(ctx->header), ctx->file) != sizeof(ctx->header)) {
fprintf(stderr, "Failed to read PK21 Header!\n"); fprintf(stderr, "Failed to read PK21 Header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
bool is_encrypted = false; bool is_encrypted = false;
for (unsigned int i = 0; i < 0x100; i++) { for (unsigned int i = 0; i < 0x100; i++) {
if (ctx->header.signature[i] != 0) { if (ctx->header.signature[i] != 0) {
@ -133,7 +133,7 @@ void pk21_process(pk21_ctx_t *ctx) {
} }
} }
is_encrypted &= ctx->header.magic != MAGIC_PK21; is_encrypted &= ctx->header.magic != MAGIC_PK21;
if (is_encrypted) { if (is_encrypted) {
if (rsa2048_pss_verify(&ctx->header.ctr, 0x100, ctx->header.signature, ctx->tool_ctx->settings.keyset.package2_fixed_key_modulus)) { if (rsa2048_pss_verify(&ctx->header.ctr, 0x100, ctx->header.signature, ctx->tool_ctx->settings.keyset.package2_fixed_key_modulus)) {
ctx->signature_validity = VALIDITY_VALID; ctx->signature_validity = VALIDITY_VALID;
@ -143,21 +143,21 @@ void pk21_process(pk21_ctx_t *ctx) {
} else { } else {
ctx->signature_validity = VALIDITY_UNCHECKED; ctx->signature_validity = VALIDITY_UNCHECKED;
} }
/* Nintendo, what the fuck? */ /* Nintendo, what the fuck? */
ctx->package_size = ctx->header.ctr_dwords[0] ^ ctx->header.ctr_dwords[2] ^ ctx->header.ctr_dwords[3]; ctx->package_size = ctx->header.ctr_dwords[0] ^ ctx->header.ctr_dwords[2] ^ ctx->header.ctr_dwords[3];
if (ctx->package_size > 0x7FC000) { if (ctx->package_size > 0x7FC000) {
fprintf(stderr, "Error: Package2 Header is corrupt!\n"); fprintf(stderr, "Error: Package2 Header is corrupt!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
aes_ctx_t *crypt_ctx = NULL; aes_ctx_t *crypt_ctx = NULL;
if (is_encrypted) { if (is_encrypted) {
unsigned char ctr[0x10]; unsigned char ctr[0x10];
pk21_header_t temp_header; pk21_header_t temp_header;
memcpy(ctr, ctx->header.ctr, sizeof(ctr)); memcpy(ctr, ctx->header.ctr, sizeof(ctr));
for (unsigned int i = 0; i < 0x20; i++) { for (unsigned int i = 0; i < 0x20; i++) {
ctx->key_rev = i; ctx->key_rev = i;
memcpy(&temp_header, &ctx->header, sizeof(temp_header)); memcpy(&temp_header, &ctx->header, sizeof(temp_header));
@ -172,29 +172,29 @@ void pk21_process(pk21_ctx_t *ctx) {
free_aes_ctx(crypt_ctx); free_aes_ctx(crypt_ctx);
crypt_ctx = NULL; crypt_ctx = NULL;
} }
if (crypt_ctx == NULL) { if (crypt_ctx == NULL) {
fprintf(stderr, "Failed to decrypt PK21! Is correct key present?\n"); fprintf(stderr, "Failed to decrypt PK21! Is correct key present?\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
if (ctx->package_size != 0x200 + ctx->header.section_sizes[0] + ctx->header.section_sizes[1] + ctx->header.section_sizes[2]) { if (ctx->package_size != 0x200 + ctx->header.section_sizes[0] + ctx->header.section_sizes[1] + ctx->header.section_sizes[2]) {
fprintf(stderr, "Error: Package2 Header is corrupt!\n"); fprintf(stderr, "Error: Package2 Header is corrupt!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
ctx->sections = malloc(ctx->package_size); ctx->sections = malloc(ctx->package_size);
if (ctx->sections == NULL) { if (ctx->sections == NULL) {
fprintf(stderr, "Failed to allocate sections!\n"); fprintf(stderr, "Failed to allocate sections!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (fread(ctx->sections, 1, ctx->package_size - 0x200, ctx->file) != ctx->package_size - 0x200) { if (fread(ctx->sections, 1, ctx->package_size - 0x200, ctx->file) != ctx->package_size - 0x200) {
fprintf(stderr, "Failed to read PK21 Sections!\n"); fprintf(stderr, "Failed to read PK21 Sections!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
uint64_t offset = 0; uint64_t offset = 0;
for (unsigned int i = 0; i < 3; i++) { for (unsigned int i = 0; i < 3; i++) {
unsigned char calc_hash[0x20]; unsigned char calc_hash[0x20];
@ -206,11 +206,11 @@ void pk21_process(pk21_ctx_t *ctx) {
} }
if (is_encrypted) { if (is_encrypted) {
aes_setiv(crypt_ctx, ctx->header.section_ctrs[i], 0x10); aes_setiv(crypt_ctx, ctx->header.section_ctrs[i], 0x10);
aes_decrypt(crypt_ctx, ctx->sections + offset, ctx->sections + offset, ctx->header.section_sizes[i]); aes_decrypt(crypt_ctx, ctx->sections + offset, ctx->sections + offset, ctx->header.section_sizes[i]);
} }
offset += ctx->header.section_sizes[i]; offset += ctx->header.section_sizes[i];
} }
ctx->ini1_ctx.tool_ctx = ctx->tool_ctx; ctx->ini1_ctx.tool_ctx = ctx->tool_ctx;
/* Support 8.0.0 INI1 embedded in Kernel */ /* Support 8.0.0 INI1 embedded in Kernel */
if (ctx->header.section_sizes[1] > 0) { if (ctx->header.section_sizes[1] > 0) {
@ -237,11 +237,11 @@ void pk21_process(pk21_ctx_t *ctx) {
offset += kip1_get_size(&ctx->ini1_ctx.kips[i]); offset += kip1_get_size(&ctx->ini1_ctx.kips[i]);
} }
} }
if (ctx->tool_ctx->action & ACTION_INFO) { if (ctx->tool_ctx->action & ACTION_INFO) {
pk21_print(ctx); pk21_print(ctx);
} }
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
pk21_save(ctx); pk21_save(ctx);
} }
@ -271,10 +271,10 @@ void pk21_print(pk21_ctx_t *ctx) {
} else { } else {
memdump(stdout, " Signature: ", &ctx->header.signature, 0x100); memdump(stdout, " Signature: ", &ctx->header.signature, 0x100);
} }
/* What the fuck? */ /* What the fuck? */
printf(" Header Version: %02"PRIx32"\n", (ctx->header.ctr_dwords[1] ^ (ctx->header.ctr_dwords[1] >> 16) ^ (ctx->header.ctr_dwords[1] >> 24)) & 0xFF); printf(" Header Version: %02"PRIx32"\n", (ctx->header.ctr_dwords[1] ^ (ctx->header.ctr_dwords[1] >> 16) ^ (ctx->header.ctr_dwords[1] >> 24)) & 0xFF);
bool is_ini1_embedded = ctx->header.section_sizes[1] == 0; bool is_ini1_embedded = ctx->header.section_sizes[1] == 0;
for (unsigned int i = 0; i < 3; i++) { for (unsigned int i = 0; i < 3; i++) {
printf(" Section %"PRId32" (%s):\n", i, pk21_get_section_name(i, is_ini1_embedded)); printf(" Section %"PRId32" (%s):\n", i, pk21_get_section_name(i, is_ini1_embedded));
@ -291,7 +291,7 @@ void pk21_print(pk21_ctx_t *ctx) {
printf(" Load Address: %08"PRIx32"\n", ctx->header.section_offsets[i] + 0x80000000); printf(" Load Address: %08"PRIx32"\n", ctx->header.section_offsets[i] + 0x80000000);
printf(" Size: %08"PRIx32"\n", ctx->header.section_sizes[i]); printf(" Size: %08"PRIx32"\n", ctx->header.section_sizes[i]);
} }
printf("\n"); printf("\n");
ini1_print(&ctx->ini1_ctx); ini1_print(&ctx->ini1_ctx);
} }
@ -307,7 +307,7 @@ void pk21_save(pk21_ctx_t *ctx) {
} }
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) { if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
os_makedir(dirpath->os_path); os_makedir(dirpath->os_path);
/* Save Decrypted.bin */ /* Save Decrypted.bin */
printf("Saving decrypted binary to %s/Decrypted.bin\n", dirpath->char_path); printf("Saving decrypted binary to %s/Decrypted.bin\n", dirpath->char_path);
char *decrypted_bin = malloc(ctx->package_size); char *decrypted_bin = malloc(ctx->package_size);
@ -319,11 +319,11 @@ void pk21_save(pk21_ctx_t *ctx) {
memcpy(decrypted_bin + sizeof(ctx->header), ctx->sections, ctx->package_size - 0x200); memcpy(decrypted_bin + sizeof(ctx->header), ctx->sections, ctx->package_size - 0x200);
save_buffer_to_directory_file(decrypted_bin, ctx->package_size, dirpath, "Decrypted.bin"); save_buffer_to_directory_file(decrypted_bin, ctx->package_size, dirpath, "Decrypted.bin");
free(decrypted_bin); free(decrypted_bin);
/* Save Kernel.bin */ /* Save Kernel.bin */
printf("Saving Kernel.bin to %s/Kernel.bin...\n", dirpath->char_path); printf("Saving Kernel.bin to %s/Kernel.bin...\n", dirpath->char_path);
save_buffer_to_directory_file(ctx->sections, ctx->header.section_sizes[0], dirpath, "Kernel.bin"); save_buffer_to_directory_file(ctx->sections, ctx->header.section_sizes[0], dirpath, "Kernel.bin");
/* Save INI1.bin */ /* Save INI1.bin */
printf("Saving INI1.bin to %s/INI1.bin...\n", dirpath->char_path); printf("Saving INI1.bin to %s/INI1.bin...\n", dirpath->char_path);
if (ctx->header.section_sizes[1] > 0) if (ctx->header.section_sizes[1] > 0)

14
pfs0.c
View file

@ -3,13 +3,13 @@
void pfs0_process(pfs0_ctx_t *ctx) { void pfs0_process(pfs0_ctx_t *ctx) {
/* Read *just* safe amount. */ /* Read *just* safe amount. */
pfs0_header_t raw_header; pfs0_header_t raw_header;
fseeko64(ctx->file, 0, SEEK_SET); fseeko64(ctx->file, 0, SEEK_SET);
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) { if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
fprintf(stderr, "Failed to read PFS0 header!\n"); fprintf(stderr, "Failed to read PFS0 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (raw_header.magic != MAGIC_PFS0) { if (raw_header.magic != MAGIC_PFS0) {
printf("Error: PFS0 is corrupt!\n"); printf("Error: PFS0 is corrupt!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -21,16 +21,16 @@ void pfs0_process(pfs0_ctx_t *ctx) {
fprintf(stderr, "Failed to allocate PFS0 header!\n"); fprintf(stderr, "Failed to allocate PFS0 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
fseeko64(ctx->file, 0, SEEK_SET); fseeko64(ctx->file, 0, SEEK_SET);
if (fread(ctx->header, 1, header_size, ctx->file) != header_size) { if (fread(ctx->header, 1, header_size, ctx->file) != header_size) {
fprintf(stderr, "Failed to read PFS0 header!\n"); fprintf(stderr, "Failed to read PFS0 header!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* Weak file validation. */ /* Weak file validation. */
uint64_t max_size = 0x1ULL; uint64_t max_size = 0x1ULL;
max_size <<= 48; /* Switch file sizes are capped at 48 bits. */ max_size <<= 48; /* Switch file sizes are capped at 48 bits. */
uint64_t cur_ofs = 0; uint64_t cur_ofs = 0;
for (unsigned int i = 0; i < ctx->header->num_files; i++) { for (unsigned int i = 0; i < ctx->header->num_files; i++) {
pfs0_file_entry_t *cur_file = pfs0_get_file_entry(ctx->header, i); pfs0_file_entry_t *cur_file = pfs0_get_file_entry(ctx->header, i);
@ -58,11 +58,11 @@ void pfs0_process(pfs0_ctx_t *ctx) {
} }
} }
} }
if (ctx->tool_ctx->action & ACTION_INFO) { if (ctx->tool_ctx->action & ACTION_INFO) {
pfs0_print(ctx); pfs0_print(ctx);
} }
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
pfs0_save(ctx); pfs0_save(ctx);
} }

2
pfs0.h
View file

@ -26,7 +26,7 @@ typedef struct {
uint32_t block_size; /* In bytes. */ uint32_t block_size; /* In bytes. */
uint32_t always_2; uint32_t always_2;
uint64_t hash_table_offset; /* Normally zero. */ uint64_t hash_table_offset; /* Normally zero. */
uint64_t hash_table_size; uint64_t hash_table_size;
uint64_t pfs0_offset; uint64_t pfs0_offset;
uint64_t pfs0_size; uint64_t pfs0_size;
uint8_t _0x48[0xF0]; uint8_t _0x48[0xF0];

View file

@ -96,14 +96,14 @@ void romfs_process(romfs_ctx_t *ctx) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
/* If there's ever anything meaningful to print about RomFS, uncomment and implement. /* If there's ever anything meaningful to print about RomFS, uncomment and implement.
* *
* if (ctx->tool_ctx->action & ACTION_INFO) { * if (ctx->tool_ctx->action & ACTION_INFO) {
* romfs_print(ctx); * romfs_print(ctx);
* } * }
*/ */
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
romfs_save(ctx); romfs_save(ctx);
} }

52
rsa.c
View file

@ -11,7 +11,7 @@
static void calculate_mgf1_and_xor(unsigned char *data, size_t data_size, const void *h_src, size_t h_src_size) { static void calculate_mgf1_and_xor(unsigned char *data, size_t data_size, const void *h_src, size_t h_src_size) {
unsigned char h_buf[RSA_2048_BYTES] = {0}; unsigned char h_buf[RSA_2048_BYTES] = {0};
memcpy(h_buf, h_src, h_src_size); memcpy(h_buf, h_src, h_src_size);
unsigned char mgf1_buf[0x20]; unsigned char mgf1_buf[0x20];
size_t ofs = 0; size_t ofs = 0;
unsigned int seed = 0; unsigned int seed = 0;
@ -34,7 +34,7 @@ int rsa2048_pss_verify(const void *data, size_t len, const unsigned char *signat
mbedtls_mpi modulus_mpi; mbedtls_mpi modulus_mpi;
mbedtls_mpi e_mpi; mbedtls_mpi e_mpi;
mbedtls_mpi message_mpi; mbedtls_mpi message_mpi;
mbedtls_mpi_init(&signature_mpi); mbedtls_mpi_init(&signature_mpi);
mbedtls_mpi_init(&modulus_mpi); mbedtls_mpi_init(&modulus_mpi);
mbedtls_mpi_init(&e_mpi); mbedtls_mpi_init(&e_mpi);
@ -44,7 +44,7 @@ int rsa2048_pss_verify(const void *data, size_t len, const unsigned char *signat
unsigned char m_buf[RSA_2048_BYTES]; unsigned char m_buf[RSA_2048_BYTES];
unsigned char h_buf[0x24]; unsigned char h_buf[0x24];
const unsigned char E[3] = {1, 0, 1}; const unsigned char E[3] = {1, 0, 1};
mbedtls_mpi_read_binary(&e_mpi, E, 3); mbedtls_mpi_read_binary(&e_mpi, E, 3);
mbedtls_mpi_read_binary(&signature_mpi, signature, RSA_2048_BYTES); mbedtls_mpi_read_binary(&signature_mpi, signature, RSA_2048_BYTES);
mbedtls_mpi_read_binary(&modulus_mpi, modulus, RSA_2048_BYTES); mbedtls_mpi_read_binary(&modulus_mpi, modulus, RSA_2048_BYTES);
@ -77,7 +77,7 @@ int rsa2048_pss_verify(const void *data, size_t len, const unsigned char *signat
if (m_buf[i] != 0) { if (m_buf[i] != 0) {
return false; return false;
} }
} }
if (m_buf[RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1] != 1) { if (m_buf[RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1] != 1) {
return false; return false;
} }
@ -98,7 +98,7 @@ int rsa2048_pkcs1_verify(const void *data, size_t len, const unsigned char *sign
mbedtls_mpi modulus_mpi; mbedtls_mpi modulus_mpi;
mbedtls_mpi e_mpi; mbedtls_mpi e_mpi;
mbedtls_mpi message_mpi; mbedtls_mpi message_mpi;
mbedtls_mpi_init(&signature_mpi); mbedtls_mpi_init(&signature_mpi);
mbedtls_mpi_init(&modulus_mpi); mbedtls_mpi_init(&modulus_mpi);
mbedtls_mpi_init(&e_mpi); mbedtls_mpi_init(&e_mpi);
@ -108,7 +108,7 @@ int rsa2048_pkcs1_verify(const void *data, size_t len, const unsigned char *sign
unsigned char m_buf[RSA_2048_BYTES]; unsigned char m_buf[RSA_2048_BYTES];
unsigned char h_buf[0x20]; unsigned char h_buf[0x20];
const unsigned char E[3] = {1, 0, 1}; const unsigned char E[3] = {1, 0, 1};
mbedtls_mpi_read_binary(&e_mpi, E, 3); mbedtls_mpi_read_binary(&e_mpi, E, 3);
mbedtls_mpi_read_binary(&signature_mpi, signature, RSA_2048_BYTES); mbedtls_mpi_read_binary(&signature_mpi, signature, RSA_2048_BYTES);
mbedtls_mpi_read_binary(&modulus_mpi, modulus, RSA_2048_BYTES); mbedtls_mpi_read_binary(&modulus_mpi, modulus, RSA_2048_BYTES);
@ -122,27 +122,27 @@ int rsa2048_pkcs1_verify(const void *data, size_t len, const unsigned char *sign
mbedtls_mpi_free(&modulus_mpi); mbedtls_mpi_free(&modulus_mpi);
mbedtls_mpi_free(&e_mpi); mbedtls_mpi_free(&e_mpi);
mbedtls_mpi_free(&message_mpi); mbedtls_mpi_free(&message_mpi);
/* For RSA-2048, this prefix is just a constant. */ /* For RSA-2048, this prefix is just a constant. */
const unsigned char pkcs1_hash_prefix[0xE0] = { const unsigned char pkcs1_hash_prefix[0xE0] = {
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x30, 0x31, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x30, 0x31, 0x30,
0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
}; };
sha256_hash_buffer(h_buf, data, len); sha256_hash_buffer(h_buf, data, len);
return memcmp(pkcs1_hash_prefix, m_buf, 0xE0) == 0 && memcmp(&m_buf[0xE0], h_buf, 0x20) == 0; return memcmp(pkcs1_hash_prefix, m_buf, 0xE0) == 0 && memcmp(&m_buf[0xE0], h_buf, 0x20) == 0;
} }
@ -152,7 +152,7 @@ int rsa2048_oaep_decrypt_verify(void *out, size_t max_out_len, const unsigned ch
mbedtls_mpi modulus_mpi; mbedtls_mpi modulus_mpi;
mbedtls_mpi exp_mpi; mbedtls_mpi exp_mpi;
mbedtls_mpi message_mpi; mbedtls_mpi message_mpi;
mbedtls_mpi_init(&signature_mpi); mbedtls_mpi_init(&signature_mpi);
mbedtls_mpi_init(&modulus_mpi); mbedtls_mpi_init(&modulus_mpi);
mbedtls_mpi_init(&exp_mpi); mbedtls_mpi_init(&exp_mpi);
@ -160,7 +160,7 @@ int rsa2048_oaep_decrypt_verify(void *out, size_t max_out_len, const unsigned ch
mbedtls_mpi_lset(&message_mpi, RSA_2048_BITS); mbedtls_mpi_lset(&message_mpi, RSA_2048_BITS);
unsigned char m_buf[RSA_2048_BYTES]; unsigned char m_buf[RSA_2048_BYTES];
mbedtls_mpi_read_binary(&exp_mpi, exponent, exponent_len); mbedtls_mpi_read_binary(&exp_mpi, exponent, exponent_len);
mbedtls_mpi_read_binary(&signature_mpi, signature, RSA_2048_BYTES); mbedtls_mpi_read_binary(&signature_mpi, signature, RSA_2048_BYTES);
mbedtls_mpi_read_binary(&modulus_mpi, modulus, RSA_2048_BYTES); mbedtls_mpi_read_binary(&modulus_mpi, modulus, RSA_2048_BYTES);
@ -174,7 +174,7 @@ int rsa2048_oaep_decrypt_verify(void *out, size_t max_out_len, const unsigned ch
mbedtls_mpi_free(&modulus_mpi); mbedtls_mpi_free(&modulus_mpi);
mbedtls_mpi_free(&exp_mpi); mbedtls_mpi_free(&exp_mpi);
mbedtls_mpi_free(&message_mpi); mbedtls_mpi_free(&message_mpi);
/* There's no automated PSS verification as far as I can tell. */ /* There's no automated PSS verification as far as I can tell. */
if (m_buf[0] != 0x00) { if (m_buf[0] != 0x00) {
return false; return false;
@ -190,7 +190,7 @@ int rsa2048_oaep_decrypt_verify(void *out, size_t max_out_len, const unsigned ch
if (memcmp(db, label_hash, 0x20) != 0) { if (memcmp(db, label_hash, 0x20) != 0) {
return false; return false;
} }
/* Validate message prefix. */ /* Validate message prefix. */
const unsigned char *data = db + 0x20; const unsigned char *data = db + 0x20;
size_t remaining = RSA_2048_BYTES - 0x20 - 1 - 0x20; size_t remaining = RSA_2048_BYTES - 0x20 - 1 - 0x20;

View file

@ -20,9 +20,9 @@ typedef struct {
unsigned char tsec_key[0x10]; /* TSEC key for use in key derivation. NOTE: CONSOLE UNIQUE. */ unsigned char tsec_key[0x10]; /* TSEC key for use in key derivation. NOTE: CONSOLE UNIQUE. */
unsigned char device_key[0x10]; /* Device key used to derive some FS keys. NOTE: CONSOLE UNIQUE. */ unsigned char device_key[0x10]; /* Device key used to derive some FS keys. NOTE: CONSOLE UNIQUE. */
unsigned char keyblob_keys[0x20][0x10]; /* Actual keys used to decrypt keyblobs. NOTE: CONSOLE UNIQUE.*/ unsigned char keyblob_keys[0x20][0x10]; /* Actual keys used to decrypt keyblobs. NOTE: CONSOLE UNIQUE.*/
unsigned char keyblob_mac_keys[0x20][0x10]; /* Keys used to validate keyblobs. NOTE: CONSOLE UNIQUE. */ unsigned char keyblob_mac_keys[0x20][0x10]; /* Keys used to validate keyblobs. NOTE: CONSOLE UNIQUE. */
unsigned char encrypted_keyblobs[0x20][0xB0]; /* Actual encrypted keyblobs (EKS). NOTE: CONSOLE UNIQUE. */ unsigned char encrypted_keyblobs[0x20][0xB0]; /* Actual encrypted keyblobs (EKS). NOTE: CONSOLE UNIQUE. */
unsigned char keyblobs[0x20][0x90]; /* Actual decrypted keyblobs (EKS). */ unsigned char keyblobs[0x20][0x90]; /* Actual decrypted keyblobs (EKS). */
unsigned char keyblob_key_sources[0x20][0x10]; /* Seeds for keyblob keys. */ unsigned char keyblob_key_sources[0x20][0x10]; /* Seeds for keyblob keys. */
unsigned char keyblob_mac_key_source[0x10]; /* Seed for keyblob MAC key derivation. */ unsigned char keyblob_mac_key_source[0x10]; /* Seed for keyblob MAC key derivation. */
unsigned char tsec_root_kek[0x10]; /* Used to generate TSEC root keys. */ unsigned char tsec_root_kek[0x10]; /* Used to generate TSEC root keys. */

26
sha.c
View file

@ -7,21 +7,21 @@
/* Allocate new context. */ /* Allocate new context. */
sha_ctx_t *new_sha_ctx(hash_type_t type, int hmac) { sha_ctx_t *new_sha_ctx(hash_type_t type, int hmac) {
sha_ctx_t *ctx; sha_ctx_t *ctx;
if ((ctx = malloc(sizeof(*ctx))) == NULL) { if ((ctx = malloc(sizeof(*ctx))) == NULL) {
FATAL_ERROR("Failed to allocate sha_ctx_t!"); FATAL_ERROR("Failed to allocate sha_ctx_t!");
} }
mbedtls_md_init(&ctx->digest); mbedtls_md_init(&ctx->digest);
if (mbedtls_md_setup(&ctx->digest, mbedtls_md_info_from_type(type), hmac)) { if (mbedtls_md_setup(&ctx->digest, mbedtls_md_info_from_type(type), hmac)) {
FATAL_ERROR("Failed to set up hash context!"); FATAL_ERROR("Failed to set up hash context!");
} }
if (mbedtls_md_starts(&ctx->digest)) { if (mbedtls_md_starts(&ctx->digest)) {
FATAL_ERROR("Failed to start hash context!"); FATAL_ERROR("Failed to start hash context!");
} }
return ctx; return ctx;
} }
@ -31,7 +31,7 @@ void free_sha_ctx(sha_ctx_t *ctx) {
if (ctx == NULL) { if (ctx == NULL) {
return; return;
} }
mbedtls_md_free(&ctx->digest); mbedtls_md_free(&ctx->digest);
free(ctx); free(ctx);
} }
@ -57,29 +57,29 @@ void sha256_hash_buffer(unsigned char *digest, const void *data, size_t l) {
/* SHA256-HMAC digest. */ /* SHA256-HMAC digest. */
void sha256_get_buffer_hmac(void *digest, const void *secret, size_t s_l, const void *data, size_t d_l) { void sha256_get_buffer_hmac(void *digest, const void *secret, size_t s_l, const void *data, size_t d_l) {
sha_ctx_t *ctx; sha_ctx_t *ctx;
if ((ctx = malloc(sizeof(*ctx))) == NULL) { if ((ctx = malloc(sizeof(*ctx))) == NULL) {
FATAL_ERROR("Failed to allocate sha_ctx_t!"); FATAL_ERROR("Failed to allocate sha_ctx_t!");
} }
mbedtls_md_init(&ctx->digest); mbedtls_md_init(&ctx->digest);
if (mbedtls_md_setup(&ctx->digest, mbedtls_md_info_from_type(HASH_TYPE_SHA256), 1)) { if (mbedtls_md_setup(&ctx->digest, mbedtls_md_info_from_type(HASH_TYPE_SHA256), 1)) {
FATAL_ERROR("Failed to set up hash context!"); FATAL_ERROR("Failed to set up hash context!");
} }
if (mbedtls_md_hmac_starts(&ctx->digest, secret, s_l)) { if (mbedtls_md_hmac_starts(&ctx->digest, secret, s_l)) {
FATAL_ERROR("Failed to set up HMAC secret context!"); FATAL_ERROR("Failed to set up HMAC secret context!");
} }
if (mbedtls_md_hmac_update(&ctx->digest, data, d_l)) { if (mbedtls_md_hmac_update(&ctx->digest, data, d_l)) {
FATAL_ERROR("Failed processing HMAC input!"); FATAL_ERROR("Failed processing HMAC input!");
} }
if (mbedtls_md_hmac_finish(&ctx->digest, digest)) { if (mbedtls_md_hmac_finish(&ctx->digest, digest)) {
FATAL_ERROR("Failed getting HMAC output!"); FATAL_ERROR("Failed getting HMAC output!");
} }
mbedtls_md_free(&ctx->digest); mbedtls_md_free(&ctx->digest);
free(ctx); free(ctx);
} }

82
xci.c
View file

@ -7,21 +7,21 @@
/* However, it (and other XCI keys) can be dumped with a GCD attack on two signatures. */ /* However, it (and other XCI keys) can be dumped with a GCD attack on two signatures. */
/* Contact SciresM for details, if curious. */ /* Contact SciresM for details, if curious. */
static const unsigned char xci_header_pubk[0x100] = { static const unsigned char xci_header_pubk[0x100] = {
0x98, 0xC7, 0x26, 0xB6, 0x0D, 0x0A, 0x50, 0xA7, 0x39, 0x21, 0x0A, 0xE3, 0x2F, 0xE4, 0x3E, 0x2E, 0x98, 0xC7, 0x26, 0xB6, 0x0D, 0x0A, 0x50, 0xA7, 0x39, 0x21, 0x0A, 0xE3, 0x2F, 0xE4, 0x3E, 0x2E,
0x5B, 0xA2, 0x86, 0x75, 0xAA, 0x5C, 0xEE, 0x34, 0xF1, 0xA3, 0x3A, 0x7E, 0xBD, 0x90, 0x4E, 0xF7, 0x5B, 0xA2, 0x86, 0x75, 0xAA, 0x5C, 0xEE, 0x34, 0xF1, 0xA3, 0x3A, 0x7E, 0xBD, 0x90, 0x4E, 0xF7,
0x8D, 0xFA, 0x17, 0xAA, 0x6B, 0xC6, 0x36, 0x6D, 0x4C, 0x9A, 0x6D, 0x57, 0x2F, 0x80, 0xA2, 0xBC, 0x8D, 0xFA, 0x17, 0xAA, 0x6B, 0xC6, 0x36, 0x6D, 0x4C, 0x9A, 0x6D, 0x57, 0x2F, 0x80, 0xA2, 0xBC,
0x38, 0x4D, 0xDA, 0x99, 0xA1, 0xD8, 0xC3, 0xE2, 0x99, 0x79, 0x36, 0x71, 0x90, 0x20, 0x25, 0x9D, 0x38, 0x4D, 0xDA, 0x99, 0xA1, 0xD8, 0xC3, 0xE2, 0x99, 0x79, 0x36, 0x71, 0x90, 0x20, 0x25, 0x9D,
0x4D, 0x11, 0xB8, 0x2E, 0x63, 0x6B, 0x5A, 0xFA, 0x1E, 0x9C, 0x04, 0xD1, 0xC5, 0xF0, 0x9C, 0xB1, 0x4D, 0x11, 0xB8, 0x2E, 0x63, 0x6B, 0x5A, 0xFA, 0x1E, 0x9C, 0x04, 0xD1, 0xC5, 0xF0, 0x9C, 0xB1,
0x0F, 0xB8, 0xC1, 0x7B, 0xBF, 0xE8, 0xB0, 0xD2, 0x2B, 0x47, 0x01, 0x22, 0x6B, 0x23, 0xC9, 0xD0, 0x0F, 0xB8, 0xC1, 0x7B, 0xBF, 0xE8, 0xB0, 0xD2, 0x2B, 0x47, 0x01, 0x22, 0x6B, 0x23, 0xC9, 0xD0,
0xBC, 0xEB, 0x75, 0x6E, 0x41, 0x7D, 0x4C, 0x26, 0xA4, 0x73, 0x21, 0xB4, 0xF0, 0x14, 0xE5, 0xD9, 0xBC, 0xEB, 0x75, 0x6E, 0x41, 0x7D, 0x4C, 0x26, 0xA4, 0x73, 0x21, 0xB4, 0xF0, 0x14, 0xE5, 0xD9,
0x8D, 0xB3, 0x64, 0xEE, 0xA8, 0xFA, 0x84, 0x1B, 0xB8, 0xB8, 0x7C, 0x88, 0x6B, 0xEF, 0xCC, 0x97, 0x8D, 0xB3, 0x64, 0xEE, 0xA8, 0xFA, 0x84, 0x1B, 0xB8, 0xB8, 0x7C, 0x88, 0x6B, 0xEF, 0xCC, 0x97,
0x04, 0x04, 0x9A, 0x67, 0x2F, 0xDF, 0xEC, 0x0D, 0xB2, 0x5F, 0xB5, 0xB2, 0xBD, 0xB5, 0x4B, 0xDE, 0x04, 0x04, 0x9A, 0x67, 0x2F, 0xDF, 0xEC, 0x0D, 0xB2, 0x5F, 0xB5, 0xB2, 0xBD, 0xB5, 0x4B, 0xDE,
0x0E, 0x88, 0xA3, 0xBA, 0xD1, 0xB4, 0xE0, 0x91, 0x81, 0xA7, 0x84, 0xEB, 0x77, 0x85, 0x8B, 0xEF, 0x0E, 0x88, 0xA3, 0xBA, 0xD1, 0xB4, 0xE0, 0x91, 0x81, 0xA7, 0x84, 0xEB, 0x77, 0x85, 0x8B, 0xEF,
0xA5, 0xE3, 0x27, 0xB2, 0xF2, 0x82, 0x2B, 0x29, 0xF1, 0x75, 0x2D, 0xCE, 0xCC, 0xAE, 0x9B, 0x8D, 0xA5, 0xE3, 0x27, 0xB2, 0xF2, 0x82, 0x2B, 0x29, 0xF1, 0x75, 0x2D, 0xCE, 0xCC, 0xAE, 0x9B, 0x8D,
0xED, 0x5C, 0xF1, 0x8E, 0xDB, 0x9A, 0xD7, 0xAF, 0x42, 0x14, 0x52, 0xCD, 0xE3, 0xC5, 0xDD, 0xCE, 0xED, 0x5C, 0xF1, 0x8E, 0xDB, 0x9A, 0xD7, 0xAF, 0x42, 0x14, 0x52, 0xCD, 0xE3, 0xC5, 0xDD, 0xCE,
0x08, 0x12, 0x17, 0xD0, 0x7F, 0x1A, 0xAA, 0x1F, 0x7D, 0xE0, 0x93, 0x54, 0xC8, 0xBC, 0x73, 0x8A, 0x08, 0x12, 0x17, 0xD0, 0x7F, 0x1A, 0xAA, 0x1F, 0x7D, 0xE0, 0x93, 0x54, 0xC8, 0xBC, 0x73, 0x8A,
0xCB, 0xAD, 0x6E, 0x93, 0xE2, 0x19, 0x72, 0x6B, 0xD3, 0x45, 0xF8, 0x73, 0x3D, 0x2B, 0x6A, 0x55, 0xCB, 0xAD, 0x6E, 0x93, 0xE2, 0x19, 0x72, 0x6B, 0xD3, 0x45, 0xF8, 0x73, 0x3D, 0x2B, 0x6A, 0x55,
0xD2, 0x3A, 0x8B, 0xB0, 0x8A, 0x42, 0xE3, 0x3D, 0xF1, 0x92, 0x23, 0x42, 0x2E, 0xBA, 0xCC, 0x9C, 0xD2, 0x3A, 0x8B, 0xB0, 0x8A, 0x42, 0xE3, 0x3D, 0xF1, 0x92, 0x23, 0x42, 0x2E, 0xBA, 0xCC, 0x9C,
0x9A, 0xC1, 0xDD, 0x62, 0x86, 0x9C, 0x2E, 0xE1, 0x2D, 0x6F, 0x62, 0x67, 0x51, 0x08, 0x0E, 0xCF 0x9A, 0xC1, 0xDD, 0x62, 0x86, 0x9C, 0x2E, 0xE1, 0x2D, 0x6F, 0x62, 0x67, 0x51, 0x08, 0x0E, 0xCF
}; };
@ -31,12 +31,12 @@ void xci_process(xci_ctx_t *ctx) {
fprintf(stderr, "Failed to read XCI header!\n"); fprintf(stderr, "Failed to read XCI header!\n");
return; return;
} }
if (ctx->header.magic != MAGIC_HEAD) { if (ctx->header.magic != MAGIC_HEAD) {
fprintf(stderr, "Error: XCI header is corrupt!\n"); fprintf(stderr, "Error: XCI header is corrupt!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (ctx->tool_ctx->action & ACTION_VERIFY) { if (ctx->tool_ctx->action & ACTION_VERIFY) {
if (rsa2048_pkcs1_verify(&ctx->header.magic, 0x100, ctx->header.header_sig, xci_header_pubk)) { if (rsa2048_pkcs1_verify(&ctx->header.magic, 0x100, ctx->header.header_sig, xci_header_pubk)) {
ctx->header_sig_validity = VALIDITY_VALID; ctx->header_sig_validity = VALIDITY_VALID;
@ -44,31 +44,31 @@ void xci_process(xci_ctx_t *ctx) {
ctx->header_sig_validity = VALIDITY_INVALID; ctx->header_sig_validity = VALIDITY_INVALID;
} }
} }
ctx->hfs0_hash_validity = check_memory_hash_table(ctx->file, ctx->header.hfs0_header_hash, ctx->header.hfs0_offset, ctx->header.hfs0_header_size, ctx->header.hfs0_header_size, 0); ctx->hfs0_hash_validity = check_memory_hash_table(ctx->file, ctx->header.hfs0_header_hash, ctx->header.hfs0_offset, ctx->header.hfs0_header_size, ctx->header.hfs0_header_size, 0);
if (ctx->hfs0_hash_validity != VALIDITY_VALID) { if (ctx->hfs0_hash_validity != VALIDITY_VALID) {
fprintf(stderr, "Error: XCI partition is corrupt!\n"); fprintf(stderr, "Error: XCI partition is corrupt!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
hactool_ctx_t blank_ctx; hactool_ctx_t blank_ctx;
memset(&blank_ctx, 0, sizeof(blank_ctx)); memset(&blank_ctx, 0, sizeof(blank_ctx));
blank_ctx.action = ctx->tool_ctx->action & ~(ACTION_EXTRACT | ACTION_INFO); blank_ctx.action = ctx->tool_ctx->action & ~(ACTION_EXTRACT | ACTION_INFO);
ctx->partition_ctx.file = ctx->file; ctx->partition_ctx.file = ctx->file;
ctx->partition_ctx.offset = ctx->header.hfs0_offset; ctx->partition_ctx.offset = ctx->header.hfs0_offset;
ctx->partition_ctx.tool_ctx = &blank_ctx; ctx->partition_ctx.tool_ctx = &blank_ctx;
ctx->partition_ctx.name = "rootpt"; ctx->partition_ctx.name = "rootpt";
hfs0_process(&ctx->partition_ctx); hfs0_process(&ctx->partition_ctx);
if (ctx->partition_ctx.header->num_files > 4) { if (ctx->partition_ctx.header->num_files > 4) {
fprintf(stderr, "Error: Invalid XCI partition!\n"); fprintf(stderr, "Error: Invalid XCI partition!\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
for (unsigned int i = 0; i < ctx->partition_ctx.header->num_files; i++) { for (unsigned int i = 0; i < ctx->partition_ctx.header->num_files; i++) {
hfs0_ctx_t *cur_ctx = NULL; hfs0_ctx_t *cur_ctx = NULL;
hfs0_file_entry_t *cur_file = hfs0_get_file_entry(ctx->partition_ctx.header, i); hfs0_file_entry_t *cur_file = hfs0_get_file_entry(ctx->partition_ctx.header, i);
char *cur_name = hfs0_get_file_name(ctx->partition_ctx.header, i); char *cur_name = hfs0_get_file_name(ctx->partition_ctx.header, i);
if (!strcmp(cur_name, "update") && ctx->update_ctx.file == NULL) { if (!strcmp(cur_name, "update") && ctx->update_ctx.file == NULL) {
@ -79,20 +79,20 @@ void xci_process(xci_ctx_t *ctx) {
cur_ctx = &ctx->secure_ctx; cur_ctx = &ctx->secure_ctx;
} else if (!strcmp(cur_name, "logo") && ctx->logo_ctx.file == NULL) { } else if (!strcmp(cur_name, "logo") && ctx->logo_ctx.file == NULL) {
cur_ctx = &ctx->logo_ctx; cur_ctx = &ctx->logo_ctx;
} }
if (cur_ctx == NULL) { if (cur_ctx == NULL) {
fprintf(stderr, "Unknown XCI partition: %s\n", cur_name); fprintf(stderr, "Unknown XCI partition: %s\n", cur_name);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
cur_ctx->name = cur_name; cur_ctx->name = cur_name;
cur_ctx->offset = ctx->partition_ctx.offset + hfs0_get_header_size(ctx->partition_ctx.header) + cur_file->offset; cur_ctx->offset = ctx->partition_ctx.offset + hfs0_get_header_size(ctx->partition_ctx.header) + cur_file->offset;
cur_ctx->tool_ctx = &blank_ctx; cur_ctx->tool_ctx = &blank_ctx;
cur_ctx->file = ctx->file; cur_ctx->file = ctx->file;
hfs0_process(cur_ctx); hfs0_process(cur_ctx);
} }
for (unsigned int i = 0; i < 0x10; i++) { for (unsigned int i = 0; i < 0x10; i++) {
ctx->iv[i] = ctx->header.reversed_iv[0xF-i]; ctx->iv[i] = ctx->header.reversed_iv[0xF-i];
} }
@ -111,12 +111,12 @@ void xci_process(xci_ctx_t *ctx) {
} else { } else {
ctx->has_decrypted_header = 0; ctx->has_decrypted_header = 0;
} }
if (ctx->tool_ctx->action & ACTION_INFO) { if (ctx->tool_ctx->action & ACTION_INFO) {
xci_print(ctx); xci_print(ctx);
} }
if (ctx->tool_ctx->action & ACTION_EXTRACT) { if (ctx->tool_ctx->action & ACTION_EXTRACT) {
xci_save(ctx); xci_save(ctx);
} }
@ -130,7 +130,7 @@ void xci_save(xci_ctx_t *ctx) {
os_makedir(dirpath->os_path); os_makedir(dirpath->os_path);
for (unsigned int i = 0; i < ctx->partition_ctx.header->num_files; i++) { for (unsigned int i = 0; i < ctx->partition_ctx.header->num_files; i++) {
hfs0_ctx_t *cur_ctx = NULL; hfs0_ctx_t *cur_ctx = NULL;
char *cur_name = hfs0_get_file_name(ctx->partition_ctx.header, i); char *cur_name = hfs0_get_file_name(ctx->partition_ctx.header, i);
if (!strcmp(cur_name, "update")) { if (!strcmp(cur_name, "update")) {
cur_ctx = &ctx->update_ctx; cur_ctx = &ctx->update_ctx;
@ -199,7 +199,7 @@ void xci_save(xci_ctx_t *ctx) {
hfs0_save_file(&ctx->logo_ctx, i, &ctx->tool_ctx->settings.logo_dir_path); hfs0_save_file(&ctx->logo_ctx, i, &ctx->tool_ctx->settings.logo_dir_path);
} }
printf("\n"); printf("\n");
} }
} }
} }
@ -250,7 +250,7 @@ static void xci_print_hfs0(hfs0_ctx_t *ctx) {
print_magic(" Magic: ", ctx->header->magic); print_magic(" Magic: ", ctx->header->magic);
printf(" Offset: %012"PRIx64"\n", ctx->offset); printf(" Offset: %012"PRIx64"\n", ctx->offset);
printf(" Number of files: %"PRId32"\n", ctx->header->num_files); printf(" Number of files: %"PRId32"\n", ctx->header->num_files);
if (ctx->header->num_files > 0 && (ctx->header->num_files < 100 || ctx->tool_ctx->action & ACTION_VERIFY)) { if (ctx->header->num_files > 0 && (ctx->header->num_files < 100 || ctx->tool_ctx->action & ACTION_VERIFY)) {
printf(" Files:"); printf(" Files:");
for (unsigned int i = 0; i < ctx->header->num_files; i++) { for (unsigned int i = 0; i < ctx->header->num_files; i++) {
@ -264,7 +264,7 @@ static void xci_print_hfs0(hfs0_ctx_t *ctx) {
} }
} }
} }
void xci_print(xci_ctx_t *ctx) { void xci_print(xci_ctx_t *ctx) {
printf("\nXCI:\n"); printf("\nXCI:\n");
print_magic("Magic: ", ctx->header.magic); print_magic("Magic: ", ctx->header.magic);
@ -278,10 +278,10 @@ void xci_print(xci_ctx_t *ctx) {
} else { } else {
memdump(stdout, "Header Signature: ", &ctx->header.header_sig, 0x100); memdump(stdout, "Header Signature: ", &ctx->header.header_sig, 0x100);
} }
printf("Cartridge Type: %s\n", xci_get_cartridge_type(ctx)); printf("Cartridge Type: %s\n", xci_get_cartridge_type(ctx));
printf("Cartridge Size: %012"PRIx64"\n", media_to_real(ctx->header.cart_size + 1)); printf("Cartridge Size: %012"PRIx64"\n", media_to_real(ctx->header.cart_size + 1));
memdump(stdout, "Header IV: ", ctx->iv, 0x10); memdump(stdout, "Header IV: ", ctx->iv, 0x10);
memdump(stdout, "Encrypted Header: ", ctx->header.encrypted_data, 0x70); memdump(stdout, "Encrypted Header: ", ctx->header.encrypted_data, 0x70);
if (ctx->has_decrypted_header) { if (ctx->has_decrypted_header) {
@ -294,7 +294,7 @@ void xci_print(xci_ctx_t *ctx) {
printf(" Write Time Wait1: %08"PRIx32"\n", gc_info->write_time_wait_1); printf(" Write Time Wait1: %08"PRIx32"\n", gc_info->write_time_wait_1);
printf(" Write Time Wait2: %08"PRIx32"\n", gc_info->write_time_wait_2); printf(" Write Time Wait2: %08"PRIx32"\n", gc_info->write_time_wait_2);
printf(" Firmware Mode: %08"PRIx32"\n", gc_info->firmware_mode); printf(" Firmware Mode: %08"PRIx32"\n", gc_info->firmware_mode);
// decode version // decode version
uint32_t ver[4] = {0}; uint32_t ver[4] = {0};
ver[0] = ((gc_info->cup_version >> 26) & 0x3f); ver[0] = ((gc_info->cup_version >> 26) & 0x3f);
@ -314,16 +314,16 @@ void xci_print(xci_ctx_t *ctx) {
printf("Root Partition:\n"); printf("Root Partition:\n");
} }
xci_print_hfs0(&ctx->partition_ctx); xci_print_hfs0(&ctx->partition_ctx);
printf("Update Partition:\n"); printf("Update Partition:\n");
xci_print_hfs0(&ctx->update_ctx); xci_print_hfs0(&ctx->update_ctx);
printf("Normal Partition:\n"); printf("Normal Partition:\n");
xci_print_hfs0(&ctx->normal_ctx); xci_print_hfs0(&ctx->normal_ctx);
printf("Secure Partition:\n"); printf("Secure Partition:\n");
xci_print_hfs0(&ctx->secure_ctx); xci_print_hfs0(&ctx->secure_ctx);
/* Ensure that Logo partition exists. */ /* Ensure that Logo partition exists. */
if (ctx->partition_ctx.header->num_files == 4) { if (ctx->partition_ctx.header->num_files == 4) {
printf("Logo Partition:\n"); printf("Logo Partition:\n");