mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00:00
Test unified NCA hash layer write function (works gucci).
This commit is contained in:
parent
62cc25805d
commit
c8c062e7fa
5 changed files with 182 additions and 71 deletions
113
source/main.c
113
source/main.c
|
@ -216,7 +216,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
u8 *buf = NULL;
|
||||
|
||||
u64 base_tid = (u64)0x01006A800016E000; // ACNH 0x01006F8002326000 | Smash 0x01006A800016E000 | Dark Souls 0x01004AB00A260000 | BotW 0x01007EF00011E000
|
||||
u64 base_tid = (u64)0x010082400BCC6000; // ACNH 0x01006F8002326000 | Smash 0x01006A800016E000 | Dark Souls 0x01004AB00A260000 | BotW 0x01007EF00011E000 | Untitled Goose Game 0x010082400BCC6000
|
||||
u64 update_tid = (base_tid | 0x800);
|
||||
|
||||
Ticket base_tik = {0}, update_tik = {0};
|
||||
|
@ -371,6 +371,117 @@ int main(int argc, char *argv[])
|
|||
|
||||
consolePrint("bktr initialize ctx succeeded\n");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
FILE *tmp_file = NULL;
|
||||
RomFileSystemFileEntry *romfs_file_entry = NULL;
|
||||
RomFileSystemFileEntryPatch romfs_patch = {0};
|
||||
|
||||
romfs_file_entry = romfsGetFileEntryByPath(&(bktr_ctx.base_romfs_ctx), "/Data/rawsettings");
|
||||
if (!romfs_file_entry)
|
||||
{
|
||||
consolePrint("romfs get file entry by path failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("romfs get file entry by path success: %s | 0x%lX | %p\n", romfs_file_entry->name, romfs_file_entry->size, romfs_file_entry);
|
||||
|
||||
if (!romfsReadFileEntryData(&(bktr_ctx.base_romfs_ctx), romfs_file_entry, buf, romfs_file_entry->size, 0))
|
||||
{
|
||||
consolePrint("romfs read file entry failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("romfs read file entry success\n");
|
||||
|
||||
memset(buf, 0xAA, romfs_file_entry->size);
|
||||
|
||||
if (!romfsGenerateFileEntryPatch(&(bktr_ctx.base_romfs_ctx), romfs_file_entry, buf, romfs_file_entry->size, 0, &romfs_patch))
|
||||
{
|
||||
consolePrint("romfs file entry patch failed\n");
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("romfs file entry patch success\n");
|
||||
|
||||
if (!ncaEncryptHeader(base_nca_ctx))
|
||||
{
|
||||
consolePrint("nca header mod not encrypted\n");
|
||||
romfsFreeFileEntryPatch(&romfs_patch);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
consolePrint("nca header mod encrypted\n");
|
||||
|
||||
tmp_file = fopen("sdmc:/program_nca_mod.bin", "wb");
|
||||
if (!tmp_file)
|
||||
{
|
||||
consolePrint("program nca mod not saved\n");
|
||||
romfsFreeFileEntryPatch(&romfs_patch);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
u64 block_size = TEST_BUF_SIZE;
|
||||
for(u64 i = 0; i < base_nca_ctx->content_size; i += block_size)
|
||||
{
|
||||
if (block_size > (base_nca_ctx->content_size - i)) block_size = (base_nca_ctx->content_size - i);
|
||||
|
||||
if (!ncaReadContentFile(base_nca_ctx, buf, block_size, i))
|
||||
{
|
||||
consolePrint("failed to read 0x%lX chunk from offset 0x%lX\n", block_size, i);
|
||||
fclose(tmp_file);
|
||||
romfsFreeFileEntryPatch(&romfs_patch);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
memcpy(buf, &(base_nca_ctx->header), sizeof(NcaHeader));
|
||||
for(u64 j = 0; j < 4; j++) memcpy(buf + sizeof(NcaHeader) + (j * sizeof(NcaFsHeader)), &(base_nca_ctx->fs_contexts[j].header), sizeof(NcaFsHeader));
|
||||
}
|
||||
|
||||
romfsWriteFileEntryPatchToMemoryBuffer(&(bktr_ctx.base_romfs_ctx), &romfs_patch, buf, block_size, i);
|
||||
|
||||
fwrite(buf, 1, block_size, tmp_file);
|
||||
fflush(tmp_file);
|
||||
|
||||
consolePrint("wrote 0x%lX bytes to offset 0x%lX\n", block_size, i);
|
||||
}
|
||||
|
||||
fclose(tmp_file);
|
||||
romfsFreeFileEntryPatch(&romfs_patch);
|
||||
|
||||
goto out2;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
shared_data.bktr_ctx = &bktr_ctx;
|
||||
shared_data.data = buf;
|
||||
shared_data.data_size = 0;
|
||||
|
|
115
source/nca.c
115
source/nca.c
|
@ -44,7 +44,6 @@ NX_INLINE bool ncaIsFsInfoEntryValid(NcaFsInfo *fs_info);
|
|||
static bool ncaDecryptHeader(NcaContext *ctx);
|
||||
static bool ncaDecryptKeyArea(NcaContext *ctx);
|
||||
|
||||
static bool ncaEncryptHeader(NcaContext *ctx);
|
||||
static bool ncaEncryptKeyArea(NcaContext *ctx);
|
||||
|
||||
NX_INLINE bool ncaIsVersion0KeyAreaEncrypted(NcaContext *ctx);
|
||||
|
@ -341,6 +340,63 @@ void ncaRemoveTitlekeyCrypto(NcaContext *ctx)
|
|||
ctx->dirty_header = true;
|
||||
}
|
||||
|
||||
bool ncaEncryptHeader(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx || !strlen(ctx->content_id_str))
|
||||
{
|
||||
LOGFILE("Invalid NCA context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t crypt_res = 0;
|
||||
const u8 *header_key = keysGetNcaHeaderKey();
|
||||
Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0};
|
||||
|
||||
/* Encrypt NCA key area. */
|
||||
if (!ncaEncryptKeyArea(ctx))
|
||||
{
|
||||
LOGFILE("Error encrypting NCA \"%s\" key area!", ctx->content_id_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Prepare AES-128-XTS contexts. */
|
||||
aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + AES_128_KEY_SIZE, true);
|
||||
if (ctx->format_version == NcaVersion_Nca0) aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_key_area.aes_xts_1, ctx->decrypted_key_area.aes_xts_2, true);
|
||||
|
||||
/* Encrypt NCA header. */
|
||||
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), sizeof(NcaHeader), 0, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != sizeof(NcaHeader))
|
||||
{
|
||||
LOGFILE("Error encrypting NCA \"%s\" header!", ctx->content_id_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Encrypt NCA FS section headers. */
|
||||
/* Both NCA2 and NCA3 place the NCA FS section headers right after the NCA header. However, NCA0 places them at the start sector from each NCA FS section. */
|
||||
/* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets. */
|
||||
for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++)
|
||||
{
|
||||
/* Don't proceed if this NCA FS section isn't populated. */
|
||||
if (ctx->format_version != NcaVersion_Nca3 && !ncaIsFsInfoEntryValid(&(ctx->header.fs_info[i]))) continue;
|
||||
|
||||
/* The AES-XTS sector number for each NCA FS header varies depending on the NCA format version. */
|
||||
/* NCA3 uses sector number 0 for the NCA header, then increases it with each new sector (e.g. making the first NCA FS section header use sector number 2, and so on). */
|
||||
/* NCA2 uses sector number 0 for each NCA FS section header. */
|
||||
/* NCA0 uses sector number 0 for the NCA header, then uses sector number 0 for the rest of the data and increases it with each new sector. */
|
||||
Aes128XtsContext *aes_xts_ctx = (ctx->format_version != NcaVersion_Nca0 ? &hdr_aes_ctx : &nca0_fs_header_ctx);
|
||||
u64 sector = (ctx->format_version == NcaVersion_Nca3 ? (2U + i) : (ctx->format_version == NcaVersion_Nca2 ? 0 : (ctx->header.fs_info[i].start_sector - 2)));
|
||||
|
||||
crypt_res = aes128XtsNintendoCrypt(aes_xts_ctx, &(ctx->fs_contexts[i].header), &(ctx->fs_contexts[i].header), sizeof(NcaFsHeader), sector, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != sizeof(NcaFsHeader))
|
||||
{
|
||||
LOGFILE("Error encrypting NCA%u \"%s\" FS section header #%u!", ctx->format_version, ctx->content_id_str, i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NX_INLINE bool ncaIsFsInfoEntryValid(NcaFsInfo *fs_info)
|
||||
{
|
||||
if (!fs_info) return false;
|
||||
|
@ -470,63 +526,6 @@ static bool ncaDecryptKeyArea(NcaContext *ctx)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool ncaEncryptHeader(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx || !strlen(ctx->content_id_str))
|
||||
{
|
||||
LOGFILE("Invalid NCA context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t crypt_res = 0;
|
||||
const u8 *header_key = keysGetNcaHeaderKey();
|
||||
Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0};
|
||||
|
||||
/* Encrypt NCA key area. */
|
||||
if (!ncaEncryptKeyArea(ctx))
|
||||
{
|
||||
LOGFILE("Error encrypting NCA \"%s\" key area!", ctx->content_id_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Prepare AES-128-XTS contexts. */
|
||||
aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + AES_128_KEY_SIZE, true);
|
||||
if (ctx->format_version == NcaVersion_Nca0) aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_key_area.aes_xts_1, ctx->decrypted_key_area.aes_xts_2, true);
|
||||
|
||||
/* Encrypt NCA header. */
|
||||
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), sizeof(NcaHeader), 0, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != sizeof(NcaHeader))
|
||||
{
|
||||
LOGFILE("Error encrypting NCA \"%s\" header!", ctx->content_id_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Encrypt NCA FS section headers. */
|
||||
/* Both NCA2 and NCA3 place the NCA FS section headers right after the NCA header. However, NCA0 places them at the start sector from each NCA FS section. */
|
||||
/* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets. */
|
||||
for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++)
|
||||
{
|
||||
/* Don't proceed if this NCA FS section isn't populated. */
|
||||
if (ctx->format_version != NcaVersion_Nca3 && !ncaIsFsInfoEntryValid(&(ctx->header.fs_info[i]))) continue;
|
||||
|
||||
/* The AES-XTS sector number for each NCA FS header varies depending on the NCA format version. */
|
||||
/* NCA3 uses sector number 0 for the NCA header, then increases it with each new sector (e.g. making the first NCA FS section header use sector number 2, and so on). */
|
||||
/* NCA2 uses sector number 0 for each NCA FS section header. */
|
||||
/* NCA0 uses sector number 0 for the NCA header, then uses sector number 0 for the rest of the data and increases it with each new sector. */
|
||||
Aes128XtsContext *aes_xts_ctx = (ctx->format_version != NcaVersion_Nca0 ? &hdr_aes_ctx : &nca0_fs_header_ctx);
|
||||
u64 sector = (ctx->format_version == NcaVersion_Nca3 ? (2U + i) : (ctx->format_version == NcaVersion_Nca2 ? 0 : (ctx->header.fs_info[i].start_sector - 2)));
|
||||
|
||||
crypt_res = aes128XtsNintendoCrypt(aes_xts_ctx, &(ctx->fs_contexts[i].header), &(ctx->fs_contexts[i].header), sizeof(NcaFsHeader), sector, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != sizeof(NcaFsHeader))
|
||||
{
|
||||
LOGFILE("Error encrypting NCA%u \"%s\" FS section header #%u!", ctx->format_version, ctx->content_id_str, i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ncaEncryptKeyArea(NcaContext *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
|
|
|
@ -305,7 +305,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
u8 storage_id; ///< NcmStorageId.
|
||||
NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data.
|
||||
NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data from eMMC/SD.
|
||||
u64 gamecard_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard.
|
||||
NcmContentId content_id; ///< Also used to read NCA data.
|
||||
char content_id_str[0x21];
|
||||
|
@ -407,7 +407,8 @@ void ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(NcaContext *ctx, NcaHierar
|
|||
/// Removes titlekey crypto dependency from a NCA context by wiping the Rights ID from the underlying NCA header and copying the decrypted titlekey to the NCA key area.
|
||||
void ncaRemoveTitlekeyCrypto(NcaContext *ctx);
|
||||
|
||||
|
||||
/// Encrypts NCA header and NCA FS headers from a NCA context.
|
||||
bool ncaEncryptHeader(NcaContext *ctx);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -282,14 +282,14 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
|
|||
/* Duplicate path to avoid problems with strtok(). */
|
||||
if (!(path_dup = strdup(path)))
|
||||
{
|
||||
LOGFILE("Unable to duplicate input path!");
|
||||
LOGFILE("Unable to duplicate input path! (\"%s\").", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pch = strtok(path_dup, "/");
|
||||
if (!pch)
|
||||
{
|
||||
LOGFILE("Failed to tokenize input path!");
|
||||
LOGFILE("Failed to tokenize input path! (\"%s\").", path);
|
||||
dir_entry = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
|
|||
{
|
||||
if (!(dir_entry = romfsGetChildDirectoryEntryByName(ctx, dir_entry, pch)))
|
||||
{
|
||||
LOGFILE("Failed to retrieve directory entry by name!");
|
||||
LOGFILE("Failed to retrieve directory entry by name for \"%s\"! (\"%s\").", pch, path);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -327,7 +327,7 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const
|
|||
/* Duplicate path. */
|
||||
if (!(path_dup = strdup(path)))
|
||||
{
|
||||
LOGFILE("Unable to duplicate input path!");
|
||||
LOGFILE("Unable to duplicate input path! (\"%s\").", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -341,7 +341,7 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const
|
|||
/* Safety check. */
|
||||
if (!path_len || !(filename = strrchr(path_dup, '/')))
|
||||
{
|
||||
LOGFILE("Invalid input path!");
|
||||
LOGFILE("Invalid input path! (\"%s\").", path);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -352,12 +352,12 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const
|
|||
/* If the first character is NULL, then just retrieve the root directory entry. */
|
||||
if (!(dir_entry = (*path_dup ? romfsGetDirectoryEntryByPath(ctx, path_dup) : romfsGetDirectoryEntryByOffset(ctx, 0))))
|
||||
{
|
||||
LOGFILE("Failed to retrieve directory entry!");
|
||||
LOGFILE("Failed to retrieve directory entry for \"%s\"! (\"%s\").", *path_dup ? path_dup : "/", path);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Retrieve file entry. */
|
||||
if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename))) LOGFILE("Failed to retrieve file entry by name!");
|
||||
if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename))) LOGFILE("Failed to retrieve file entry by name for \"%s\"! (\"%s\").", filename, path);
|
||||
|
||||
end:
|
||||
if (path_dup) free(path_dup);
|
||||
|
@ -522,7 +522,7 @@ static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSys
|
|||
while(dir_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !child_dir_entry->name_length) return NULL;
|
||||
if (child_dir_entry->name_length == name_len && !strcmp(child_dir_entry->name, name)) return child_dir_entry;
|
||||
if (child_dir_entry->name_length == name_len && !strncmp(child_dir_entry->name, name, name_len)) return child_dir_entry;
|
||||
dir_offset = child_dir_entry->next_offset;
|
||||
}
|
||||
|
||||
|
@ -541,7 +541,7 @@ static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext
|
|||
while(file_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset)) || !child_file_entry->name_length) return NULL;
|
||||
if (child_file_entry->name_length == name_len && !strcmp(child_file_entry->name, name)) return child_file_entry;
|
||||
if (child_file_entry->name_length == name_len && !strncmp(child_file_entry->name, name, name_len)) return child_file_entry;
|
||||
file_offset = child_file_entry->next_offset;
|
||||
}
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ NX_INLINE RomFileSystemFileEntry *romfsGetFileEntryByOffset(RomFileSystemContext
|
|||
return (RomFileSystemFileEntry*)((u8*)ctx->file_table + file_entry_offset);
|
||||
}
|
||||
|
||||
NX_INLINE void romfsWriteFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
|
||||
NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
|
||||
{
|
||||
if (!ctx || !ctx->nca_fs_ctx || !patch || (!patch->use_old_format_patch && ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs) || \
|
||||
(patch->use_old_format_patch && ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)) return;
|
||||
|
|
Loading…
Reference in a new issue