Finish Package2 support, add support for INI1, KIP1 (Closes #4)

This commit is contained in:
Michael Scire 2018-02-07 00:10:26 -08:00
parent 9ddd0d070e
commit 3e1fddd9de
10 changed files with 335 additions and 11 deletions

View file

@ -13,7 +13,7 @@ all:
.c.o: .c.o:
$(CC) $(INCLUDE) -c $(CFLAGS) -o $@ $< $(CC) $(INCLUDE) -c $(CFLAGS) -o $@ $<
hactool: sha.o aes.o extkeys.o rsa.o npdm.o bktr.o packages.o pki.o pfs0.o hfs0.o romfs.o utils.o nca.o xci.o main.o filepath.o ConvertUTF.o hactool: sha.o aes.o extkeys.o rsa.o npdm.o bktr.o kip.o packages.o pki.o pfs0.o hfs0.o romfs.o utils.o nca.o xci.o main.o filepath.o ConvertUTF.o
$(CC) -o $@ $^ $(LDFLAGS) -L $(LIBDIR) $(CC) -o $@ $^ $(LDFLAGS) -L $(LIBDIR)
aes.o: aes.h types.h aes.o: aes.h types.h
@ -26,9 +26,11 @@ filepath.o: filepath.c types.h
hfs0.o: hfs0.h types.h hfs0.o: hfs0.h types.h
kip.o: kip.h types.h
main.o: main.c pki.h types.h main.o: main.c pki.h types.h
packages.o: packages.h aes.h types.h packages.o: packages.h aes.h kip.h types.h
pfs0.o: pfs0.h types.h pfs0.o: pfs0.h types.h

146
kip.c Normal file
View file

@ -0,0 +1,146 @@
#include <string.h>
#include <stdio.h>
#include "kip.h"
#include "npdm.h"
void ini1_process(ini1_ctx_t *ctx) {
/* Read *just* safe amount. */
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);
}
ctx->header = malloc(raw_header.size);
if (ctx->header == NULL) {
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;
ctx->kips[i].header = (kip1_header_t *)&ctx->header->kip_data[offset];
if (ctx->kips[i].header->magic != MAGIC_KIP1) {
fprintf(stderr, "INI1 is corrupted!\n");
exit(EXIT_FAILURE);
}
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);
}
}
void ini1_print(ini1_ctx_t *ctx) {
printf("INI1:\n");
printf(" Number of Processes: %02"PRIx32"\n", ctx->header->num_processes);
printf(" Size: %08"PRIx32"\n", ctx->header->size);
printf("\n");
for (unsigned int i = 0; i < ctx->header->num_processes; i++) {
printf("Process %02"PRIx32":\n", i);
kip1_print(&ctx->kips[i], 1);
printf("\n");
}
printf("\n");
}
void ini1_save(ini1_ctx_t *ctx) {
filepath_t *dirpath;
if (ctx->tool_ctx->file_type == FILETYPE_INI1 && ctx->tool_ctx->settings.out_dir_path.enabled) {
dirpath = &ctx->tool_ctx->settings.out_dir_path.path;
}
if (dirpath == NULL || dirpath->valid != VALIDITY_VALID) {
dirpath = &ctx->tool_ctx->settings.ini1_dir_path;
}
if (dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
os_makedir(dirpath->os_path);
for (unsigned int i = 0; i < ctx->header->num_processes; i++) {
char padded_name[0x20];
memset(&padded_name, 0, sizeof(padded_name));
memcpy(&padded_name, ctx->kips[i].header->name, sizeof(ctx->kips[i].header->name));
strcat(padded_name, ".kip1");
printf("Saving %s to %s/%s...\n", padded_name, dirpath->char_path, padded_name);
save_buffer_to_directory_file(ctx->kips[i].header, kip1_get_size(&ctx->kips[i]), dirpath, padded_name);
}
}
}
void kip1_process(kip1_ctx_t *ctx) {
/* Read *just* safe amount. */
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);
}
uint64_t size = kip1_get_size_from_header(&raw_header);
ctx->header = malloc(size);
if (ctx->header == NULL) {
fprintf(stderr, "Failed to allocate KIP1 header!\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);
}
}
void kip1_print(kip1_ctx_t *ctx, int suppress) {
if (!suppress) printf("KIP1:\n");
printf(" Title ID: %016"PRIx64"\n", ctx->header->title_id);
char padded_name[13];
memset(&padded_name, 0, sizeof(padded_name));
memcpy(&padded_name, ctx->header->name, sizeof(ctx->header->name));
printf(" Name: %s\n", padded_name);
printf(" Process Category: %s\n", npdm_get_proc_category(ctx->header->process_category));
printf(" Main Thread Priority: %"PRId8"\n", ctx->header->main_thread_priority);
printf(" Default CPU Core: %"PRId8"\n", ctx->header->default_core);
printf(" Is 64 Bit: %s\n", (ctx->header->flags & (1 << 3)) ? "True" : "False");
printf(" Is Address Space 64 Bit: %s\n", (ctx->header->flags & (1 << 4)) ? "True" : "False");
printf(" Sections:\n");
printf(" .text: %08"PRIx32"-%08"PRIx32"\n", ctx->header->section_headers[0].out_offset, ctx->header->section_headers[0].out_offset + align(ctx->header->section_headers[0].out_size, 0x1000));
printf(" .rodata: %08"PRIx32"-%08"PRIx32"\n", ctx->header->section_headers[1].out_offset, ctx->header->section_headers[1].out_offset + align(ctx->header->section_headers[1].out_size, 0x1000));
printf(" .rwdata: %08"PRIx32"-%08"PRIx32"\n", ctx->header->section_headers[2].out_offset, ctx->header->section_headers[2].out_offset + align(ctx->header->section_headers[2].out_size, 0x1000));
printf(" .bss: %08"PRIx32"-%08"PRIx32"\n", ctx->header->section_headers[3].out_offset, ctx->header->section_headers[3].out_offset + align(ctx->header->section_headers[3].out_size, 0x1000));
printf(" Kernel Access Control:\n");
kac_print(ctx->header->capabilities, 0x20);
printf("\n");
}
void kip1_save(kip1_ctx_t *ctx) {
/* Do nothing. */
}

72
kip.h Normal file
View file

@ -0,0 +1,72 @@
#ifndef HACTOOL_KIP_H
#define HACTOOL_KIP_H
#include "types.h"
#include "utils.h"
#include "settings.h"
#define MAGIC_INI1 0x31494E49
#define MAGIC_KIP1 0x3150494B
#define INI1_MAX_KIPS 0x50
typedef struct {
uint32_t magic;
uint32_t size;
uint32_t num_processes;
uint32_t _0xC;
char kip_data[];
} ini1_header_t;
typedef struct {
uint32_t out_offset;
uint32_t out_size;
uint32_t compressed_size;
uint32_t attribute;
} kip_section_header_t;
typedef struct {
uint32_t magic;
char name[0xC];
uint64_t title_id;
uint32_t process_category;
uint8_t main_thread_priority;
uint8_t default_core;
uint8_t _0x1E;
uint8_t flags;
kip_section_header_t section_headers[6];
uint32_t capabilities[0x20];
unsigned char data[];
} kip1_header_t;
typedef struct {
FILE *file;
hactool_ctx_t *tool_ctx;
kip1_header_t *header;
} kip1_ctx_t;
typedef struct {
FILE *file;
hactool_ctx_t *tool_ctx;
ini1_header_t *header;
kip1_ctx_t kips[INI1_MAX_KIPS];
} ini1_ctx_t;
void ini1_process(ini1_ctx_t *ctx);
void ini1_print(ini1_ctx_t *ctx);
void ini1_save(ini1_ctx_t *ctx);
void kip1_process(kip1_ctx_t *ctx);
void kip1_print(kip1_ctx_t *ctx, int suppress);
void kip1_save(kip1_ctx_t *ctx);
static inline uint64_t kip1_get_size(kip1_ctx_t *ctx) {
/* Header + .text + .rodata + .rwdata */
return 0x100 + ctx->header->section_headers[0].compressed_size + ctx->header->section_headers[1].compressed_size + ctx->header->section_headers[2].compressed_size;
}
static inline uint64_t kip1_get_size_from_header(kip1_header_t *header) {
/* Header + .text + .rodata + .rwdata */
return 0x100 + header->section_headers[0].compressed_size + header->section_headers[1].compressed_size + header->section_headers[2].compressed_size;
}
#endif

46
main.c
View file

@ -30,7 +30,7 @@ static void usage(void) {
" -y, --verify Verify hashes and signatures.\n" " -y, --verify Verify hashes and signatures.\n"
" -d, --dev Decrypt with development keys instead of retail.\n" " -d, --dev Decrypt with development keys instead of retail.\n"
" -k, --keyset Load keys from an external file.\n" " -k, --keyset Load keys from an external file.\n"
" -t, --intype=type Specify input file type [nca, xci, pfs0, romfs, hfs0, npdm, pk11]\n" " -t, --intype=type Specify input file type [nca, xci, pfs0, romfs, hfs0, npdm, pk11, pk21, ini1, kip1]\n"
" --titlekey=key Set title key for Rights ID crypto titles.\n" " --titlekey=key Set title key for Rights ID crypto titles.\n"
" --contentkey=key Set raw key for NCA body decryption.\n" " --contentkey=key Set raw key for NCA body decryption.\n"
"NCA options:\n" "NCA options:\n"
@ -72,6 +72,12 @@ static void usage(void) {
"Package1 options:\n" "Package1 options:\n"
" --package1dir=dir Specify Package1 directory path.\n" " --package1dir=dir Specify Package1 directory path.\n"
" --outdir=dir Specify Package1 directory path. Overrides previous path, if present.\n" " --outdir=dir Specify Package1 directory path. Overrides previous path, if present.\n"
"Package2 options:\n"
" --package2dir=dir Specify Package1 directory path.\n"
" --outdir=dir Specify Package1 directory path. Overrides previous path, if present.\n"
"INI1 options:\n"
" --ini1dir=dir Specify Package1 directory path.\n"
" --outdir=dir Specify Package1 directory path. Overrides previous path, if present.\n"
"\n", __TIME__, __DATE__, prog_name); "\n", __TIME__, __DATE__, prog_name);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -137,6 +143,9 @@ int main(int argc, char **argv) {
{"normaldir", 1, NULL, 24}, {"normaldir", 1, NULL, 24},
{"securedir", 1, NULL, 25}, {"securedir", 1, NULL, 25},
{"package1dir", 1, NULL, 26}, {"package1dir", 1, NULL, 26},
{"package2dir", 1, NULL, 27},
{"ini1dir", 1, NULL, 28},
{"extractini1", 0, NULL, 29},
{NULL, 0, NULL, 0}, {NULL, 0, NULL, 0},
}; };
@ -182,6 +191,10 @@ int main(int argc, char **argv) {
nca_ctx.tool_ctx->file_type = FILETYPE_PACKAGE1; nca_ctx.tool_ctx->file_type = FILETYPE_PACKAGE1;
} else if (!strcmp(optarg, "package2") || !strcmp(optarg, "pk21")) { } else if (!strcmp(optarg, "package2") || !strcmp(optarg, "pk21")) {
nca_ctx.tool_ctx->file_type = FILETYPE_PACKAGE2; nca_ctx.tool_ctx->file_type = FILETYPE_PACKAGE2;
} else if (!strcmp(optarg, "ini1")) {
nca_ctx.tool_ctx->file_type = FILETYPE_INI1;
} else if (!strcmp(optarg, "kip1") || !strcmp(optarg, "kip")) {
nca_ctx.tool_ctx->file_type = FILETYPE_KIP1;
} }
break; break;
case 0: filepath_set(&nca_ctx.tool_ctx->settings.section_paths[0], optarg); break; case 0: filepath_set(&nca_ctx.tool_ctx->settings.section_paths[0], optarg); break;
@ -280,6 +293,15 @@ int main(int argc, char **argv) {
case 26: case 26:
filepath_set(&tool_ctx.settings.pk11_dir_path, optarg); filepath_set(&tool_ctx.settings.pk11_dir_path, optarg);
break; break;
case 27:
filepath_set(&tool_ctx.settings.pk21_dir_path, optarg);
break;
case 28:
filepath_set(&tool_ctx.settings.ini1_dir_path, optarg);
break;
case 29:
tool_ctx.action |= ACTION_EXTRACTINI1;
break;
default: default:
usage(); usage();
return EXIT_FAILURE; return EXIT_FAILURE;
@ -445,6 +467,28 @@ int main(int argc, char **argv) {
} }
break; break;
} }
case FILETYPE_INI1: {
ini1_ctx_t ini1_ctx;
memset(&ini1_ctx, 0, sizeof(ini1_ctx));
ini1_ctx.file = tool_ctx.file;
ini1_ctx.tool_ctx = &tool_ctx;
ini1_process(&ini1_ctx);
if (ini1_ctx.header) {
free(ini1_ctx.header);
}
break;
}
case FILETYPE_KIP1: {
kip1_ctx_t kip1_ctx;
memset(&kip1_ctx, 0, sizeof(kip1_ctx));
kip1_ctx.file = tool_ctx.file;
kip1_ctx.tool_ctx = &tool_ctx;
kip1_process(&kip1_ctx);
if (kip1_ctx.header) {
free(kip1_ctx.header);
}
break;
}
case FILETYPE_XCI: { case FILETYPE_XCI: {
xci_ctx_t xci_ctx; xci_ctx_t xci_ctx;
memset(&xci_ctx, 0, sizeof(xci_ctx)); memset(&xci_ctx, 0, sizeof(xci_ctx));

2
nca.c
View file

@ -625,7 +625,7 @@ void nca_print_sections(nca_ctx_t *ctx) {
/* Print out information about the NCA. */ /* Print out information about the NCA. */
void nca_print(nca_ctx_t *ctx) { void nca_print(nca_ctx_t *ctx) {
printf("\nNCA:\n"); printf("\nNCA:\n");
print_magic("Magic: ", ctx->header.magic); print_magic("Magic: ", ctx->header.magic);
if (ctx->tool_ctx->action & ACTION_VERIFY && ctx->fixed_sig_validity != VALIDITY_UNCHECKED) { if (ctx->tool_ctx->action & ACTION_VERIFY && ctx->fixed_sig_validity != VALIDITY_UNCHECKED) {
if (ctx->fixed_sig_validity == VALIDITY_VALID) { if (ctx->fixed_sig_validity == VALIDITY_VALID) {

4
npdm.c
View file

@ -212,8 +212,8 @@ const fs_perm_t fs_permissions_bool[MAX_FS_PERM_BOOL] = {
{"Unknown (0x1A)", 0x8000000000004020} {"Unknown (0x1A)", 0x8000000000004020}
}; };
char *npdm_get_proc_category(npdm_t *npdm) { char *npdm_get_proc_category(int process_category) {
switch (npdm->process_category) { switch (process_category) {
case 0: case 0:
return "Regular Title"; return "Regular Title";
case 1: case 1:

3
npdm.h
View file

@ -134,4 +134,7 @@ static inline npdm_aci0_t *npdm_get_aci0(npdm_t *npdm) {
void npdm_print(npdm_t *npdm, hactool_ctx_t *tool_ctx); void npdm_print(npdm_t *npdm, hactool_ctx_t *tool_ctx);
char *npdm_get_proc_category(int process_category);
void kac_print(uint32_t *descriptors, uint32_t num_descriptors);
#endif #endif

View file

@ -194,7 +194,20 @@ void pk21_process(pk21_ctx_t *ctx) {
offset += ctx->header.section_sizes[i]; offset += ctx->header.section_sizes[i];
} }
/* TODO: Parse INI1 */ ctx->ini1_ctx.tool_ctx = ctx->tool_ctx;
ctx->ini1_ctx.header = (ini1_header_t *)(ctx->sections + ctx->header.section_sizes[0]);
if (ctx->ini1_ctx.header->magic == MAGIC_INI1 && ctx->ini1_ctx.header->num_processes <= INI1_MAX_KIPS) {
offset = 0;
for (unsigned int i = 0; i < ctx->ini1_ctx.header->num_processes; i++) {
ctx->ini1_ctx.kips[i].tool_ctx = ctx->tool_ctx;
ctx->ini1_ctx.kips[i].header = (kip1_header_t *)&ctx->ini1_ctx.header->kip_data[offset];
if (ctx->ini1_ctx.kips[i].header->magic != MAGIC_KIP1) {
fprintf(stderr, "INI1 is corrupted!\n");
exit(EXIT_FAILURE);
}
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);
@ -244,12 +257,49 @@ 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);
} }
void pk21_save(pk21_ctx_t *ctx) { void pk21_save(pk21_ctx_t *ctx) {
printf("Saving PK21 currently not implemented.\n"); /* Extract to directory. */
/* TODO: Save sections + extract INI1 */ filepath_t *dirpath = NULL;
if (ctx->tool_ctx->file_type == FILETYPE_PACKAGE2 && ctx->tool_ctx->settings.out_dir_path.enabled) {
dirpath = &ctx->tool_ctx->settings.out_dir_path.path;
}
if (dirpath == NULL || dirpath->valid != VALIDITY_VALID) {
dirpath = &ctx->tool_ctx->settings.pk21_dir_path;
}
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);
if (decrypted_bin == NULL) {
fprintf(stderr, "Failed to allocate buffer!\n");
exit(EXIT_FAILURE);
}
memcpy(decrypted_bin, &ctx->header, 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");
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);
save_buffer_to_directory_file(ctx->sections + ctx->header.section_sizes[0], ctx->header.section_sizes[1], dirpath, "INI1.bin");
}
if (ctx->ini1_ctx.header != NULL && (ctx->tool_ctx->action & ACTION_EXTRACTINI1 || ctx->tool_ctx->settings.ini1_dir_path.valid == VALIDITY_VALID)) {
filepath_t *ini1_dirpath = &ctx->tool_ctx->settings.ini1_dir_path;
if (ini1_dirpath->valid != VALIDITY_VALID && dirpath != NULL && dirpath->valid == VALIDITY_VALID) {
filepath_copy(ini1_dirpath, dirpath);
filepath_append(ini1_dirpath, "INI1");
}
ini1_save(&ctx->ini1_ctx);
}
} }

View file

@ -4,6 +4,7 @@
#include "types.h" #include "types.h"
#include "utils.h" #include "utils.h"
#include "settings.h" #include "settings.h"
#include "kip.h"
#define MAGIC_PK11 0x31314B50 #define MAGIC_PK11 0x31314B50
#define MAGIC_PK21 0x31324B50 #define MAGIC_PK21 0x31324B50
@ -87,6 +88,7 @@ typedef struct {
validity_t section_validities[4]; validity_t section_validities[4];
unsigned char *sections; unsigned char *sections;
pk21_header_t header; pk21_header_t header;
ini1_ctx_t ini1_ctx;
} pk21_ctx_t; } pk21_ctx_t;
void pk21_process(pk21_ctx_t *ctx); void pk21_process(pk21_ctx_t *ctx);

View file

@ -57,6 +57,8 @@ typedef struct {
filepath_t pfs0_dir_path; filepath_t pfs0_dir_path;
filepath_t hfs0_dir_path; filepath_t hfs0_dir_path;
filepath_t pk11_dir_path; filepath_t pk11_dir_path;
filepath_t pk21_dir_path;
filepath_t ini1_dir_path;
filepath_t dec_nca_path; filepath_t dec_nca_path;
filepath_t rootpt_dir_path; filepath_t rootpt_dir_path;
filepath_t update_dir_path; filepath_t update_dir_path;
@ -74,7 +76,9 @@ enum hactool_file_type
FILETYPE_XCI, FILETYPE_XCI,
FILETYPE_NPDM, FILETYPE_NPDM,
FILETYPE_PACKAGE1, FILETYPE_PACKAGE1,
FILETYPE_PACKAGE2 FILETYPE_PACKAGE2,
FILETYPE_INI1,
FILETYPE_KIP1
}; };
#define ACTION_INFO (1<<0) #define ACTION_INFO (1<<0)
@ -83,6 +87,7 @@ enum hactool_file_type
#define ACTION_RAW (1<<3) #define ACTION_RAW (1<<3)
#define ACTION_LISTROMFS (1<<4) #define ACTION_LISTROMFS (1<<4)
#define ACTION_DEV (1<<5) #define ACTION_DEV (1<<5)
#define ACTION_EXTRACTINI1 (1<<6)
struct nca_ctx; /* This will get re-defined by nca.h. */ struct nca_ctx; /* This will get re-defined by nca.h. */