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

Normalize goto tag names + support for gamecard key area.

Big thanks to SciresM.
This commit is contained in:
Pablo Curiel 2020-07-13 02:36:17 -04:00
parent 8baa5800a1
commit 73861bc52f
17 changed files with 758 additions and 462 deletions

View file

@ -66,14 +66,14 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
if (!out->indirect_block)
{
LOGFILE("Unable to allocate memory for the BKTR Indirect Storage Block!");
goto exit;
goto end;
}
/* Read indirect storage block data. */
if (!ncaReadFsSection(update_nca_fs_ctx, out->indirect_block, patch_info->indirect_size, patch_info->indirect_offset))
{
LOGFILE("Failed to read BKTR Indirect Storage Block data!");
goto exit;
goto end;
}
/* Allocate space for an extra (fake) AesCtrEx storage entry, to simplify our logic. */
@ -81,20 +81,20 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
if (!out->aes_ctr_ex_block)
{
LOGFILE("Unable to allocate memory for the BKTR AesCtrEx Storage Block!");
goto exit;
goto end;
}
/* Read AesCtrEx storage block data. */
if (!ncaReadFsSection(update_nca_fs_ctx, out->aes_ctr_ex_block, patch_info->aes_ctr_ex_size, patch_info->aes_ctr_ex_offset))
{
LOGFILE("Failed to read BKTR AesCtrEx Storage Block data!");
goto exit;
goto end;
}
if (out->aes_ctr_ex_block->physical_size != patch_info->aes_ctr_ex_offset)
{
LOGFILE("Invalid BKTR AesCtrEx Storage Block size!");
goto exit;
goto end;
}
/* This simplifies logic greatly... */
@ -144,13 +144,13 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
if (!bktrPhysicalSectionRead(out, &(out->patch_romfs_ctx.header), sizeof(RomFileSystemHeader), out->patch_romfs_ctx.offset))
{
LOGFILE("Failed to read update NCA RomFS header!");
goto exit;
goto end;
}
if (out->patch_romfs_ctx.header.cur_format.header_size != ROMFS_HEADER_SIZE)
{
LOGFILE("Invalid update NCA RomFS header size!");
goto exit;
goto end;
}
/* Read directory entries table. */
@ -160,20 +160,20 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
if (!dir_table_offset || !out->patch_romfs_ctx.dir_table_size)
{
LOGFILE("Invalid update NCA RomFS directory entries table!");
goto exit;
goto end;
}
out->patch_romfs_ctx.dir_table = malloc(out->patch_romfs_ctx.dir_table_size);
if (!out->patch_romfs_ctx.dir_table)
{
LOGFILE("Unable to allocate memory for the update NCA RomFS directory entries table!");
goto exit;
goto end;
}
if (!bktrPhysicalSectionRead(out, out->patch_romfs_ctx.dir_table, out->patch_romfs_ctx.dir_table_size, out->patch_romfs_ctx.offset + dir_table_offset))
{
LOGFILE("Failed to read update NCA RomFS directory entries table!");
goto exit;
goto end;
}
/* Read file entries table. */
@ -183,20 +183,20 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
if (!file_table_offset || !out->patch_romfs_ctx.file_table_size)
{
LOGFILE("Invalid update NCA RomFS file entries table!");
goto exit;
goto end;
}
out->patch_romfs_ctx.file_table = malloc(out->patch_romfs_ctx.file_table_size);
if (!out->patch_romfs_ctx.file_table)
{
LOGFILE("Unable to allocate memory for the update NCA RomFS file entries table!");
goto exit;
goto end;
}
if (!bktrPhysicalSectionRead(out, out->patch_romfs_ctx.file_table, out->patch_romfs_ctx.file_table_size, out->patch_romfs_ctx.offset + file_table_offset))
{
LOGFILE("Failed to read update NCA RomFS file entries table!");
goto exit;
goto end;
}
/* Get file data body offset. */
@ -204,7 +204,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
success = true;
exit:
end:
if (!success) bktrFreeContext(out);
return success;

View file

@ -57,16 +57,16 @@ bool certRetrieveCertificateByName(Certificate *dst, const char *name)
if (!dst || !name || !strlen(name))
{
LOGFILE("Invalid parameters!");
goto exit;
goto end;
}
if (!certOpenEsCertSaveFile()) goto exit;
if (!certOpenEsCertSaveFile()) goto end;
ret = _certRetrieveCertificateByName(dst, name);
certCloseEsCertSaveFile();
exit:
end:
mutexUnlock(&g_esCertSaveMutex);
return ret;
@ -82,16 +82,16 @@ bool certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const
if (!dst || !issuer || !(issuer_len = strlen(issuer)) || issuer_len <= 5 || strcmp(issuer, "Root-") != 0)
{
LOGFILE("Invalid parameters!");
goto exit;
goto end;
}
if (!certOpenEsCertSaveFile()) goto exit;
if (!certOpenEsCertSaveFile()) goto end;
ret = _certRetrieveCertificateChainBySignatureIssuer(dst, issuer);
certCloseEsCertSaveFile();
exit:
end:
mutexUnlock(&g_esCertSaveMutex);
return ret;
@ -121,13 +121,13 @@ u8 *certGenerateRawCertificateChainBySignatureIssuer(const char *issuer, u64 *ou
if (!raw_chain)
{
LOGFILE("Unable to allocate memory for raw \"%s\" certificate chain! (0x%lX).", issuer, raw_chain_size);
goto out;
goto end;
}
certCopyCertificateChainDataToMemoryBuffer(raw_chain, &chain);
*out_size = raw_chain_size;
out:
end:
certFreeCertificateChain(&chain);
return raw_chain;
@ -171,13 +171,13 @@ u8 *certRetrieveRawCertificateChainFromGameCardByRightsId(const FsRightsId *id,
if (!gamecardReadStorage(raw_chain, raw_chain_size, raw_chain_offset))
{
LOGFILE("Failed to read \"%s\" data from the inserted gamecard!", raw_chain_filename);
goto out;
goto end;
}
*out_size = raw_chain_size;
success = true;
out:
end:
if (!success && raw_chain)
{
free(raw_chain);

View file

@ -19,6 +19,7 @@
*/
#include "utils.h"
#include "mem.h"
#include "gamecard.h"
#define GAMECARD_HFS0_MAGIC 0x48465330 /* "HFS0". */
@ -27,8 +28,6 @@
#define GAMECARD_ACCESS_WAIT_TIME 3 /* Seconds. */
#define GAMECARD_UPDATE_TID (u64)0x0100000000000816
#define GAMECARD_ECC_BLOCK_SIZE 0x200
#define GAMECARD_ECC_DATA_SIZE 0x24
@ -43,6 +42,15 @@
/* Type definitions. */
typedef struct {
u32 memory_interface_mode;
u32 asic_status;
u8 card_id_area[0x48];
u8 reserved[0x1B0];
FsGameCardCertificate certificate;
GameCardKeyArea key_area;
} GameCardSecurityInformation;
typedef struct {
u32 magic; ///< "HFS0".
u32 entry_count;
@ -98,6 +106,15 @@ static u64 g_gameCardCapacity = 0;
static u8 *g_gameCardHfsRootHeader = NULL; /// GameCardHashFileSystemHeader + (entry_count * GameCardHashFileSystemEntry) + Name Table.
static GameCardHashFileSystemPartitionInfo *g_gameCardHfsPartitions = NULL;
static MemoryLocation g_fsProgramMemory = {
.program_id = FS_SYSMODULE_TID,
.mask = 0,
.data = NULL,
.data_size = 0
};
static GameCardSecurityInformation g_gameCardSecurityInfo = {0};
/* Function prototypes. */
static bool gamecardCreateDetectionThread(void);
@ -109,6 +126,8 @@ NX_INLINE bool gamecardIsInserted(void);
static void gamecardLoadInfo(void);
static void gamecardFreeInfo(void);
static bool gamecardReadSecurityInformation(void);
static bool gamecardGetHandle(void);
NX_INLINE void gamecardCloseHandle(void);
@ -133,14 +152,14 @@ bool gamecardInitialize(void)
Result rc = 0;
bool ret = g_gamecardInterfaceInit;
if (ret) goto out;
if (ret) goto end;
/* Allocate memory for the gamecard read buffer. */
g_gameCardReadBuf = malloc(GAMECARD_READ_BUFFER_SIZE);
if (!g_gameCardReadBuf)
{
LOGFILE("Unable to allocate memory for the gamecard read buffer!");
goto out;
goto end;
}
/* Open device operator. */
@ -148,7 +167,7 @@ bool gamecardInitialize(void)
if (R_FAILED(rc))
{
LOGFILE("fsOpenDeviceOperator failed! (0x%08X).", rc);
goto out;
goto end;
}
g_openDeviceOperator = true;
@ -158,7 +177,7 @@ bool gamecardInitialize(void)
if (R_FAILED(rc))
{
LOGFILE("fsOpenGameCardDetectionEventNotifier failed! (0x%08X)", rc);
goto out;
goto end;
}
g_openEventNotifier = true;
@ -168,7 +187,7 @@ bool gamecardInitialize(void)
if (R_FAILED(rc))
{
LOGFILE("fsEventNotifierGetEventHandle failed! (0x%08X)", rc);
goto out;
goto end;
}
g_loadKernelEvent = true;
@ -180,11 +199,11 @@ bool gamecardInitialize(void)
ueventCreate(&g_gameCardStatusChangeEvent, true);
/* Create gamecard detection thread. */
if (!(g_gameCardDetectionThreadCreated = gamecardCreateDetectionThread())) goto out;
if (!(g_gameCardDetectionThreadCreated = gamecardCreateDetectionThread())) goto end;
ret = g_gamecardInterfaceInit = true;
out:
end:
mutexUnlock(&g_gamecardMutex);
return ret;
@ -255,6 +274,15 @@ bool gamecardReadStorage(void *out, u64 read_size, u64 offset)
return gamecardReadStorageArea(out, read_size, offset, true);
}
bool gamecardGetKeyArea(GameCardKeyArea *out)
{
mutexLock(&g_gamecardMutex);
bool ret = (g_gameCardInserted && g_gameCardInfoLoaded && out);
if (ret) memcpy(out, &(g_gameCardSecurityInfo.key_area), sizeof(GameCardKeyArea));
mutexUnlock(&g_gamecardMutex);
return ret;
}
bool gamecardGetHeader(GameCardHeader *out)
{
mutexLock(&g_gamecardMutex);
@ -264,6 +292,23 @@ bool gamecardGetHeader(GameCardHeader *out)
return ret;
}
bool gamecardGetCertificate(FsGameCardCertificate *out)
{
Result rc = 0;
bool ret = false;
mutexLock(&g_gamecardMutex);
if (g_gameCardInserted && g_gameCardHandle.value && out)
{
rc = fsDeviceOperatorGetGameCardDeviceCertificate(&g_deviceOperator, &g_gameCardHandle, out);
if (R_FAILED(rc)) LOGFILE("fsDeviceOperatorGetGameCardDeviceCertificate failed! (0x%08X)", rc);
ret = R_SUCCEEDED(rc);
}
mutexUnlock(&g_gamecardMutex);
return ret;
}
bool gamecardGetTotalSize(u64 *out)
{
mutexLock(&g_gamecardMutex);
@ -291,23 +336,6 @@ bool gamecardGetRomCapacity(u64 *out)
return ret;
}
bool gamecardGetCertificate(FsGameCardCertificate *out)
{
Result rc = 0;
bool ret = false;
mutexLock(&g_gamecardMutex);
if (g_gameCardInserted && g_gameCardHandle.value && out)
{
rc = fsDeviceOperatorGetGameCardDeviceCertificate(&g_deviceOperator, &g_gameCardHandle, out);
if (R_FAILED(rc)) LOGFILE("fsDeviceOperatorGetGameCardDeviceCertificate failed! (0x%08X)", rc);
ret = R_SUCCEEDED(rc);
}
mutexUnlock(&g_gamecardMutex);
return ret;
}
bool gamecardGetBundledFirmwareUpdateVersion(u32 *out)
{
Result rc = 0;
@ -367,14 +395,14 @@ bool gamecardGetEntryInfoFromHashFileSystemPartitionByIndex(u8 hfs_partition_typ
if (!fs_header)
{
LOGFILE("Failed to retrieve hash FS partition header!");
goto out;
goto end;
}
fs_entry = gamecardGetHashFileSystemEntryByIndex(fs_header, idx);
if (!fs_entry)
{
LOGFILE("Failed to retrieve hash FS partition entry by index!");
goto out;
goto end;
}
if (out_offset)
@ -395,21 +423,21 @@ bool gamecardGetEntryInfoFromHashFileSystemPartitionByIndex(u8 hfs_partition_typ
if (!entry_name || !strlen(entry_name))
{
LOGFILE("Invalid hash FS partition entry name!");
goto out;
goto end;
}
*out_name = strdup(entry_name);
if (!*out_name)
{
LOGFILE("Failed to duplicate hash FS partition entry name!");
goto out;
goto end;
}
}
ret = true;
}
out:
end:
mutexUnlock(&g_gamecardMutex);
return ret;
@ -430,20 +458,20 @@ bool gamecardGetEntryInfoFromHashFileSystemPartitionByName(u8 hfs_partition_type
if (!fs_header)
{
LOGFILE("Failed to retrieve hash FS partition header!");
goto out;
goto end;
}
if (!gamecardGetHashFileSystemEntryIndexByName(fs_header, name, &fs_entry_idx))
{
LOGFILE("Failed to retrieve hash FS partition entry index by name!");
goto out;
goto end;
}
fs_entry = gamecardGetHashFileSystemEntryByIndex(fs_header, fs_entry_idx);
if (!fs_entry)
{
LOGFILE("Failed to retrieve hash FS partition entry by index!");
goto out;
goto end;
}
if (out_offset)
@ -461,7 +489,7 @@ bool gamecardGetEntryInfoFromHashFileSystemPartitionByName(u8 hfs_partition_type
ret = true;
}
out:
end:
mutexUnlock(&g_gamecardMutex);
return ret;
@ -560,21 +588,21 @@ static void gamecardLoadInfo(void)
if (!gamecardGetStorageAreasSizes())
{
LOGFILE("Failed to retrieve gamecard storage area sizes!");
goto out;
goto end;
}
/* Read gamecard header. */
if (!gamecardReadStorageArea(&g_gameCardHeader, sizeof(GameCardHeader), 0, false))
{
LOGFILE("Failed to read gamecard header!");
goto out;
goto end;
}
/* Check magic word from gamecard header. */
if (__builtin_bswap32(g_gameCardHeader.magic) != GAMECARD_HEAD_MAGIC)
{
LOGFILE("Invalid gamecard header magic word! (0x%08X)", __builtin_bswap32(g_gameCardHeader.magic));
goto out;
goto end;
}
/* Get gamecard capacity. */
@ -582,7 +610,7 @@ static void gamecardLoadInfo(void)
if (!g_gameCardCapacity)
{
LOGFILE("Invalid gamecard capacity value! (0x%02X).", g_gameCardHeader.rom_size);
goto out;
goto end;
}
if (utilsGetCustomFirmwareType() == UtilsCustomFirmwareType_SXOS)
@ -597,14 +625,14 @@ static void gamecardLoadInfo(void)
if (!g_gameCardHfsRootHeader)
{
LOGFILE("Unable to allocate memory for the root hash FS header!");
goto out;
goto end;
}
/* Read root hash FS header. */
if (!gamecardReadStorageArea(g_gameCardHfsRootHeader, g_gameCardHeader.partition_fs_header_size, g_gameCardHeader.partition_fs_header_address, false))
{
LOGFILE("Failed to read root hash FS header from offset 0x%lX!", g_gameCardHeader.partition_fs_header_address);
goto out;
goto end;
}
fs_header = (GameCardHashFileSystemHeader*)g_gameCardHfsRootHeader;
@ -612,14 +640,14 @@ static void gamecardLoadInfo(void)
if (__builtin_bswap32(fs_header->magic) != GAMECARD_HFS0_MAGIC)
{
LOGFILE("Invalid magic word in root hash FS header! (0x%08X).", __builtin_bswap32(fs_header->magic));
goto out;
goto end;
}
if (!fs_header->entry_count || !fs_header->name_table_size || \
(sizeof(GameCardHashFileSystemHeader) + (fs_header->entry_count * sizeof(GameCardHashFileSystemEntry)) + fs_header->name_table_size) > g_gameCardHeader.partition_fs_header_size)
{
LOGFILE("Invalid file count and/or name table size in root hash FS header!");
goto out;
goto end;
}
/* Allocate memory for the hash FS partitions info. */
@ -627,7 +655,7 @@ static void gamecardLoadInfo(void)
if (!g_gameCardHfsPartitions)
{
LOGFILE("Unable to allocate memory for the hash FS partitions info!");
goto out;
goto end;
}
/* Read hash FS partitions. */
@ -637,7 +665,7 @@ static void gamecardLoadInfo(void)
if (!fs_entry || !fs_entry->size)
{
LOGFILE("Invalid hash FS partition entry!");
goto out;
goto end;
}
g_gameCardHfsPartitions[i].offset = (g_gameCardHeader.partition_fs_header_address + g_gameCardHeader.partition_fs_header_size + fs_entry->offset);
@ -648,19 +676,19 @@ static void gamecardLoadInfo(void)
if (!gamecardReadStorageArea(&partition_header, sizeof(GameCardHashFileSystemHeader), g_gameCardHfsPartitions[i].offset, false))
{
LOGFILE("Failed to partially read hash FS partition #%u header from offset 0x%lX!", i, g_gameCardHfsPartitions[i].offset);
goto out;
goto end;
}
if (__builtin_bswap32(partition_header.magic) != GAMECARD_HFS0_MAGIC)
{
LOGFILE("Invalid magic word in hash FS partition #%u header! (0x%08X).", i, __builtin_bswap32(partition_header.magic));
goto out;
goto end;
}
if (!partition_header.name_table_size)
{
LOGFILE("Invalid name table size in hash FS partition #%u header!", i);
goto out;
goto end;
}
/* Calculate the full header size for the current hash FS partition and round it to a GAMECARD_MEDIA_UNIT_SIZE bytes boundary. */
@ -672,20 +700,30 @@ static void gamecardLoadInfo(void)
if (!g_gameCardHfsPartitions[i].header)
{
LOGFILE("Unable to allocate memory for the hash FS partition #%u header!", i);
goto out;
goto end;
}
/* Finally, read the full hash FS partition header. */
if (!gamecardReadStorageArea(g_gameCardHfsPartitions[i].header, g_gameCardHfsPartitions[i].header_size, g_gameCardHfsPartitions[i].offset, false))
{
LOGFILE("Failed to read full hash FS partition #%u header from offset 0x%lX!", i, g_gameCardHfsPartitions[i].offset);
goto out;
goto end;
}
}
/* Read full FS program memory to retrieve the GameCardSecurityInformation data, which holds the gamecard key area. */
/* This must be performed while the gamecard is in secure mode, which is already taken care of in the gamecardReadStorageArea() calls from the last iteration in the previous for() loop. */
/* GameCardSecurityInformation data is returned by Lotus command "ChangeToSecureMode" (0xF), and kept in FS program memory only after the gamecard secure area has been both mounted and read from. */
/* Under some circumstances, the gamecard key area is located *after* the GameCardSecurityInformation area (offset 0x600), instead of its common location at offset 0x400. */
if (!gamecardReadSecurityInformation())
{
LOGFILE("Failed to read gamecard security information area from FS program memory!");
goto end;
}
g_gameCardInfoLoaded = true;
out:
end:
if (!g_gameCardInfoLoaded) gamecardFreeInfo();
}
@ -693,6 +731,8 @@ static void gamecardFreeInfo(void)
{
memset(&g_gameCardHeader, 0, sizeof(GameCardHeader));
memset(&g_gameCardSecurityInfo, 0, sizeof(GameCardSecurityInformation));
g_gameCardStorageNormalAreaSize = 0;
g_gameCardStorageSecureAreaSize = 0;
@ -725,6 +765,49 @@ static void gamecardFreeInfo(void)
g_gameCardInfoLoaded = false;
}
static bool gamecardReadSecurityInformation(void)
{
bool found = false;
/* Retrieve full FS program memory dump. */
if (!memRetrieveFullProgramMemory(&g_fsProgramMemory))
{
LOGFILE("Failed to retrieve full FS program memory dump!");
return false;
}
/* Look for the gamecard header in the FS memory dump. */
for(u64 offset = 0; offset < g_fsProgramMemory.data_size; offset++)
{
if (memcmp(&(g_gameCardHeader.magic), g_fsProgramMemory.data + offset, 0x90) != 0) continue;
/* Found the gamecard header. Let's read the GameCardSecurityInformation element. */
offset += 0x100;
memcpy(&g_gameCardSecurityInfo, g_fsProgramMemory.data + offset, sizeof(GameCardSecurityInformation));
/* Check the key_source / package_id value. */
if (g_gameCardSecurityInfo.key_area.package_id == g_gameCardHeader.package_id)
{
/* Jackpot. */
found = true;
} else {
/* Copy the sector right after the GameCardSecurityInformation element from the memory dump, since it may hold the gamecard key area. */
offset += sizeof(GameCardSecurityInformation);
memcpy(&(g_gameCardSecurityInfo.key_area), g_fsProgramMemory.data + offset, sizeof(GameCardKeyArea));
found = (g_gameCardSecurityInfo.key_area.package_id == g_gameCardHeader.package_id);
}
break;
}
if (!found) LOGFILE("Failed to locate gamecard key area!");
/* Free FS memory dump. */
memFreeMemoryLocation(&g_fsProgramMemory);
return found;
}
static bool gamecardGetHandle(void)
{
if (!g_gameCardInserted)
@ -818,7 +901,7 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l
offset >= (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize) || (offset + read_size) > (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize))
{
LOGFILE("Invalid parameters!");
goto exit;
goto end;
}
Result rc = 0;
@ -831,7 +914,7 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l
/* Calculate normal storage area size difference. */
u64 diff_size = (g_gameCardStorageNormalAreaSize - offset);
if (!gamecardReadStorageArea(out_u8, diff_size, offset, false)) goto exit;
if (!gamecardReadStorageArea(out_u8, diff_size, offset, false)) goto end;
/* Adjust variables to read right from the start of the secure storage area. */
read_size -= diff_size;
@ -845,7 +928,7 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l
if (!gamecardOpenStorageArea(area))
{
LOGFILE("Failed to open %s storage area!", GAMECARD_STORAGE_AREA_NAME(area));
goto exit;
goto end;
}
/* Calculate appropiate storage area offset and retrieve the right storage area pointer. */
@ -858,7 +941,7 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l
if (R_FAILED(rc))
{
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (aligned).", read_size, base_offset, GAMECARD_STORAGE_AREA_NAME(area), rc);
goto exit;
goto end;
}
success = true;
@ -876,7 +959,7 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l
if (R_FAILED(rc))
{
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (unaligned).", chunk_size, block_start_offset, GAMECARD_STORAGE_AREA_NAME(area), rc);
goto exit;
goto end;
}
memcpy(out_u8, g_gameCardReadBuf + data_start_offset, out_chunk_size);
@ -884,7 +967,7 @@ static bool gamecardReadStorageArea(void *out, u64 read_size, u64 offset, bool l
success = (block_size > GAMECARD_READ_BUFFER_SIZE ? gamecardReadStorageArea(out_u8 + out_chunk_size, read_size - out_chunk_size, base_offset + out_chunk_size, false) : true);
}
exit:
end:
if (lock) mutexUnlock(&g_gamecardMutex);
return success;

View file

@ -30,10 +30,26 @@
#define GAMECARD_MEDIA_UNIT_SIZE 0x200
#define GAMECARD_UPDATE_TID (u64)0x0100000000000816
#define GAMECARD_HFS_PARTITION_NAME(x) ((x) == GameCardHashFileSystemPartitionType_Root ? "root" : ((x) == GameCardHashFileSystemPartitionType_Update ? "update" : \
((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : ((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : \
((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown")))))
typedef struct {
union {
u8 key_source[0x10];
struct {
u64 package_id; ///< Matches package_id from GameCardHeader.
u64 padding; ///< Just zeroes.
};
};
u8 encrypted_titlekey[0x10];
u8 mac[0x10];
u8 nonce[0xC];
u8 reserved[0x1C4];
} GameCardKeyArea;
typedef enum {
GameCardKekIndex_Version0 = 0,
GameCardKekIndex_VersionForDev = 1
@ -153,11 +169,12 @@ bool gamecardReadStorage(void *out, u64 read_size, u64 offset);
/// Miscellaneous functions.
bool gamecardGetKeyArea(GameCardKeyArea *out);
bool gamecardGetHeader(GameCardHeader *out);
bool gamecardGetCertificate(FsGameCardCertificate *out);
bool gamecardGetTotalSize(u64 *out);
bool gamecardGetTrimmedSize(u64 *out);
bool gamecardGetRomCapacity(u64 *out); ///< Not the same as gamecardGetTotalSize().
bool gamecardGetCertificate(FsGameCardCertificate *out);
bool gamecardGetBundledFirmwareUpdateVersion(u32 *out);
/// Retrieves the entry count from a hash FS partition.

View file

@ -22,51 +22,37 @@
#include "utils.h"
#include "keys.h"
#include "mem.h"
#include "nca.h"
#define KEYS_FILE_PATH "sdmc:/switch/prod.keys" /* Location used by Lockpick_RCM. */
#define FS_SYSMODULE_TID (u64)0x0100000000000000
#define BOOT_SYSMODULE_TID (u64)0x0100000000000005
#define SPL_SYSMODULE_TID (u64)0x0100000000000028
#define SEGMENT_TEXT BIT(0)
#define SEGMENT_RODATA BIT(1)
#define SEGMENT_DATA BIT(2)
/* Type definitions. */
typedef struct {
u64 program_id;
u8 mask;
u8 *data;
u64 data_size;
} keysMemoryLocation;
typedef struct {
char name[64];
u8 hash[SHA256_HASH_SIZE];
u64 size;
void *dst;
} keysMemoryKey;
} KeysMemoryKey;
typedef struct {
keysMemoryLocation location;
MemoryLocation location;
u32 key_count;
keysMemoryKey keys[];
} keysMemoryInfo;
KeysMemoryKey keys[];
} KeysMemoryInfo;
typedef struct {
///< Needed to decrypt the NCA header using AES-128-XTS.
u8 header_kek_source[0x10]; ///< Seed for header kek. Retrieved from the .rodata section in the FS sysmodule.
u8 header_key_source[0x20]; ///< Seed for NCA header key. Retrieved from the .data section in the FS sysmodule.
u8 header_kek_source[0x10]; ///< Seed for header kek. Retrieved from the .rodata segment in the FS sysmodule.
u8 header_key_source[0x20]; ///< Seed for NCA header key. Retrieved from the .data segment in the FS sysmodule.
u8 header_kek[0x10]; ///< NCA header kek. Generated from header_kek_source.
u8 header_key[0x20]; ///< NCA header key. Generated from header_kek and header_key_source.
///< Needed to derive the KAEK used to decrypt the NCA key area.
u8 key_area_key_application_source[0x10]; ///< Seed for kaek 0. Retrieved from the .rodata section in the FS sysmodule.
u8 key_area_key_ocean_source[0x10]; ///< Seed for kaek 1. Retrieved from the .rodata section in the FS sysmodule.
u8 key_area_key_system_source[0x10]; ///< Seed for kaek 2. Retrieved from the .rodata section in the FS sysmodule.
u8 key_area_key_application_source[0x10]; ///< Seed for kaek 0. Retrieved from the .rodata segment in the FS sysmodule.
u8 key_area_key_ocean_source[0x10]; ///< Seed for kaek 1. Retrieved from the .rodata segment in the FS sysmodule.
u8 key_area_key_system_source[0x10]; ///< Seed for kaek 2. Retrieved from the .rodata segment in the FS sysmodule.
///< Needed to decrypt the titlekey block from a ticket. Retrieved from the Lockpick_RCM keys file.
u8 eticket_rsa_kek[0x10]; ///< eTicket RSA kek (generic).
@ -83,10 +69,10 @@ static keysNcaKeyset g_ncaKeyset = {0};
static bool g_ncaKeysetLoaded = false;
static Mutex g_ncaKeysetMutex = 0;
static keysMemoryInfo g_fsRodataMemoryInfo = {
static KeysMemoryInfo g_fsRodataMemoryInfo = {
.location = {
.program_id = FS_SYSMODULE_TID,
.mask = SEGMENT_RODATA,
.mask = MemoryProgramSegmentType_Rodata,
.data = NULL,
.data_size = 0
},
@ -123,10 +109,10 @@ static keysMemoryInfo g_fsRodataMemoryInfo = {
}
};
static keysMemoryInfo g_fsDataMemoryInfo = {
static KeysMemoryInfo g_fsDataMemoryInfo = {
.location = {
.program_id = FS_SYSMODULE_TID,
.mask = SEGMENT_DATA,
.mask = MemoryProgramSegmentType_Data,
.data = NULL,
.data_size = 0
},
@ -144,10 +130,7 @@ static keysMemoryInfo g_fsDataMemoryInfo = {
/* Function prototypes. */
static bool keysRetrieveDebugHandleFromProcessByProgramId(Handle *out, u64 program_id);
static bool keysRetrieveProcessMemory(keysMemoryLocation *location);
static void keysFreeProcessMemory(keysMemoryLocation *location);
static bool keysRetrieveKeysFromProcessMemory(keysMemoryInfo *info);
static bool keysRetrieveKeysFromProgramMemory(KeysMemoryInfo *info);
static bool keysDeriveNcaHeaderKey(void);
static int keysGetKeyAndValueFromFile(FILE *f, char **key, char **value);
static char keysConvertHexCharToBinary(char c);
@ -159,7 +142,7 @@ bool keysLoadNcaKeyset(void)
mutexLock(&g_ncaKeysetMutex);
bool ret = g_ncaKeysetLoaded;
if (ret) goto exit;
if (ret) goto end;
if (!(envIsSyscallHinted(0x60) && /* svcDebugActiveProcess. */
envIsSyscallHinted(0x63) && /* svcGetDebugEvent. */
@ -168,32 +151,32 @@ bool keysLoadNcaKeyset(void)
envIsSyscallHinted(0x6A))) /* svcReadDebugProcessMemory. */
{
LOGFILE("Debug SVC permissions not available!");
goto exit;
goto end;
}
if (!keysRetrieveKeysFromProcessMemory(&g_fsRodataMemoryInfo))
if (!keysRetrieveKeysFromProgramMemory(&g_fsRodataMemoryInfo))
{
LOGFILE("Unable to retrieve keys from FS .rodata section!");
goto exit;
LOGFILE("Unable to retrieve keys from FS .rodata segment!");
goto end;
}
if (!keysRetrieveKeysFromProcessMemory(&g_fsDataMemoryInfo))
if (!keysRetrieveKeysFromProgramMemory(&g_fsDataMemoryInfo))
{
LOGFILE("Unable to retrieve keys from FS .data section!");
goto exit;
LOGFILE("Unable to retrieve keys from FS .data segment!");
goto end;
}
if (!keysDeriveNcaHeaderKey())
{
LOGFILE("Unable to derive NCA header key!");
goto exit;
goto end;
}
if (!keysReadKeysFromFile()) goto exit;
if (!keysReadKeysFromFile()) goto end;
ret = g_ncaKeysetLoaded = true;
exit:
end:
mutexUnlock(&g_ncaKeysetMutex);
return ret;
@ -264,190 +247,7 @@ const u8 *keysGetKeyAreaEncryptionKey(u8 key_generation, u8 kaek_index)
return (const u8*)(g_ncaKeyset.key_area_keys[key_gen_val][kaek_index]);
}
static bool keysRetrieveDebugHandleFromProcessByProgramId(Handle *out, u64 program_id)
{
if (!out || !program_id)
{
LOGFILE("Invalid parameters!");
return false;
}
Result rc = 0;
u64 d[8] = {0};
Handle debug_handle = INVALID_HANDLE;
if (program_id > BOOT_SYSMODULE_TID && program_id != SPL_SYSMODULE_TID)
{
/* If not a kernel process, get PID from pm:dmnt. */
u64 pid;
rc = pmdmntGetProcessId(&pid, program_id);
if (R_FAILED(rc))
{
LOGFILE("pmdmntGetProcessId failed! (0x%08X).", rc);
return false;
}
rc = svcDebugActiveProcess(&debug_handle, pid);
if (R_FAILED(rc))
{
LOGFILE("svcDebugActiveProcess failed! (0x%08X).", rc);
return false;
}
rc = svcGetDebugEvent((u8*)&d, debug_handle);
if (R_FAILED(rc))
{
LOGFILE("svcGetDebugEvent failed! (0x%08X).", rc);
return false;
}
} else {
/* Otherwise, query svc for the process list. */
u32 i, num_processes = 0;
u64 *pids = calloc(300, sizeof(u64));
if (!pids)
{
LOGFILE("Failed to allocate memory for PID list!");
return false;
}
rc = svcGetProcessList((s32*)&num_processes, pids, 300);
if (R_FAILED(rc))
{
LOGFILE("svcGetProcessList failed! (0x%08X).", rc);
return false;
}
for(i = 0; i < (num_processes - 1); i++)
{
rc = svcDebugActiveProcess(&debug_handle, pids[i]);
if (R_FAILED(rc)) continue;
rc = svcGetDebugEvent((u8*)&d, debug_handle);
if (R_SUCCEEDED(rc) && d[2] == program_id) break;
svcCloseHandle(debug_handle);
debug_handle = INVALID_HANDLE;
}
free(pids);
if (i == (num_processes - 1))
{
LOGFILE("Kernel process lookup failed! (0x%08X).", rc);
return false;
}
}
*out = debug_handle;
return true;
}
static bool keysRetrieveProcessMemory(keysMemoryLocation *location)
{
if (!location || !location->program_id || !location->mask)
{
LOGFILE("Invalid parameters!");
return false;
}
Result rc = 0;
Handle debug_handle = INVALID_HANDLE;
MemoryInfo mem_info = {0};
u32 page_info = 0;
u64 addr = 0, last_text_addr = 0;
u8 segment = 0;
u8 *tmp = NULL;
bool success = true;
if (!keysRetrieveDebugHandleFromProcessByProgramId(&debug_handle, location->program_id))
{
LOGFILE("Unable to retrieve debug handle for program %016lX!", location->program_id);
return false;
}
/* Locate "real" .text segment as Atmosphere emuMMC has two. */
for(;;)
{
rc = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr);
if (R_FAILED(rc))
{
LOGFILE("svcQueryDebugProcessMemory failed! (0x%08X).", rc);
success = false;
goto out;
}
if ((mem_info.perm & Perm_X) && ((mem_info.type & 0xFF) >= MemType_CodeStatic) && ((mem_info.type & 0xFF) < MemType_Heap)) last_text_addr = mem_info.addr;
addr = (mem_info.addr + mem_info.size);
if (!addr) break;
}
addr = last_text_addr;
for(segment = 1; segment < BIT(3);)
{
rc = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr);
if (R_FAILED(rc))
{
LOGFILE("svcQueryDebugProcessMemory failed! (0x%08X).", rc);
success = false;
break;
}
/* Code to allow for bitmasking segments. */
if ((mem_info.perm & Perm_R) && ((mem_info.type & 0xFF) >= MemType_CodeStatic) && ((mem_info.type & 0xFF) < MemType_Heap) && ((segment <<= 1) >> 1 & location->mask) > 0)
{
/* If location->data == NULL, realloc will essentially act as a malloc. */
tmp = realloc(location->data, location->data_size + mem_info.size);
if (!tmp)
{
LOGFILE("Failed to resize key location data buffer to 0x%lX bytes.", location->data_size + mem_info.size);
success = false;
break;
}
location->data = tmp;
tmp = NULL;
rc = svcReadDebugProcessMemory(location->data + location->data_size, debug_handle, mem_info.addr, mem_info.size);
if (R_FAILED(rc))
{
LOGFILE("svcReadDebugProcessMemory failed! (0x%08X).", rc);
success = false;
break;
}
location->data_size += mem_info.size;
}
addr = (mem_info.addr + mem_info.size);
if (addr == 0) break;
}
out:
svcCloseHandle(debug_handle);
if (success && (!location->data || !location->data_size)) success = false;
return success;
}
static void keysFreeProcessMemory(keysMemoryLocation *location)
{
if (location && location->data)
{
free(location->data);
location->data = NULL;
}
}
static bool keysRetrieveKeysFromProcessMemory(keysMemoryInfo *info)
static bool keysRetrieveKeysFromProgramMemory(KeysMemoryInfo *info)
{
if (!info || !info->key_count)
{
@ -459,11 +259,7 @@ static bool keysRetrieveKeysFromProcessMemory(keysMemoryInfo *info)
u8 tmp_hash[SHA256_HASH_SIZE];
bool success = false;
if (!keysRetrieveProcessMemory(&(info->location)))
{
LOGFILE("Unable to retrieve process memory from program %016lX!", info->location.program_id);
return false;
}
if (!memRetrieveProgramMemorySegment(&(info->location))) return false;
for(u32 i = 0; i < info->key_count; i++)
{
@ -472,7 +268,7 @@ static bool keysRetrieveKeysFromProcessMemory(keysMemoryInfo *info)
if (!info->keys[i].dst)
{
LOGFILE("Invalid destination pointer for key \"%s\" in program %016lX!", info->keys[i].name, info->location.program_id);
goto out;
goto end;
}
/* Hash every key length-sized byte chunk in the process memory buffer until a match is found. */
@ -494,14 +290,14 @@ static bool keysRetrieveKeysFromProcessMemory(keysMemoryInfo *info)
if (!found)
{
LOGFILE("Unable to locate key \"%s\" in process memory from program %016lX!", info->keys[i].name, info->location.program_id);
goto out;
goto end;
}
}
success = true;
out:
keysFreeProcessMemory(&(info->location));
end:
memFreeMemoryLocation(&(info->location));
return success;
}

View file

@ -333,6 +333,8 @@ int main(int argc, char *argv[])
shared_data.data_written = 0;
romfsGetTotalDataSize(&romfs_ctx, &(shared_data.total_size));
goto out2;
consolePrint("waiting for usb connection... ");
time_t start = time(NULL);
@ -450,6 +452,26 @@ out2:
consolePrint("press any button to exit\n");
utilsWaitForButtonPress(KEY_NONE);
if (gamecardIsReady())
{
GameCardKeyArea key_area = {0};
if (gamecardGetKeyArea(&key_area))
{
FILE *kafd = fopen("sdmc:/gc_key_area.bin", "wb");
if (kafd)
{
fwrite(&key_area, 1, sizeof(GameCardKeyArea), kafd);
fclose(kafd);
}
}
}
lrExit();
if (serviceIsActive(&(ncm_storage.s))) ncmContentStorageClose(&ncm_storage);

262
source/mem.c Normal file
View file

@ -0,0 +1,262 @@
/*
* mem.c
*
* Copyright (c) 2019, shchmue.
* 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 "utils.h"
#include "mem.h"
#define MEMLOG(fmt, ...) LOGBUF(g_memLogBuf, sizeof(g_memLogBuf), fmt, ##__VA_ARGS__)
/* Global variables. */
static char g_memLogBuf[512] = {0};
static Mutex g_memMutex = 0;
/* Function prototypes. */
static bool memRetrieveProgramMemory(MemoryLocation *location, bool is_segment);
static bool memRetrieveDebugHandleFromProgramById(Handle *out, u64 program_id);
bool memRetrieveProgramMemorySegment(MemoryLocation *location)
{
if (!location || !location->program_id || !location->mask || location->mask >= BIT(3))
{
LOGFILE("Invalid parameters!");
return false;
}
mutexLock(&g_memMutex);
bool ret = memRetrieveProgramMemory(location, true);
mutexUnlock(&g_memMutex);
return ret;
}
bool memRetrieveFullProgramMemory(MemoryLocation *location)
{
if (!location || !location->program_id)
{
LOGFILE("Invalid parameters!");
return false;
}
mutexLock(&g_memMutex);
bool ret = memRetrieveProgramMemory(location, false);
mutexUnlock(&g_memMutex);
return ret;
}
static bool memRetrieveProgramMemory(MemoryLocation *location, bool is_segment)
{
Result rc = 0;
Handle debug_handle = INVALID_HANDLE;
MemoryInfo mem_info = {0};
u32 page_info = 0;
u64 addr = 0, last_text_addr = 0;
u8 segment = 1, mem_type = 0;
u8 *tmp = NULL;
bool success = true;
*g_memLogBuf = '\0';
/* Clear output MemoryLocation element. */
memFreeMemoryLocation(location);
/* LOGFILE() will be useless if the target program is the FS sysmodule. */
/* This is because any FS I/O operation *will* lock up the console while FS itself is being debugged. */
/* So we'll just temporarily log data to a char array using LOGBUF(), then write it all out after calling svcCloseHandle(). */
/* However, we must prevent other threads from logging data as well in order to avoid a lock up, so we'll temporarily lock the logfile mutex. */
utilsLogFileMutexControl(true);
/* Retrieve debug handle by program ID. */
if (!memRetrieveDebugHandleFromProgramById(&debug_handle, location->program_id))
{
MEMLOG("Unable to retrieve debug handle for program %016lX!", location->program_id);
goto end;
}
if (is_segment && location->program_id == FS_SYSMODULE_TID)
{
/* If dealing with FS, locate the "real" .text segment, since Atmosphere emuMMC has two. */
do {
rc = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr);
if (R_FAILED(rc))
{
MEMLOG("svcQueryDebugProcessMemory failed for program %016lX! (0x%08X).", location->program_id, rc);
success = false;
goto end;
}
mem_type = (u8)(mem_info.type & 0xFF);
if ((mem_info.perm & Perm_X) && (mem_type == MemType_CodeStatic || mem_type == MemType_CodeMutable)) last_text_addr = mem_info.addr;
addr = (mem_info.addr + mem_info.size);
} while(addr != 0);
addr = last_text_addr;
}
do {
rc = svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr);
if (R_FAILED(rc))
{
MEMLOG("svcQueryDebugProcessMemory failed for program %016lX! (0x%08X).", location->program_id, rc);
success = false;
break;
}
mem_type = (u8)(mem_info.type & 0xFF);
/* Code to allow for bitmasking segments. */
if ((mem_info.perm & Perm_R) && ((!is_segment && mem_type != MemType_Unmapped && mem_type != MemType_Io && mem_type != MemType_ThreadLocal && mem_type != MemType_Reserved && !mem_info.attr) || \
(is_segment && (mem_type == MemType_CodeStatic || mem_type == MemType_CodeMutable) && (((segment <<= 1) >> 1) & location->mask))))
{
/* If location->data == NULL, realloc() will essentially act as a malloc(). */
tmp = realloc(location->data, location->data_size + mem_info.size);
if (!tmp)
{
MEMLOG("Failed to resize segment data buffer to 0x%lX bytes for program %016lX!", location->data_size + mem_info.size, location->program_id);
success = false;
break;
}
location->data = tmp;
tmp = NULL;
rc = svcReadDebugProcessMemory(location->data + location->data_size, debug_handle, mem_info.addr, mem_info.size);
if (R_FAILED(rc))
{
MEMLOG("svcReadDebugProcessMemory failed for program %016lX! (0x%08X).", location->program_id, rc);
success = false;
break;
}
location->data_size += mem_info.size;
}
addr = (mem_info.addr + mem_info.size);
} while(addr != 0 && segment < BIT(3));
end:
/* Close debug handle. */
if (debug_handle != INVALID_HANDLE) svcCloseHandle(debug_handle);
/* Unlock logfile mutex. */
utilsLogFileMutexControl(false);
if (success && (!location->data || !location->data_size))
{
MEMLOG("total size: 0x%lX", location->data_size);
MEMLOG("Unable to locate readable program %016lX memory pages that match the required criteria!", location->program_id);
success = false;
}
if (!success) memFreeMemoryLocation(location);
/* Write log buffer data. This will do nothing if the log buffer length is zero. */
utilsWriteLogBufferToLogFile(g_memLogBuf);
return success;
}
static bool memRetrieveDebugHandleFromProgramById(Handle *out, u64 program_id)
{
if (!out || !program_id)
{
MEMLOG("Invalid parameters!");
return false;
}
Result rc = 0;
u64 pid = 0, d[8] = {0};
Handle debug_handle = INVALID_HANDLE;
u32 i = 0, num_processes = 0;
u64 *pids = NULL;
if (program_id > BOOT_SYSMODULE_TID && program_id != SPL_SYSMODULE_TID)
{
/* If not a kernel process, get PID from pm:dmnt. */
rc = pmdmntGetProcessId(&pid, program_id);
if (R_FAILED(rc))
{
MEMLOG("pmdmntGetProcessId failed for program %016lX! (0x%08X).", program_id, rc);
return false;
}
/* Retrieve debug handle right away. */
rc = svcDebugActiveProcess(&debug_handle, pid);
if (R_FAILED(rc))
{
MEMLOG("svcDebugActiveProcess failed for program %016lX! (0x%08X).", program_id, rc);
return false;
}
} else {
/* Otherwise, query svc for the process list. */
pids = calloc(300, sizeof(u64));
if (!pids)
{
MEMLOG("Failed to allocate memory for PID list!");
return false;
}
rc = svcGetProcessList((s32*)&num_processes, pids, 300);
if (R_FAILED(rc))
{
MEMLOG("svcGetProcessList failed! (0x%08X).", rc);
free(pids);
return false;
}
/* Perform a lookup using the retrieved process list. */
for(i = 0; i < num_processes; i++)
{
/* Retrieve debug handle for the current PID. */
rc = svcDebugActiveProcess(&debug_handle, pids[i]);
if (R_FAILED(rc)) continue;
/* Get debug event using the debug handle. */
/* This will let us know the program ID from the current PID. */
rc = svcGetDebugEvent((u8*)&d, debug_handle);
if (R_SUCCEEDED(rc) && d[2] == program_id) break;
/* No match. Close debug handle and keep looking for our program. */
svcCloseHandle(debug_handle);
debug_handle = INVALID_HANDLE;
}
free(pids);
if (i == num_processes)
{
MEMLOG("Unable to find program %016lX in kernel process list! (0x%08X).", program_id, rc);
return false;
}
}
/* Set output debug handle. */
*out = debug_handle;
return true;
}

62
source/mem.h Normal file
View file

@ -0,0 +1,62 @@
/*
* mem.h
*
* Copyright (c) 2019, shchmue.
* 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 __MEM_H__
#define __MEM_H__
#define FS_SYSMODULE_TID (u64)0x0100000000000000
#define BOOT_SYSMODULE_TID (u64)0x0100000000000005
#define SPL_SYSMODULE_TID (u64)0x0100000000000028
typedef enum {
MemoryProgramSegmentType_Text = BIT(0),
MemoryProgramSegmentType_Rodata = BIT(1),
MemoryProgramSegmentType_Data = BIT(2)
} MemoryProgramSegmentType;
typedef struct {
u64 program_id;
u8 mask; ///< MemoryProgramSegmentType. Used with memRetrieveProgramMemorySegment(). Ignored in memRetrieveFullProgramMemory().
u8 *data;
u64 data_size;
} MemoryLocation;
/// Retrieves memory segment (.text, .rodata, .data) data from a running program.
/// These are memory pages with read permission (Perm_R) enabled and type MemType_CodeStatic or MemType_CodeMutable.
bool memRetrieveProgramMemorySegment(MemoryLocation *location);
/// Retrieves full memory data from a running program.
/// These are any type of memory pages with read permission (Perm_R) enabled.
/// MemType_Unmapped, MemType_Io, MemType_ThreadLocal and MemType_Reserved memory pages are excluded, as well as memory pages with a populated MemoryAttribute value.
bool memRetrieveFullProgramMemory(MemoryLocation *location);
/// Frees a populated MemoryLocation element.
NX_INLINE void memFreeMemoryLocation(MemoryLocation *location)
{
if (!location) return;
if (location->data) free(location->data);
location->data = NULL;
location->data_size = 0;
}
#endif /* __MEM_H__ */

View file

@ -432,7 +432,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
(data_offset + data_size) > hash_target_layer_size || !out)
{
LOGFILE("Invalid parameters!");
goto exit;
goto end;
}
/* Calculate required offsets and sizes. */
@ -455,14 +455,14 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
if (!hash_data_layer)
{
LOGFILE("Unable to allocate 0x%lX bytes buffer for the full HierarchicalSha256 hash data layer!", hash_data_layer_size);
goto exit;
goto end;
}
/* Read full hash data layer. */
if (!_ncaReadFsSection(ctx, hash_data_layer, hash_data_layer_size, hash_data_layer_offset, false))
{
LOGFILE("Failed to read full HierarchicalSha256 hash data layer!");
goto exit;
goto end;
}
/* Allocate memory for the modified hash target layer block. */
@ -470,14 +470,14 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
if (!hash_target_block)
{
LOGFILE("Unable to allocate 0x%lX bytes buffer for the modified HierarchicalSha256 hash target layer block!", hash_target_size);
goto exit;
goto end;
}
/* Read hash target layer block. */
if (!_ncaReadFsSection(ctx, hash_target_block, hash_target_size, hash_target_start_offset, false))
{
LOGFILE("Failed to read HierarchicalSha256 hash target layer block!");
goto exit;
goto end;
}
/* Replace data. */
@ -496,7 +496,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
if (!out->hash_data_layer_patch.data)
{
LOGFILE("Failed to generate encrypted HierarchicalSha256 hash data layer block!");
goto exit;
goto end;
}
/* Reencrypt hash target layer block. */
@ -505,7 +505,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
if (!out->hash_target_layer_patch.data)
{
LOGFILE("Failed to generate encrypted HierarchicalSha256 hash target layer block!");
goto exit;
goto end;
}
/* Recalculate master hash from hash info block. */
@ -519,7 +519,7 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
success = true;
exit:
end:
if (hash_target_block) free(hash_target_block);
if (hash_data_layer) free(hash_data_layer);
@ -549,7 +549,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
(data_offset + data_size) > ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size)
{
LOGFILE("Invalid parameters!");
goto exit;
goto end;
}
/* Process each IVFC layer. */
@ -565,7 +565,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
if (!cur_layer_info->size || !cur_layer_info->block_size || (parent_layer_info && (!parent_layer_info->size || !parent_layer_info->block_size)))
{
LOGFILE("Invalid HierarchicalIntegrity parent/child layer!");
goto exit;
goto end;
}
/* Calculate required offsets and sizes. */
@ -604,7 +604,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
if (!hash_target_block)
{
LOGFILE("Unable to allocate 0x%lX bytes for the HierarchicalIntegrity hash target layer block!");
goto exit;
goto end;
}
/* Adjust hash target layer end offset and size if needed to avoid read errors. */
@ -618,7 +618,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
if (!_ncaReadFsSection(ctx, hash_target_block, hash_target_size, hash_target_start_offset, false))
{
LOGFILE("Failed to read HierarchicalIntegrity hash target layer block!");
goto exit;
goto end;
}
/* Replace hash target layer block data. */
@ -631,14 +631,14 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
if (!hash_data_block)
{
LOGFILE("Unable to allocate 0x%lX bytes for the HierarchicalIntegrity hash data layer block!");
goto exit;
goto end;
}
/* Read hash target layer block. */
if (!_ncaReadFsSection(ctx, hash_data_block, hash_data_size, hash_data_layer_offset + hash_data_start_offset, false))
{
LOGFILE("Failed to read HierarchicalIntegrity hash data layer block!");
goto exit;
goto end;
}
/* Recalculate hashes. */
@ -656,7 +656,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
if (!cur_layer_patch->data)
{
LOGFILE("Failed to generate encrypted HierarchicalIntegrity hash target layer block!");
goto exit;
goto end;
}
/* Free hash target layer block. */
@ -684,7 +684,7 @@ bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void
success = true;
exit:
end:
if (hash_data_block) free(hash_data_block);
if (hash_target_block) free(hash_target_block);
@ -936,7 +936,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
offset >= ctx->section_size || (offset + read_size) > ctx->section_size)
{
LOGFILE("Invalid NCA FS section header parameters!");
goto exit;
goto end;
}
size_t crypt_res = 0;
@ -952,7 +952,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
content_offset >= nca_ctx->content_size || (content_offset + read_size) > nca_ctx->content_size)
{
LOGFILE("Invalid NCA header parameters!");
goto exit;
goto end;
}
/* Optimization for reads from plaintext FS sections or reads that are aligned to the AES-CTR / AES-XTS sector size. */
@ -964,14 +964,14 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset))
{
LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
goto exit;
goto end;
}
/* Return right away if we're dealing with a plaintext FS section. */
if (ctx->encryption_type == NcaEncryptionType_None)
{
ret = true;
goto exit;
goto end;
}
/* Decrypt data. */
@ -983,7 +983,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
if (crypt_res != read_size)
{
LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
goto exit;
goto end;
}
} else
if (ctx->encryption_type == NcaEncryptionType_AesCtr || ctx->encryption_type == NcaEncryptionType_AesCtrEx)
@ -994,7 +994,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
}
ret = true;
goto exit;
goto end;
}
/* Calculate offsets and block sizes. */
@ -1010,7 +1010,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
if (!ncaReadContentFile(nca_ctx, g_ncaCryptoBuffer, chunk_size, block_start_offset))
{
LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num);
goto exit;
goto end;
}
/* Decrypt data. */
@ -1022,7 +1022,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
if (crypt_res != chunk_size)
{
LOGFILE("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num);
goto exit;
goto end;
}
} else
if (ctx->encryption_type == NcaEncryptionType_AesCtr || ctx->encryption_type == NcaEncryptionType_AesCtrEx)
@ -1037,7 +1037,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadFsSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, false) : true);
exit:
end:
if (lock) mutexUnlock(&g_ncaCryptoBufferMutex);
return ret;
@ -1054,7 +1054,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
(offset + read_size) > ctx->section_size)
{
LOGFILE("Invalid NCA FS section header parameters!");
goto exit;
goto end;
}
NcaContext *nca_ctx = (NcaContext*)ctx->nca_ctx;
@ -1067,7 +1067,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
content_offset >= nca_ctx->content_size || (content_offset + read_size) > nca_ctx->content_size)
{
LOGFILE("Invalid NCA header parameters!");
goto exit;
goto end;
}
/* Optimization for reads that are aligned to the AES-CTR sector size. */
@ -1077,7 +1077,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset))
{
LOGFILE("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
goto exit;
goto end;
}
/* Decrypt data */
@ -1086,7 +1086,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
aes128CtrCrypt(&(ctx->ctr_ctx), out, out, read_size);
ret = true;
goto exit;
goto end;
}
/* Calculate offsets and block sizes. */
@ -1102,7 +1102,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
if (!ncaReadContentFile(nca_ctx, g_ncaCryptoBuffer, chunk_size, block_start_offset))
{
LOGFILE("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, ctx->section_num);
goto exit;
goto end;
}
/* Decrypt data. */
@ -1115,7 +1115,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadAesCtrExStorageFromBktrSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, ctr_val, false) : true);
exit:
end:
if (lock) mutexUnlock(&g_ncaCryptoBufferMutex);
return ret;
@ -1133,7 +1133,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
data_offset >= ctx->section_size || (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset)
{
LOGFILE("Invalid NCA FS section header parameters!");
goto exit;
goto end;
}
size_t crypt_res = 0;
@ -1149,7 +1149,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
content_offset >= nca_ctx->content_size || (content_offset + data_size) > nca_ctx->content_size)
{
LOGFILE("Invalid NCA header parameters!");
goto exit;
goto end;
}
/* Optimization for blocks from plaintext FS sections or blocks that are aligned to the AES-CTR / AES-XTS sector size. */
@ -1162,7 +1162,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
if (!out)
{
LOGFILE("Unable to allocate 0x%lX bytes buffer! (aligned).", data_size);
goto exit;
goto end;
}
/* Copy data. */
@ -1177,7 +1177,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
if (crypt_res != data_size)
{
LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", data_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
goto exit;
goto end;
}
} else
if (ctx->encryption_type == NcaEncryptionType_AesCtr)
@ -1191,7 +1191,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
*out_block_offset = content_offset;
success = true;
goto exit;
goto end;
}
/* Calculate block offsets and size. */
@ -1207,14 +1207,14 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
if (!out)
{
LOGFILE("Unable to allocate 0x%lX bytes buffer! (unaligned).", block_size);
goto exit;
goto end;
}
/* Read decrypted data using aligned offset and size. */
if (!_ncaReadFsSection(ctx, out, block_size, block_start_offset, false))
{
LOGFILE("Failed to read decrypted NCA \"%s\" FS section #%u data block!", nca_ctx->content_id_str, ctx->section_num);
goto exit;
goto end;
}
/* Replace plaintext data. */
@ -1229,7 +1229,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
if (crypt_res != block_size)
{
LOGFILE("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", block_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
goto exit;
goto end;
}
} else
if (ctx->encryption_type == NcaEncryptionType_AesCtr)
@ -1244,7 +1244,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
success = true;
exit:
end:
if (!success && out)
{
free(out);

View file

@ -102,7 +102,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_
if (!ncaReadFsSection(nca_fs_ctx, out->dir_table, out->dir_table_size, out->offset + dir_table_offset))
{
LOGFILE("Failed to read RomFS directory entries table!");
goto exit;
goto end;
}
/* Read file entries table. */
@ -112,20 +112,20 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_
if (!out->file_table_size || file_table_offset >= out->size || (file_table_offset + out->file_table_size) > out->size)
{
LOGFILE("Invalid RomFS file entries table!");
goto exit;
goto end;
}
out->file_table = malloc(out->file_table_size);
if (!out->file_table)
{
LOGFILE("Unable to allocate memory for RomFS file entries table!");
goto exit;
goto end;
}
if (!ncaReadFsSection(nca_fs_ctx, out->file_table, out->file_table_size, out->offset + file_table_offset))
{
LOGFILE("Failed to read RomFS file entries table!");
goto exit;
goto end;
}
/* Get file data body offset. */
@ -133,12 +133,12 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_
if (out->body_offset >= out->size)
{
LOGFILE("Invalid RomFS file data body!");
goto exit;
goto end;
}
success = true;
exit:
end:
if (!success) romfsFreeContext(out);
return success;
@ -281,7 +281,7 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
{
LOGFILE("Failed to tokenize input path!");
dir_entry = NULL;
goto out;
goto end;
}
while(pch)
@ -295,7 +295,7 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *
pch = strtok(NULL, "/");
}
out:
end:
if (path_dup) free(path_dup);
return dir_entry;
@ -332,7 +332,7 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const
if (!path_len || !(filename = strrchr(path_dup, '/')))
{
LOGFILE("Invalid input path!");
goto out;
goto end;
}
/* Remove leading slash and adjust filename string pointer. */
@ -343,13 +343,13 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const
if (!(dir_entry = (*path_dup ? romfsGetDirectoryEntryByPath(ctx, path_dup) : romfsGetDirectoryEntryByOffset(ctx, 0))))
{
LOGFILE("Failed to retrieve directory entry!");
goto out;
goto end;
}
/* Retrieve file entry. */
if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename))) LOGFILE("Failed to retrieve file entry by name!");
out:
end:
if (path_dup) free(path_dup);
return file_entry;
@ -397,7 +397,7 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste
if (!(tmp_dir_entries = realloc(dir_entries, (dir_entries_count + 1) * sizeof(RomFileSystemDirectoryEntry*))))
{
LOGFILE("Unable to reallocate directory entries buffer!");
goto out;
goto end;
}
dir_entries = tmp_dir_entries;
@ -406,7 +406,7 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste
if (!(dir_entries[dir_entries_count] = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !dir_entries[dir_entries_count]->name_length)
{
LOGFILE("Failed to retrieve directory entry!");
goto out;
goto end;
}
path_len += (1 + dir_entries[dir_entries_count]->name_length);
@ -416,7 +416,7 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste
if (path_len >= out_path_size)
{
LOGFILE("Output path length exceeds output buffer size!");
goto out;
goto end;
}
/* Generate output path. */
@ -431,7 +431,7 @@ bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSyste
success = true;
out:
end:
if (dir_entries) free(dir_entries);
return success;

View file

@ -117,7 +117,7 @@ bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, s
if (ret != 0)
{
LOGFILE("mbedtls_ctr_drbg_seed failed! (%d).", ret);
goto out;
goto end;
}
/* Parse private key. */
@ -125,7 +125,7 @@ bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, s
if (ret != 0)
{
LOGFILE("mbedtls_pk_parse_key failed! (%d).", ret);
goto out;
goto end;
}
/* Set RSA padding. */
@ -136,14 +136,14 @@ bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, s
if (ret != 0)
{
LOGFILE("mbedtls_pk_sign failed! (%d).", ret);
goto out;
goto end;
}
/* Copy signature to output buffer. */
memcpy(dst, buf, RSA2048_SIG_SIZE);
success = true;
out:
end:
mbedtls_pk_free(&pk);
mbedtls_entropy_free(&entropy);
mbedtls_ctr_drbg_free(&ctr_drbg);

View file

@ -144,7 +144,7 @@ static remap_segment_ctx_t *save_remap_init_segments(remap_header_t *header, rem
if (!seg->entries)
{
LOGFILE("Failed to allocate memory for remap segment entry #%u!", entry_idx);
goto out;
goto end;
}
seg->entries[seg->entry_count++] = &map_entries[entry_idx];
@ -160,7 +160,7 @@ static remap_segment_ctx_t *save_remap_init_segments(remap_header_t *header, rem
if (!ptr)
{
LOGFILE("Failed to allocate memory for remap segment entry #%u!", entry_idx);
goto out;
goto end;
}
memcpy(ptr, seg->entries, sizeof(remap_entry_ctx_t*) * seg->entry_count);
@ -174,7 +174,7 @@ static remap_segment_ctx_t *save_remap_init_segments(remap_header_t *header, rem
success = true;
out:
end:
if (!success)
{
entry_idx = 0;
@ -400,7 +400,7 @@ static bool save_ivfc_storage_init(hierarchical_integrity_verification_storage_c
if (!ctx->level_validities)
{
LOGFILE("Failed to allocate memory for level validities!");
goto out;
goto end;
}
for(u32 i = 1; i < ivfc->num_levels; i++)
@ -417,7 +417,7 @@ static bool save_ivfc_storage_init(hierarchical_integrity_verification_storage_c
if (!level_data->block_validities)
{
LOGFILE("Failed to allocate memory for block validities in IVFC level #%u!", i);
goto out;
goto end;
}
ctx->level_validities[i - 1] = level_data->block_validities;
@ -429,7 +429,7 @@ static bool save_ivfc_storage_init(hierarchical_integrity_verification_storage_c
success = true;
out:
end:
if (!success && ctx->level_validities)
{
free(ctx->level_validities);
@ -909,21 +909,21 @@ u32 save_fs_list_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_
if (!ctx || !key)
{
LOGFILE("Invalid parameters!");
goto out;
goto end;
}
u32 capacity = save_fs_list_get_capacity(ctx);
if (!capacity)
{
LOGFILE("Failed to retrieve FS capacity!");
goto out;
goto end;
}
save_fs_list_entry_t entry;
if (!save_fs_list_read_entry(ctx, ctx->used_list_head_index, &entry))
{
LOGFILE("Failed to read FS entry for initial index %u!", ctx->used_list_head_index);
goto out;
goto end;
}
*prev_index = ctx->used_list_head_index;
@ -951,7 +951,7 @@ u32 save_fs_list_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_
if (!index) LOGFILE("Unable to find FS index from key!");
out:
end:
*prev_index = 0xFFFFFFFF;
return 0xFFFFFFFF;
}
@ -1292,7 +1292,7 @@ bool save_process(save_ctx_t *ctx)
if (fr || br != 0x20)
{
LOGFILE("Failed to read data remap storage entry #%u! (%u).", i, fr);
goto out;
goto end;
}
ctx->data_remap_storage.map_entries[i].physical_offset_end = (ctx->data_remap_storage.map_entries[i].physical_offset + ctx->data_remap_storage.map_entries[i].size);
@ -1304,7 +1304,7 @@ bool save_process(save_ctx_t *ctx)
if (!ctx->data_remap_storage.segments)
{
LOGFILE("Failed to retrieve data remap storage segments!");
goto out;
goto end;
}
/* Initialize duplex storage. */
@ -1316,26 +1316,26 @@ bool save_process(save_ctx_t *ctx)
if (!ctx->duplex_layers[1].data_a)
{
LOGFILE("Failed to allocate memory for data_a block in duplex layer #1!");
goto out;
goto end;
}
if (save_remap_read(&ctx->data_remap_storage, ctx->duplex_layers[1].data_a, ctx->header.layout.duplex_l1_offset_a, ctx->header.layout.duplex_l1_size) != ctx->header.layout.duplex_l1_size)
{
LOGFILE("Failed to read data_a block from duplex layer #1 in data remap storage!");
goto out;
goto end;
}
ctx->duplex_layers[1].data_b = calloc(1, ctx->header.layout.duplex_l1_size);
if (!ctx->duplex_layers[1].data_b)
{
LOGFILE("Failed to allocate memory for data_b block in duplex layer #1!");
goto out;
goto end;
}
if (save_remap_read(&ctx->data_remap_storage, ctx->duplex_layers[1].data_b, ctx->header.layout.duplex_l1_offset_b, ctx->header.layout.duplex_l1_size) != ctx->header.layout.duplex_l1_size)
{
LOGFILE("Failed to read data_b block from duplex layer #1 in data remap storage!");
goto out;
goto end;
}
memcpy(&ctx->duplex_layers[1].info, &ctx->header.duplex_header.layers[1], sizeof(duplex_info_t));
@ -1344,26 +1344,26 @@ bool save_process(save_ctx_t *ctx)
if (!ctx->duplex_layers[2].data_a)
{
LOGFILE("Failed to allocate memory for data_a block in duplex layer #2!");
goto out;
goto end;
}
if (save_remap_read(&ctx->data_remap_storage, ctx->duplex_layers[2].data_a, ctx->header.layout.duplex_data_offset_a, ctx->header.layout.duplex_data_size) != ctx->header.layout.duplex_data_size)
{
LOGFILE("Failed to read data_a block from duplex layer #2 in data remap storage!");
goto out;
goto end;
}
ctx->duplex_layers[2].data_b = calloc(1, ctx->header.layout.duplex_data_size);
if (!ctx->duplex_layers[2].data_b)
{
LOGFILE("Failed to allocate memory for data_b block in duplex layer #2!");
goto out;
goto end;
}
if (save_remap_read(&ctx->data_remap_storage, ctx->duplex_layers[2].data_b, ctx->header.layout.duplex_data_offset_b, ctx->header.layout.duplex_data_size) != ctx->header.layout.duplex_data_size)
{
LOGFILE("Failed to read data_b block from duplex layer #2 in data remap storage!");
goto out;
goto end;
}
memcpy(&ctx->duplex_layers[2].info, &ctx->header.duplex_header.layers[2], sizeof(duplex_info_t));
@ -1374,7 +1374,7 @@ bool save_process(save_ctx_t *ctx)
if (!save_duplex_storage_init(&ctx->duplex_storage.layers[0], &ctx->duplex_layers[1], bitmap, ctx->header.layout.duplex_master_size))
{
LOGFILE("Failed to initialize duplex storage layer #0!");
goto out;
goto end;
}
ctx->duplex_storage.layers[0]._length = ctx->header.layout.duplex_l1_size;
@ -1383,20 +1383,20 @@ bool save_process(save_ctx_t *ctx)
if (!bitmap)
{
LOGFILE("Failed to allocate memory for duplex storage layer #0 bitmap!");
goto out;
goto end;
}
if (save_duplex_storage_read(&ctx->duplex_storage.layers[0], bitmap, 0, ctx->duplex_storage.layers[0]._length) != ctx->duplex_storage.layers[0]._length)
{
LOGFILE("Failed to read duplex storage layer #0 bitmap!");
free(bitmap);
goto out;
goto end;
}
if (!save_duplex_storage_init(&ctx->duplex_storage.layers[1], &ctx->duplex_layers[2], bitmap, ctx->duplex_storage.layers[0]._length))
{
LOGFILE("Failed to initialize duplex storage layer #1!");
goto out;
goto end;
}
ctx->duplex_storage.layers[1]._length = ctx->header.layout.duplex_data_size;
@ -1413,14 +1413,14 @@ bool save_process(save_ctx_t *ctx)
if (!ctx->meta_remap_storage.map_entries)
{
LOGFILE("Failed to allocate memory for meta remap storage entries!");
goto out;
goto end;
}
fr = f_lseek(ctx->file, ctx->header.layout.meta_map_entry_offset);
if (fr || f_tell(ctx->file) != ctx->header.layout.meta_map_entry_offset)
{
LOGFILE("Failed to seek to meta map entry offset 0x%lX in savefile! (%u).", ctx->header.layout.meta_map_entry_offset, fr);
goto out;
goto end;
}
for(u32 i = 0; i < ctx->meta_remap_storage.header->map_entry_count; i++)
@ -1429,7 +1429,7 @@ bool save_process(save_ctx_t *ctx)
if (fr || br != 0x20)
{
LOGFILE("Failed to read meta remap storage entry #%u! (%u).", i, fr);
goto out;
goto end;
}
ctx->meta_remap_storage.map_entries[i].physical_offset_end = (ctx->meta_remap_storage.map_entries[i].physical_offset + ctx->meta_remap_storage.map_entries[i].size);
@ -1440,7 +1440,7 @@ bool save_process(save_ctx_t *ctx)
if (!ctx->meta_remap_storage.segments)
{
LOGFILE("Failed to retrieve meta remap storage segments!");
goto out;
goto end;
}
/* Initialize journal map. */
@ -1448,13 +1448,13 @@ bool save_process(save_ctx_t *ctx)
if (!ctx->journal_map_info.map_storage)
{
LOGFILE("Failed to allocate memory for journal map info!");
goto out;
goto end;
}
if (save_remap_read(&ctx->meta_remap_storage, ctx->journal_map_info.map_storage, ctx->header.layout.journal_map_table_offset, ctx->header.layout.journal_map_table_size) != ctx->header.layout.journal_map_table_size)
{
LOGFILE("Failed to read map storage from journal map info in meta remap storage!");
goto out;
goto end;
}
/* Initialize journal storage. */
@ -1469,7 +1469,7 @@ bool save_process(save_ctx_t *ctx)
if (!ctx->journal_storage.map.entries)
{
LOGFILE("Failed to allocate memory for journal map storage entries!");
goto out;
goto end;
}
u32 *pos = (u32*)ctx->journal_storage.map.map_storage;
@ -1490,7 +1490,7 @@ bool save_process(save_ctx_t *ctx)
if (!save_ivfc_storage_init(&ctx->core_data_ivfc_storage, ctx->header.layout.ivfc_master_hash_offset_a, &ctx->header.data_ivfc_header))
{
LOGFILE("Failed to initialize core IVFC storage!");
goto out;
goto end;
}
/* Initialize FAT storage. */
@ -1500,13 +1500,13 @@ bool save_process(save_ctx_t *ctx)
if (!ctx->fat_storage)
{
LOGFILE("Failed to allocate memory for FAT storage!");
goto out;
goto end;
}
if (save_remap_read(&ctx->meta_remap_storage, ctx->fat_storage, ctx->header.layout.fat_offset, ctx->header.layout.fat_size) != ctx->header.layout.fat_size)
{
LOGFILE("Failed to read FAT storage from meta remap storage!");
goto out;
goto end;
}
} else {
for(u32 i = 0; i < 5; i++) ctx->fat_ivfc_storage.levels[i].save_ctx = ctx;
@ -1514,20 +1514,20 @@ bool save_process(save_ctx_t *ctx)
if (!save_ivfc_storage_init(&ctx->fat_ivfc_storage, ctx->header.layout.fat_ivfc_master_hash_a, &ctx->header.fat_ivfc_header))
{
LOGFILE("Failed to initialize FAT storage! (IVFC).");
goto out;
goto end;
}
ctx->fat_storage = calloc(1, ctx->fat_ivfc_storage._length);
if (!ctx->fat_storage)
{
LOGFILE("Failed to allocate memory for FAT storage! (IVFC).");
goto out;
goto end;
}
if (save_remap_read(&ctx->meta_remap_storage, ctx->fat_storage, ctx->header.fat_ivfc_header.level_headers[ctx->header.fat_ivfc_header.num_levels - 2].logical_offset, ctx->fat_ivfc_storage._length) != ctx->fat_ivfc_storage._length)
{
LOGFILE("Failed to read FAT storage from meta remap storage! (IVFC).");
goto out;
goto end;
}
}
@ -1536,7 +1536,7 @@ bool save_process(save_ctx_t *ctx)
if (save_filesystem_verify(ctx) == VALIDITY_INVALID)
{
LOGFILE("Savefile FS verification failed!");
goto out;
goto end;
}
}
@ -1545,12 +1545,12 @@ bool save_process(save_ctx_t *ctx)
if (!save_filesystem_init(&ctx->save_filesystem_core, ctx->fat_storage, &ctx->header.save_header, &ctx->header.fat_header))
{
LOGFILE("Failed to initialize savefile FS!");
goto out;
goto end;
}
success = true;
out:
end:
if (!success) save_free_contexts(ctx);
return success;
@ -1741,7 +1741,7 @@ save_ctx_t *save_open_savefile(const char *path, u32 action)
if (fr != FR_OK)
{
LOGFILE("Failed to open \"%s\" savefile from BIS System partition! (%u).", path, fr);
goto out;
goto end;
}
open_savefile = true;
@ -1750,7 +1750,7 @@ save_ctx_t *save_open_savefile(const char *path, u32 action)
if (!save_ctx)
{
LOGFILE("Unable to allocate memory for savefile \"%s\" context!", path);
goto out;
goto end;
}
save_ctx->file = save_fd;
@ -1759,7 +1759,7 @@ save_ctx_t *save_open_savefile(const char *path, u32 action)
success = save_process(save_ctx);
if (!success) LOGFILE("Failed to process savefile \"%s\"!", path);
out:
end:
if (!success)
{
if (save_ctx)

View file

@ -146,7 +146,7 @@ bool servicesCheckInitializedServiceByName(const char *name)
bool ret = false;
if (!name || !strlen(name)) goto exit;
if (!name || !strlen(name)) goto end;
size_t name_len = strlen(name);
@ -159,7 +159,7 @@ bool servicesCheckInitializedServiceByName(const char *name)
}
}
exit:
end:
mutexUnlock(&g_servicesMutex);
return ret;

View file

@ -240,20 +240,20 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight
if (!save_get_fat_storage_from_file_entry_by_path(save_ctx, TIK_SAVEFILE_STORAGE_PATH, &fat_storage, &ticket_bin_size))
{
LOGFILE("Failed to locate \"%s\" in ES %s ticket system save!", TIK_SAVEFILE_STORAGE_PATH, titlekey_type == TikTitleKeyType_Common ? "common" : "personalized");
goto out;
goto end;
}
if (ticket_bin_size < SIGNED_TIK_MIN_SIZE || (ticket_bin_size % SIGNED_TIK_MAX_SIZE) != 0)
{
LOGFILE("Invalid size for \"%s\"! (0x%lX).", TIK_SAVEFILE_STORAGE_PATH, ticket_bin_size);
goto out;
goto end;
}
ticket_bin_buf = malloc(buf_size);
if (!ticket_bin_buf)
{
LOGFILE("Unable to allocate 0x%lX bytes block for temporary read buffer!", buf_size);
goto out;
goto end;
}
while(total_br < ticket_bin_size)
@ -265,7 +265,7 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight
{
LOGFILE("Failed to read 0x%lX bytes chunk at offset 0x%lX from \"%s\" in ES %s ticket system save!", buf_size, total_br, TIK_SAVEFILE_STORAGE_PATH, \
(titlekey_type == TikTitleKeyType_Common ? "common" : "personalized"));
goto out;
goto end;
}
total_br += br;
@ -289,20 +289,20 @@ static bool tikRetrieveTicketFromEsSaveDataByRightsId(Ticket *dst, const FsRight
if (!found_tik)
{
LOGFILE("Unable to find a matching ticket entry for the provided Rights ID!");
goto out;
goto end;
}
if (!tikGetTicketTypeAndSize(ticket_bin_buf + i, SIGNED_TIK_MAX_SIZE, &(dst->type), &(dst->size)))
{
LOGFILE("Unable to determine ticket type and size!");
goto out;
goto end;
}
memcpy(dst->data, ticket_bin_buf + i, dst->size);
success = true;
out:
end:
if (ticket_bin_buf) free(ticket_bin_buf);
if (save_ctx) save_close_savefile(save_ctx);

View file

@ -149,14 +149,14 @@ bool usbInitialize(void)
if (!usbAllocateTransferBuffer())
{
LOGFILE("Failed to allocate memory for the USB transfer buffer!");
goto exit;
goto end;
}
/* Initialize USB device interface. */
if (!usbInitializeComms())
{
LOGFILE("Failed to initialize USB device interface!");
goto exit;
goto end;
}
/* Retrieve USB state change kernel event. */
@ -164,7 +164,7 @@ bool usbInitialize(void)
if (!g_usbStateChangeEvent)
{
LOGFILE("Failed to retrieve USB state change kernel event!");
goto exit;
goto end;
}
/* Create usermode exit event. */
@ -175,11 +175,11 @@ bool usbInitialize(void)
/* Create USB detection thread. */
atomic_store(&g_usbDetectionThreadCreated, usbCreateDetectionThread());
if (!atomic_load(&g_usbDetectionThreadCreated)) goto exit;
if (!atomic_load(&g_usbDetectionThreadCreated)) goto end;
ret = true;
exit:
end:
rwlockWriteUnlock(&g_usbDeviceLock);
return ret;
@ -240,7 +240,7 @@ bool usbSendFileProperties(u64 file_size, const char *filename)
!(filename_length = (u32)strlen(filename)) || filename_length >= FS_MAX_PATH)
{
LOGFILE("Invalid parameters!");
goto exit;
goto end;
}
usbPrepareCommandHeader(UsbCommandType_SendFileProperties, (u32)sizeof(UsbCommandSendFileProperties));
@ -263,7 +263,7 @@ bool usbSendFileProperties(u64 file_size, const char *filename)
usbLogStatusDetail(status);
}
exit:
end:
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
rwlockWriteUnlock(&g_usbDeviceLock);
@ -283,7 +283,7 @@ bool usbSendFileData(void *data, u64 data_size)
!data_size || data_size > USB_TRANSFER_BUFFER_SIZE || data_size > g_usbTransferRemainingSize)
{
LOGFILE("Invalid parameters!");
goto exit;
goto end;
}
/* Optimization for buffers that already are page aligned. */
@ -298,7 +298,7 @@ bool usbSendFileData(void *data, u64 data_size)
if (!usbWrite(buf, data_size))
{
LOGFILE("Failed to write 0x%lX bytes long file data chunk!", data_size);
goto exit;
goto end;
}
ret = true;
@ -311,7 +311,7 @@ bool usbSendFileData(void *data, u64 data_size)
{
LOGFILE("Failed to read 0x%lX bytes long status block!", sizeof(UsbStatus));
ret = false;
goto exit;
goto end;
}
cmd_status = (UsbStatus*)g_usbTransferBuffer;
@ -320,14 +320,14 @@ bool usbSendFileData(void *data, u64 data_size)
{
LOGFILE("Invalid status block magic word!");
ret = false;
goto exit;
goto end;
}
ret = (cmd_status->status == UsbStatusType_Success);
if (!ret) usbLogStatusDetail(cmd_status->status);
}
exit:
end:
if (!ret) g_usbTransferRemainingSize = 0;
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
@ -550,13 +550,13 @@ static bool usbInitializeComms(void)
Result rc = 0;
bool ret = (g_usbDeviceInterfaceInitialized && g_usbDeviceInterface.initialized);
if (ret) goto exit;
if (ret) goto end;
rc = usbDsInitialize();
if (R_FAILED(rc))
{
LOGFILE("usbDsInitialize failed! (0x%08X).", rc);
goto exit;
goto end;
}
if (hosversionAtLeast(5,0,0))
@ -673,7 +673,7 @@ static bool usbInitializeComms(void)
if (R_FAILED(rc)) LOGFILE("usbDsSetVidPidBcd failed! (0x%08X).", rc);
}
if (R_FAILED(rc)) goto exit;
if (R_FAILED(rc)) goto end;
/* Initialize USB device interface. */
rwlockWriteLock(&(g_usbDeviceInterface.lock));
@ -689,7 +689,7 @@ static bool usbInitializeComms(void)
if (!dev_iface_init)
{
LOGFILE("Failed to initialize USB device interface!");
goto exit;
goto end;
}
if (hosversionAtLeast(5,0,0))
@ -698,13 +698,13 @@ static bool usbInitializeComms(void)
if (R_FAILED(rc))
{
LOGFILE("usbDsEnable failed! (0x%08X).", rc);
goto exit;
goto end;
}
}
ret = g_usbDeviceInterfaceInitialized = true;
exit:
end:
if (!ret) usbCloseComms();
return ret;

View file

@ -66,52 +66,52 @@ bool utilsInitializeResources(void)
mutexLock(&g_resourcesMutex);
bool ret = g_resourcesInitialized;
if (ret) goto exit;
if (ret) goto end;
/* Initialize needed services. */
if (!servicesInitialize())
{
LOGFILE("Failed to initialize needed services!");
goto exit;
goto end;
}
/* Initialize USB interface. */
if (!usbInitialize())
{
LOGFILE("Failed to initialize USB interface!");
goto exit;
goto end;
}
/* Load NCA keyset. */
if (!keysLoadNcaKeyset())
{
LOGFILE("Failed to load NCA keyset!");
goto exit;
goto end;
}
/* Allocate NCA crypto buffer. */
if (!ncaAllocateCryptoBuffer())
{
LOGFILE("Unable to allocate memory for NCA crypto buffer!");
goto exit;
goto end;
}
/* Initialize gamecard interface. */
if (!gamecardInitialize())
{
LOGFILE("Failed to initialize gamecard interface!");
goto exit;
goto end;
}
/* Retrieve SD card FsFileSystem element. */
if (!(g_sdCardFileSystem = fsdevGetDeviceFileSystem("sdmc:")))
{
LOGFILE("fsdevGetDeviceFileSystem failed!");
goto exit;
goto end;
}
/* Mount eMMC BIS System partition. */
if (!utilsMountEmmcBisSystemPartitionStorage()) goto exit;
if (!utilsMountEmmcBisSystemPartitionStorage()) goto end;
/* Get applet type. */
g_programAppletType = appletGetAppletType();
@ -136,7 +136,7 @@ bool utilsInitializeResources(void)
ret = g_resourcesInitialized = true;
exit:
end:
mutexUnlock(&g_resourcesMutex);
return ret;
@ -210,15 +210,17 @@ void utilsWaitForButtonPress(u64 flag)
}
}
void utilsWriteLogMessage(const char *func_name, const char *fmt, ...)
void utilsWriteMessageToLogFile(const char *func_name, const char *fmt, ...)
{
if (!func_name || !strlen(func_name) || !fmt || !strlen(fmt)) return;
mutexLock(&g_logfileMutex);
va_list args;
FILE *logfile = NULL;
logfile = fopen(LOGFILE_PATH, "a+");
if (!logfile) goto out;
if (!logfile) goto end;
time_t now = time(NULL);
struct tm *ts = localtime(&now);
@ -232,10 +234,58 @@ void utilsWriteLogMessage(const char *func_name, const char *fmt, ...)
fprintf(logfile, "\r\n");
fclose(logfile);
out:
end:
mutexUnlock(&g_logfileMutex);
}
void utilsWriteMessageToLogBuffer(char *dst, size_t dst_size, const char *func_name, const char *fmt, ...)
{
if (!dst || !dst_size || !func_name || !strlen(func_name) || !fmt || !strlen(fmt)) return;
va_list args;
time_t now = time(NULL);
struct tm *ts = localtime(&now);
char msg[512] = {0};
size_t msg_len = 0, dst_len = strlen(dst);
snprintf(msg, sizeof(msg), "%d-%02d-%02d %02d:%02d:%02d -> %s: ", ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, func_name);
msg_len = strlen(msg);
va_start(args, fmt);
vsnprintf(msg + msg_len, sizeof(msg) - msg_len, fmt, args);
va_end(args);
msg_len = strlen(msg);
if ((dst_size - dst_len) > (msg_len + 2)) snprintf(dst + dst_len, dst_size - dst_len, "%s\r\n", msg);
}
void utilsWriteLogBufferToLogFile(const char *src)
{
if (!src || !strlen(src)) return;
mutexLock(&g_logfileMutex);
FILE *logfile = fopen(LOGFILE_PATH, "a+");
if (!logfile) goto end;
fprintf(logfile, "%s", src);
fclose(logfile);
end:
mutexUnlock(&g_logfileMutex);
}
void utilsLogFileMutexControl(bool lock)
{
if (lock)
{
mutexLock(&g_logfileMutex);
} else {
mutexUnlock(&g_logfileMutex);
}
}
void utilsReplaceIllegalCharacters(char *str, bool ascii_only)
{
size_t strsize = 0;

View file

@ -40,12 +40,13 @@
#define APP_BASE_PATH "sdmc:/switch/nxdumptool/"
#define LOGFILE_PATH APP_BASE_PATH "nxdumptool.log"
#define LOGFILE(fmt, ...) utilsWriteLogMessage(__func__, fmt, ##__VA_ARGS__)
#define MEMBER_SIZE(type, member) sizeof(((type*)NULL)->member)
#define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0])))
#define LOGFILE(fmt, ...) utilsWriteMessageToLogFile(__func__, fmt, ##__VA_ARGS__)
#define LOGBUF(dst, dst_size, fmt, ...) utilsWriteMessageToLogBuffer(dst, dst_size, __func__, fmt, ##__VA_ARGS__)
#define ALIGN_DOWN(x, y) ((x) & ~((y) - 1))
#define ALIGN_UP(x, y) ((((y) - 1) + (x)) & ~((y) - 1))
#define IS_ALIGNED(x, y) (((x) & ((y) - 1)) == 0)
@ -83,7 +84,10 @@ void utilsCloseResources(void);
u64 utilsReadInput(u8 input_type);
void utilsWaitForButtonPress(u64 flag);
void utilsWriteLogMessage(const char *func_name, const char *fmt, ...);
void utilsWriteMessageToLogFile(const char *func_name, const char *fmt, ...);
void utilsWriteMessageToLogBuffer(char *dst, size_t dst_size, const char *func_name, const char *fmt, ...);
void utilsWriteLogBufferToLogFile(const char *src);
void utilsLogFileMutexControl(bool lock);
void utilsReplaceIllegalCharacters(char *str, bool ascii_only);