From 495e331306d558fc586d6f9f9dfffa6944861dfe Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Sun, 11 Oct 2020 14:13:09 -0400 Subject: [PATCH] Fix support for NSOs without a valid .api_info section + ProgramInfo context. ProgramInfo XML generation is still missing. Getting real close to reimplementing NSP dumping. --- code_templates/xml_generator.c | 45 ++++++++++--- source/nso.c | 4 +- source/nso.h | 5 +- source/program_info.c | 111 +++++++++++++++++++++++++++++++++ source/program_info.h | 88 ++++++++++++++++++++++++++ 5 files changed, 242 insertions(+), 11 deletions(-) create mode 100644 source/program_info.c create mode 100644 source/program_info.h diff --git a/code_templates/xml_generator.c b/code_templates/xml_generator.c index 45ac59a..04360fb 100644 --- a/code_templates/xml_generator.c +++ b/code_templates/xml_generator.c @@ -22,6 +22,7 @@ #include "gamecard.h" #include "title.h" #include "cnmt.h" +#include "program_info.h" #include "nacp.h" #include "legal_info.h" @@ -65,6 +66,7 @@ int main(int argc, char *argv[]) Ticket tik = {0}; ContentMetaContext cnmt_ctx = {0}; + ProgramInfoContext program_info_ctx = {0}; NacpContext nacp_ctx = {0}; LegalInfoContext legal_info_ctx = {0}; @@ -192,7 +194,7 @@ int main(int argc, char *argv[]) consolePrint("nca ctx calloc succeeded\n"); - u32 meta_idx = (user_app_data.app_info->content_count - 1), control_idx = 0, legal_idx = 0; + u32 meta_idx = (user_app_data.app_info->content_count - 1), program_idx = 0, control_idx = 0, legal_idx = 0; for(u32 i = 0, j = 0; i < user_app_data.app_info->content_count; i++) { @@ -205,6 +207,8 @@ int main(int argc, char *argv[]) goto out2; } + if (user_app_data.app_info->content_infos[i].content_type == NcmContentType_Program && user_app_data.app_info->content_infos[i].id_offset == 0) control_idx = j; + if (user_app_data.app_info->content_infos[i].content_type == NcmContentType_Control && user_app_data.app_info->content_infos[i].id_offset == 0) control_idx = j; if (user_app_data.app_info->content_infos[i].content_type == NcmContentType_LegalInformation && user_app_data.app_info->content_infos[i].id_offset == 0) legal_idx = j; @@ -222,7 +226,9 @@ int main(int argc, char *argv[]) consolePrint("Meta nca initialize ctx succeeded\n"); - sprintf(path, "sdmc:/%016lX_xml", app_metadata[selected_idx]->title_id); + mkdir("sdmc:/at_xml", 0777); + + sprintf(path, "sdmc:/at_xml/%016lX", app_metadata[selected_idx]->title_id); mkdir(path, 0777); if (!cnmtInitializeContext(&cnmt_ctx, &(nca_ctx[meta_idx]))) @@ -237,7 +243,7 @@ int main(int argc, char *argv[]) { consolePrint("cnmt xml succeeded\n"); - sprintf(path, "sdmc:/%016lX_xml/%s.cnmt", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str); + sprintf(path, "sdmc:/at_xml/%016lX/%s.cnmt", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str); xml_fd = fopen(path, "wb"); if (xml_fd) @@ -247,7 +253,7 @@ int main(int argc, char *argv[]) xml_fd = NULL; } - sprintf(path, "sdmc:/%016lX_xml/%s.cnmt.xml", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str); + sprintf(path, "sdmc:/at_xml/%016lX/%s.cnmt.xml", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str); xml_fd = fopen(path, "wb"); if (xml_fd) @@ -260,6 +266,27 @@ int main(int argc, char *argv[]) consolePrint("cnmt xml failed\n"); } + if (!programInfoInitializeContext(&program_info_ctx, &(nca_ctx[program_idx]))) + { + consolePrint("program info initialize ctx failed\n"); + goto out2; + } + + consolePrint("program info initialize ctx succeeded\n"); + + + + + + + + + + + + + + if (!nacpInitializeContext(&nacp_ctx, &(nca_ctx[control_idx]))) { consolePrint("nacp initialize ctx failed\n"); @@ -272,7 +299,7 @@ int main(int argc, char *argv[]) { consolePrint("nacp xml succeeded\n"); - sprintf(path, "sdmc:/%016lX_xml/%s.nacp", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str); + sprintf(path, "sdmc:/at_xml/%016lX/%s.nacp", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str); xml_fd = fopen(path, "wb"); if (xml_fd) @@ -282,7 +309,7 @@ int main(int argc, char *argv[]) xml_fd = NULL; } - sprintf(path, "sdmc:/%016lX_xml/%s.nacp.xml", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str); + sprintf(path, "sdmc:/at_xml/%016lX/%s.nacp.xml", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str); xml_fd = fopen(path, "wb"); if (xml_fd) @@ -296,7 +323,7 @@ int main(int argc, char *argv[]) { NacpIconContext *icon_ctx = &(nacp_ctx.icon_ctx[i]); - sprintf(path, "sdmc:/%016lX_xml/%s.nx.%s.jpg", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str, nacpGetLanguageString(icon_ctx->language)); + sprintf(path, "sdmc:/at_xml/%016lX/%s.nx.%s.jpg", app_metadata[selected_idx]->title_id, nacp_ctx.nca_ctx->content_id_str, nacpGetLanguageString(icon_ctx->language)); xml_fd = fopen(path, "wb"); if (xml_fd) @@ -318,7 +345,7 @@ int main(int argc, char *argv[]) consolePrint("legalinfo initialize ctx succeeded\n"); - sprintf(path, "sdmc:/%016lX_xml/%s.legalinfo.xml", app_metadata[selected_idx]->title_id, legal_info_ctx.nca_ctx->content_id_str); + sprintf(path, "sdmc:/at_xml/%016lX/%s.legalinfo.xml", app_metadata[selected_idx]->title_id, legal_info_ctx.nca_ctx->content_id_str); xml_fd = fopen(path, "wb"); if (xml_fd) @@ -339,6 +366,8 @@ out2: nacpFreeContext(&nacp_ctx); + programInfoFreeContext(&program_info_ctx); + cnmtFreeContext(&cnmt_ctx); if (nca_ctx) free(nca_ctx); diff --git a/source/nso.c b/source/nso.c index 9fb67f3..218f167 100644 --- a/source/nso.c +++ b/source/nso.c @@ -108,7 +108,7 @@ bool nsoInitializeContext(NsoContext *out, PartitionFileSystemContext *pfs_ctx, goto end; } - if (!out->nso_header.api_info_section_header.size || (out->nso_header.api_info_section_header.offset + out->nso_header.api_info_section_header.size) > out->nso_header.rodata_segment_header.size) + if (out->nso_header.api_info_section_header.size && (out->nso_header.api_info_section_header.offset + out->nso_header.api_info_section_header.size) > out->nso_header.rodata_segment_header.size) { LOGFILE("Invalid .api_info section offset/size for NSO \"%s\"! (0x%08X, 0x%08X).", out->nso_filename, out->nso_header.api_info_section_header.offset, out->nso_header.api_info_section_header.size); goto end; @@ -279,6 +279,8 @@ static bool nsoGetModuleInfoName(NsoContext *nso_ctx, u8 *rodata_buf) static bool nsoGetSectionFromRodataSegment(NsoContext *nso_ctx, u8 *rodata_buf, u8 **section_ptr, u64 section_offset, u64 section_size) { + if (!section_size) return true; + /* Allocate memory for the desired .rodata section. */ if (!(*section_ptr = malloc(section_size))) { diff --git a/source/nso.h b/source/nso.h index ec986a6..8220926 100644 --- a/source/nso.h +++ b/source/nso.h @@ -116,8 +116,9 @@ typedef struct { NsoHeader nso_header; ///< NSO header. char *module_name; ///< Pointer to a dynamically allocated buffer that holds the NSO module name, if available. Otherwise, this is set to NULL. char *module_info_name; ///< Pointer to a dynamically allocated buffer that holds the .rodata module info module name, if available. Otherwise, this is set to NULL. - char *rodata_api_info_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata API info section data. Middleware/GuidelineApi data is retrieved from this section. - u64 rodata_api_info_section_size; ///< .rodata API info section size. Kept here for convenience - this is part of 'nso_header'. + char *rodata_api_info_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata API info section data, if available. Otherwise, this is set to NULL. + ///< Middleware and GuidelineApi entries are retrieved from this section. + u64 rodata_api_info_section_size; ///< .rodata API info section size, if available. Otherwise, this is set to 0. Kept here for convenience - this is part of 'nso_header'. char *rodata_dynstr_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata dynamic string section data. UnresolvedApi data is retrieved from this section. u64 rodata_dynstr_section_size; ///< .rodata dynamic string section size. Kept here for convenience - this is part of 'nso_header'. u8 *rodata_dynsym_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata dynamic symbol section data. Used to retrieve pointers to symbol strings within dynstr. diff --git a/source/program_info.c b/source/program_info.c new file mode 100644 index 0000000..40f0203 --- /dev/null +++ b/source/program_info.c @@ -0,0 +1,111 @@ +/* + * program_info.c + * + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * nxdumptool is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "utils.h" +#include "program_info.h" +#include "elf_symbol.h" + +bool programInfoInitializeContext(ProgramInfoContext *out, NcaContext *nca_ctx) +{ + if (!out || !nca_ctx || !strlen(nca_ctx->content_id_str) || nca_ctx->content_type != NcmContentType_Program || nca_ctx->content_size < NCA_FULL_HEADER_LENGTH || \ + (nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \ + nca_ctx->header.content_type != NcaContentType_Program || !out) + { + LOGFILE("Invalid parameters!"); + return false; + } + + u32 i = 0, pfs_entry_count = 0, magic = 0; + NsoContext *tmp_nso_ctx = NULL; + + bool success = false; + + /* Free output context beforehand. */ + programInfoFreeContext(out); + + /* Initialize Partition FS context. */ + if (!pfsInitializeContext(&(out->pfs_ctx), &(nca_ctx->fs_contexts[0]))) + { + LOGFILE("Failed to initialize Partition FS context!"); + goto end; + } + + /* Check if we're indeed dealing with an ExeFS. */ + if (!out->pfs_ctx.is_exefs) + { + LOGFILE("Initialized Partition FS is not an ExeFS!"); + goto end; + } + + /* Get ExeFS entry count. Edge case, we should never trigger this. */ + if (!(pfs_entry_count = pfsGetEntryCount(&(out->pfs_ctx)))) + { + LOGFILE("ExeFS has no file entries!"); + goto end; + } + + /* Initialize NPDM context. */ + if (!npdmInitializeContext(&(out->npdm_ctx), &(out->pfs_ctx))) + { + LOGFILE("Failed to initialize NPDM context!"); + goto end; + } + + /* Initialize NSO contexts. */ + for(i = 0; i < pfs_entry_count; i++) + { + /* Skip the main.npdm entry, as well as any other entries without a NSO header. */ + PartitionFileSystemEntry *pfs_entry = pfsGetEntryByIndex(&(out->pfs_ctx), i); + char *pfs_entry_name = pfsGetEntryName(&(out->pfs_ctx), pfs_entry); + if (!pfs_entry || !pfs_entry_name || !strncmp(pfs_entry_name, "main.npdm", 9) || !pfsReadEntryData(&(out->pfs_ctx), pfs_entry, &magic, sizeof(u32), 0) || \ + __builtin_bswap32(magic) != NSO_HEADER_MAGIC) continue; + + /* Reallocate NSO context buffer. */ + if (!(tmp_nso_ctx = realloc(out->nso_ctx, (out->nso_count + 1) * sizeof(NsoContext)))) + { + LOGFILE("Failed to reallocate NSO context buffer for NSO \"%s\"! (entry #%u).", pfs_entry_name, i); + goto end; + } + + out->nso_ctx = tmp_nso_ctx; + tmp_nso_ctx = NULL; + + memset(&(out->nso_ctx[out->nso_count]), 0, sizeof(NsoContext)); + + /* Initialize NSO context. */ + if (!nsoInitializeContext(&(out->nso_ctx[out->nso_count]), &(out->pfs_ctx), pfs_entry)) + { + LOGFILE("Failed to initialize context for NSO \"%s\"! (entry #%u).", pfs_entry_name, i); + goto end; + } + + /* Update NSO count. */ + out->nso_count++; + } + + success = true; + +end: + if (!success) programInfoFreeContext(out); + + return success; +} diff --git a/source/program_info.h b/source/program_info.h new file mode 100644 index 0000000..815ba1a --- /dev/null +++ b/source/program_info.h @@ -0,0 +1,88 @@ +/* + * program_info.h + * + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * nxdumptool is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifndef __PROGRAM_INFO_H__ +#define __PROGRAM_INFO_H__ + +#include "npdm.h" +#include "nso.h" + +typedef struct { + NcaContext *nca_ctx; ///< Pointer to the NCA context for the Program NCA from which program data (NPDM / NSO) is retrieved. + PartitionFileSystemContext pfs_ctx; ///< PartitionFileSystemContext for the Program NCA ExeFS, which is where program data (NPDM / NSO) is stored. + NpdmContext npdm_ctx; ///< NpdmContext for the NPDM stored in Program NCA ExeFS. Holds its own NcaHierarchicalSha256Patch that may be applied to the Program NCA if needed. + u32 nso_count; ///< Number of NSOs stored in Program NCA FS section #0. + NsoContext *nso_ctx; ///< Pointer to a dynamically allocated buffer that holds 'nso_count' NSO contexts. + char *authoring_tool_xml; ///< Pointer to a dynamically allocated, NULL-terminated buffer that holds AuthoringTool-like XML data. + ///< This is always NULL unless programInfoGenerateAuthoringToolXml() is used on this ProgramInfoContext. + u64 authoring_tool_xml_size; ///< Size for the AuthoringTool-like XML. This is essentially the same as using strlen() on 'authoring_tool_xml'. + ///< This is always 0 unless programInfoGenerateAuthoringToolXml() is used on this ProgramInfoContext. +} ProgramInfoContext; + +/// Initializes a ProgramInfoContext using a previously initialized NcaContext (which must belong to a Program NCA). +bool programInfoInitializeContext(ProgramInfoContext *out, NcaContext *nca_ctx); + +/// Generates an AuthoringTool-like XML using information from a previously initialized ProgramInfoContext. +/// If the function succeeds, XML data and size will get saved to the 'authoring_tool_xml' and 'authoring_tool_xml_size' members from the ProgramInfoContext. +bool programInfoGenerateAuthoringToolXml(ProgramInfoContext *program_info_ctx); + +/// Helper inline functions. + +NX_INLINE void programInfoFreeContext(ProgramInfoContext *program_info_ctx) +{ + if (!program_info_ctx) return; + + pfsFreeContext(&(program_info_ctx->pfs_ctx)); + npdmFreeContext(&(program_info_ctx->npdm_ctx)); + + if (program_info_ctx->nso_ctx) + { + for(u32 i = 0; i < program_info_ctx->nso_count; i++) nsoFreeContext(&(program_info_ctx->nso_ctx[i])); + free(program_info_ctx->nso_ctx); + } + + if (program_info_ctx->authoring_tool_xml) free(program_info_ctx->authoring_tool_xml); + memset(program_info_ctx, 0, sizeof(ProgramInfoContext)); +} + +NX_INLINE bool programInfoIsValidContext(ProgramInfoContext *program_info_ctx) +{ + return (program_info_ctx && program_info_ctx->nca_ctx && npdmIsValidContext(&(program_info_ctx->npdm_ctx)) && program_info_ctx->npdm_ctx.pfs_ctx == &(program_info_ctx->pfs_ctx) && \ + program_info_ctx->nso_count && program_info_ctx->nso_ctx); +} + +NX_INLINE bool programInfoChangeAcidPublicKeyAndNcaSignature(ProgramInfoContext *program_info_ctx) +{ + return (programInfoIsValidContext(program_info_ctx) && npdmChangeAcidPublicKeyAndNcaSignature(&(program_info_ctx->npdm_ctx))); +} + +NX_INLINE bool programInfoIsNcaPatchRequired(ProgramInfoContext *program_info_ctx) +{ + return (programInfoIsValidContext(program_info_ctx) && npdmIsNcaPatchRequired(&(program_info_ctx->npdm_ctx))); +} + +NX_INLINE bool programInfoGenerateNcaPatch(ProgramInfoContext *program_info_ctx) +{ + return (programInfoIsValidContext(program_info_ctx) && npdmGenerateNcaPatch(&(program_info_ctx->npdm_ctx))); +} + +#endif /* __PROGRAM_INFO_H__ */