Skip to content

Commit

Permalink
system_update: add menu option.
Browse files Browse the repository at this point in the history
Makes it possible to dump a system update. The SystemVersion file is retrieved to get additional information about the system update.
  • Loading branch information
DarkMatterCore committed Oct 27, 2024
1 parent d2baf7e commit 160236c
Show file tree
Hide file tree
Showing 7 changed files with 447 additions and 28 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Currently planned changes for this branch include:
* Improved support for multigame gamecards and titles with more than one Program NCA (e.g. SM3DAS). :white_check_mark:
* Control.nacp patching while dumping NSPs (lets you patch screenshot, video, user account and HDCP restrictions). :white_check_mark:
* Partition FS / Hash FS / RomFS browser using custom devoptab wrappers. :white_check_mark:
* Full system update dumps. :x:
* Full system update dumps with checksum and signature verification. :white_check_mark:
* Batch NSP dumps. :x:
* `FsFileSystem` + `FatFs` based eMMC browser using a custom devoptab wrapper (allows copying files protected by the FS sysmodule at runtime). :x:
* New UI using a [customized borealis fork](https://github.com/DarkMatterCore/borealis/tree/nxdumptool-legacy). :warning:
Expand Down
350 changes: 336 additions & 14 deletions code_templates/nxdt_rw_poc.c

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions include/core/hos_version_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ typedef struct {

NXDT_ASSERT(Version, 0x4);

/// Used by the SystemVersion title file (0100000000000809).
typedef struct {
u8 major;
u8 minor;
u8 micro;
u8 reserved_1;
u8 rev_major;
u8 rev_minor;
u8 reserved_2[0x2];
char platform[0x20]; ///< e.g. "NX".
char hash[0x40]; ///< e.g. "52971eebbba7ab9e6e23d73753aa63e0c3794b16".
char display_version[0x18]; ///< e.g. "19.0.0".
char display_title[0x80]; ///< e.g. "NintendoSDK Firmware for NX 19.0.0-4.0".
} SystemVersionFile;

NXDT_ASSERT(SystemVersionFile, 0x100);

#ifdef __cplusplus
}
#endif
Expand Down
16 changes: 9 additions & 7 deletions include/core/system_update.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#ifndef __SYSTEM_UPDATE_H__
#define __SYSTEM_UPDATE_H__

#include "hos_version_structs.h"
#include "nca.h"
#include "title.h"

Expand All @@ -32,13 +33,14 @@ extern "C" {
#endif

typedef struct {
u64 cur_size; ///< Current dump size.
u64 total_size; ///< Total dump size.
u32 content_idx; ///< Current content index.
u32 content_count; ///< Total content count.
u64 cur_content_offset; ///< Current content offset.
Sha256Context sha256_ctx; ///< SHA-256 hash context. Used to verify dumped NCAs.
NcaContext **nca_ctxs; ///< NCA context pointer array for all system update contents. Used to read content data.
u64 cur_size; ///< Current dump size.
u64 total_size; ///< Total dump size.
u32 content_idx; ///< Current content index.
u32 content_count; ///< Total content count.
u64 cur_content_offset; ///< Current content offset.
Sha256Context sha256_ctx; ///< SHA-256 hash context. Used to verify dumped NCAs.
NcaContext **nca_ctxs; ///< NCA context pointer array for all system update contents. Used to read content data.
SystemVersionFile version_file; ///< File data from the SystemVersion title.
} SystemUpdateDumpContext;

/// Initializes the system update interface.
Expand Down
2 changes: 1 addition & 1 deletion include/core/tik.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ typedef enum {
TikPropertyMask_SharedTitle = BIT(1), ///< Determines if the title holds shared contents only. Most likely unused -- a remnant from previous ticket formats.
TikPropertyMask_AllContents = BIT(2), ///< Determines if the content index mask shall be bypassed. Most likely unused -- a remnant from previous ticket formats.
TikPropertyMask_DeviceLinkIndepedent = BIT(3), ///< Determines if the console should *not* connect to the Internet to verify if the title's being used by the primary console.
TikPropertyMask_Volatile = BIT(4), ///< Determines if the ticket copy inside ticket.bin should be encrypted or not.
TikPropertyMask_Volatile = BIT(4), ///< Determines if the ticket copy inside ticket.bin is available after reboot. Can be encrypted.
TikPropertyMask_ELicenseRequired = BIT(5), ///< Determines if the console should connect to the Internet to perform license verification.
TikPropertyMask_Count = 6 ///< Total values supported by this enum.
} TikPropertyMask;
Expand Down
1 change: 1 addition & 0 deletions include/defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
#define BOOT_SYSMODULE_TID (u64)0x0100000000000005
#define SPL_SYSMODULE_TID (u64)0x0100000000000028
#define ES_SYSMODULE_TID (u64)0x0100000000000033
#define SYSTEM_VERSION_TID (u64)0x0100000000000809
#define SYSTEM_UPDATE_TID (u64)0x0100000000000816
#define QLAUNCH_TID (u64)0x0100000000001000

Expand Down
87 changes: 82 additions & 5 deletions source/core/system_update.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#include <core/nxdt_utils.h>
#include <core/system_update.h>
#include <core/cnmt.h>
#include <core/romfs.h>

#define SYSTEM_VERSION_FILE_PATH "/file"

/* Global variables. */

Expand All @@ -41,6 +44,8 @@ static bool systemUpdateProcessContentRecords(SystemUpdateDumpContext *ctx, Titl

static int systemUpdateNcaContextSortFunction(const void *a, const void *b);

static bool systemUpdateGetSystemVersionFileData(SystemUpdateDumpContext *ctx);

NX_INLINE NcaContext *systemUpdateGetCurrentNcaContextFromDumpContext(SystemUpdateDumpContext *ctx);

bool systemUpdateInitialize(void)
Expand Down Expand Up @@ -175,6 +180,7 @@ bool systemUpdateReadCurrentContentFileFromDumpContext(SystemUpdateDumpContext *
return false;
}

u8 nca_hash[SHA256_HASH_SIZE] = {0};
bool success = false;

/* Read NCA data. */
Expand All @@ -199,9 +205,9 @@ bool systemUpdateReadCurrentContentFileFromDumpContext(SystemUpdateDumpContext *
if (ctx->cur_content_offset >= nca_ctx->content_size)
{
/* Verify SHA-256 hash for this content. */
sha256ContextGetHash(&(ctx->sha256_ctx), nca_ctx->hash);
sha256ContextGetHash(&(ctx->sha256_ctx), nca_hash);

if (memcmp(nca_ctx->hash, nca_ctx->content_id.c, sizeof(nca_ctx->content_id.c)) != 0)
if (memcmp(nca_hash, nca_ctx->content_id.c, sizeof(nca_ctx->content_id.c)) != 0)
{
LOG_MSG_ERROR("SHA-256 checksum mismatch for %s NCA \"%s\"! (title %016lX).", titleGetNcmContentTypeName(nca_ctx->content_type), \
nca_ctx->content_id_str, nca_ctx->title_id);
Expand Down Expand Up @@ -248,15 +254,18 @@ static bool _systemUpdateInitializeDumpContext(SystemUpdateDumpContext *ctx)

/* Manually add SystemUpdate content records. */
/* The SystemUpdate CNMT doesn't reference itself. */
success = systemUpdateProcessContentRecords(ctx, g_systemUpdateTitleInfo);
if (!success)
if (!systemUpdateProcessContentRecords(ctx, g_systemUpdateTitleInfo))
{
LOG_MSG_ERROR("Failed to process SystemUpdate content records!");
goto end;
}

/* Sort NCA contexts. */
qsort(ctx->nca_ctxs, ctx->content_count, sizeof(NcaContext*), &systemUpdateNcaContextSortFunction);
if (ctx->content_count > 1) qsort(ctx->nca_ctxs, ctx->content_count, sizeof(NcaContext*), &systemUpdateNcaContextSortFunction);

/* Retrieve system version file data. */
success = systemUpdateGetSystemVersionFileData(ctx);
if (!success) LOG_MSG_ERROR("Failed to retrieve SystemVersion file data!");

end:
/* Free output context, if needed. */
Expand Down Expand Up @@ -414,6 +423,74 @@ static int systemUpdateNcaContextSortFunction(const void *a, const void *b)
return 0;
}

static bool systemUpdateGetSystemVersionFileData(SystemUpdateDumpContext *ctx)
{
if (!systemUpdateIsValidDumpContext(ctx))
{
LOG_MSG_ERROR("Invalid parameters!");
return false;
}

NcaContext *nca_ctx = NULL;

RomFileSystemContext romfs_ctx = {0};
RomFileSystemFileEntry *romfs_file_entry = NULL;

bool success = false;

/* Loop through our NCA contexts until we find the Data NCA for the SystemVersion title. */
for(u32 i = 0; i < ctx->content_count; i++)
{
nca_ctx = ctx->nca_ctxs[i];
if (nca_ctx && nca_ctx->title_id == SYSTEM_VERSION_TID && nca_ctx->content_type == NcmContentType_Data) break;
nca_ctx = NULL;
}

if (!nca_ctx)
{
LOG_MSG_ERROR("Unable to find Data NCA for SystemVersion title!");
goto end;
}

LOG_MSG_DEBUG("Found Data NCA \"%s\" for SystemVersion title.", nca_ctx->content_id_str);

/* Initialize RomFS context. */
if (!romfsInitializeContext(&romfs_ctx, &(nca_ctx->fs_ctx[0]), NULL))
{
LOG_MSG_ERROR("Failed to initialize RomFS context for SystemVersion Data NCA!");
goto end;
}

/* Get RomFS file entry. */
if (!(romfs_file_entry = romfsGetFileEntryByPath(&romfs_ctx, SYSTEM_VERSION_FILE_PATH)))
{
LOG_MSG_ERROR("Failed to retrieve RomFS file entry for SystemVersion Data NCA!");
goto end;
}

/* Validate file size. */
if (romfs_file_entry->size != sizeof(ctx->version_file))
{
LOG_MSG_ERROR("Invalid RomFS file entry size in SystemVersion Data NCA! Got 0x%lX, expected 0x%lX.", romfs_file_entry->size, sizeof(ctx->version_file));
goto end;
}

/* Read SystemVersion file data. */
success = romfsReadFileEntryData(&romfs_ctx, romfs_file_entry, &(ctx->version_file), sizeof(ctx->version_file), 0);
if (!success)
{
LOG_MSG_ERROR("Failed to read SystemVersion file data!");
goto end;
}

LOG_DATA_DEBUG(&(ctx->version_file), sizeof(ctx->version_file), "SystemVersion file data:");

end:
romfsFreeContext(&romfs_ctx);

return success;
}

NX_INLINE NcaContext *systemUpdateGetCurrentNcaContextFromDumpContext(SystemUpdateDumpContext *ctx)
{
return ((systemUpdateIsValidDumpContext(ctx) && !systemUpdateIsDumpContextFinished(ctx)) ? ctx->nca_ctxs[ctx->content_idx] : NULL);
Expand Down

0 comments on commit 160236c

Please sign in to comment.