Fix regression causing segfault on remapped saves (#88)

* save: Find file by path, ignore fail on validate

* save: Revert faulty semantics, fix wrong hash size

* save: Add missing save type from help string

* save: Fix bug in remap storage init
This commit is contained in:
shchmue 2020-06-01 01:05:38 -06:00 committed by GitHub
parent bea6345a48
commit 55b13f0d67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 25 deletions

View file

@ -19,7 +19,7 @@ Options:
-y, --verify Verify hashes and signatures.
-d, --dev Decrypt with development keys instead of retail.
-k, --keyset Load keys from an external file.
-t, --intype=type Specify input file type [nca, xci, pfs0, romfs, hfs0, npdm, pk11, pk21, ini1, kip1, nax0, keygen]
-t, --intype=type Specify input file type [nca, xci, pfs0, romfs, hfs0, npdm, pk11, pk21, ini1, kip1, nax0, save, keygen]
--titlekey=key Set title key for Rights ID crypto titles.
--contentkey=key Set raw key for NCA body decryption.
--disablekeywarns Disables warning output when loading external keys.

2
main.c
View file

@ -34,7 +34,7 @@ static void usage(void) {
" -y, --verify Verify hashes and signatures.\n"
" -d, --dev Decrypt with development keys instead of retail.\n"
" -k, --keyset Load keys from an external file.\n"
" -t, --intype=type Specify input file type [nca, xci, pfs0, romfs, hfs0, npdm, pk11, pk21, ini1, kip1, nax0, keygen]\n"
" -t, --intype=type Specify input file type [nca, xci, pfs0, romfs, hfs0, npdm, pk11, pk21, ini1, kip1, nax0, save, keygen]\n"
" --titlekey=key Set title key for Rights ID crypto titles.\n"
" --contentkey=key Set raw key for NCA body decryption.\n"
" --disablekeywarns Disables warning output when loading external keys.\n"

60
save.c
View file

@ -1,3 +1,4 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "save.h"
@ -68,27 +69,34 @@ 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;
remap_entry_ctx_t **ptr = malloc(sizeof(remap_entry_ctx_t *) * (seg->entry_count + 1));
if (!ptr) {
fprintf(stderr, "Failed to allocate entries in remap storage!\n");
exit(EXIT_FAILURE);
}
seg->entries = ptr;
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++;
ptr = realloc(seg->entries, sizeof(remap_entry_ctx_t *) * (seg->entry_count + 1));
if (!ptr) {
fprintf(stderr, "Failed to reallocate entries in remap storage!\n");
exit(EXIT_FAILURE);
}
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;
}
@ -97,8 +105,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];
}
fprintf(stderr, "Remap offset %"PRIx64" out of range!\n", offset);
exit(EXIT_FAILURE);
@ -178,7 +186,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},
@ -272,7 +280,7 @@ void save_ivfc_storage_read(integrity_verification_storage_ctx_t *ctx, void *buf
sha_ctx_t *sha_ctx = new_sha_ctx(HASH_TYPE_SHA256, 0);
sha_update(sha_ctx, ctx->salt, 0x20);
sha_update(sha_ctx, data_buffer, count);
sha_update(sha_ctx, data_buffer, ctx->sector_size);
sha_get_hash(sha_ctx, hash);
free_sha_ctx(sha_ctx);
hash[0x1F] |= 0x80;
@ -482,6 +490,18 @@ uint32_t save_fs_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_
return 0xFFFFFFFF;
}
int save_hierarchical_file_table_find_path_recursive(hierarchical_save_file_table_ctx_t *ctx, save_entry_key_t *key, char *path) {
memcpy(key->name, path, SAVE_FS_LIST_MAX_NAME_LENGTH);
key->parent = 0;
char *pos = path;
while (pos) {
key->parent = save_fs_get_index_from_key(&ctx->directory_table, key, NULL);
if (key->parent == 0xFFFFFFFF) return 0;
pos = strchr(pos, '/');
}
return 1;
}
int save_hierarchical_file_table_find_next_file(hierarchical_save_file_table_ctx_t *ctx, save_find_position_t *position, save_file_info_t *info, char *name) {
if (position->next_file == 0) {
return 0;
@ -627,7 +647,7 @@ void save_process(save_ctx_t *ctx) {
ctx->data_remap_storage.type = STORAGE_BYTES;
ctx->data_remap_storage.base_storage_offset = ctx->header.layout.file_map_data_offset;
ctx->data_remap_storage.header = &ctx->header.main_remap_header;
ctx->data_remap_storage.map_entries = malloc(sizeof(remap_entry_ctx_t) * ctx->data_remap_storage.header->map_entry_count);
ctx->data_remap_storage.map_entries = calloc(1, sizeof(remap_entry_ctx_t) * ctx->data_remap_storage.header->map_entry_count);
ctx->data_remap_storage.file = ctx->file;
fseeko64(ctx->file, ctx->header.layout.file_map_entry_offset, SEEK_SET);
for (unsigned int i = 0; i < ctx->data_remap_storage.header->map_entry_count; i++) {
@ -763,15 +783,11 @@ void save_process_header(save_ctx_t *ctx) {
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);

2
save.h
View file

@ -146,7 +146,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;
};