From 8a742a45d4963aa9d0332f5a9e3dfbe4ace5a262 Mon Sep 17 00:00:00 2001 From: shchmue Date: Wed, 15 Apr 2020 16:22:23 -0600 Subject: [PATCH] save: Fix remap init, add graceful fail paths --- source/keys/keys.c | 18 ++++++++----- source/keys/save.c | 67 +++++++++++++++++++++++++--------------------- source/keys/save.h | 6 ++--- 3 files changed, 51 insertions(+), 40 deletions(-) diff --git a/source/keys/keys.c b/source/keys/keys.c index 6d56304..216297c 100644 --- a/source/keys/keys.c +++ b/source/keys/keys.c @@ -780,7 +780,7 @@ get_titlekeys: save_ctx = calloc(1, sizeof(save_ctx_t)); u8 M[0x100]; if (f_open(&fp, "emmc:/save/80000000000000E1", FA_READ | FA_OPEN_EXISTING)) { - EPRINTF("Unable to open ES save 1. Skipping."); + EPRINTF("Unable to open e1 save. Skipping."); free(buffer); goto dismount; } @@ -791,7 +791,9 @@ get_titlekeys: save_ctx->file = &fp; save_ctx->tool_ctx.action = 0; memcpy(save_ctx->save_mac_key, save_mac_key, 0x10); - save_process(save_ctx); + if (!save_process(save_ctx)) { + EPRINTF("Failed to process e1 save."); + } char ticket_bin_path[SAVE_FS_LIST_MAX_NAME_LENGTH] = "/ticket.bin"; char ticket_list_bin_path[SAVE_FS_LIST_MAX_NAME_LENGTH] = "/ticket_list.bin"; @@ -813,7 +815,7 @@ get_titlekeys: } } if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_bin_path, &entry)) { - EPRINTF("Unable to locate ticket.bin in e1."); + EPRINTF("Unable to locate ticket.bin in e1 save."); goto dismount; } save_open_fat_storage(&save_ctx->save_filesystem_core, &fat_storage, entry.value.save_file_info.start_block); @@ -852,7 +854,7 @@ get_titlekeys: u32 common_titlekey_count = _titlekey_count; if (f_open(&fp, "emmc:/save/80000000000000E2", FA_READ | FA_OPEN_EXISTING)) { - EPRINTF("Unable to open ES save 2. Skipping."); + EPRINTF("Unable to open e2 save. Skipping."); free(buffer); goto dismount; } @@ -860,10 +862,12 @@ get_titlekeys: save_ctx->file = &fp; save_ctx->tool_ctx.action = 0; memcpy(save_ctx->save_mac_key, save_mac_key, 0x10); - save_process(save_ctx); + if (!save_process(save_ctx)) { + EPRINTF("Failed to process e1 save."); + } if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_list_bin_path, &entry)) { - EPRINTF("Unable to locate ticket_list.bin in e2."); + EPRINTF("Unable to locate ticket_list.bin in e2 save."); goto dismount; } save_open_fat_storage(&save_ctx->save_filesystem_core, &fat_storage, entry.value.save_file_info.start_block); @@ -881,7 +885,7 @@ get_titlekeys: } } if (!save_hierarchical_file_table_get_file_entry_by_path(&save_ctx->save_filesystem_core.file_table, ticket_bin_path, &entry)) { - EPRINTF("Unable to locate ticket.bin in e2."); + EPRINTF("Unable to locate ticket.bin in e2 save."); goto dismount; } diff --git a/source/keys/save.c b/source/keys/save.c index bcac3b4..7b0695a 100644 --- a/source/keys/save.c +++ b/source/keys/save.c @@ -70,27 +70,27 @@ uint32_t save_duplex_storage_read(duplex_storage_ctx_t *ctx, void *buffer, uint6 } remap_segment_ctx_t *save_remap_init_segments(remap_header_t *header, remap_entry_ctx_t *map_entries, uint32_t num_map_entries) { - remap_segment_ctx_t *segments = malloc(sizeof(remap_segment_ctx_t) * header->map_segment_count); + remap_segment_ctx_t *segments = calloc(1, sizeof(remap_segment_ctx_t) * header->map_segment_count); unsigned int entry_idx = 0; for (unsigned int i = 0; i < header->map_segment_count; i++) { remap_segment_ctx_t *seg = &segments[i]; - seg->entries = malloc(sizeof(remap_entry_ctx_t)); - memcpy(seg->entries, &map_entries[entry_idx], sizeof(remap_entry_ctx_t)); + seg->entry_count = 0; + seg->entries = malloc(sizeof(remap_entry_ctx_t *)); + seg->entries[seg->entry_count++] = &map_entries[entry_idx]; seg->offset = map_entries[entry_idx].virtual_offset; - map_entries[entry_idx].segment = seg; - seg->entry_count = 1; - entry_idx++; + map_entries[entry_idx++].segment = seg; while (entry_idx < num_map_entries && map_entries[entry_idx - 1].virtual_offset_end == map_entries[entry_idx].virtual_offset) { map_entries[entry_idx].segment = seg; map_entries[entry_idx - 1].next = &map_entries[entry_idx]; - seg->entries = malloc(sizeof(remap_entry_ctx_t)); - memcpy(seg->entries, &map_entries[entry_idx], sizeof(remap_entry_ctx_t)); - seg->entry_count++; - entry_idx++; + remap_entry_ctx_t **ptr = calloc(1, sizeof(remap_entry_ctx_t *) * (seg->entry_count + 1)); + memcpy(ptr, seg->entries, sizeof(remap_entry_ctx_t *) * (seg->entry_count)); + free(seg->entries); + seg->entries = ptr; + seg->entries[seg->entry_count++] = &map_entries[entry_idx++]; } - seg->length = seg->entries[seg->entry_count - 1].virtual_offset_end - seg->entries[0].virtual_offset; + seg->length = seg->entries[seg->entry_count - 1]->virtual_offset_end - seg->entries[0]->virtual_offset; } return segments; } @@ -99,8 +99,8 @@ remap_entry_ctx_t *save_remap_get_map_entry(remap_storage_ctx_t *ctx, uint64_t o uint32_t segment_idx = (uint32_t)(offset >> (64 - ctx->header->segment_bits)); if (segment_idx < ctx->header->map_segment_count) { for (unsigned int i = 0; i < ctx->segments[segment_idx].entry_count; i++) - if (ctx->segments[segment_idx].entries[i].virtual_offset_end > offset) - return &ctx->segments[segment_idx].entries[i]; + if (ctx->segments[segment_idx].entries[i]->virtual_offset_end > offset) + return ctx->segments[segment_idx].entries[i]; } return NULL; } @@ -179,7 +179,7 @@ void save_ivfc_storage_init(hierarchical_integrity_verification_storage_ctx_t *c uint32_t length; }; - static struct salt_source_t salt_sources[6] = { + static const struct salt_source_t salt_sources[6] = { {"HierarchicalIntegrityVerificationStorage::Master", 48}, {"HierarchicalIntegrityVerificationStorage::L1", 44}, {"HierarchicalIntegrityVerificationStorage::L2", 44}, @@ -238,12 +238,14 @@ size_t save_ivfc_level_fread(ivfc_level_save_ctx_t *ctx, void *buffer, uint64_t void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buffer, uint64_t offset, size_t count, uint32_t verify) { if (count > ctx->sector_size) { EPRINTF("IVFC read exceeds sector size!\n"); + return; } uint64_t block_index = offset / ctx->sector_size; if (ctx->block_validities[block_index] == VALIDITY_INVALID && verify) { EPRINTFARGS("Hash error from previous check\n found at offset %x count %x!\n", (u32)offset, count); + return; } uint8_t hash_buffer[0x20] = {0}; @@ -270,7 +272,7 @@ void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buf uint8_t hash[0x20] = {0}; uint8_t *data_buffer = calloc(1, ctx->sector_size + 0x20); memcpy(data_buffer, ctx->salt, 0x20); - memcpy(data_buffer + 0x20, buffer, count); + memcpy(data_buffer + 0x20, buffer, ctx->sector_size); se_calc_sha256(hash, data_buffer, ctx->sector_size + 0x20); hash[0x1F] |= 0x80; @@ -284,6 +286,7 @@ void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buf if (ctx->block_validities[block_index] == VALIDITY_INVALID && verify) { EPRINTFARGS("Hash error from current check\n found at offset %x count %x!\n", (u32)offset, count); + return; } } @@ -295,6 +298,7 @@ uint32_t save_allocation_table_read_entry_with_length(allocation_table_ctx_t *ct if ((entries[0].next & 0x80000000) == 0) { if (entries[0].prev & 0x80000000 && entries[0].prev != 0x80000000) { EPRINTF("Invalid range entry in allocation table!\n"); + return 0; } } else { length = entries[1].next - entry_index + 1; @@ -354,6 +358,7 @@ void save_allocation_table_iterator_begin(allocation_table_iterator_ctx_t *ctx, if (ctx->prev_block != 0xFFFFFFFF) { EPRINTFARGS("Attempted to start FAT iteration from\n invalid block %x!\n", initial_block); + return; } } @@ -464,6 +469,8 @@ uint32_t save_fs_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_ while (index) { if (index > capacity) { EPRINTFARGS("Save entry index %d out of range!", index); + *prev_index = 0xFFFFFFFF; + return 0xFFFFFFFF; } save_fs_list_read_entry(ctx, index, &entry); if (entry.parent == key->parent && !strcmp(entry.name, key->name)) { @@ -619,26 +626,25 @@ validity_t save_filesystem_verify(save_ctx_t *ctx) { return journal_validity; } -void save_process(save_ctx_t *ctx) { +bool save_process(save_ctx_t *ctx) { /* Try to parse Header A. */ f_lseek(ctx->file, 0); if (f_read(ctx->file, &ctx->header, sizeof(ctx->header), NULL)) { EPRINTF("Failed to read save header!\n"); + return false; } - save_process_header(ctx); - - if (ctx->header_hash_validity == VALIDITY_INVALID) { + if (!save_process_header(ctx) || (ctx->header_hash_validity == VALIDITY_INVALID)) { /* Try to parse Header B. */ f_lseek(ctx->file, 0x4000); if (f_read(ctx->file, &ctx->header, sizeof(ctx->header), NULL)) { EPRINTF("Failed to read save header!\n"); + return false; } - save_process_header(ctx); - - if (ctx->header_hash_validity == VALIDITY_INVALID) { + if (!save_process_header(ctx) || (ctx->header_hash_validity == VALIDITY_INVALID)) { EPRINTF("Error: Save header is invalid!\n"); + return false; } } @@ -759,14 +765,18 @@ void save_process(save_ctx_t *ctx) { /* Initialize core save filesystem. */ ctx->save_filesystem_core.base_storage = &ctx->core_data_ivfc_storage; save_filesystem_init(&ctx->save_filesystem_core, ctx->fat_storage, &ctx->header.save_header, &ctx->header.fat_header); + + return true; } -void save_process_header(save_ctx_t *ctx) { +bool save_process_header(save_ctx_t *ctx) { if (ctx->header.layout.magic != MAGIC_DISF || ctx->header.duplex_header.magic != MAGIC_DPFS || ctx->header.data_ivfc_header.magic != MAGIC_IVFC || ctx->header.journal_header.magic != MAGIC_JNGL || ctx->header.save_header.magic != MAGIC_SAVE || ctx->header.main_remap_header.magic != MAGIC_RMAP || - ctx->header.meta_remap_header.magic != MAGIC_RMAP) { + ctx->header.meta_remap_header.magic != MAGIC_RMAP) + { EPRINTF("Error: Save header is corrupt!\n"); + return false; } ctx->data_ivfc_master = (uint8_t *)&ctx->header + ctx->header.layout.ivfc_master_hash_offset_a; @@ -781,19 +791,16 @@ void save_process_header(save_ctx_t *ctx) { if (ctx->header.layout.version >= 0x50000) { ctx->header.fat_ivfc_header.num_levels = 4; } + return true; } void save_free_contexts(save_ctx_t *ctx) { for (unsigned int i = 0; i < ctx->data_remap_storage.header->map_segment_count; i++) { - for (unsigned int j = 0; j < ctx->data_remap_storage.segments[i].entry_count; j++) { - free(&ctx->data_remap_storage.segments[i].entries[j]); - } + free(ctx->data_remap_storage.segments[i].entries); } free(ctx->data_remap_storage.segments); for (unsigned int i = 0; i < ctx->meta_remap_storage.header->map_segment_count; i++) { - for (unsigned int j = 0; j < ctx->meta_remap_storage.segments[i].entry_count; j++) { - free(&ctx->meta_remap_storage.segments[i].entries[j]); - } + free(ctx->meta_remap_storage.segments[i].entries); } free(ctx->meta_remap_storage.segments); free(ctx->data_remap_storage.map_entries); diff --git a/source/keys/save.h b/source/keys/save.h index ea44906..f7006ca 100644 --- a/source/keys/save.h +++ b/source/keys/save.h @@ -157,7 +157,7 @@ struct remap_entry_ctx_t { struct remap_segment_ctx_t{ uint64_t offset; uint64_t length; - remap_entry_ctx_t *entries; + remap_entry_ctx_t **entries; uint64_t entry_count; }; @@ -472,8 +472,8 @@ static inline uint32_t save_allocation_table_get_free_list_block_index(allocatio return allocation_table_entry_index_to_block(save_allocation_table_get_free_list_entry_index(ctx)); } -void save_process(save_ctx_t *ctx); -void save_process_header(save_ctx_t *ctx); +bool save_process(save_ctx_t *ctx); +bool save_process_header(save_ctx_t *ctx); void save_save(save_ctx_t *ctx); void save_print(save_ctx_t *ctx);