mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2025-01-10 03:27:23 -03: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 u32 menuGetElementCount(const Menu *menu);
|
||||||
|
|
||||||
static bool sendGameCardKeyAreaViaUsb(void);
|
static bool sendGameCardKeyAreaViaUsb(void);
|
||||||
|
static bool sendGameCardSpecificDataViaUsb(void);
|
||||||
static bool sendGameCardCertificateViaUsb(void);
|
static bool sendGameCardCertificateViaUsb(void);
|
||||||
static bool sendGameCardImageViaUsb(void);
|
static bool sendGameCardImageViaUsb(void);
|
||||||
|
static bool sendConsoleLafwViaUsb(void);
|
||||||
|
|
||||||
static void changeKeyAreaOption(u32 idx);
|
static void changeKeyAreaOption(u32 idx);
|
||||||
static void changeCertificateOption(u32 idx);
|
static void changeCertificateOption(u32 idx);
|
||||||
|
@ -150,23 +152,35 @@ static Menu g_xciMenu = {
|
||||||
|
|
||||||
static MenuElement *g_rootMenuElements[] = {
|
static MenuElement *g_rootMenuElements[] = {
|
||||||
&(MenuElement){
|
&(MenuElement){
|
||||||
.str = "dump key area (initial data)",
|
.str = "dump gamecard initial data",
|
||||||
.child_menu = NULL,
|
.child_menu = NULL,
|
||||||
.task_func = &sendGameCardKeyAreaViaUsb,
|
.task_func = &sendGameCardKeyAreaViaUsb,
|
||||||
.element_options = NULL
|
.element_options = NULL
|
||||||
},
|
},
|
||||||
&(MenuElement){
|
&(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,
|
.child_menu = NULL,
|
||||||
.task_func = &sendGameCardCertificateViaUsb,
|
.task_func = &sendGameCardCertificateViaUsb,
|
||||||
.element_options = NULL
|
.element_options = NULL
|
||||||
},
|
},
|
||||||
&(MenuElement){
|
&(MenuElement){
|
||||||
.str = "dump xci",
|
.str = "dump gamecard xci",
|
||||||
.child_menu = &g_xciMenu,
|
.child_menu = &g_xciMenu,
|
||||||
.task_func = NULL,
|
.task_func = NULL,
|
||||||
.element_options = NULL
|
.element_options = NULL
|
||||||
},
|
},
|
||||||
|
&(MenuElement){
|
||||||
|
.str = "dump console LAFW",
|
||||||
|
.child_menu = NULL,
|
||||||
|
.task_func = &sendConsoleLafwViaUsb,
|
||||||
|
.element_options = NULL
|
||||||
|
},
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -454,6 +468,24 @@ static bool dumpGameCardKeyArea(GameCardKeyArea *out)
|
||||||
return true;
|
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)
|
static bool sendGameCardKeyAreaViaUsb(void)
|
||||||
{
|
{
|
||||||
if (!waitForGameCardAndUsb()) return false;
|
if (!waitForGameCardAndUsb()) return false;
|
||||||
|
@ -489,6 +521,38 @@ end:
|
||||||
return success;
|
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)
|
static bool sendGameCardCertificateViaUsb(void)
|
||||||
{
|
{
|
||||||
if (!waitForGameCardAndUsb()) return false;
|
if (!waitForGameCardAndUsb()) return false;
|
||||||
|
@ -527,6 +591,53 @@ end:
|
||||||
return success;
|
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)
|
static bool sendGameCardImageViaUsb(void)
|
||||||
{
|
{
|
||||||
if (!waitForGameCardAndUsb()) return false;
|
if (!waitForGameCardAndUsb()) return false;
|
||||||
|
|
|
@ -86,6 +86,30 @@ typedef struct {
|
||||||
|
|
||||||
NXDT_ASSERT(GameCardKeyArea, 0x1000);
|
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 {
|
typedef enum {
|
||||||
GameCardKekIndex_Version0 = 0,
|
GameCardKekIndex_Version0 = 0,
|
||||||
GameCardKekIndex_VersionForDev = 1
|
GameCardKekIndex_VersionForDev = 1
|
||||||
|
@ -212,6 +236,40 @@ typedef enum {
|
||||||
GameCardHashFileSystemPartitionType_Count = 7 ///< Not a real value.
|
GameCardHashFileSystemPartitionType_Count = 7 ///< Not a real value.
|
||||||
} GameCardHashFileSystemPartitionType;
|
} 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.
|
/// 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.
|
/// Also spans a background thread to automatically detect gamecard status changes and to cache data from the inserted gamecard.
|
||||||
bool gamecardInitialize(void);
|
bool gamecardInitialize(void);
|
||||||
|
@ -236,6 +294,10 @@ bool gamecardReadStorage(void *out, u64 read_size, u64 offset);
|
||||||
/// This area can't be read using gamecardReadStorage().
|
/// This area can't be read using gamecardReadStorage().
|
||||||
bool gamecardGetKeyArea(GameCardKeyArea *out);
|
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.
|
/// Fills the provided FsGameCardIdSet pointer.
|
||||||
/// This area can't be read using gamecardReadStorage().
|
/// This area can't be read using gamecardReadStorage().
|
||||||
bool gamecardGetIdSet(FsGameCardIdSet *out);
|
bool gamecardGetIdSet(FsGameCardIdSet *out);
|
||||||
|
@ -280,6 +342,12 @@ const char *gamecardGetRequiredHosVersionString(u64 fw_version);
|
||||||
/// Returns NULL if the provided value is out of range.
|
/// Returns NULL if the provided value is out of range.
|
||||||
const char *gamecardGetCompatibilityTypeString(u8 compatibility_type);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -56,52 +56,6 @@ typedef enum {
|
||||||
GameCardCapacity_32GiB = BIT_LONG(35)
|
GameCardCapacity_32GiB = BIT_LONG(35)
|
||||||
} GameCardCapacity;
|
} 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. */
|
/* Global variables. */
|
||||||
|
|
||||||
static Mutex g_gameCardMutex = 0;
|
static Mutex g_gameCardMutex = 0;
|
||||||
|
@ -180,7 +134,7 @@ static bool gamecardReadHeader(void);
|
||||||
|
|
||||||
static bool _gamecardGetDecryptedCardInfoArea(void);
|
static bool _gamecardGetDecryptedCardInfoArea(void);
|
||||||
|
|
||||||
static bool gamecardReadInitialData(GameCardKeyArea *out);
|
static bool gamecardReadSecurityInformation(GameCardSecurityInformation *out);
|
||||||
|
|
||||||
static bool gamecardGetHandleAndStorage(u32 partition);
|
static bool gamecardGetHandleAndStorage(u32 partition);
|
||||||
NX_INLINE void gamecardCloseHandle(void);
|
NX_INLINE void gamecardCloseHandle(void);
|
||||||
|
@ -336,14 +290,30 @@ bool gamecardReadStorage(void *out, u64 read_size, u64 offset)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read full FS program memory to retrieve the GameCardInitialData block, which is part of the GameCardKeyArea block. */
|
/* Read full FS program memory by calling gamecardGetSecurityInformation, and then extracts out the GameCardInitialData 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. */
|
|
||||||
bool gamecardGetKeyArea(GameCardKeyArea *out)
|
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;
|
bool ret = false;
|
||||||
SCOPED_LOCK(&g_gameCardMutex) ret = gamecardReadInitialData(out);
|
SCOPED_LOCK(&g_gameCardMutex) ret = gamecardReadSecurityInformation(out);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,66 +529,93 @@ const char *gamecardGetCompatibilityTypeString(u8 compatibility_type)
|
||||||
return (compatibility_type < GameCardCompatibilityType_Count ? g_gameCardCompatibilityTypeStrings[compatibility_type] : NULL);
|
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)
|
static bool gamecardGetLotusAsicFirmwareVersion(void)
|
||||||
{
|
{
|
||||||
u64 fw_version = 0;
|
bool ret = false;
|
||||||
bool ret = false, found = false, dev_unit = utilsIsDevelopmentUnit();
|
LotusAsicFirmware lafw;
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
|
/* Retrieve LAFW. */
|
||||||
|
ret = gamecardGetLotusAsicFirmware(&lafw);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
{
|
{
|
||||||
LOG_MSG("Failed to retrieve FS .data segment dump!");
|
LOG_MSG("Failed to retrieve Lotus Asic Firmware!");
|
||||||
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");
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert LAFW version bitmask to an integer. */
|
/* Convert LAFW version bitmask to an integer. */
|
||||||
g_lafwVersion = 0;
|
g_lafwVersion = gamecardConvertLotusAsicFirmwareVersionBitmask(&lafw);
|
||||||
|
|
||||||
while(fw_version)
|
|
||||||
{
|
|
||||||
g_lafwVersion += (fw_version & 1);
|
|
||||||
fw_version >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_MSG("LAFW version: %lu.", g_lafwVersion);
|
LOG_MSG("LAFW version: %lu.", g_lafwVersion);
|
||||||
|
|
||||||
/* Update flag. */
|
/* Update flag. */
|
||||||
ret = true;
|
ret = true;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
memFreeMemoryLocation(&g_fsProgramMemory);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -901,7 +898,7 @@ static bool _gamecardGetDecryptedCardInfoArea(void)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool gamecardReadInitialData(GameCardKeyArea *out)
|
static bool gamecardReadSecurityInformation(GameCardSecurityInformation *out)
|
||||||
{
|
{
|
||||||
if (!g_gameCardInterfaceInit || g_gameCardStatus != GameCardStatus_InsertedAndInfoLoaded || !out)
|
if (!g_gameCardInterfaceInit || g_gameCardStatus != GameCardStatus_InsertedAndInfoLoaded || !out)
|
||||||
{
|
{
|
||||||
|
@ -910,7 +907,7 @@ static bool gamecardReadInitialData(GameCardKeyArea *out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear output. */
|
/* Clear output. */
|
||||||
memset(out, 0, sizeof(GameCardKeyArea));
|
memset(out, 0, sizeof(GameCardSecurityInformation));
|
||||||
|
|
||||||
/* Open secure storage area. */
|
/* Open secure storage area. */
|
||||||
if (!gamecardOpenStorageArea(GameCardStorageArea_Secure))
|
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))
|
if (!memcmp(tmp_hash, g_gameCardHeader.initial_data_hash, SHA256_HASH_SIZE))
|
||||||
{
|
{
|
||||||
/* Jackpot. */
|
/* 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;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue