mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 05:32:47 +00:00
parent
bd74ad00d8
commit
87d4152d4e
@ -118,6 +118,9 @@ u64 IdentifyFileType(const char* path) {
|
||||
} else if ((fsize > sizeof(ThreedsxHeader)) &&
|
||||
(memcmp(data, threedsx_magic, sizeof(threedsx_magic)) == 0)) {
|
||||
return GAME_3DSX; // 3DSX (executable) file
|
||||
} else if ((fsize > sizeof(CmdHeader)) &&
|
||||
CheckCmdSize((CmdHeader*) data, fsize) == 0) {
|
||||
return GAME_CMD; // CMD file
|
||||
} else if ((fsize > sizeof(NcchInfoHeader)) &&
|
||||
(GetNcchInfoVersion((NcchInfoHeader*) data)) &&
|
||||
(strncasecmp(fname, NCCHINFO_NAME, 32) == 0)) {
|
||||
|
@ -8,31 +8,32 @@
|
||||
#define GAME_NCSD (1ULL<<3)
|
||||
#define GAME_NCCH (1ULL<<4)
|
||||
#define GAME_TMD (1ULL<<5)
|
||||
#define GAME_EXEFS (1ULL<<6)
|
||||
#define GAME_ROMFS (1ULL<<7)
|
||||
#define GAME_BOSS (1ULL<<8)
|
||||
#define GAME_NUSCDN (1ULL<<9)
|
||||
#define GAME_TICKET (1ULL<<10)
|
||||
#define GAME_SMDH (1ULL<<11)
|
||||
#define GAME_3DSX (1ULL<<12)
|
||||
#define GAME_NDS (1ULL<<13)
|
||||
#define GAME_GBA (1ULL<<14)
|
||||
#define GAME_TAD (1ULL<<15)
|
||||
#define SYS_FIRM (1ULL<<16)
|
||||
#define SYS_DIFF (1ULL<<17)
|
||||
#define SYS_DISA (1ULL<<18) // not yet used
|
||||
#define SYS_AGBSAVE (1ULL<<19)
|
||||
#define SYS_TICKDB (1ULL<<20)
|
||||
#define BIN_NCCHNFO (1ULL<<21)
|
||||
#define BIN_TIKDB (1ULL<<22)
|
||||
#define BIN_KEYDB (1ULL<<23)
|
||||
#define BIN_LEGKEY (1ULL<<24)
|
||||
#define TXT_SCRIPT (1ULL<<25)
|
||||
#define TXT_GENERIC (1ULL<<26)
|
||||
#define GFX_PNG (1ULL<<27)
|
||||
#define FONT_PBM (1ULL<<28)
|
||||
#define NOIMG_NAND (1ULL<<29)
|
||||
#define HDR_NAND (1ULL<<30)
|
||||
#define GAME_CMD (1ULL<<6)
|
||||
#define GAME_EXEFS (1ULL<<7)
|
||||
#define GAME_ROMFS (1ULL<<8)
|
||||
#define GAME_BOSS (1ULL<<9)
|
||||
#define GAME_NUSCDN (1ULL<<10)
|
||||
#define GAME_TICKET (1ULL<<11)
|
||||
#define GAME_SMDH (1ULL<<12)
|
||||
#define GAME_3DSX (1ULL<<13)
|
||||
#define GAME_NDS (1ULL<<14)
|
||||
#define GAME_GBA (1ULL<<15)
|
||||
#define GAME_TAD (1ULL<<16)
|
||||
#define SYS_FIRM (1ULL<<17)
|
||||
#define SYS_DIFF (1ULL<<18)
|
||||
#define SYS_DISA (1ULL<<19) // not yet used
|
||||
#define SYS_AGBSAVE (1ULL<<20)
|
||||
#define SYS_TICKDB (1ULL<<21)
|
||||
#define BIN_NCCHNFO (1ULL<<22)
|
||||
#define BIN_TIKDB (1ULL<<23)
|
||||
#define BIN_KEYDB (1ULL<<24)
|
||||
#define BIN_LEGKEY (1ULL<<25)
|
||||
#define TXT_SCRIPT (1ULL<<26)
|
||||
#define TXT_GENERIC (1ULL<<27)
|
||||
#define GFX_PNG (1ULL<<28)
|
||||
#define FONT_PBM (1ULL<<29)
|
||||
#define NOIMG_NAND (1ULL<<30)
|
||||
#define HDR_NAND (1ULL<<31)
|
||||
#define TYPE_BASE 0xFFFFFFFFULL // 32 bit reserved for base types
|
||||
|
||||
// #define FLAG_FIRM (1ULL<<57) // <--- for CXIs containing FIRMs
|
||||
|
11
arm9/source/game/cmd.c
Normal file
11
arm9/source/game/cmd.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include "cmd.h"
|
||||
|
||||
|
||||
u32 CheckCmdSize(CmdHeader* cmd, u64 fsize) {
|
||||
u64 cmdsize = sizeof(CmdHeader) +
|
||||
(cmd->n_entries * sizeof(u32)) +
|
||||
(cmd->n_cmacs * sizeof(u32)) +
|
||||
(cmd->n_entries * 0x10);
|
||||
|
||||
return (fsize == cmdsize) ? 0 : 1;
|
||||
}
|
18
arm9/source/game/cmd.h
Normal file
18
arm9/source/game/cmd.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
||||
// from: http://3dbrew.org/wiki/Titles#Data_Structure
|
||||
typedef struct {
|
||||
u32 cmd_id; // same as filename id, <cmd_id>.cmd
|
||||
u32 n_entries; // matches highest content index
|
||||
u32 n_cmacs; // number of cmacs in file (excluding the one @0x10)
|
||||
u32 unknown; // usually 1
|
||||
u8 cmac[0x10]; // calculated from first 0x10 byte of data, no hashing
|
||||
// followed by u32 list of content ids (sorted by index, 0xFFFFFFFF for unavailable)
|
||||
// followed by u32 list of content ids (sorted by id?)
|
||||
// followed by <n_entries> CMACs (may contain garbage)
|
||||
} __attribute__((packed, aligned(4))) CmdHeader;
|
||||
|
||||
u32 CheckCmdSize(CmdHeader* cmd, u64 fsize);
|
@ -13,5 +13,7 @@
|
||||
#include "gba.h"
|
||||
#include "tad.h"
|
||||
#include "3dsx.h"
|
||||
#include "tmd.h"
|
||||
#include "cmd.h"
|
||||
#include "ticketdb.h"
|
||||
#include "ncchinfo.h"
|
||||
|
@ -875,23 +875,34 @@ u32 Sha256Calculator(const char* path) {
|
||||
|
||||
u32 CmacCalculator(const char* path) {
|
||||
char pathstr[32 + 1];
|
||||
u8 cmac[16];
|
||||
TruncateString(pathstr, path, 32, 8);
|
||||
if (CalculateFileCmac(path, cmac) != 0) {
|
||||
ShowPrompt(false, "Calculating CMAC: failed!");
|
||||
return 1;
|
||||
} else {
|
||||
u8 cmac_file[16];
|
||||
bool identical = ((ReadFileCmac(path, cmac_file) == 0) && (memcmp(cmac, cmac_file, 16) == 0));
|
||||
if (ShowPrompt(!identical, "%s\n%016llX%016llX\n%s%s%s",
|
||||
pathstr, getbe64(cmac + 0), getbe64(cmac + 8),
|
||||
"CMAC verification: ", (identical) ? "passed!" : "failed!",
|
||||
(!identical) ? "\n \nFix CMAC in file?" : "") &&
|
||||
!identical && (WriteFileCmac(path, cmac) != 0)) {
|
||||
if (IdentifyFileType(path) != GAME_CMD) {
|
||||
u8 cmac[16] __attribute__((aligned(4)));
|
||||
if (CalculateFileCmac(path, cmac) != 0) {
|
||||
ShowPrompt(false, "Calculating CMAC: failed!");
|
||||
return 1;
|
||||
} else {
|
||||
u8 cmac_file[16];
|
||||
bool identical = ((ReadFileCmac(path, cmac_file) == 0) && (memcmp(cmac, cmac_file, 16) == 0));
|
||||
if (ShowPrompt(!identical, "%s\n%016llX%016llX\n%s%s%s",
|
||||
pathstr, getbe64(cmac + 0), getbe64(cmac + 8),
|
||||
"CMAC verification: ", (identical) ? "passed!" : "failed!",
|
||||
(!identical) ? "\n \nFix CMAC in file?" : "") &&
|
||||
!identical && (WriteFileCmac(path, cmac) != 0)) {
|
||||
ShowPrompt(false, "Fixing CMAC: failed!");
|
||||
}
|
||||
}
|
||||
} else { // special handling for CMD files
|
||||
bool correct = (CheckCmdCmac(path) == 0);
|
||||
if (ShowPrompt(!correct, "%s\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n%s%s%s",
|
||||
pathstr, "CMAC verification: ", (correct) ? "passed!" : "failed!",
|
||||
(!correct) ? "\n \nFix CMAC in file?" : "") &&
|
||||
!correct && (FixCmdCmac(path) != 0)) {
|
||||
ShowPrompt(false, "Fixing CMAC: failed!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "nandcmac.h"
|
||||
#include "fsperm.h"
|
||||
#include "gba.h"
|
||||
#include "cmd.h"
|
||||
#include "sha.h"
|
||||
#include "aes.h"
|
||||
#include "vff.h"
|
||||
@ -21,6 +22,8 @@
|
||||
#define CMAC_MOVABLE 9
|
||||
#define CMAC_AGBSAVE 10
|
||||
#define CMAC_AGBSAVE_SD 11
|
||||
#define CMAC_CMD_SD 12
|
||||
#define CMAC_CMD_TWLN 13 // unsupported
|
||||
|
||||
// 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, NULL
|
||||
@ -97,6 +100,7 @@ u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write) {
|
||||
if (!cmac_type) return 1;
|
||||
else if (cmac_type == CMAC_MOVABLE) offset = 0x130;
|
||||
else if ((cmac_type == CMAC_AGBSAVE) || (cmac_type == CMAC_AGBSAVE_SD)) offset = 0x010;
|
||||
else if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1; // can't do that here
|
||||
else offset = 0x000;
|
||||
|
||||
if (do_write && !CheckWritePermissions(path)) return 1;
|
||||
@ -128,6 +132,9 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
|
||||
ext && (strncasecmp(ext, "sav", 4) == 0)) {
|
||||
// cmac_type = (CheckCmacHeader(path) == 0) ? CMAC_SAVEDATA_SD : (CheckAgbSaveHeader(path) == 0) ? CMAC_AGBSAVE_SD : 0;
|
||||
cmac_type = (CheckCmacHeader(path) == 0) ? CMAC_SAVEDATA_SD : 0;
|
||||
} else if ((sscanf(path, "%c:/title/%08lx/%08lx/content/cmd/%08lx.cmd", &drv, &tid_high, &tid_low, &sid) == 4) &&
|
||||
ext && (strncasecmp(ext, "cmd", 4) == 0)) {
|
||||
cmac_type = CMAC_CMD_SD; // this needs special handling, it's in here just for detection
|
||||
}
|
||||
} else if ((drv == '1') || (drv == '4') || (drv == '7')) { // data on CTRNAND
|
||||
u64 id0_high, id0_low; // ID0
|
||||
@ -140,6 +147,11 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
|
||||
cmac_type = CMAC_EXTDATA_SYS;
|
||||
} else if (sscanf(path, "%c:/data/%016llx%016llx/sysdata/%08lx/%08lx", &drv, &id0_high, &id0_low, &fid_low, &fid_high) == 5)
|
||||
cmac_type = CMAC_SAVEDATA_SYS;
|
||||
} else if ((drv == '2') || (drv == '5') || (drv == '8')) { // data on TWLN
|
||||
if ((sscanf(path, "%c:/title/%08lx/%08lx/content/cmd/%08lx.cmd", &drv, &tid_high, &tid_low, &sid) == 4) &&
|
||||
ext && (strncasecmp(ext, "cmd", 4) == 0)) {
|
||||
cmac_type = CMAC_CMD_TWLN; // this is not supported (yet), it's in here just for detection
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmac_type) { // path independent stuff
|
||||
@ -157,6 +169,7 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
|
||||
// exit with cmac_type if (u8*) cmac is NULL
|
||||
// somewhat hacky, but can be used to check if file has a CMAC
|
||||
if (!cmac) return cmac_type;
|
||||
else if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) return 1;
|
||||
else if (!cmac_type) return 1;
|
||||
|
||||
const u32 cmac_keyslot[] = { CMAC_KEYSLOT };
|
||||
@ -234,15 +247,26 @@ u32 CalculateFileCmac(const char* path, u8* cmac) {
|
||||
}
|
||||
|
||||
u32 CheckFileCmac(const char* path) {
|
||||
u8 fcmac[16];
|
||||
u8 ccmac[16];
|
||||
return ((ReadFileCmac(path, fcmac) == 0) && (CalculateFileCmac(path, ccmac) == 0) &&
|
||||
(memcmp(fcmac, ccmac, 16) == 0)) ? 0 : 1;
|
||||
u32 cmac_type = CalculateFileCmac(path, NULL);
|
||||
if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) {
|
||||
return CheckCmdCmac(path);
|
||||
} else if (cmac_type) {
|
||||
u8 fcmac[16];
|
||||
u8 ccmac[16];
|
||||
return ((ReadFileCmac(path, fcmac) == 0) && (CalculateFileCmac(path, ccmac) == 0) &&
|
||||
(memcmp(fcmac, ccmac, 16) == 0)) ? 0 : 1;
|
||||
} else return 1;
|
||||
|
||||
}
|
||||
|
||||
u32 FixFileCmac(const char* path) {
|
||||
u8 ccmac[16];
|
||||
return ((CalculateFileCmac(path, ccmac) == 0) && (WriteFileCmac(path, ccmac) == 0)) ? 0 : 1;
|
||||
u32 cmac_type = CalculateFileCmac(path, NULL);
|
||||
if ((cmac_type == CMAC_CMD_SD) || (cmac_type == CMAC_CMD_TWLN)) {
|
||||
return FixCmdCmac(path);
|
||||
} else if (cmac_type) {
|
||||
u8 ccmac[16];
|
||||
return ((CalculateFileCmac(path, ccmac) == 0) && (WriteFileCmac(path, ccmac) == 0)) ? 0 : 1;
|
||||
} else return 1;
|
||||
}
|
||||
|
||||
u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv) {
|
||||
@ -281,6 +305,100 @@ u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 CheckFixCmdCmac(const char* path, bool fix) {
|
||||
u8 cmac[16] __attribute__((aligned(4)));
|
||||
u32 keyslot = ((*path == 'A') || (*path == 'B')) ? 0x30 : 0x0B;
|
||||
bool fixed = false;
|
||||
|
||||
// setup the keyslot if required
|
||||
if ((keyslot == 0x30) && (SetupSlot0x30(*path) != 0))
|
||||
return 1;
|
||||
|
||||
// set up the temporary path for contents
|
||||
u32 pos_name_content = 2 + 1 + 5 + 1 + 8 + 1 + 8 + 1 + 7 + 1;
|
||||
char path_content[256]; // that will be more than enough
|
||||
char* name_content = path_content + pos_name_content;
|
||||
strncpy(path_content, path, 256);
|
||||
if (strnlen(path_content, 256) < pos_name_content)
|
||||
return 1;
|
||||
|
||||
// hacky check for DLC conents
|
||||
bool is_dlc = (strncasecmp(path + 2 + 1 + 5 + 1, "0004008c", 8) == 0);
|
||||
|
||||
// cmd data
|
||||
u64 cmd_size = fvx_qsize(path);
|
||||
u8* cmd_data = malloc(cmd_size);
|
||||
CmdHeader* cmd = (CmdHeader*) (void*) cmd_data;
|
||||
|
||||
// check for out of memory
|
||||
if (cmd_data == NULL) return 1;
|
||||
|
||||
// read the full file to memory and check it (we may write it back later)
|
||||
if ((fvx_qread(path, cmd_data, 0, cmd_size, NULL) != FR_OK) ||
|
||||
(CheckCmdSize(cmd, cmd_size) != 0)) {
|
||||
free(cmd_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// now, check the CMAC@0x10
|
||||
use_aeskey(keyslot);
|
||||
aes_cmac(cmd_data, cmac, 1);
|
||||
if (memcmp(cmd->cmac, cmac, 0x10) != 0) {
|
||||
if (fix) {
|
||||
fixed = true;
|
||||
memcpy(cmd->cmac, cmac, 0x10);
|
||||
} else {
|
||||
free(cmd_data);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// further checking will be more complicated
|
||||
// set up pointers to cmd data (pointer arithemtic is hard)
|
||||
u32 n_entries = cmd->n_entries;
|
||||
u32* cnt_id = (u32*) (cmd + 1);
|
||||
u8* cnt_cmac = (u8*) (cnt_id + cmd->n_entries + cmd->n_cmacs);
|
||||
|
||||
// check all ids and cmacs
|
||||
for (u32 cnt_idx = 0; cnt_idx < n_entries; cnt_idx++, cnt_id++, cnt_cmac += 0x10) {
|
||||
u8 hashdata[0x108] __attribute__((aligned(4)));
|
||||
u8 shasum[32];
|
||||
if (*cnt_id == 0xFFFFFFFF) continue; // unavailable content
|
||||
snprintf(name_content, 32, "%s%08lX.app", (is_dlc) ? "00000000/" : "", *cnt_id);
|
||||
if (fvx_qread(path_content, hashdata, 0x100, 0x100, NULL) != FR_OK) {
|
||||
free(cmd_data);
|
||||
return 1; // failed to read content
|
||||
}
|
||||
memcpy(hashdata + 0x100, &cnt_idx, 4);
|
||||
memcpy(hashdata + 0x104, cnt_id, 4);
|
||||
// hash block complete, check it
|
||||
sha_quick(shasum, hashdata, 0x108, SHA256_MODE);
|
||||
use_aeskey(keyslot);
|
||||
aes_cmac(shasum, cmac, 2);
|
||||
if (memcmp(cnt_cmac, cmac, 0x10) != 0) {
|
||||
if (fix) {
|
||||
fixed = true;
|
||||
memcpy(cnt_cmac, cmac, 0x10);
|
||||
} else {
|
||||
free(cmd_data);
|
||||
return 1; // bad cmac
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if fixing is enable, write back cmd file
|
||||
if (fix && fixed && CheckWritePermissions(path) &&
|
||||
(fvx_qwrite(path, cmd_data, 0, cmd_size, NULL) != FR_OK)) {
|
||||
free(cmd_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// if we end up here, everything is fine
|
||||
free(cmd_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 RecursiveFixFileCmacWorker(char* path) {
|
||||
FILINFO fno;
|
||||
DIR pdir;
|
||||
|
@ -3,7 +3,9 @@
|
||||
#include "common.h"
|
||||
|
||||
#define ReadFileCmac(path, cmac) ReadWriteFileCmac(path, cmac, false)
|
||||
#define WriteFileCmac(path, cmac) ReadWriteFileCmac(path, cmac, true)
|
||||
#define WriteFileCmac(path, cmac) ReadWriteFileCmac(path, cmac, true)
|
||||
#define CheckCmdCmac(path) CheckFixCmdCmac(path, false)
|
||||
#define FixCmdCmac(path) CheckFixCmdCmac(path, true)
|
||||
|
||||
u32 CheckCmacPath(const char* path);
|
||||
u32 ReadWriteFileCmac(const char* path, u8* cmac, bool do_write);
|
||||
@ -11,4 +13,5 @@ u32 CalculateFileCmac(const char* path, u8* cmac);
|
||||
u32 CheckFileCmac(const char* path);
|
||||
u32 FixFileCmac(const char* path);
|
||||
u32 FixAgbSaveCmac(void* data, u8* cmac, const char* sddrv);
|
||||
u32 CheckFixCmdCmac(const char* path, bool fix);
|
||||
u32 RecursiveFixFileCmac(const char* path);
|
||||
|
Loading…
x
Reference in New Issue
Block a user