mirror of
https://github.com/SciresM/hactool.git
synced 2024-11-23 04:29:41 +00:00
Implement npdm -> JSON autogeneration.
This commit is contained in:
parent
e288a141c4
commit
fa2730ef59
6
Makefile
6
Makefile
@ -13,7 +13,7 @@ all:
|
||||
.c.o:
|
||||
$(CC) $(INCLUDE) -c $(CFLAGS) -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 nax0.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 nax0.o nca.o xci.o main.o filepath.o ConvertUTF.o cJSON.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS) -L $(LIBDIR)
|
||||
|
||||
aes.o: aes.h types.h
|
||||
@ -40,7 +40,7 @@ nax0.o: nax0.h aes.h sha.h types.h
|
||||
|
||||
nca.o: nca.h aes.h sha.h rsa.h bktr.h filepath.h types.h
|
||||
|
||||
npdm.o: npdm.c types.h
|
||||
npdm.o: npdm.c cJSON.h types.h
|
||||
|
||||
romfs.o: ivfc.h types.h
|
||||
|
||||
@ -54,6 +54,8 @@ xci.o: xci.h types.h hfs0.h
|
||||
|
||||
ConvertUTF.o: ConvertUTF.h
|
||||
|
||||
cJSON.o: cJSON.h
|
||||
|
||||
clean:
|
||||
rm -f *.o hactool hactool.exe
|
||||
|
||||
|
277
cJSON.h
Normal file
277
cJSON.h
Normal file
@ -0,0 +1,277 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef cJSON__h
|
||||
#define cJSON__h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* project version */
|
||||
#define CJSON_VERSION_MAJOR 1
|
||||
#define CJSON_VERSION_MINOR 7
|
||||
#define CJSON_VERSION_PATCH 6
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_Invalid (0)
|
||||
#define cJSON_False (1 << 0)
|
||||
#define cJSON_True (1 << 1)
|
||||
#define cJSON_NULL (1 << 2)
|
||||
#define cJSON_Number (1 << 3)
|
||||
#define cJSON_String (1 << 4)
|
||||
#define cJSON_Array (1 << 5)
|
||||
#define cJSON_Object (1 << 6)
|
||||
#define cJSON_Raw (1 << 7) /* raw json */
|
||||
|
||||
#define cJSON_IsReference 256
|
||||
#define cJSON_StringIsConst 512
|
||||
|
||||
/* The cJSON structure: */
|
||||
typedef struct cJSON
|
||||
{
|
||||
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *next;
|
||||
struct cJSON *prev;
|
||||
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
struct cJSON *child;
|
||||
|
||||
/* The type of the item, as above. */
|
||||
int type;
|
||||
|
||||
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
|
||||
char *valuestring;
|
||||
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
||||
int valueint;
|
||||
/* The item's number, if type==cJSON_Number */
|
||||
double valuedouble;
|
||||
|
||||
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
char *string;
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks
|
||||
{
|
||||
void *(*malloc_fn)(size_t sz);
|
||||
void (*free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
typedef int cJSON_bool;
|
||||
|
||||
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options:
|
||||
|
||||
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
|
||||
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
|
||||
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
|
||||
|
||||
For *nix builds that support visibility attribute, you can define similar behavior by
|
||||
|
||||
setting default visibility to hidden by adding
|
||||
-fvisibility=hidden (for gcc)
|
||||
or
|
||||
-xldscope=hidden (for sun cc)
|
||||
to CFLAGS
|
||||
|
||||
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
|
||||
|
||||
*/
|
||||
|
||||
/* export symbols by default, this is necessary for copy pasting the C and header file */
|
||||
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_EXPORT_SYMBOLS
|
||||
#endif
|
||||
|
||||
#if defined(CJSON_HIDE_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) type __stdcall
|
||||
#elif defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall
|
||||
#elif defined(CJSON_IMPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall
|
||||
#endif
|
||||
#else /* !WIN32 */
|
||||
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
|
||||
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
||||
#else
|
||||
#define CJSON_PUBLIC(type) type
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
||||
* This is to prevent stack overflows. */
|
||||
#ifndef CJSON_NESTING_LIMIT
|
||||
#define CJSON_NESTING_LIMIT 1000
|
||||
#endif
|
||||
|
||||
/* returns the version of cJSON as a string */
|
||||
CJSON_PUBLIC(const char*) cJSON_Version(void);
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
|
||||
/* Render a cJSON entity to text for transfer/storage. */
|
||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
|
||||
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||
|
||||
/* Check if the item is a string and return its valuestring */
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item);
|
||||
|
||||
/* These functions check the type of an item */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
||||
/* raw json */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
||||
|
||||
/* Create a string where valuestring references a string so
|
||||
* it will not be freed by cJSON_Delete */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
|
||||
/* Create an object/arrray that only references it's elements so
|
||||
* they will not be freed by cJSON_Delete */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||
|
||||
/* These utilities create an Array of count items. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
||||
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
|
||||
* writing to `item->string` */
|
||||
CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
|
||||
/* Remove/Detatch items from Arrays/Objects. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
||||
|
||||
|
||||
CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
||||
|
||||
/* Helper functions for creating and adding items to an object at the same time.
|
||||
* They return the added item or NULL on failure. */
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
||||
/* helper for the cJSON_SetNumberValue macro */
|
||||
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
|
||||
|
||||
/* Macro for iterating over an array or object */
|
||||
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
||||
|
||||
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
||||
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||
CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
8
main.c
8
main.c
@ -54,6 +54,8 @@ static void usage(void) {
|
||||
" --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"
|
||||
"NPDM options:\n"
|
||||
" --json=file Specify file path for saving JSON representation of NPDM to.\n"
|
||||
"PFS0 options:\n"
|
||||
" --pfs0dir=dir Specify PFS0 directory path.\n"
|
||||
" --outdir=dir Specify PFS0 directory path. Overrides previous path, if present.\n"
|
||||
@ -163,6 +165,7 @@ int main(int argc, char **argv) {
|
||||
{"sdpath", 1, NULL, 33},
|
||||
{"sbk", 1, NULL, 34},
|
||||
{"tseckey", 1, NULL, 35},
|
||||
{"json", 1, NULL, 36},
|
||||
{NULL, 0, NULL, 0},
|
||||
};
|
||||
|
||||
@ -353,6 +356,9 @@ int main(int argc, char **argv) {
|
||||
case 35:
|
||||
parse_hex_key(nca_ctx.tool_ctx->settings.keygen_tsec, optarg, 16);
|
||||
break;
|
||||
case 36:
|
||||
filepath_set(&tool_ctx.settings.npdm_json_path, optarg);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return EXIT_FAILURE;
|
||||
@ -536,7 +542,7 @@ int main(int argc, char **argv) {
|
||||
fprintf(stderr, "Failed to read NPDM!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
npdm_print(npdm, &tool_ctx);
|
||||
npdm_process(npdm, &tool_ctx);
|
||||
break;
|
||||
}
|
||||
case FILETYPE_HFS0: {
|
||||
|
2
nca.c
2
nca.c
@ -691,7 +691,7 @@ void nca_print(nca_ctx_t *ctx) {
|
||||
}
|
||||
|
||||
if (ctx->npdm) {
|
||||
npdm_print(ctx->npdm, ctx->tool_ctx);
|
||||
npdm_process(ctx->npdm, ctx->tool_ctx);
|
||||
}
|
||||
|
||||
nca_print_sections(ctx);
|
||||
|
322
npdm.c
322
npdm.c
@ -4,6 +4,7 @@
|
||||
#include "utils.h"
|
||||
#include "settings.h"
|
||||
#include "rsa.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
const char *svc_names[0x80] = {
|
||||
"svcUnknown",
|
||||
@ -302,20 +303,22 @@ void kac_print(uint32_t *descriptors, uint32_t num_descriptors) {
|
||||
}
|
||||
break;
|
||||
case 6: /* Map IO/Normal. */
|
||||
if (cur_mmio == NULL) {
|
||||
cur_mmio = calloc(1, sizeof(kac_mmio_t));
|
||||
if (cur_mmio == NULL) {
|
||||
fprintf(stderr, "Failed to allocate MMIO descriptor!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
cur_mmio->address = (desc & 0xFFFFFF) << 12;
|
||||
cur_mmio->is_ro = desc >> 24;
|
||||
} else {
|
||||
cur_mmio->size = (desc & 0xFFFFFF) << 12;
|
||||
cur_mmio->is_norm = desc >> 24;
|
||||
kac_add_mmio(&kac, cur_mmio);
|
||||
cur_mmio = NULL;
|
||||
cur_mmio = calloc(1, sizeof(kac_mmio_t));
|
||||
cur_mmio->address = (desc & 0xFFFFFF) << 12;
|
||||
cur_mmio->is_ro = desc >> 24;
|
||||
if (i == num_descriptors - 1) {
|
||||
fprintf(stderr, "Error: Invalid Kernel Access Control Descriptors!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
desc = descriptors[++i];
|
||||
if ((desc & 0x7F) != 0x3F) {
|
||||
fprintf(stderr, "Error: Invalid Kernel Access Control Descriptors!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
desc >>= 7;
|
||||
cur_mmio->size = (desc & 0xFFFFFF) << 12;
|
||||
cur_mmio->is_norm = desc >> 24;
|
||||
kac_add_mmio(&kac, cur_mmio);
|
||||
break;
|
||||
case 7: /* Map Normal Page. */
|
||||
page_mmio = calloc(1, sizeof(kac_mmio_t));
|
||||
@ -435,6 +438,10 @@ void kac_print(uint32_t *descriptors, uint32_t num_descriptors) {
|
||||
if (kac.has_handle_table_size) {
|
||||
printf(" Handle Table Size: %"PRId32"\n", kac.handle_table_size);
|
||||
}
|
||||
|
||||
if (kac.has_kern_ver) {
|
||||
printf(" Minimum Kernel Version: %"PRIx8"\n", kac.kernel_release_version);
|
||||
}
|
||||
|
||||
if (kac.has_debug_flags) {
|
||||
printf(" Allow Debug: %s\n", kac.allow_debug ? "YES" : "NO");
|
||||
@ -466,30 +473,33 @@ int sac_matches(sac_entry_t *lst, char *service) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sac_print(char *acid_sac, uint32_t acid_size, char *aci0_sac, uint32_t aci0_size) {
|
||||
/* Parse the ACID sac. */
|
||||
sac_entry_t *acid_accesses = NULL;
|
||||
sac_entry_t *acid_hosts = NULL;
|
||||
void sac_parse(char *sac, uint32_t sac_size, sac_entry_t *r_host, sac_entry_t *r_accesses, sac_entry_t **out_hosts, sac_entry_t **out_accesses) {
|
||||
sac_entry_t *accesses = NULL;
|
||||
sac_entry_t *hosts = NULL;
|
||||
sac_entry_t *cur_entry = NULL;
|
||||
sac_entry_t *temp = NULL;
|
||||
uint32_t ofs = 0;
|
||||
uint32_t service_len;
|
||||
char ctrl;
|
||||
while (ofs < acid_size) {
|
||||
ctrl = acid_sac[ofs++];
|
||||
while (ofs < sac_size) {
|
||||
ctrl = sac[ofs++];
|
||||
service_len = (ctrl & 0xF) + 1;
|
||||
cur_entry = calloc(1, sizeof(sac_entry_t));
|
||||
cur_entry->valid = 1;
|
||||
strncpy(cur_entry->service, &acid_sac[ofs], service_len);
|
||||
if (ctrl & 0x80 && acid_hosts == NULL) {
|
||||
acid_hosts = cur_entry;
|
||||
} else if (!(ctrl & 0x80) && acid_accesses == NULL) {
|
||||
acid_accesses = cur_entry;
|
||||
if (ctrl & 0x80) {
|
||||
cur_entry->valid = r_host != NULL ? sac_matches(r_host, cur_entry->service) : 1;
|
||||
} else {
|
||||
cur_entry->valid = r_host != NULL ? sac_matches(r_accesses, cur_entry->service) : 1;
|
||||
}
|
||||
strncpy(cur_entry->service, &sac[ofs], service_len);
|
||||
if (ctrl & 0x80 && hosts == NULL) {
|
||||
hosts = cur_entry;
|
||||
} else if (!(ctrl & 0x80) && accesses == NULL) {
|
||||
accesses = cur_entry;
|
||||
} else {
|
||||
if (ctrl & 0x80) {
|
||||
temp = acid_hosts;
|
||||
temp = hosts;
|
||||
} else {
|
||||
temp = acid_accesses;
|
||||
temp = accesses;
|
||||
}
|
||||
while (temp->next != NULL) {
|
||||
temp = temp->next;
|
||||
@ -499,39 +509,21 @@ void sac_print(char *acid_sac, uint32_t acid_size, char *aci0_sac, uint32_t aci0
|
||||
cur_entry = NULL;
|
||||
ofs += service_len;
|
||||
}
|
||||
*out_hosts = hosts;
|
||||
*out_accesses = accesses;
|
||||
}
|
||||
|
||||
void sac_print(char *acid_sac, uint32_t acid_size, char *aci0_sac, uint32_t aci0_size) {
|
||||
/* Parse the ACID sac. */
|
||||
sac_entry_t *acid_accesses = NULL;
|
||||
sac_entry_t *acid_hosts = NULL;
|
||||
sac_entry_t *temp = NULL;
|
||||
sac_parse(acid_sac, acid_size, NULL, NULL, &acid_hosts, &acid_accesses);
|
||||
|
||||
/* The ACID sac restricts the ACI0 sac... */
|
||||
sac_entry_t *aci0_accesses = NULL;
|
||||
sac_entry_t *aci0_hosts = NULL;
|
||||
ofs = 0;
|
||||
while (ofs < aci0_size) {
|
||||
ctrl = aci0_sac[ofs++];
|
||||
service_len = (ctrl & 0xF) + 1;
|
||||
cur_entry = calloc(1, sizeof(sac_entry_t));
|
||||
strncpy(cur_entry->service, &aci0_sac[ofs], service_len);
|
||||
if (ctrl & 0x80) {
|
||||
cur_entry->valid = sac_matches(acid_hosts, cur_entry->service);
|
||||
} else {
|
||||
cur_entry->valid = sac_matches(acid_accesses, cur_entry->service);
|
||||
}
|
||||
if (ctrl & 0x80 && aci0_hosts == NULL) {
|
||||
aci0_hosts = cur_entry;
|
||||
} else if (!(ctrl & 0x80) && aci0_accesses == NULL) {
|
||||
aci0_accesses = cur_entry;
|
||||
} else {
|
||||
if (ctrl & 0x80) {
|
||||
temp = aci0_hosts;
|
||||
} else {
|
||||
temp = aci0_accesses;
|
||||
}
|
||||
while (temp->next != NULL) {
|
||||
temp = temp->next;
|
||||
}
|
||||
temp->next = cur_entry;
|
||||
}
|
||||
cur_entry = NULL;
|
||||
ofs += service_len;
|
||||
}
|
||||
sac_parse(aci0_sac, aci0_size, acid_hosts, acid_accesses, &aci0_hosts, &aci0_accesses);
|
||||
|
||||
int first = 1;
|
||||
while (aci0_hosts != NULL) {
|
||||
@ -599,6 +591,15 @@ void fac_print(fac_t *fac, fah_t *fah) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void npdm_process(npdm_t *npdm, hactool_ctx_t *tool_ctx) {
|
||||
if (tool_ctx->action & ACTION_INFO) {
|
||||
npdm_print(npdm, tool_ctx);
|
||||
}
|
||||
|
||||
if (tool_ctx->action & ACTION_EXTRACT) {
|
||||
npdm_save(npdm, tool_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void npdm_print(npdm_t *npdm, hactool_ctx_t *tool_ctx) {
|
||||
printf("NPDM:\n");
|
||||
@ -623,7 +624,8 @@ void npdm_print(npdm_t *npdm, hactool_ctx_t *tool_ctx) {
|
||||
memdump(stdout, " Signature: ", &acid->signature, 0x100);
|
||||
}
|
||||
memdump(stdout, " Header Modulus: ", &acid->modulus, 0x100);
|
||||
printf(" Is Retail: %"PRId32"\n", acid->is_retail);
|
||||
printf(" Is Retail: %"PRId32"\n", acid->flags & 1);
|
||||
printf(" Pool Partition: %"PRId32"\n", (acid->flags >> 2) & 3);
|
||||
printf(" Title ID Range: %016"PRIx64"-%016"PRIx64"\n", acid->title_id_range_min, acid->title_id_range_max);
|
||||
printf(" ACI0:\n");
|
||||
print_magic(" Magic: ", aci0->magic);
|
||||
@ -656,3 +658,209 @@ void npdm_print(npdm_t *npdm, hactool_ctx_t *tool_ctx) {
|
||||
printf(" Filesystem Access Control:\n");
|
||||
fac_print(fac, fah);
|
||||
}
|
||||
|
||||
|
||||
void npdm_save(npdm_t *npdm, hactool_ctx_t *tool_ctx) {
|
||||
filepath_t *json_path = &tool_ctx->settings.npdm_json_path;
|
||||
if (json_path->valid == VALIDITY_VALID) {
|
||||
FILE *f_json = os_fopen(json_path->os_path, OS_MODE_WRITE);
|
||||
if (f_json == NULL) {
|
||||
fprintf(stderr, "Failed to open %s!\n", json_path->char_path);
|
||||
return;
|
||||
}
|
||||
const char *json = npdm_get_json(npdm);
|
||||
if (fwrite(json, 1, strlen(json), f_json) != strlen(json)) {
|
||||
fprintf(stderr, "Failed to write JSON file!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fclose(f_json);
|
||||
}
|
||||
}
|
||||
|
||||
void cJSON_AddU8ToObject(cJSON *obj, char *name, unsigned int val) {
|
||||
char buf[0x20] = {0};
|
||||
snprintf(buf, sizeof(buf), "0x%02x", val);
|
||||
cJSON_AddStringToObject(obj, name, buf);
|
||||
}
|
||||
|
||||
void cJSON_AddU16ToObject(cJSON *obj, char *name, uint16_t val) {
|
||||
char buf[0x20] = {0};
|
||||
snprintf(buf, sizeof(buf), "0x%04x", val & 0xFFFF);
|
||||
cJSON_AddStringToObject(obj, name, buf);
|
||||
}
|
||||
|
||||
void cJSON_AddU64ToObject(cJSON *obj, char *name, uint64_t val) {
|
||||
char buf[0x20] = {0};
|
||||
snprintf(buf, sizeof(buf), "0x%016llx", val);
|
||||
cJSON_AddStringToObject(obj, name, buf);
|
||||
}
|
||||
|
||||
cJSON *sac_get_json(char *sac, uint32_t sac_size) {
|
||||
cJSON *sac_json = cJSON_CreateObject();
|
||||
char service[9] = {0};
|
||||
uint32_t ofs = 0;
|
||||
uint32_t service_len;
|
||||
char ctrl;
|
||||
while (ofs < sac_size) {
|
||||
memset(service, 0, sizeof(service));
|
||||
ctrl = sac[ofs++];
|
||||
service_len = (ctrl & 0x7) + 1;
|
||||
memcpy(service, &sac[ofs], service_len);
|
||||
cJSON_AddBoolToObject(sac_json, service, (ctrl & 0x80) != 0);
|
||||
ofs += service_len;
|
||||
}
|
||||
|
||||
return sac_json;
|
||||
}
|
||||
|
||||
cJSON *kac_get_json(uint32_t *descriptors, uint32_t num_descriptors) {
|
||||
cJSON *kac_json = cJSON_CreateObject();
|
||||
cJSON *temp = NULL;
|
||||
bool first_syscall = false;
|
||||
unsigned int syscall_base;
|
||||
for (uint32_t i = 0; i < num_descriptors; i++) {
|
||||
uint32_t desc = descriptors[i];
|
||||
if (desc == 0xFFFFFFFF) {
|
||||
continue;
|
||||
}
|
||||
unsigned int low_bits = 0;
|
||||
while (desc & 1) {
|
||||
desc >>= 1;
|
||||
low_bits++;
|
||||
}
|
||||
desc >>= 1;
|
||||
switch (low_bits) {
|
||||
case 3: /* Kernel flags. */
|
||||
temp = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(temp, "highest_thread_priority", desc & 0x3F);
|
||||
desc >>= 6;
|
||||
cJSON_AddNumberToObject(temp, "lowest_thread_priority", desc & 0x3F);
|
||||
desc >>= 6;
|
||||
cJSON_AddNumberToObject(temp, "lowest_cpu_id", desc & 0xFF);
|
||||
desc >>= 8;
|
||||
cJSON_AddNumberToObject(temp, "highest_cpu_id", desc & 0xFF);
|
||||
cJSON_AddItemToObject(kac_json, "kernel_flags", temp);
|
||||
break;
|
||||
case 4: /* Syscall mask. */
|
||||
temp = cJSON_GetObjectItemCaseSensitive(kac_json, "syscalls");
|
||||
if (temp == NULL) {
|
||||
first_syscall = true;
|
||||
temp = cJSON_CreateObject();
|
||||
} else {
|
||||
first_syscall = false;
|
||||
}
|
||||
syscall_base = (desc >> 24) * 0x18;
|
||||
for (unsigned int sc = 0; sc < 0x18 && syscall_base + sc < 0x80; sc++) {
|
||||
if (desc & 1) {
|
||||
cJSON_AddU8ToObject(temp, strdup(svc_names[sc + syscall_base]), sc + syscall_base);
|
||||
}
|
||||
desc >>= 1;
|
||||
}
|
||||
if (first_syscall) {
|
||||
cJSON_AddItemToObject(kac_json, "syscalls", temp);
|
||||
}
|
||||
break;
|
||||
case 6: /* Map IO/Normal. */
|
||||
temp = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddU64ToObject(temp, "address", (desc & 0xFFFFFF) << 12);
|
||||
cJSON_AddBoolToObject(temp, "is_ro", (desc >> 24) & 1);
|
||||
if (i == num_descriptors - 1) {
|
||||
fprintf(stderr, "Error: Invalid Kernel Access Control Descriptors!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
desc = descriptors[++i];
|
||||
if ((desc & 0x7F) != 0x3F) {
|
||||
fprintf(stderr, "Error: Invalid Kernel Access Control Descriptors!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
desc >>= 7;
|
||||
cJSON_AddU64ToObject(temp, "size", (desc & 0xFFFFFF) << 12);
|
||||
cJSON_AddBoolToObject(temp, "is_io", ((desc >> 24) & 1) == 0);
|
||||
cJSON_AddItemToObject(kac_json, "map", temp);
|
||||
break;
|
||||
case 7: /* Map Normal Page. */
|
||||
cJSON_AddU64ToObject(kac_json, "map_page", desc << 12);
|
||||
break;
|
||||
case 11: /* IRQ Pair. */
|
||||
temp = cJSON_CreateArray();
|
||||
if ((desc & 0x3FF) == 0x3FF) {
|
||||
cJSON_AddItemToArray(temp, cJSON_CreateNull());
|
||||
} else {
|
||||
cJSON_AddItemToArray(temp, cJSON_CreateNumber(desc & 0x3FF));
|
||||
}
|
||||
desc >>= 10;
|
||||
if ((desc & 0x3FF) == 0x3FF) {
|
||||
cJSON_AddItemToArray(temp, cJSON_CreateNull());
|
||||
} else {
|
||||
cJSON_AddItemToArray(temp, cJSON_CreateNumber(desc & 0x3FF));
|
||||
}
|
||||
cJSON_AddItemToObject(kac_json, "irq_pair", temp);
|
||||
break;
|
||||
case 13: /* App Type. */
|
||||
cJSON_AddNumberToObject(kac_json, "application_type", desc & 7);
|
||||
break;
|
||||
case 14: /* Kernel Release Version. */
|
||||
cJSON_AddU16ToObject(kac_json, "min_kernel_version", desc & 0xFFFF);
|
||||
break;
|
||||
case 15: /* Handle Table Size. */
|
||||
cJSON_AddNumberToObject(kac_json, "handle_table_size", desc);
|
||||
break;
|
||||
case 16: /* Debug Flags. */
|
||||
temp = cJSON_CreateObject();
|
||||
cJSON_AddBoolToObject(temp, "allow_debug", (desc >> 0) & 1);
|
||||
cJSON_AddBoolToObject(temp, "force_debug", (desc >> 1) & 1);
|
||||
cJSON_AddItemToObject(kac_json, "debug_flags", temp);
|
||||
|
||||
// kac.has_debug_flags = 1;
|
||||
// kac.allow_debug = desc & 1;
|
||||
// kac.force_debug = (desc >> 1) & 1;
|
||||
break;
|
||||
}
|
||||
temp = NULL;
|
||||
}
|
||||
return kac_json;
|
||||
}
|
||||
|
||||
const char *npdm_get_json(npdm_t *npdm) {
|
||||
npdm_acid_t *acid = npdm_get_acid(npdm);
|
||||
npdm_aci0_t *aci0 = npdm_get_aci0(npdm);
|
||||
cJSON *npdm_json = cJSON_CreateObject();
|
||||
const char *output_str = NULL;
|
||||
char work_buffer[0x300] = {0};
|
||||
|
||||
/* Add NPDM header fields. */
|
||||
strcpy(work_buffer, npdm->title_name);
|
||||
cJSON_AddStringToObject(npdm_json, "name", work_buffer);
|
||||
cJSON_AddU64ToObject(npdm_json, "title_id", aci0->title_id);
|
||||
cJSON_AddU64ToObject(npdm_json, "title_id_range_min", acid->title_id_range_min);
|
||||
cJSON_AddU64ToObject(npdm_json, "title_id_range_max", acid->title_id_range_max);
|
||||
cJSON_AddU64ToObject(npdm_json, "main_thread_stack_size", npdm->main_stack_size);
|
||||
cJSON_AddNumberToObject(npdm_json, "main_thread_priority", npdm->main_thread_prio);
|
||||
cJSON_AddNumberToObject(npdm_json, "default_cpu_id", npdm->default_cpuid);
|
||||
cJSON_AddNumberToObject(npdm_json, "process_category", npdm->process_category);
|
||||
cJSON_AddBoolToObject(npdm_json, "is_retail", acid->flags & 1);
|
||||
cJSON_AddNumberToObject(npdm_json, "pool_partition", (acid->flags >> 2) & 3);
|
||||
cJSON_AddBoolToObject(npdm_json, "is_64_bit", npdm->mmu_flags & 1);
|
||||
cJSON_AddNumberToObject(npdm_json, "address_space_type", (npdm->mmu_flags >> 1) & 7);
|
||||
|
||||
/* Add FAC. */
|
||||
fac_t *fac = (fac_t *)((char *)acid + acid->fac_offset);
|
||||
fah_t *fah = (fah_t *)((char *)aci0 + aci0->fah_offset);
|
||||
cJSON *fac_json = cJSON_CreateObject();
|
||||
cJSON_AddU64ToObject(fac_json, "permissions", fac->perms & fah->perms);
|
||||
cJSON_AddItemToObject(npdm_json, "filesystem_access", fac_json);
|
||||
|
||||
/* Add SAC. */
|
||||
cJSON *sac_json = sac_get_json((char *)aci0 + aci0->sac_offset, aci0->sac_size);
|
||||
cJSON_AddItemToObject(npdm_json, "service_access", sac_json);
|
||||
|
||||
/* Add KAC. */
|
||||
cJSON *kac_json = kac_get_json((uint32_t *)((char *)aci0 + aci0->kac_offset), aci0->kac_size / sizeof(uint32_t));
|
||||
cJSON_AddItemToObject(npdm_json, "kernel_capabilities", kac_json);
|
||||
|
||||
output_str = cJSON_Print(npdm_json);
|
||||
|
||||
cJSON_Delete(npdm_json);
|
||||
return output_str;
|
||||
}
|
5
npdm.h
5
npdm.h
@ -94,7 +94,7 @@ typedef struct {
|
||||
uint32_t magic;
|
||||
uint32_t size;
|
||||
uint32_t _0x208;
|
||||
uint32_t is_retail;
|
||||
uint32_t flags;
|
||||
uint64_t title_id_range_min;
|
||||
uint64_t title_id_range_max;
|
||||
uint32_t fac_offset;
|
||||
@ -132,9 +132,12 @@ static inline npdm_aci0_t *npdm_get_aci0(npdm_t *npdm) {
|
||||
return (npdm_aci0_t *)((char *)npdm + npdm->aci0_offset);
|
||||
}
|
||||
|
||||
void npdm_process(npdm_t *npdm, hactool_ctx_t *tool_ctx);
|
||||
void npdm_print(npdm_t *npdm, hactool_ctx_t *tool_ctx);
|
||||
void npdm_save(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);
|
||||
const char *npdm_get_json(npdm_t *npdm);
|
||||
|
||||
#endif
|
||||
|
2
pfs0.c
2
pfs0.c
@ -130,6 +130,6 @@ void pfs0_print(pfs0_ctx_t *ctx) {
|
||||
}
|
||||
}
|
||||
if (ctx->is_exefs) {
|
||||
npdm_print(ctx->npdm, ctx->tool_ctx);
|
||||
npdm_process(ctx->npdm, ctx->tool_ctx);
|
||||
}
|
||||
}
|
@ -84,6 +84,7 @@ typedef struct {
|
||||
filepath_t header_path;
|
||||
filepath_t nax0_path;
|
||||
filepath_t nax0_sd_path;
|
||||
filepath_t npdm_json_path;
|
||||
} hactool_settings_t;
|
||||
|
||||
enum hactool_file_type
|
||||
|
2
utils.h
2
utils.h
@ -55,7 +55,7 @@ inline int fseeko64(FILE *__stream, long long __off, int __whence)
|
||||
// OS X file I/O is 64bit
|
||||
#define fseeko64 fseek
|
||||
#elif __linux__ || __WIN32
|
||||
extern int fseeko64 (FILE *__stream, __off64_t __off, int __whence);
|
||||
extern int fseeko64 (FILE *__stream, _off64_t __off, int __whence);
|
||||
#else
|
||||
/* fseeko is guaranteed by POSIX, hopefully the OS made their off_t definition 64-bit;
|
||||
* known sane on FreeBSD and OpenBSD.
|
||||
|
Loading…
Reference in New Issue
Block a user