mirror of
https://github.com/SciresM/hactool.git
synced 2024-11-23 04:29:41 +00:00
Fix trailing spaces
This commit is contained in:
parent
55e625d67d
commit
e37d4d2da2
30
aes.c
30
aes.c
@ -8,24 +8,24 @@
|
||||
/* Allocate a new context. */
|
||||
aes_ctx_t *new_aes_ctx(const void *key, unsigned int key_size, aes_mode_t mode) {
|
||||
aes_ctx_t *ctx;
|
||||
|
||||
|
||||
if ((ctx = malloc(sizeof(*ctx))) == NULL) {
|
||||
FATAL_ERROR("Failed to allocate aes_ctx_t!");
|
||||
}
|
||||
|
||||
mbedtls_cipher_init(&ctx->cipher_dec);
|
||||
mbedtls_cipher_init(&ctx->cipher_enc);
|
||||
|
||||
|
||||
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))) {
|
||||
FATAL_ERROR("Failed to set up AES context!");
|
||||
}
|
||||
|
||||
|
||||
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)) {
|
||||
FATAL_ERROR("Failed to set key for AES context!");
|
||||
}
|
||||
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ void free_aes_ctx(aes_ctx_t *ctx) {
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mbedtls_cipher_free(&ctx->cipher_dec);
|
||||
mbedtls_cipher_free(&ctx->cipher_enc);
|
||||
free(ctx);
|
||||
@ -65,17 +65,17 @@ void aes_calculate_cmac(void *dst, void *src, size_t size, const void *key) {
|
||||
/* Encrypt with context. */
|
||||
void aes_encrypt(aes_ctx_t *ctx, void *dst, const void *src, size_t l) {
|
||||
size_t out_len = 0;
|
||||
|
||||
|
||||
/* Prepare context */
|
||||
mbedtls_cipher_reset(&ctx->cipher_enc);
|
||||
|
||||
|
||||
/* XTS doesn't need per-block updating */
|
||||
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);
|
||||
else
|
||||
{
|
||||
unsigned int blk_size = mbedtls_cipher_get_block_size(&ctx->cipher_enc);
|
||||
|
||||
|
||||
/* Do per-block updating */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Flush all data */
|
||||
mbedtls_cipher_finish(&ctx->cipher_enc, NULL, NULL);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
@ -105,17 +105,17 @@ void aes_decrypt(aes_ctx_t *ctx, void *dst, const void *src, size_t l)
|
||||
}
|
||||
|
||||
size_t out_len = 0;
|
||||
|
||||
|
||||
/* Prepare context */
|
||||
mbedtls_cipher_reset(&ctx->cipher_dec);
|
||||
|
||||
|
||||
/* XTS doesn't need per-block updating */
|
||||
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);
|
||||
else
|
||||
{
|
||||
unsigned int blk_size = mbedtls_cipher_get_block_size(&ctx->cipher_dec);
|
||||
|
||||
|
||||
/* Do per-block updating */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Flush all data */
|
||||
mbedtls_cipher_finish(&ctx->cipher_dec, NULL, NULL);
|
||||
|
||||
|
16
bktr.c
16
bktr.c
@ -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");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
uint32_t bucket_num = 0;
|
||||
for (unsigned int i = 1; i < block->num_buckets; i++) {
|
||||
if (block->bucket_virtual_offsets[i] <= offset) {
|
||||
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. */
|
||||
return &bucket->entries[0];
|
||||
}
|
||||
|
||||
|
||||
/* Binary search. */
|
||||
uint32_t low = 0, high = bucket->num_entries - 1;
|
||||
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) {
|
||||
return &last_bucket->entries[last_bucket->num_entries];
|
||||
}
|
||||
|
||||
|
||||
uint32_t bucket_num = 0;
|
||||
for (unsigned int i = 1; i < block->num_buckets; i++) {
|
||||
if (block->bucket_physical_offsets[i] <= offset) {
|
||||
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. */
|
||||
return &bucket->entries[0];
|
||||
}
|
||||
|
||||
|
||||
/* Binary search. */
|
||||
uint32_t low = 0, high = bucket->num_entries - 1;
|
||||
while (low <= high) {
|
||||
|
46
extkeys.c
46
extkeys.c
@ -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) {
|
||||
char *key, *value;
|
||||
int ret;
|
||||
|
||||
|
||||
while ((ret = get_kv(f, &key, &value)) != 1 && ret != -2) {
|
||||
if (ret == 0) {
|
||||
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 titlekey[0x10];
|
||||
|
||||
|
||||
bool should_ignore_key = false;
|
||||
if (strlen(key) != 0x20) {
|
||||
should_ignore_key = true;
|
||||
@ -214,7 +214,7 @@ void extkeys_initialize_settings(hactool_settings_t *settings, FILE *f) {
|
||||
char *key, *value;
|
||||
int ret;
|
||||
nca_keyset_t *keyset = &settings->keyset;
|
||||
|
||||
|
||||
while ((ret = get_kv(f, &key, &value)) != 1 && ret != -2) {
|
||||
if (ret == 0) {
|
||||
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));
|
||||
matched_key = 1;
|
||||
} 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;
|
||||
} 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));
|
||||
@ -307,28 +307,28 @@ void extkeys_initialize_settings(hactool_settings_t *settings, FILE *f) {
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "keyblob_key_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->keyblob_keys[i], value, sizeof(keyset->keyblob_keys[i]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "keyblob_mac_key_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->keyblob_mac_keys[i], value, sizeof(keyset->keyblob_mac_keys[i]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "encrypted_keyblob_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->encrypted_keyblobs[i], value, sizeof(keyset->encrypted_keyblobs[i]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "keyblob_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "tsec_root_key_%02"PRIx32, i - 6);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->tsec_root_keys[i - 6], value, sizeof(keyset->tsec_root_keys[i - 6]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "master_kek_source_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->master_kek_sources[i], value, sizeof(keyset->master_kek_sources[i]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "package1_mac_key_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->master_keks[i], value, sizeof(keyset->master_keks[i]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "master_key_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->master_keys[i], value, sizeof(keyset->master_keys[i]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "package1_key_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->package1_keys[i], value, sizeof(keyset->package1_keys[i]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "package2_key_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->package2_keys[i], value, sizeof(keyset->package2_keys[i]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "titlekek_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->titlekeks[i], value, sizeof(keyset->titlekeks[i]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "key_area_key_application_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->key_area_keys[i][0], value, sizeof(keyset->key_area_keys[i][0]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "key_area_key_ocean_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
parse_hex_key(keyset->key_area_keys[i][1], value, sizeof(keyset->key_area_keys[i][1]));
|
||||
matched_key = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
snprintf(test_name, sizeof(test_name), "key_area_key_system_%02"PRIx32, i);
|
||||
if (strcmp(key, test_name) == 0) {
|
||||
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");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
/* Ensure enough space for keys. */
|
||||
if (settings->known_titlekeys.count == 0) {
|
||||
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");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
titlekey_entry_t *new_key = &settings->known_titlekeys.titlekeys[settings->known_titlekeys.count++];
|
||||
|
||||
|
||||
memcpy(new_key->rights_id, rights_id, 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 NULL;
|
||||
}
|
||||
|
||||
|
14
hfs0.c
14
hfs0.c
@ -3,13 +3,13 @@
|
||||
|
||||
void hfs0_process(hfs0_ctx_t *ctx) {
|
||||
/* Read *just* safe amount. */
|
||||
hfs0_header_t raw_header;
|
||||
hfs0_header_t raw_header;
|
||||
fseeko64(ctx->file, ctx->offset, SEEK_SET);
|
||||
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
|
||||
fprintf(stderr, "Failed to read HFS0 header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (raw_header.magic != MAGIC_HFS0) {
|
||||
memdump(stdout, "Sanity: ", &raw_header, sizeof(raw_header));
|
||||
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");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
fseeko64(ctx->file, ctx->offset, SEEK_SET);
|
||||
if (fread(ctx->header, 1, header_size, ctx->file) != header_size) {
|
||||
fprintf(stderr, "Failed to read HFS0 header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
/* Weak file validation. */
|
||||
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;
|
||||
for (unsigned int i = 0; i < ctx->header->num_files; 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;
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
hfs0_print(ctx);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
hfs0_save(ctx);
|
||||
}
|
||||
|
42
kip.c
42
kip.c
@ -6,13 +6,13 @@
|
||||
|
||||
void ini1_process(ini1_ctx_t *ctx) {
|
||||
/* Read *just* safe amount. */
|
||||
ini1_header_t raw_header;
|
||||
ini1_header_t raw_header;
|
||||
fseeko64(ctx->file, 0, SEEK_SET);
|
||||
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
|
||||
fprintf(stderr, "Failed to read INI1 header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (raw_header.magic != MAGIC_INI1 || raw_header.num_processes > INI1_MAX_KIPS) {
|
||||
printf("Error: INI1 is corrupt!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
@ -23,13 +23,13 @@ void ini1_process(ini1_ctx_t *ctx) {
|
||||
fprintf(stderr, "Failed to allocate INI1 header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
fseeko64(ctx->file, 0, SEEK_SET);
|
||||
if (fread(ctx->header, 1, raw_header.size, ctx->file) != raw_header.size) {
|
||||
fprintf(stderr, "Failed to read INI1!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
uint64_t offset = 0;
|
||||
for (unsigned int i = 0; i < ctx->header->num_processes; i++) {
|
||||
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]);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
ini1_print(ctx);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
ini1_save(ctx);
|
||||
}
|
||||
@ -109,7 +109,7 @@ char *kip1_get_json(kip1_ctx_t *ctx) {
|
||||
cJSON *kip_json = cJSON_CreateObject();
|
||||
char *output_str = NULL;
|
||||
char work_buffer[0x300] = {0};
|
||||
|
||||
|
||||
/* Add KIP1 header fields. */
|
||||
strcpy(work_buffer, ctx->header->name);
|
||||
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, "default_cpu_id", ctx->header->default_core);
|
||||
cJSON_AddNumberToObject(kip_json, "process_category", ctx->header->process_category);
|
||||
|
||||
|
||||
/* Add KAC. */
|
||||
cJSON *kac_json = kac_get_json(ctx->header->capabilities, sizeof(ctx->header->capabilities) / sizeof(uint32_t));
|
||||
cJSON_AddItemToObject(kip_json, "kernel_capabilities", kac_json);
|
||||
|
||||
|
||||
output_str = cJSON_Print(kip_json);
|
||||
|
||||
|
||||
cJSON_Delete(kip_json);
|
||||
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 header_size = ((uint32_t *)hdr_end)[-2];
|
||||
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);
|
||||
uint32_t cmp_ofs = cmp_and_hdr_size - header_size;
|
||||
uint32_t out_ofs = cmp_and_hdr_size + addl_size;
|
||||
|
||||
|
||||
while (out_ofs) {
|
||||
unsigned char control = cmp_start[--cmp_ofs];
|
||||
for (unsigned int i = 0; i < 8; i++) {
|
||||
@ -155,7 +155,7 @@ static void kip1_blz_uncompress(void *hdr_end) {
|
||||
seg_size = out_ofs;
|
||||
}
|
||||
out_ofs -= seg_size;
|
||||
|
||||
|
||||
for (unsigned int j = 0; j < seg_size; j++) {
|
||||
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.flags &= 0xF8;
|
||||
|
||||
|
||||
*size = kip1_get_size_from_header(&new_header);
|
||||
unsigned char *new_kip = calloc(1, *size);
|
||||
if (new_kip == NULL) {
|
||||
@ -190,7 +190,7 @@ static void *kip1_uncompress(kip1_ctx_t *ctx, uint64_t *size) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*((kip1_header_t *)new_kip) = new_header;
|
||||
|
||||
|
||||
uint64_t new_offset = 0x100;
|
||||
uint64_t old_offset = 0x100;
|
||||
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;
|
||||
old_offset += ctx->header->section_headers[i].compressed_size;
|
||||
}
|
||||
|
||||
|
||||
return new_kip;
|
||||
}
|
||||
|
||||
void kip1_process(kip1_ctx_t *ctx) {
|
||||
/* Read *just* safe amount. */
|
||||
kip1_header_t raw_header;
|
||||
kip1_header_t raw_header;
|
||||
fseeko64(ctx->file, 0, SEEK_SET);
|
||||
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
|
||||
fprintf(stderr, "Failed to read KIP1 header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (raw_header.magic != MAGIC_KIP1) {
|
||||
printf("Error: KIP1 is corrupt!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
@ -224,17 +224,17 @@ void kip1_process(kip1_ctx_t *ctx) {
|
||||
fprintf(stderr, "Failed to allocate KIP1!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
fseeko64(ctx->file, 0, SEEK_SET);
|
||||
if (fread(ctx->header, 1, size, ctx->file) != size) {
|
||||
fprintf(stderr, "Failed to read KIP1!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
kip1_print(ctx, 0);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
kip1_save(ctx);
|
||||
}
|
||||
|
76
main.c
76
main.c
@ -19,7 +19,7 @@ static const char *prog_name = "hactool";
|
||||
|
||||
/* Print usage. Taken largely from ctrtool. */
|
||||
static void usage(void) {
|
||||
fprintf(stderr,
|
||||
fprintf(stderr,
|
||||
"hactool (c) SciresM.\n"
|
||||
"Built: %s %s\n"
|
||||
"\n"
|
||||
@ -56,7 +56,7 @@ static void usage(void) {
|
||||
" --baseromfs Set Base RomFS 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"
|
||||
" --onlyupdated Ignore non-updated files in update partitions.\n"
|
||||
" --onlyupdated Ignore non-updated files in update partitions.\n"
|
||||
"NPDM options:\n"
|
||||
" --json=file Specify file path for saving JSON representation of program permissions to.\n"
|
||||
"KIP1 options:\n"
|
||||
@ -124,7 +124,7 @@ int main(int argc, char **argv) {
|
||||
filepath_init(&keypath);
|
||||
nca_ctx.tool_ctx = &tool_ctx;
|
||||
nca_ctx.is_cli_target = true;
|
||||
|
||||
|
||||
nca_ctx.tool_ctx->file_type = FILETYPE_NCA;
|
||||
base_ctx.file_type = FILETYPE_NCA;
|
||||
|
||||
@ -134,7 +134,7 @@ int main(int argc, char **argv) {
|
||||
while (1) {
|
||||
int option_index;
|
||||
int c;
|
||||
static struct option long_options[] =
|
||||
static struct option long_options[] =
|
||||
{
|
||||
{"extract", 0, NULL, 'x'},
|
||||
{"info", 0, NULL, 'i'},
|
||||
@ -192,7 +192,7 @@ int main(int argc, char **argv) {
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c)
|
||||
switch (c)
|
||||
{
|
||||
case 'i':
|
||||
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")) {
|
||||
nca_ctx.tool_ctx->file_type = FILETYPE_PFS0;
|
||||
} 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")) {
|
||||
nca_ctx.tool_ctx->file_type = FILETYPE_NCA0_ROMFS;
|
||||
nca_ctx.tool_ctx->file_type = FILETYPE_NCA0_ROMFS;
|
||||
} else if (!strcmp(optarg, "hfs0")) {
|
||||
nca_ctx.tool_ctx->file_type = FILETYPE_HFS0;
|
||||
} 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 8:
|
||||
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;
|
||||
case 9:
|
||||
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;
|
||||
case 10:
|
||||
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;
|
||||
case 11:
|
||||
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;
|
||||
case 12:
|
||||
parse_hex_key(nca_ctx.tool_ctx->settings.cli_titlekey, optarg, 16);
|
||||
@ -314,43 +314,43 @@ int main(int argc, char **argv) {
|
||||
break;
|
||||
case 17:
|
||||
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;
|
||||
case 18:
|
||||
filepath_set(&nca_ctx.tool_ctx->settings.plaintext_path, optarg);
|
||||
filepath_set(&nca_ctx.tool_ctx->settings.plaintext_path, optarg);
|
||||
break;
|
||||
case 19:
|
||||
filepath_set(&nca_ctx.tool_ctx->settings.header_path, optarg);
|
||||
filepath_set(&nca_ctx.tool_ctx->settings.header_path, optarg);
|
||||
break;
|
||||
case 20:
|
||||
filepath_set(&tool_ctx.settings.pfs0_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.pfs0_dir_path, optarg);
|
||||
break;
|
||||
case 21:
|
||||
filepath_set(&tool_ctx.settings.hfs0_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.hfs0_dir_path, optarg);
|
||||
break;
|
||||
case 22:
|
||||
filepath_set(&tool_ctx.settings.rootpt_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.rootpt_dir_path, optarg);
|
||||
break;
|
||||
case 23:
|
||||
filepath_set(&tool_ctx.settings.update_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.update_dir_path, optarg);
|
||||
break;
|
||||
case 24:
|
||||
filepath_set(&tool_ctx.settings.normal_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.normal_dir_path, optarg);
|
||||
break;
|
||||
case 25:
|
||||
filepath_set(&tool_ctx.settings.secure_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.secure_dir_path, optarg);
|
||||
break;
|
||||
case 26:
|
||||
filepath_set(&tool_ctx.settings.logo_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.logo_dir_path, optarg);
|
||||
break;
|
||||
case 27:
|
||||
filepath_set(&tool_ctx.settings.pk11_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.pk11_dir_path, optarg);
|
||||
break;
|
||||
case 28:
|
||||
filepath_set(&tool_ctx.settings.pk21_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.pk21_dir_path, optarg);
|
||||
break;
|
||||
case 29:
|
||||
filepath_set(&tool_ctx.settings.ini1_dir_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.ini1_dir_path, optarg);
|
||||
break;
|
||||
case 30:
|
||||
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);
|
||||
break;
|
||||
case 37:
|
||||
filepath_set(&tool_ctx.settings.npdm_json_path, optarg);
|
||||
filepath_set(&tool_ctx.settings.npdm_json_path, optarg);
|
||||
break;
|
||||
case 38:
|
||||
tool_ctx.action |= ACTION_SAVEINIJSON;
|
||||
break;
|
||||
case 39:
|
||||
filepath_set(&nca_ctx.tool_ctx->settings.uncompressed_path, optarg);
|
||||
filepath_set(&nca_ctx.tool_ctx->settings.uncompressed_path, optarg);
|
||||
break;
|
||||
case 40:
|
||||
nca_ctx.tool_ctx->settings.skip_key_warnings = 1;
|
||||
@ -405,7 +405,7 @@ int main(int argc, char **argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Try to populate default keyfile. */
|
||||
FILE *keyfile = NULL;
|
||||
if (keypath.valid == VALIDITY_VALID) {
|
||||
@ -432,7 +432,7 @@ int main(int argc, char **argv) {
|
||||
pki_derive_keys(&tool_ctx.settings.keyset);
|
||||
fclose(keyfile);
|
||||
}
|
||||
|
||||
|
||||
/* Try to load titlekeys. */
|
||||
FILE *titlekeyfile = open_key_file("title");
|
||||
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))) {
|
||||
usage();
|
||||
}
|
||||
|
||||
|
||||
/* Special case NAX0. */
|
||||
if (tool_ctx.file_type == FILETYPE_NAX0) {
|
||||
nax0_ctx_t nax_ctx;
|
||||
@ -453,10 +453,10 @@ int main(int argc, char **argv) {
|
||||
filepath_set(&nax_ctx.base_path, input_name);
|
||||
nax_ctx.tool_ctx = &tool_ctx;
|
||||
nax0_process(&nax_ctx);
|
||||
|
||||
|
||||
if (nax_ctx.aes_ctx) {
|
||||
free_aes_ctx(nax_ctx.aes_ctx);
|
||||
}
|
||||
}
|
||||
if (nax_ctx.num_files) {
|
||||
for (unsigned int i = 0; i < nax_ctx.num_files; i++) {
|
||||
fclose(nax_ctx.files[i]);
|
||||
@ -467,13 +467,13 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
printf("Done!\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
switch (tool_ctx.file_type) {
|
||||
case FILETYPE_NCA: {
|
||||
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_process(&nca_ctx);
|
||||
nca_free_section_contexts(&nca_ctx);
|
||||
|
||||
|
||||
if (nca_ctx.tool_ctx->base_file_type == BASEFILE_FAKE) {
|
||||
nca_ctx.tool_ctx->base_file = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (nca_ctx.tool_ctx->base_file != NULL) {
|
||||
fclose(nca_ctx.tool_ctx->base_file);
|
||||
if (nca_ctx.tool_ctx->base_file_type == BASEFILE_NCA) {
|
||||
nca_free_section_contexts(nca_ctx.tool_ctx->base_nca_ctx);
|
||||
free(nca_ctx.tool_ctx->base_nca_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FILETYPE_PFS0: {
|
||||
@ -699,7 +699,7 @@ int main(int argc, char **argv) {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (tool_ctx.settings.known_titlekeys.titlekeys != NULL) {
|
||||
free(tool_ctx.settings.known_titlekeys.titlekeys);
|
||||
}
|
||||
|
18
nax0.c
18
nax0.c
@ -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);
|
||||
return fread(dst, 1, size, ctx->files[0]);
|
||||
}
|
||||
|
||||
|
||||
FILE *which = ctx->files[offset / 0xFFFF0000ULL];
|
||||
uint64_t offset_in_file = offset % 0xFFFF0000ULL;
|
||||
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));
|
||||
if (ctx->header.magic != MAGIC_NAX0) {
|
||||
printf("Error: File has invalid NAX0 magic!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
memcpy(ctx->encrypted_keys, ctx->header.keys, sizeof(ctx->header.keys));
|
||||
|
||||
|
||||
int found = 0;
|
||||
for (ctx->k = 0; ctx->k < 2; ctx->k++) {
|
||||
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);
|
||||
free_aes_ctx(nax_k_ctx);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
if (memcmp(ctx->header.hmac_header, validation_mac, 0x20) == 0) {
|
||||
@ -87,18 +87,18 @@ void nax0_process(nax0_ctx_t *ctx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!found) {
|
||||
printf("Error: NAX0 key derivation failed. Check SD card seed and relative path?\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ctx->aes_ctx = new_aes_ctx(ctx->header.keys, 0x20, AES_MODE_XTS);
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
nax0_print(ctx);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
nax0_save(ctx);
|
||||
}
|
||||
|
2
nca.h
2
nca.h
@ -165,7 +165,7 @@ typedef struct {
|
||||
|
||||
typedef struct nca_ctx {
|
||||
FILE *file; /* File for this NCA. */
|
||||
size_t file_size;
|
||||
size_t file_size;
|
||||
unsigned char crypto_type;
|
||||
int has_rights_id;
|
||||
int is_decrypted;
|
||||
|
@ -16,7 +16,7 @@ static void nca0_romfs_visit_file(nca0_romfs_ctx_t *ctx, uint32_t file_offset, f
|
||||
if (entry->name_size) {
|
||||
filepath_append_n(cur_path, entry->name_size, "%s", entry->name);
|
||||
}
|
||||
|
||||
|
||||
/* If we're extracting... */
|
||||
if ((ctx->tool_ctx->action & ACTION_LISTROMFS) == 0) {
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* If there's ever anything meaningful to print about RomFS, uncomment and implement.
|
||||
*
|
||||
* if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
* nca0_romfs_print(ctx);
|
||||
* }
|
||||
*/
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
nca0_romfs_save(ctx);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ typedef struct {
|
||||
uint32_t block_size; /* In bytes. */
|
||||
uint32_t always_2;
|
||||
uint64_t hash_table_offset; /* Normally zero. */
|
||||
uint64_t hash_table_size;
|
||||
uint64_t hash_table_size;
|
||||
uint64_t romfs_offset;
|
||||
uint64_t romfs_size;
|
||||
uint8_t _0x48[0xF0];
|
||||
|
20
nso.c
20
nso.c
@ -15,7 +15,7 @@ static void *nso_uncompress(nso0_ctx_t *ctx) {
|
||||
new_header.segments[1].align_or_total_size = 0;
|
||||
/* Clear compression flags. */
|
||||
new_header.flags &= 0xF8;
|
||||
|
||||
|
||||
uint64_t size = nso_get_size(&new_header);
|
||||
nso0_header_t *new_nso = calloc(1, size);
|
||||
if (new_nso == NULL) {
|
||||
@ -23,7 +23,7 @@ static void *nso_uncompress(nso0_ctx_t *ctx) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*((nso0_header_t *)new_nso) = new_header;
|
||||
|
||||
|
||||
for (unsigned int segment = 0; segment < 3; segment++) {
|
||||
char *src = (char *)ctx->header + ctx->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;
|
||||
}
|
||||
|
||||
void nso0_process(nso0_ctx_t *ctx) {
|
||||
/* Read *just* safe amount. */
|
||||
nso0_header_t raw_header;
|
||||
nso0_header_t raw_header;
|
||||
fseeko64(ctx->file, 0, SEEK_SET);
|
||||
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
|
||||
fprintf(stderr, "Failed to read NSO0 header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (raw_header.magic != MAGIC_NSO0) {
|
||||
printf("Error: NSO0 is corrupt!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
@ -69,19 +69,19 @@ void nso0_process(nso0_ctx_t *ctx) {
|
||||
fprintf(stderr, "Failed to allocate NSO0!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
fseeko64(ctx->file, 0, SEEK_SET);
|
||||
if (fread(ctx->header, 1, size, ctx->file) != size) {
|
||||
fprintf(stderr, "Failed to read NSO0!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
ctx->uncompressed_header = nso_uncompress(ctx);
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
nso0_print(ctx);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
nso0_save(ctx);
|
||||
}
|
||||
@ -111,7 +111,7 @@ void nso0_print(nso0_ctx_t *ctx) {
|
||||
|
||||
void nso0_save(nso0_ctx_t *ctx) {
|
||||
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);
|
||||
if (f_uncmp == NULL) {
|
||||
fprintf(stderr, "Failed to open %s!\n", uncmp_path->char_path);
|
||||
|
70
packages.c
70
packages.c
@ -11,7 +11,7 @@ void pk11_process(pk11_ctx_t *ctx) {
|
||||
fprintf(stderr, "Failed to read PK11 Stage 1!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
/* Check if PK11 was built in 2016. */
|
||||
/* 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') {
|
||||
@ -19,18 +19,18 @@ void pk11_process(pk11_ctx_t *ctx) {
|
||||
} else {
|
||||
ctx->is_pilot = 0;
|
||||
}
|
||||
|
||||
|
||||
ctx->pk11 = malloc(ctx->stage1.pk11_size);
|
||||
if (ctx->pk11 == NULL) {
|
||||
fprintf(stderr, "Failed to allocate PK11!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (fread(ctx->pk11, 1, ctx->stage1.pk11_size, ctx->file) != ctx->stage1.pk11_size) {
|
||||
fprintf(stderr, "Failed to read PK11!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
aes_ctx_t *crypt_ctx = NULL;
|
||||
pk11_t dec_header;
|
||||
for (unsigned int i = 0; i < 0x20; i++) {
|
||||
@ -44,26 +44,26 @@ void pk11_process(pk11_ctx_t *ctx) {
|
||||
free_aes_ctx(crypt_ctx);
|
||||
crypt_ctx = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (crypt_ctx == NULL) {
|
||||
fprintf(stderr, "Failed to decrypt PK11! Is correct key present?\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
aes_setiv(crypt_ctx, ctx->stage1.ctr, 0x10);
|
||||
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;
|
||||
pk11_size = align64(pk11_size, 0x10);
|
||||
if (pk11_size != ctx->stage1.pk11_size) {
|
||||
fprintf(stderr, "PK11 seems corrupt!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
pk11_print(ctx);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
pk11_save(ctx);
|
||||
}
|
||||
@ -92,7 +92,7 @@ void pk11_save(pk11_ctx_t *ctx) {
|
||||
}
|
||||
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
||||
os_makedir(dirpath->os_path);
|
||||
|
||||
|
||||
/* Save Decrypted.bin */
|
||||
printf("Saving decrypted binary to %s/Decrypted.bin\n", dirpath->char_path);
|
||||
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);
|
||||
save_buffer_to_directory_file(decrypted_bin, sizeof(ctx->stage1) + ctx->stage1.pk11_size, dirpath, "Decrypted.bin");
|
||||
free(decrypted_bin);
|
||||
|
||||
|
||||
/* Save Warmboot.bin */
|
||||
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 NX_Bootloader.bin */
|
||||
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 Secure_Monitor.bin */
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
void pk21_process(pk21_ctx_t *ctx) {
|
||||
void pk21_process(pk21_ctx_t *ctx) {
|
||||
fseeko64(ctx->file, 0, SEEK_SET);
|
||||
if (fread(&ctx->header, 1, sizeof(ctx->header), ctx->file) != sizeof(ctx->header)) {
|
||||
fprintf(stderr, "Failed to read PK21 Header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
bool is_encrypted = false;
|
||||
for (unsigned int i = 0; i < 0x100; i++) {
|
||||
if (ctx->header.signature[i] != 0) {
|
||||
@ -133,7 +133,7 @@ void pk21_process(pk21_ctx_t *ctx) {
|
||||
}
|
||||
}
|
||||
is_encrypted &= ctx->header.magic != MAGIC_PK21;
|
||||
|
||||
|
||||
if (is_encrypted) {
|
||||
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;
|
||||
@ -143,21 +143,21 @@ void pk21_process(pk21_ctx_t *ctx) {
|
||||
} else {
|
||||
ctx->signature_validity = VALIDITY_UNCHECKED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Nintendo, what the fuck? */
|
||||
ctx->package_size = ctx->header.ctr_dwords[0] ^ ctx->header.ctr_dwords[2] ^ ctx->header.ctr_dwords[3];
|
||||
if (ctx->package_size > 0x7FC000) {
|
||||
fprintf(stderr, "Error: Package2 Header is corrupt!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
aes_ctx_t *crypt_ctx = NULL;
|
||||
if (is_encrypted) {
|
||||
unsigned char ctr[0x10];
|
||||
pk21_header_t temp_header;
|
||||
memcpy(ctr, ctx->header.ctr, sizeof(ctr));
|
||||
|
||||
|
||||
for (unsigned int i = 0; i < 0x20; i++) {
|
||||
ctx->key_rev = i;
|
||||
memcpy(&temp_header, &ctx->header, sizeof(temp_header));
|
||||
@ -172,29 +172,29 @@ void pk21_process(pk21_ctx_t *ctx) {
|
||||
free_aes_ctx(crypt_ctx);
|
||||
crypt_ctx = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (crypt_ctx == NULL) {
|
||||
fprintf(stderr, "Failed to decrypt PK21! Is correct key present?\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
ctx->sections = malloc(ctx->package_size);
|
||||
if (ctx->sections == NULL) {
|
||||
fprintf(stderr, "Failed to allocate sections!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (fread(ctx->sections, 1, ctx->package_size - 0x200, ctx->file) != ctx->package_size - 0x200) {
|
||||
fprintf(stderr, "Failed to read PK21 Sections!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
uint64_t offset = 0;
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
unsigned char calc_hash[0x20];
|
||||
@ -206,11 +206,11 @@ void pk21_process(pk21_ctx_t *ctx) {
|
||||
}
|
||||
if (is_encrypted) {
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
ctx->ini1_ctx.tool_ctx = ctx->tool_ctx;
|
||||
/* Support 8.0.0 INI1 embedded in Kernel */
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
pk21_print(ctx);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
pk21_save(ctx);
|
||||
}
|
||||
@ -271,10 +271,10 @@ void pk21_print(pk21_ctx_t *ctx) {
|
||||
} else {
|
||||
memdump(stdout, " Signature: ", &ctx->header.signature, 0x100);
|
||||
}
|
||||
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
bool is_ini1_embedded = ctx->header.section_sizes[1] == 0;
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
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(" Size: %08"PRIx32"\n", ctx->header.section_sizes[i]);
|
||||
}
|
||||
|
||||
|
||||
printf("\n");
|
||||
ini1_print(&ctx->ini1_ctx);
|
||||
}
|
||||
@ -307,7 +307,7 @@ void pk21_save(pk21_ctx_t *ctx) {
|
||||
}
|
||||
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
|
||||
os_makedir(dirpath->os_path);
|
||||
|
||||
|
||||
/* Save Decrypted.bin */
|
||||
printf("Saving decrypted binary to %s/Decrypted.bin\n", dirpath->char_path);
|
||||
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);
|
||||
save_buffer_to_directory_file(decrypted_bin, ctx->package_size, dirpath, "Decrypted.bin");
|
||||
free(decrypted_bin);
|
||||
|
||||
|
||||
/* Save Kernel.bin */
|
||||
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 INI1.bin */
|
||||
printf("Saving INI1.bin to %s/INI1.bin...\n", dirpath->char_path);
|
||||
if (ctx->header.section_sizes[1] > 0)
|
||||
|
14
pfs0.c
14
pfs0.c
@ -3,13 +3,13 @@
|
||||
|
||||
void pfs0_process(pfs0_ctx_t *ctx) {
|
||||
/* Read *just* safe amount. */
|
||||
pfs0_header_t raw_header;
|
||||
pfs0_header_t raw_header;
|
||||
fseeko64(ctx->file, 0, SEEK_SET);
|
||||
if (fread(&raw_header, 1, sizeof(raw_header), ctx->file) != sizeof(raw_header)) {
|
||||
fprintf(stderr, "Failed to read PFS0 header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (raw_header.magic != MAGIC_PFS0) {
|
||||
printf("Error: PFS0 is corrupt!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
@ -21,16 +21,16 @@ void pfs0_process(pfs0_ctx_t *ctx) {
|
||||
fprintf(stderr, "Failed to allocate PFS0 header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
fseeko64(ctx->file, 0, SEEK_SET);
|
||||
if (fread(ctx->header, 1, header_size, ctx->file) != header_size) {
|
||||
fprintf(stderr, "Failed to read PFS0 header!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
/* Weak file validation. */
|
||||
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;
|
||||
for (unsigned int i = 0; i < ctx->header->num_files; 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) {
|
||||
pfs0_print(ctx);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
pfs0_save(ctx);
|
||||
}
|
||||
|
2
pfs0.h
2
pfs0.h
@ -26,7 +26,7 @@ typedef struct {
|
||||
uint32_t block_size; /* In bytes. */
|
||||
uint32_t always_2;
|
||||
uint64_t hash_table_offset; /* Normally zero. */
|
||||
uint64_t hash_table_size;
|
||||
uint64_t hash_table_size;
|
||||
uint64_t pfs0_offset;
|
||||
uint64_t pfs0_size;
|
||||
uint8_t _0x48[0xF0];
|
||||
|
4
romfs.c
4
romfs.c
@ -96,14 +96,14 @@ void romfs_process(romfs_ctx_t *ctx) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* If there's ever anything meaningful to print about RomFS, uncomment and implement.
|
||||
*
|
||||
* if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
* romfs_print(ctx);
|
||||
* }
|
||||
*/
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
romfs_save(ctx);
|
||||
}
|
||||
|
52
rsa.c
52
rsa.c
@ -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) {
|
||||
unsigned char h_buf[RSA_2048_BYTES] = {0};
|
||||
memcpy(h_buf, h_src, h_src_size);
|
||||
|
||||
|
||||
unsigned char mgf1_buf[0x20];
|
||||
size_t ofs = 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 e_mpi;
|
||||
mbedtls_mpi message_mpi;
|
||||
|
||||
|
||||
mbedtls_mpi_init(&signature_mpi);
|
||||
mbedtls_mpi_init(&modulus_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 h_buf[0x24];
|
||||
const unsigned char E[3] = {1, 0, 1};
|
||||
|
||||
|
||||
mbedtls_mpi_read_binary(&e_mpi, E, 3);
|
||||
mbedtls_mpi_read_binary(&signature_mpi, signature, 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) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_buf[RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1] != 1) {
|
||||
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 e_mpi;
|
||||
mbedtls_mpi message_mpi;
|
||||
|
||||
|
||||
mbedtls_mpi_init(&signature_mpi);
|
||||
mbedtls_mpi_init(&modulus_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 h_buf[0x20];
|
||||
const unsigned char E[3] = {1, 0, 1};
|
||||
|
||||
|
||||
mbedtls_mpi_read_binary(&e_mpi, E, 3);
|
||||
mbedtls_mpi_read_binary(&signature_mpi, signature, 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(&e_mpi);
|
||||
mbedtls_mpi_free(&message_mpi);
|
||||
|
||||
|
||||
/* For RSA-2048, this prefix is just a constant. */
|
||||
const unsigned char pkcs1_hash_prefix[0xE0] = {
|
||||
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, 0x00, 0x30, 0x31, 0x30,
|
||||
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, 0x00, 0x30, 0x31, 0x30,
|
||||
0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
|
||||
};
|
||||
|
||||
|
||||
sha256_hash_buffer(h_buf, data, len);
|
||||
|
||||
|
||||
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 exp_mpi;
|
||||
mbedtls_mpi message_mpi;
|
||||
|
||||
|
||||
mbedtls_mpi_init(&signature_mpi);
|
||||
mbedtls_mpi_init(&modulus_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);
|
||||
|
||||
unsigned char m_buf[RSA_2048_BYTES];
|
||||
|
||||
|
||||
mbedtls_mpi_read_binary(&exp_mpi, exponent, exponent_len);
|
||||
mbedtls_mpi_read_binary(&signature_mpi, signature, 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(&exp_mpi);
|
||||
mbedtls_mpi_free(&message_mpi);
|
||||
|
||||
|
||||
/* There's no automated PSS verification as far as I can tell. */
|
||||
if (m_buf[0] != 0x00) {
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Validate message prefix. */
|
||||
const unsigned char *data = db + 0x20;
|
||||
size_t remaining = RSA_2048_BYTES - 0x20 - 1 - 0x20;
|
||||
|
@ -20,9 +20,9 @@ typedef struct {
|
||||
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 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 encrypted_keyblobs[0x20][0xB0]; /* Actual encrypted keyblobs (EKS). NOTE: CONSOLE UNIQUE. */
|
||||
unsigned char keyblobs[0x20][0x90]; /* Actual decrypted keyblobs (EKS). */
|
||||
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 keyblobs[0x20][0x90]; /* Actual decrypted keyblobs (EKS). */
|
||||
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 tsec_root_kek[0x10]; /* Used to generate TSEC root keys. */
|
||||
|
26
sha.c
26
sha.c
@ -7,21 +7,21 @@
|
||||
/* Allocate new context. */
|
||||
sha_ctx_t *new_sha_ctx(hash_type_t type, int hmac) {
|
||||
sha_ctx_t *ctx;
|
||||
|
||||
|
||||
if ((ctx = malloc(sizeof(*ctx))) == NULL) {
|
||||
FATAL_ERROR("Failed to allocate sha_ctx_t!");
|
||||
}
|
||||
|
||||
|
||||
mbedtls_md_init(&ctx->digest);
|
||||
|
||||
|
||||
if (mbedtls_md_setup(&ctx->digest, mbedtls_md_info_from_type(type), hmac)) {
|
||||
FATAL_ERROR("Failed to set up hash context!");
|
||||
}
|
||||
|
||||
|
||||
if (mbedtls_md_starts(&ctx->digest)) {
|
||||
FATAL_ERROR("Failed to start hash context!");
|
||||
}
|
||||
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ void free_sha_ctx(sha_ctx_t *ctx) {
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mbedtls_md_free(&ctx->digest);
|
||||
free(ctx);
|
||||
}
|
||||
@ -57,29 +57,29 @@ void sha256_hash_buffer(unsigned char *digest, const void *data, size_t l) {
|
||||
/* SHA256-HMAC digest. */
|
||||
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;
|
||||
|
||||
|
||||
if ((ctx = malloc(sizeof(*ctx))) == NULL) {
|
||||
FATAL_ERROR("Failed to allocate sha_ctx_t!");
|
||||
}
|
||||
|
||||
|
||||
mbedtls_md_init(&ctx->digest);
|
||||
|
||||
|
||||
if (mbedtls_md_setup(&ctx->digest, mbedtls_md_info_from_type(HASH_TYPE_SHA256), 1)) {
|
||||
FATAL_ERROR("Failed to set up hash context!");
|
||||
}
|
||||
|
||||
|
||||
if (mbedtls_md_hmac_starts(&ctx->digest, secret, s_l)) {
|
||||
FATAL_ERROR("Failed to set up HMAC secret context!");
|
||||
}
|
||||
|
||||
|
||||
if (mbedtls_md_hmac_update(&ctx->digest, data, d_l)) {
|
||||
FATAL_ERROR("Failed processing HMAC input!");
|
||||
}
|
||||
|
||||
|
||||
if (mbedtls_md_hmac_finish(&ctx->digest, digest)) {
|
||||
FATAL_ERROR("Failed getting HMAC output!");
|
||||
}
|
||||
|
||||
|
||||
mbedtls_md_free(&ctx->digest);
|
||||
free(ctx);
|
||||
}
|
||||
|
82
xci.c
82
xci.c
@ -7,21 +7,21 @@
|
||||
/* However, it (and other XCI keys) can be dumped with a GCD attack on two signatures. */
|
||||
/* Contact SciresM for details, if curious. */
|
||||
static const unsigned char xci_header_pubk[0x100] = {
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
0xD2, 0x3A, 0x8B, 0xB0, 0x8A, 0x42, 0xE3, 0x3D, 0xF1, 0x92, 0x23, 0x42, 0x2E, 0xBA, 0xCC, 0x9C,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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
|
||||
};
|
||||
|
||||
@ -31,12 +31,12 @@ void xci_process(xci_ctx_t *ctx) {
|
||||
fprintf(stderr, "Failed to read XCI header!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (ctx->header.magic != MAGIC_HEAD) {
|
||||
fprintf(stderr, "Error: XCI header is corrupt!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_VERIFY) {
|
||||
if (rsa2048_pkcs1_verify(&ctx->header.magic, 0x100, ctx->header.header_sig, xci_header_pubk)) {
|
||||
ctx->header_sig_validity = VALIDITY_VALID;
|
||||
@ -44,31 +44,31 @@ void xci_process(xci_ctx_t *ctx) {
|
||||
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);
|
||||
if (ctx->hfs0_hash_validity != VALIDITY_VALID) {
|
||||
fprintf(stderr, "Error: XCI partition is corrupt!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
hactool_ctx_t blank_ctx;
|
||||
memset(&blank_ctx, 0, sizeof(blank_ctx));
|
||||
blank_ctx.action = ctx->tool_ctx->action & ~(ACTION_EXTRACT | ACTION_INFO);
|
||||
|
||||
|
||||
ctx->partition_ctx.file = ctx->file;
|
||||
ctx->partition_ctx.offset = ctx->header.hfs0_offset;
|
||||
ctx->partition_ctx.tool_ctx = &blank_ctx;
|
||||
ctx->partition_ctx.name = "rootpt";
|
||||
hfs0_process(&ctx->partition_ctx);
|
||||
|
||||
|
||||
if (ctx->partition_ctx.header->num_files > 4) {
|
||||
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++) {
|
||||
hfs0_ctx_t *cur_ctx = NULL;
|
||||
|
||||
|
||||
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);
|
||||
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;
|
||||
} else if (!strcmp(cur_name, "logo") && ctx->logo_ctx.file == NULL) {
|
||||
cur_ctx = &ctx->logo_ctx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (cur_ctx == NULL) {
|
||||
fprintf(stderr, "Unknown XCI partition: %s\n", cur_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
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->tool_ctx = &blank_ctx;
|
||||
cur_ctx->file = ctx->file;
|
||||
hfs0_process(cur_ctx);
|
||||
}
|
||||
|
||||
|
||||
for (unsigned int i = 0; i < 0x10; i++) {
|
||||
ctx->iv[i] = ctx->header.reversed_iv[0xF-i];
|
||||
}
|
||||
@ -111,12 +111,12 @@ void xci_process(xci_ctx_t *ctx) {
|
||||
} else {
|
||||
ctx->has_decrypted_header = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_INFO) {
|
||||
xci_print(ctx);
|
||||
}
|
||||
|
||||
|
||||
if (ctx->tool_ctx->action & ACTION_EXTRACT) {
|
||||
xci_save(ctx);
|
||||
}
|
||||
@ -130,7 +130,7 @@ void xci_save(xci_ctx_t *ctx) {
|
||||
os_makedir(dirpath->os_path);
|
||||
for (unsigned int i = 0; i < ctx->partition_ctx.header->num_files; i++) {
|
||||
hfs0_ctx_t *cur_ctx = NULL;
|
||||
|
||||
|
||||
char *cur_name = hfs0_get_file_name(ctx->partition_ctx.header, i);
|
||||
if (!strcmp(cur_name, "update")) {
|
||||
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);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,7 +250,7 @@ static void xci_print_hfs0(hfs0_ctx_t *ctx) {
|
||||
print_magic(" Magic: ", ctx->header->magic);
|
||||
printf(" Offset: %012"PRIx64"\n", ctx->offset);
|
||||
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)) {
|
||||
printf(" Files:");
|
||||
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) {
|
||||
printf("\nXCI:\n");
|
||||
print_magic("Magic: ", ctx->header.magic);
|
||||
@ -278,10 +278,10 @@ void xci_print(xci_ctx_t *ctx) {
|
||||
} else {
|
||||
memdump(stdout, "Header Signature: ", &ctx->header.header_sig, 0x100);
|
||||
}
|
||||
|
||||
|
||||
printf("Cartridge Type: %s\n", xci_get_cartridge_type(ctx));
|
||||
printf("Cartridge Size: %012"PRIx64"\n", media_to_real(ctx->header.cart_size + 1));
|
||||
|
||||
|
||||
memdump(stdout, "Header IV: ", ctx->iv, 0x10);
|
||||
memdump(stdout, "Encrypted Header: ", ctx->header.encrypted_data, 0x70);
|
||||
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 Wait2: %08"PRIx32"\n", gc_info->write_time_wait_2);
|
||||
printf(" Firmware Mode: %08"PRIx32"\n", gc_info->firmware_mode);
|
||||
|
||||
|
||||
// decode version
|
||||
uint32_t ver[4] = {0};
|
||||
ver[0] = ((gc_info->cup_version >> 26) & 0x3f);
|
||||
@ -314,16 +314,16 @@ void xci_print(xci_ctx_t *ctx) {
|
||||
printf("Root Partition:\n");
|
||||
}
|
||||
xci_print_hfs0(&ctx->partition_ctx);
|
||||
|
||||
|
||||
printf("Update Partition:\n");
|
||||
xci_print_hfs0(&ctx->update_ctx);
|
||||
|
||||
|
||||
printf("Normal Partition:\n");
|
||||
xci_print_hfs0(&ctx->normal_ctx);
|
||||
|
||||
|
||||
printf("Secure Partition:\n");
|
||||
xci_print_hfs0(&ctx->secure_ctx);
|
||||
|
||||
|
||||
/* Ensure that Logo partition exists. */
|
||||
if (ctx->partition_ctx.header->num_files == 4) {
|
||||
printf("Logo Partition:\n");
|
||||
|
Loading…
Reference in New Issue
Block a user