mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 12:12:02 +00:00
Add support for dumping full GameCardSecurityInformation (#105)
* Add support for dumping full GameCardSecurityInformation * Add support for dumping LAFW * Clear out ASIC session hash data Co-authored-by: Pablo Curiel <pabloacurielz@gmail.com>
This commit is contained in:
parent
4929330e32
commit
e79b03afeb
3 changed files with 283 additions and 103 deletions
|
@ -74,8 +74,10 @@ static void consolePrint(const char *text, ...);
|
|||
static u32 menuGetElementCount(const Menu *menu);
|
||||
|
||||
static bool sendGameCardKeyAreaViaUsb(void);
|
||||
static bool sendGameCardSpecificDataViaUsb(void);
|
||||
static bool sendGameCardCertificateViaUsb(void);
|
||||
static bool sendGameCardImageViaUsb(void);
|
||||
static bool sendConsoleLafwViaUsb(void);
|
||||
|
||||
static void changeKeyAreaOption(u32 idx);
|
||||
static void changeCertificateOption(u32 idx);
|
||||
|
@ -150,23 +152,35 @@ static Menu g_xciMenu = {
|
|||
|
||||
static MenuElement *g_rootMenuElements[] = {
|
||||
&(MenuElement){
|
||||
.str = "dump key area (initial data)",
|
||||
.str = "dump gamecard initial data",
|
||||
.child_menu = NULL,
|
||||
.task_func = &sendGameCardKeyAreaViaUsb,
|
||||
.element_options = NULL
|
||||
},
|
||||
&(MenuElement){
|
||||
.str = "dump certificate",
|
||||
.str = "dump gamecard specific data",
|
||||
.child_menu = NULL,
|
||||
.task_func = &sendGameCardSpecificDataViaUsb,
|
||||
.element_options = NULL
|
||||
},
|
||||
&(MenuElement){
|
||||
.str = "dump gamecard certificate",
|
||||
.child_menu = NULL,
|
||||
.task_func = &sendGameCardCertificateViaUsb,
|
||||
.element_options = NULL
|
||||
},
|
||||
&(MenuElement){
|
||||
.str = "dump xci",
|
||||
.str = "dump gamecard xci",
|
||||
.child_menu = &g_xciMenu,
|
||||
.task_func = NULL,
|
||||
.element_options = NULL
|
||||
},
|
||||
&(MenuElement){
|
||||
.str = "dump console LAFW",
|
||||
.child_menu = NULL,
|
||||
.task_func = &sendConsoleLafwViaUsb,
|
||||
.element_options = NULL
|
||||
},
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -454,6 +468,24 @@ static bool dumpGameCardKeyArea(GameCardKeyArea *out)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool dumpGameCardSecurityInformation(GameCardSecurityInformation *out)
|
||||
{
|
||||
if (!out)
|
||||
{
|
||||
consolePrint("invalid parameters to dump gamecard security information!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gamecardGetSecurityInformation(out))
|
||||
{
|
||||
consolePrint("failed to get gamecard security information\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
consolePrint("get gamecard security information ok\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sendGameCardKeyAreaViaUsb(void)
|
||||
{
|
||||
if (!waitForGameCardAndUsb()) return false;
|
||||
|
@ -489,6 +521,38 @@ end:
|
|||
return success;
|
||||
}
|
||||
|
||||
static bool sendGameCardSpecificDataViaUsb(void)
|
||||
{
|
||||
if (!waitForGameCardAndUsb()) return false;
|
||||
|
||||
utilsSetLongRunningProcessState(true);
|
||||
|
||||
GameCardSecurityInformation gc_security_information = {0};
|
||||
bool success = false;
|
||||
u32 crc = 0;
|
||||
char *filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, TitleFileNameIllegalCharReplaceType_IllegalFsChars);
|
||||
|
||||
if (!dumpGameCardSecurityInformation(&gc_security_information) || !filename) goto end;
|
||||
|
||||
crc = crc32Calculate(&(gc_security_information.specific_data), sizeof(GameCardSpecificData));
|
||||
snprintf(path, MAX_ELEMENTS(path), "%s (Specific Data) (%08X).bin", filename, crc);
|
||||
|
||||
if (!sendFileData(path, &(gc_security_information.specific_data), sizeof(GameCardSpecificData))) goto end;
|
||||
|
||||
printf("successfully sent specific data as \"%s\"\n", path);
|
||||
success = true;
|
||||
|
||||
end:
|
||||
if (filename) free(filename);
|
||||
|
||||
utilsSetLongRunningProcessState(false);
|
||||
|
||||
consolePrint("press any button to continue");
|
||||
utilsWaitForButtonPress(0);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool sendGameCardCertificateViaUsb(void)
|
||||
{
|
||||
if (!waitForGameCardAndUsb()) return false;
|
||||
|
@ -527,6 +591,53 @@ end:
|
|||
return success;
|
||||
}
|
||||
|
||||
static bool sendConsoleLafwViaUsb(void)
|
||||
{
|
||||
if (!waitForGameCardAndUsb()) return false;
|
||||
|
||||
utilsSetLongRunningProcessState(true);
|
||||
|
||||
LotusAsicFirmware lafw = {0};
|
||||
bool success = false;
|
||||
u32 crc = 0;
|
||||
|
||||
if (!gamecardGetLotusAsicFirmware(&lafw))
|
||||
{
|
||||
consolePrint("failed to get console LAFW\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
u32 lafw_version = gamecardConvertLotusAsicFirmwareVersionBitmask(&lafw);
|
||||
|
||||
const char* filename = "";
|
||||
switch(lafw.fw_type) {
|
||||
case LotusAsicFirmwareType_ReadFw: filename = "ReadFw"; break;
|
||||
case LotusAsicFirmwareType_ReadDevFw: filename = "ReadDevFw"; break;
|
||||
case LotusAsicFirmwareType_WriterFw: filename = "WriterFw"; break;
|
||||
case LotusAsicFirmwareType_RmaFw: filename = "RmaFw"; break;
|
||||
default: filename = "Unknown"; break;
|
||||
}
|
||||
|
||||
consolePrint("get console LAFW ok\n");
|
||||
|
||||
crc = crc32Calculate(&lafw, sizeof(LotusAsicFirmware));
|
||||
snprintf(path, MAX_ELEMENTS(path), "LAFW (%s) (v%d) (%08X).bin", filename, lafw_version, crc);
|
||||
|
||||
if (!sendFileData(path, &lafw, sizeof(LotusAsicFirmware))) goto end;
|
||||
|
||||
printf("successfully sent lafw as \"%s\"\n", path);
|
||||
success = true;
|
||||
|
||||
end:
|
||||
utilsSetLongRunningProcessState(false);
|
||||
|
||||
consolePrint("press any button to continue");
|
||||
utilsWaitForButtonPress(0);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
static bool sendGameCardImageViaUsb(void)
|
||||
{
|
||||
if (!waitForGameCardAndUsb()) return false;
|
||||
|
|
|
@ -86,6 +86,30 @@ typedef struct {
|
|||
|
||||
NXDT_ASSERT(GameCardKeyArea, 0x1000);
|
||||
|
||||
typedef struct {
|
||||
u32 asic_security_mode;
|
||||
u32 asic_status;
|
||||
u32 cardid1;
|
||||
u32 cardid2;
|
||||
u8 card_uid[0x40];
|
||||
u8 reserved[0x190];
|
||||
u8 asic_session_hash[0x20];
|
||||
} GameCardSpecificData;
|
||||
|
||||
NXDT_ASSERT(GameCardSpecificData, 0x200);
|
||||
|
||||
/// This struct is returned by Lotus command "ChangeToSecureMode" (0xF)
|
||||
/// A copy of the gamecard header without the RSA-2048 signature and a plaintext GameCardInfo precedes this struct in FS program memory.
|
||||
typedef struct {
|
||||
GameCardSpecificData specific_data;
|
||||
FsGameCardCertificate certificate;
|
||||
u8 ffpadding[0x200];
|
||||
GameCardInitialData initial_data;
|
||||
} GameCardSecurityInformation;
|
||||
|
||||
NXDT_ASSERT(GameCardSecurityInformation, 0x800);
|
||||
|
||||
|
||||
typedef enum {
|
||||
GameCardKekIndex_Version0 = 0,
|
||||
GameCardKekIndex_VersionForDev = 1
|
||||
|
@ -212,6 +236,40 @@ typedef enum {
|
|||
GameCardHashFileSystemPartitionType_Count = 7 ///< Not a real value.
|
||||
} GameCardHashFileSystemPartitionType;
|
||||
|
||||
typedef enum {
|
||||
LotusAsicFirmwareType_ReadFw = 0xFF,
|
||||
LotusAsicFirmwareType_ReadDevFw = 0xFFFF,
|
||||
LotusAsicFirmwareType_WriterFw = 0xFFFFFF,
|
||||
LotusAsicFirmwareType_RmaFw = 0xFFFFFFFF
|
||||
} LotusAsicFirmwareType;
|
||||
|
||||
typedef enum {
|
||||
LotusAsicDeviceType_Test = 0,
|
||||
LotusAsicDeviceType_Dev = 1,
|
||||
LotusAsicDeviceType_Prod = 2,
|
||||
LotusAsicDeviceType_Prod2Dev = 3
|
||||
} LotusAsicDeviceType;
|
||||
|
||||
typedef struct {
|
||||
u8 signature[0x100];
|
||||
u32 magic; ///< "LAFW".
|
||||
u32 fw_type; ///< LotusAsicFirmwareType.
|
||||
u8 reserved_1[0x8];
|
||||
struct {
|
||||
u64 fw_version : 62; ///< Stored using a bitmask.
|
||||
u64 device_type : 2; ///< LotusAsicDeviceType.
|
||||
};
|
||||
u32 data_size;
|
||||
u8 reserved_2[0x4];
|
||||
u8 data_iv[AES_128_KEY_SIZE];
|
||||
char placeholder_str[0x10]; ///< "IDIDIDIDIDIDIDID".
|
||||
u8 reserved_3[0x40];
|
||||
u8 data[0x7680];
|
||||
} LotusAsicFirmware;
|
||||
|
||||
NXDT_ASSERT(LotusAsicFirmware, 0x7800);
|
||||
|
||||
|
||||
/// Initializes data needed to access raw gamecard storage areas.
|
||||
/// Also spans a background thread to automatically detect gamecard status changes and to cache data from the inserted gamecard.
|
||||
bool gamecardInitialize(void);
|
||||
|
@ -236,6 +294,10 @@ bool gamecardReadStorage(void *out, u64 read_size, u64 offset);
|
|||
/// This area can't be read using gamecardReadStorage().
|
||||
bool gamecardGetKeyArea(GameCardKeyArea *out);
|
||||
|
||||
/// Fills the provided GameCardSecurityInformation pointer
|
||||
/// This area can't be read using gamecardReadStorage().
|
||||
bool gamecardGetSecurityInformation(GameCardSecurityInformation* out);
|
||||
|
||||
/// Fills the provided FsGameCardIdSet pointer.
|
||||
/// This area can't be read using gamecardReadStorage().
|
||||
bool gamecardGetIdSet(FsGameCardIdSet *out);
|
||||
|
@ -280,6 +342,12 @@ const char *gamecardGetRequiredHosVersionString(u64 fw_version);
|
|||
/// Returns NULL if the provided value is out of range.
|
||||
const char *gamecardGetCompatibilityTypeString(u8 compatibility_type);
|
||||
|
||||
/// Fills the provided LotusAsicFirmware pointer with FS's current LAFW blob.
|
||||
bool gamecardGetLotusAsicFirmware(LotusAsicFirmware* out);
|
||||
|
||||
/// Convert LAFW version bitmask to an integer
|
||||
u32 gamecardConvertLotusAsicFirmwareVersionBitmask(LotusAsicFirmware* lafw);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -56,52 +56,6 @@ typedef enum {
|
|||
GameCardCapacity_32GiB = BIT_LONG(35)
|
||||
} GameCardCapacity;
|
||||
|
||||
/// Only kept for documentation purposes, not really used.
|
||||
/// A copy of the gamecard header without the RSA-2048 signature and a plaintext GameCardInfo precedes this struct in FS program memory.
|
||||
typedef struct {
|
||||
u32 memory_interface_mode;
|
||||
u32 asic_status;
|
||||
u8 card_id_area[0x48];
|
||||
u8 reserved[0x1B0];
|
||||
FsGameCardCertificate certificate;
|
||||
GameCardInitialData initial_data;
|
||||
} GameCardSecurityInformation;
|
||||
|
||||
NXDT_ASSERT(GameCardSecurityInformation, 0x600);
|
||||
|
||||
typedef enum {
|
||||
LotusAsicFirmwareType_ReadFw = 0xFF,
|
||||
LotusAsicFirmwareType_ReadDevFw = 0xFFFF,
|
||||
LotusAsicFirmwareType_WriterFw = 0xFFFFFF,
|
||||
LotusAsicFirmwareType_RmaFw = 0xFFFFFFFF
|
||||
} LotusAsicFirmwareType;
|
||||
|
||||
typedef enum {
|
||||
LotusAsicDeviceType_Test = 0,
|
||||
LotusAsicDeviceType_Dev = 1,
|
||||
LotusAsicDeviceType_Prod = 2,
|
||||
LotusAsicDeviceType_Prod2Dev = 3
|
||||
} LotusAsicDeviceType;
|
||||
|
||||
typedef struct {
|
||||
u8 signature[0x100];
|
||||
u32 magic; ///< "LAFW".
|
||||
u32 fw_type; ///< LotusAsicFirmwareType.
|
||||
u8 reserved_1[0x8];
|
||||
struct {
|
||||
u64 fw_version : 62; ///< Stored using a bitmask.
|
||||
u64 device_type : 2; ///< LotusAsicDeviceType.
|
||||
};
|
||||
u32 data_size;
|
||||
u8 reserved_2[0x4];
|
||||
u8 data_iv[AES_128_KEY_SIZE];
|
||||
char placeholder_str[0x10]; ///< "IDIDIDIDIDIDIDID".
|
||||
u8 reserved_3[0x40];
|
||||
u8 data[0x7680];
|
||||
} LotusAsicFirmwareBlob;
|
||||
|
||||
NXDT_ASSERT(LotusAsicFirmwareBlob, 0x7800);
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
static Mutex g_gameCardMutex = 0;
|
||||
|
@ -180,7 +134,7 @@ static bool gamecardReadHeader(void);
|
|||
|
||||
static bool _gamecardGetDecryptedCardInfoArea(void);
|
||||
|
||||
static bool gamecardReadInitialData(GameCardKeyArea *out);
|
||||
static bool gamecardReadSecurityInformation(GameCardSecurityInformation *out);
|
||||
|
||||
static bool gamecardGetHandleAndStorage(u32 partition);
|
||||
NX_INLINE void gamecardCloseHandle(void);
|
||||
|
@ -336,14 +290,30 @@ bool gamecardReadStorage(void *out, u64 read_size, u64 offset)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Read full FS program memory to retrieve the GameCardInitialData block, which is part of the GameCardKeyArea block. */
|
||||
/* In FS program memory, this is stored as part of the GameCardSecurityInformation struct, which is returned by Lotus command "ChangeToSecureMode" (0xF). */
|
||||
/* This means it is only available *after* the gamecard secure area has been mounted, which is taken care of in gamecardReadInitialData(). */
|
||||
/* The GameCardSecurityInformation struct is only kept for documentation purposes. It isn't used at all to retrieve the GameCardInitialData block. */
|
||||
/* Read full FS program memory by calling gamecardGetSecurityInformation, and then extracts out the GameCardInitialData block. */
|
||||
bool gamecardGetKeyArea(GameCardKeyArea *out)
|
||||
{
|
||||
GameCardSecurityInformation securityInformation;
|
||||
|
||||
/* Clear output. */
|
||||
memset(out, 0, sizeof(GameCardKeyArea));
|
||||
|
||||
if (!gamecardGetSecurityInformation(&securityInformation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&out->initial_data, &securityInformation.initial_data, sizeof(GameCardInitialData));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Read full FS program memory to retrieve the GameCardSecurityInformation block. */
|
||||
/* In FS program memory, this is returned by Lotus command "ChangeToSecureMode" (0xF). */
|
||||
/* This means it is only available *after* the gamecard secure area has been mounted, which is taken care of in gamecardReadSecurityInformation(). */
|
||||
bool gamecardGetSecurityInformation(GameCardSecurityInformation *out)
|
||||
{
|
||||
bool ret = false;
|
||||
SCOPED_LOCK(&g_gameCardMutex) ret = gamecardReadInitialData(out);
|
||||
SCOPED_LOCK(&g_gameCardMutex) ret = gamecardReadSecurityInformation(out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -559,66 +529,93 @@ const char *gamecardGetCompatibilityTypeString(u8 compatibility_type)
|
|||
return (compatibility_type < GameCardCompatibilityType_Count ? g_gameCardCompatibilityTypeStrings[compatibility_type] : NULL);
|
||||
}
|
||||
|
||||
bool gamecardGetLotusAsicFirmware(LotusAsicFirmware* out)
|
||||
{
|
||||
bool ret = false;
|
||||
SCOPED_LOCK(&g_gameCardMutex)
|
||||
{
|
||||
bool found = false, dev_unit = utilsIsDevelopmentUnit();
|
||||
|
||||
/* Temporarily set the segment mask to .data. */
|
||||
g_fsProgramMemory.mask = MemoryProgramSegmentType_Data;
|
||||
|
||||
/* Retrieve full FS program memory dump. */
|
||||
ret = memRetrieveProgramMemorySegment(&g_fsProgramMemory);
|
||||
|
||||
/* Clear segment mask. */
|
||||
g_fsProgramMemory.mask = 0;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
LOG_MSG("Failed to retrieve FS .data segment dump!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Look for the LAFW ReadFw blob in the FS .data memory dump. */
|
||||
for(u64 offset = 0; offset < g_fsProgramMemory.data_size; offset++)
|
||||
{
|
||||
if ((g_fsProgramMemory.data_size - offset) < sizeof(LotusAsicFirmware)) break;
|
||||
|
||||
LotusAsicFirmware *lafw_blob = (LotusAsicFirmware*)(g_fsProgramMemory.data + offset);
|
||||
u32 magic = __builtin_bswap32(lafw_blob->magic), fw_type = lafw_blob->fw_type;
|
||||
|
||||
if (magic == LAFW_MAGIC && ((!dev_unit && fw_type == LotusAsicFirmwareType_ReadFw) || (dev_unit && fw_type == LotusAsicFirmwareType_ReadDevFw)))
|
||||
{
|
||||
/* Jackpot. */
|
||||
memcpy(out, lafw_blob, sizeof(LotusAsicFirmware));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
LOG_MSG("Unable to locate Lotus %s blob in FS .data segment!", dev_unit ? "ReadDevFw" : "ReadFw");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Update flag. */
|
||||
ret = true;
|
||||
|
||||
end:
|
||||
memFreeMemoryLocation(&g_fsProgramMemory);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 gamecardConvertLotusAsicFirmwareVersionBitmask(LotusAsicFirmware* lafw) {
|
||||
u64 fw_version_bitmask = lafw->fw_version;
|
||||
u32 fw_version = 0;
|
||||
|
||||
while(fw_version_bitmask)
|
||||
{
|
||||
fw_version += (fw_version_bitmask & 1);
|
||||
fw_version_bitmask >>= 1;
|
||||
}
|
||||
return fw_version;
|
||||
}
|
||||
|
||||
static bool gamecardGetLotusAsicFirmwareVersion(void)
|
||||
{
|
||||
u64 fw_version = 0;
|
||||
bool ret = false, found = false, dev_unit = utilsIsDevelopmentUnit();
|
||||
|
||||
/* Temporarily set the segment mask to .data. */
|
||||
g_fsProgramMemory.mask = MemoryProgramSegmentType_Data;
|
||||
|
||||
/* Retrieve full FS program memory dump. */
|
||||
ret = memRetrieveProgramMemorySegment(&g_fsProgramMemory);
|
||||
|
||||
/* Clear segment mask. */
|
||||
g_fsProgramMemory.mask = 0;
|
||||
bool ret = false;
|
||||
LotusAsicFirmware lafw;
|
||||
|
||||
/* Retrieve LAFW. */
|
||||
ret = gamecardGetLotusAsicFirmware(&lafw);
|
||||
if (!ret)
|
||||
{
|
||||
LOG_MSG("Failed to retrieve FS .data segment dump!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Look for the LAFW ReadFw blob in the FS .data memory dump. */
|
||||
for(u64 offset = 0; offset < g_fsProgramMemory.data_size; offset++)
|
||||
{
|
||||
if ((g_fsProgramMemory.data_size - offset) < sizeof(LotusAsicFirmwareBlob)) break;
|
||||
|
||||
LotusAsicFirmwareBlob *lafw_blob = (LotusAsicFirmwareBlob*)(g_fsProgramMemory.data + offset);
|
||||
u32 magic = __builtin_bswap32(lafw_blob->magic), fw_type = lafw_blob->fw_type;
|
||||
|
||||
if (magic == LAFW_MAGIC && ((!dev_unit && fw_type == LotusAsicFirmwareType_ReadFw) || (dev_unit && fw_type == LotusAsicFirmwareType_ReadDevFw)))
|
||||
{
|
||||
/* Jackpot. */
|
||||
fw_version = lafw_blob->fw_version;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
LOG_MSG("Unable to locate Lotus %s blob in FS .data segment!", dev_unit ? "ReadDevFw" : "ReadFw");
|
||||
LOG_MSG("Failed to retrieve Lotus Asic Firmware!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Convert LAFW version bitmask to an integer. */
|
||||
g_lafwVersion = 0;
|
||||
|
||||
while(fw_version)
|
||||
{
|
||||
g_lafwVersion += (fw_version & 1);
|
||||
fw_version >>= 1;
|
||||
}
|
||||
|
||||
g_lafwVersion = gamecardConvertLotusAsicFirmwareVersionBitmask(&lafw);
|
||||
LOG_MSG("LAFW version: %lu.", g_lafwVersion);
|
||||
|
||||
/* Update flag. */
|
||||
ret = true;
|
||||
|
||||
end:
|
||||
memFreeMemoryLocation(&g_fsProgramMemory);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -901,7 +898,7 @@ static bool _gamecardGetDecryptedCardInfoArea(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool gamecardReadInitialData(GameCardKeyArea *out)
|
||||
static bool gamecardReadSecurityInformation(GameCardSecurityInformation *out)
|
||||
{
|
||||
if (!g_gameCardInterfaceInit || g_gameCardStatus != GameCardStatus_InsertedAndInfoLoaded || !out)
|
||||
{
|
||||
|
@ -910,7 +907,7 @@ static bool gamecardReadInitialData(GameCardKeyArea *out)
|
|||
}
|
||||
|
||||
/* Clear output. */
|
||||
memset(out, 0, sizeof(GameCardKeyArea));
|
||||
memset(out, 0, sizeof(GameCardSecurityInformation));
|
||||
|
||||
/* Open secure storage area. */
|
||||
if (!gamecardOpenStorageArea(GameCardStorageArea_Secure))
|
||||
|
@ -941,7 +938,11 @@ static bool gamecardReadInitialData(GameCardKeyArea *out)
|
|||
if (!memcmp(tmp_hash, g_gameCardHeader.initial_data_hash, SHA256_HASH_SIZE))
|
||||
{
|
||||
/* Jackpot. */
|
||||
memcpy(&(out->initial_data), g_fsProgramMemory.data + offset, sizeof(GameCardInitialData));
|
||||
memcpy(out, g_fsProgramMemory.data + offset - 0x600, sizeof(GameCardSecurityInformation));
|
||||
|
||||
// Clear out the asic session hash of the current Lotus session with the console.
|
||||
// It's not actually part of the gamecard data, and this changes every time the gamecard is reinserted.
|
||||
memset(out->specific_data.asic_session_hash, 0xFF, 32);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue