1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-26 12:12:02 +00:00

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.
This commit is contained in:
Pablo Curiel 2020-10-11 14:13:09 -04:00
parent f45d1a21b5
commit 495e331306
5 changed files with 242 additions and 11 deletions

View file

@ -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);

View file

@ -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)))
{

View file

@ -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.

111
source/program_info.c Normal file
View file

@ -0,0 +1,111 @@
/*
* program_info.c
*
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mbedtls/base64.h>
#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;
}

88
source/program_info.h Normal file
View file

@ -0,0 +1,88 @@
/*
* program_info.h
*
* Copyright (c) 2020, DarkMatterCore <pabloacurielz@gmail.com>.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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__ */