From 73861bc52f3bd102c048cefa84be7ae87f9c8daa Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Mon, 13 Jul 2020 02:36:17 -0400 Subject: [PATCH] Normalize goto tag names + support for gamecard key area. Big thanks to SciresM. --- source/bktr.c | 28 ++--- source/cert.c | 20 ++-- source/gamecard.c | 197 ++++++++++++++++++++++++---------- source/gamecard.h | 19 +++- source/keys.c | 268 ++++++---------------------------------------- source/main.c | 22 ++++ source/mem.c | 262 ++++++++++++++++++++++++++++++++++++++++++++ source/mem.h | 62 +++++++++++ source/nca.c | 80 +++++++------- source/romfs.c | 30 +++--- source/rsa.c | 8 +- source/save.c | 86 +++++++-------- source/services.c | 4 +- source/tik.c | 14 +-- source/usb.c | 36 +++---- source/utils.c | 74 ++++++++++--- source/utils.h | 10 +- 17 files changed, 758 insertions(+), 462 deletions(-) create mode 100644 source/mem.c create mode 100644 source/mem.h diff --git a/source/bktr.c b/source/bktr.c index db1a3b1..533c1ad 100644 --- a/source/bktr.c +++ b/source/bktr.c @@ -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; diff --git a/source/cert.c b/source/cert.c index 9897d97..3250ada 100644 --- a/source/cert.c +++ b/source/cert.c @@ -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); diff --git a/source/gamecard.c b/source/gamecard.c index 5d4ba54..f6f7643 100644 --- a/source/gamecard.c +++ b/source/gamecard.c @@ -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; diff --git a/source/gamecard.h b/source/gamecard.h index a9b711c..0156f83 100644 --- a/source/gamecard.h +++ b/source/gamecard.h @@ -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. diff --git a/source/keys.c b/source/keys.c index 2d11ed4..e6d07e4 100644 --- a/source/keys.c +++ b/source/keys.c @@ -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; } diff --git a/source/main.c b/source/main.c index 5ccaa8e..8d32b99 100644 --- a/source/main.c +++ b/source/main.c @@ -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); diff --git a/source/mem.c b/source/mem.c new file mode 100644 index 0000000..d4143a8 --- /dev/null +++ b/source/mem.c @@ -0,0 +1,262 @@ +/* + * mem.c + * + * Copyright (c) 2019, shchmue. + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * nxdumptool is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "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; +} diff --git a/source/mem.h b/source/mem.h new file mode 100644 index 0000000..64cfd02 --- /dev/null +++ b/source/mem.h @@ -0,0 +1,62 @@ +/* + * mem.h + * + * Copyright (c) 2019, shchmue. + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * nxdumptool is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifndef __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__ */ diff --git a/source/nca.c b/source/nca.c index c6bed1f..67c7319 100644 --- a/source/nca.c +++ b/source/nca.c @@ -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); diff --git a/source/romfs.c b/source/romfs.c index b6c1484..2f4b4bb 100644 --- a/source/romfs.c +++ b/source/romfs.c @@ -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; diff --git a/source/rsa.c b/source/rsa.c index dc82ad1..66dfa3f 100644 --- a/source/rsa.c +++ b/source/rsa.c @@ -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); diff --git a/source/save.c b/source/save.c index 1c56f5b..78900e7 100644 --- a/source/save.c +++ b/source/save.c @@ -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) diff --git a/source/services.c b/source/services.c index 79b1329..e02af97 100644 --- a/source/services.c +++ b/source/services.c @@ -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; diff --git a/source/tik.c b/source/tik.c index 9b51318..8cfbaed 100644 --- a/source/tik.c +++ b/source/tik.c @@ -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); diff --git a/source/usb.c b/source/usb.c index 8fb9a3d..a2b84c8 100644 --- a/source/usb.c +++ b/source/usb.c @@ -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; diff --git a/source/utils.c b/source/utils.c index 3dc8f4b..cc12b27 100644 --- a/source/utils.c +++ b/source/utils.c @@ -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; diff --git a/source/utils.h b/source/utils.h index f865238..877459e 100644 --- a/source/utils.h +++ b/source/utils.h @@ -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);