Added CMAC and GBA VC inject support for SD AGBSAVEs

This commit is contained in:
d0k3 2017-10-10 16:00:19 +02:00
parent 9dc8105e9c
commit c24a654cb9
2 changed files with 36 additions and 10 deletions

View File

@ -860,7 +860,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
bool bootable = (FTYPE_BOOTABLE(filetype)); bool bootable = (FTYPE_BOOTABLE(filetype));
bool installable = (FTYPE_INSTALLABLE(filetype)); bool installable = (FTYPE_INSTALLABLE(filetype));
bool agbexportable = (FTPYE_AGBSAVE(filetype)); bool agbexportable = (FTPYE_AGBSAVE(filetype));
bool agbimportable = (FTPYE_AGBSAVE(filetype) && (drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); bool agbimportable = (FTPYE_AGBSAVE(filetype));
bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || transferable || hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable || bootable || scriptable || installable || agbexportable || agbimportable; bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || transferable || hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable || bootable || scriptable || installable || agbexportable || agbimportable;
char pathstr[32+1]; char pathstr[32+1];
@ -1771,7 +1771,7 @@ u32 GodMode(bool is_b9s) {
// bootloader handler // bootloader handler
if (bootloader) { if (bootloader) {
const char* bootfirm_paths[] = { BOOTFIRM_PATHS }; const char* bootfirm_paths[] = { BOOTFIRM_PATHS };
if (IsBootableFirm(firm_in_mem, FIRM_MAX_SIZE)) BootFirm(firm_in_mem, "0:/bootonce.firm"); if (IsBootableFirm(firm_in_mem, FIRM_MAX_SIZE)) BootFirm(firm_in_mem, "sdmc:/bootonce.firm");
for (u32 i = 0; i < sizeof(bootfirm_paths) / sizeof(char*); i++) { for (u32 i = 0; i < sizeof(bootfirm_paths) / sizeof(char*); i++) {
BootFirmHandler(bootfirm_paths[i], false, (BOOTFIRM_TEMPS >> i) & 0x1); BootFirmHandler(bootfirm_paths[i], false, (BOOTFIRM_TEMPS >> i) & 0x1);
} }

View File

@ -19,12 +19,13 @@
#define CMAC_TITLEDB_SD 8 #define CMAC_TITLEDB_SD 8
#define CMAC_MOVABLE 9 #define CMAC_MOVABLE 9
#define CMAC_AGBSAVE 10 #define CMAC_AGBSAVE 10
#define CMAC_AGBSAVE_SD 11
// see: https://www.3dbrew.org/wiki/Savegames#AES_CMAC_header // see: https://www.3dbrew.org/wiki/Savegames#AES_CMAC_header
#define CMAC_SAVETYPE NULL, "CTR-EXT0", "CTR-EXT0", "CTR-SYS0", "CTR-NOR0", "CTR-SAV0", "CTR-SIGN", "CTR-9DB0", "CTR-9DB0", NULL, NULL #define CMAC_SAVETYPE NULL, "CTR-EXT0", "CTR-EXT0", "CTR-SYS0", "CTR-NOR0", "CTR-SAV0", "CTR-SIGN", "CTR-9DB0", "CTR-9DB0", NULL, NULL, NULL
// see: http://3dbrew.org/wiki/AES_Registers#Keyslots // see: http://3dbrew.org/wiki/AES_Registers#Keyslots
#define CMAC_KEYSLOT 0xFF, 0x30 /*0x3A?*/, 0x30, 0x30, 0x33 /*0x19*/, 0xFF, 0x30, 0x0B, 0x30, 0x0B, 0x24 #define CMAC_KEYSLOT 0xFF, 0x30 /*0x3A?*/, 0x30, 0x30, 0x33 /*0x19*/, 0xFF, 0x30, 0x0B, 0x30, 0x0B, 0x24, 0x30
// see: https://www.3dbrew.org/wiki/Title_Database // see: https://www.3dbrew.org/wiki/Title_Database
#define SYS_DB_NAMES "ticket.db", "certs.db", "title.db", "import.db", "tmp_t.db", "tmp_i.db" #define SYS_DB_NAMES "ticket.db", "certs.db", "title.db", "import.db", "tmp_t.db", "tmp_i.db"
@ -47,6 +48,16 @@
// "%c:/agbsave.bin" virtual AGBSAVE file // "%c:/agbsave.bin" virtual AGBSAVE file
u32 CheckAgbSaveHeader(const char* path) {
AgbSaveHeader agbsave;
UINT br;
if ((fvx_qread(path, &agbsave, 0, 0x200, &br) != FR_OK) || (br != 0x200))
return 1;
return ValidateAgbSaveHeader(&agbsave);
}
u32 CheckCmacHeader(const char* path) { u32 CheckCmacHeader(const char* path) {
u8 cmac_hdr[0x100]; u8 cmac_hdr[0x100];
UINT br; UINT br;
@ -70,7 +81,7 @@ u32 ReadFileCmac(const char* path, u8* cmac) {
if (!cmac_type) return 1; if (!cmac_type) return 1;
else if (cmac_type == CMAC_MOVABLE) offset = 0x130; else if (cmac_type == CMAC_MOVABLE) offset = 0x130;
else if (cmac_type == CMAC_AGBSAVE) offset = 0x010; else if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) offset = 0x010;
else offset = 0x000; else offset = 0x000;
return ((fvx_qread(path, cmac, offset, 0x10, &br) != FR_OK) || (br != 0x10)) ? 1 : 0; return ((fvx_qread(path, cmac, offset, 0x10, &br) != FR_OK) || (br != 0x10)) ? 1 : 0;
@ -83,7 +94,7 @@ u32 WriteFileCmac(const char* path, u8* cmac) {
if (!cmac_type) return 1; if (!cmac_type) return 1;
else if (cmac_type == CMAC_MOVABLE) offset = 0x130; else if (cmac_type == CMAC_MOVABLE) offset = 0x130;
else if (cmac_type == CMAC_AGBSAVE) offset = 0x010; else if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) offset = 0x010;
else offset = 0x000; else offset = 0x000;
if (!CheckWritePermissions(path)) return 1; if (!CheckWritePermissions(path)) return 1;
@ -111,8 +122,8 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
sid = 1; sid = 1;
cmac_type = CMAC_EXTDATA_SD; cmac_type = CMAC_EXTDATA_SD;
} else if ((sscanf(path, "%c:/title/%08lx/%08lx/data/%08lx.sav", &drv, &tid_high, &tid_low, &sid) == 4) && } else if ((sscanf(path, "%c:/title/%08lx/%08lx/data/%08lx.sav", &drv, &tid_high, &tid_low, &sid) == 4) &&
ext && (strncmp(ext, "sav", 4) == 0) && (CheckCmacHeader(path) == 0)) { ext && (strncasecmp(ext, "sav", 4) == 0)) {
cmac_type = CMAC_SAVEDATA_SD; cmac_type = (CheckCmacHeader(path) == 0) ? CMAC_SAVEDATA_SD : (CheckAgbSaveHeader(path) == 0) ? CMAC_AGBSAVE_SD : 0;
} }
} else if ((drv == '1') || (drv == '4') || (drv == '7')) { // data on CTRNAND } else if ((drv == '1') || (drv == '4') || (drv == '7')) { // data on CTRNAND
u64 id0_high, id0_low; // ID0 u64 id0_high, id0_low; // ID0
@ -162,14 +173,29 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
} }
// build hash data block, get size // build hash data block, get size
if (cmac_type == CMAC_AGBSAVE) { // agbsaves if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) { // agbsaves
// see: https://www.3dbrew.org/wiki/Savegames#AES_CMAC_header
AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) data; AgbSaveHeader* agbsave = (AgbSaveHeader*) (void*) data;
if ((TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) || (fvx_qread(path, agbsave, 0, AGBSAVE_MAX_SIZE, &br) != FR_OK) || if ((TEMP_BUFFER_SIZE < AGBSAVE_MAX_SIZE) || (fvx_qread(path, agbsave, 0, AGBSAVE_MAX_SIZE, &br) != FR_OK) ||
(br < 0x200) || (ValidateAgbSaveHeader(agbsave) != 0) || (0x200 + agbsave->save_size > br)) (br < 0x200) || (ValidateAgbSaveHeader(agbsave) != 0) || (0x200 + agbsave->save_size > br))
return 1; return 1;
hashsize = (0x200 - 0x30) + agbsave->save_size; hashsize = (0x200 - 0x30) + agbsave->save_size;
hashdata = data + 0x30; hashdata = data + 0x30;
if (cmac_type == CMAC_AGBSAVE_SD) {
// see: https://www.3dbrew.org/wiki/Savegames#AES_CMAC_header
// thanks to TuxSH, AuroraWright and Wolfvak for helping me
// reverse engineering P9 and figuring out AGBSAVE on SD CMACs
// this won't work on devkits(!!!)
const char* cmac_savetype[] = { CMAC_SAVETYPE };
u8* hashdata0 = data + AGBSAVE_MAX_SIZE;
memcpy(hashdata0 + 0x00, cmac_savetype[CMAC_SAVEGAME], 8);
sha_quick(hashdata0 + 0x08, hashdata, hashsize, SHA256_MODE);
hashdata = data;
memcpy(hashdata + 0x00, cmac_savetype[CMAC_SAVEDATA_SD], 8);
memcpy(hashdata + 0x08, &(agbsave->title_id), 8);
sha_quick(hashdata + 0x10, hashdata0, 0x28, SHA256_MODE);
hashsize = 0x30;
}
} else if (cmac_type == CMAC_MOVABLE) { // movable.sed } else if (cmac_type == CMAC_MOVABLE) { // movable.sed
// see: https://3dbrew.org/wiki/Nand/private/movable.sed // see: https://3dbrew.org/wiki/Nand/private/movable.sed
if ((fvx_qread(path, data, 0, 0x140, &br) != FR_OK) || (br != 0x140)) if ((fvx_qread(path, data, 0, 0x140, &br) != FR_OK) || (br != 0x140))