mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Also enable verification of TMD files
this verifies all referenced contents
This commit is contained in:
parent
fec4f081a0
commit
baf1a645ca
@ -46,6 +46,9 @@ u32 IdentifyFileType(const char* path) {
|
|||||||
NcchHeader* ncch = (NcchHeader*) (void*) header;
|
NcchHeader* ncch = (NcchHeader*) (void*) header;
|
||||||
if (fsize >= (ncch->size * NCCH_MEDIA_UNIT))
|
if (fsize >= (ncch->size * NCCH_MEDIA_UNIT))
|
||||||
return GAME_NCCH; // NCSD (".3DS") file
|
return GAME_NCCH; // NCSD (".3DS") file
|
||||||
|
} else if (strncmp(CIA_TMD_ISSUER, (char*) (header + 0x140), 0x40) == 0) {
|
||||||
|
if (fsize >= CIA_TMD_SIZE_N(getbe16(header + 0x1DE)))
|
||||||
|
return GAME_TMD; // TMD file
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2,11 +2,17 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#define IMG_FAT 1
|
#define IMG_FAT (1<<0)
|
||||||
#define IMG_NAND 2
|
#define IMG_NAND (1<<1)
|
||||||
|
|
||||||
#define GAME_CIA 3
|
#define GAME_CIA (1<<2)
|
||||||
#define GAME_NCSD 4
|
#define GAME_NCSD (1<<3)
|
||||||
#define GAME_NCCH 5
|
#define GAME_NCCH (1<<4)
|
||||||
|
#define GAME_TMD (1<<5)
|
||||||
|
// #define GAME_EXEFS (1<<6)
|
||||||
|
// #define GAME_ROMFS (1<<7)
|
||||||
|
|
||||||
|
#define FTYPE_MOUNTABLE (IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH)
|
||||||
|
#define FYTPE_VERIFICABLE (GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD)
|
||||||
|
|
||||||
u32 IdentifyFileType(const char* path);
|
u32 IdentifyFileType(const char* path);
|
||||||
|
@ -85,7 +85,7 @@ u32 GetTitleKey(u8* titlekey, Ticket* ticket) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetCiaCtr(u8* ctr, TmdContentChunk* chunk) {
|
u32 GetTmdCtr(u8* ctr, TmdContentChunk* chunk) {
|
||||||
memset(ctr, 0, 16);
|
memset(ctr, 0, 16);
|
||||||
memcpy(ctr, chunk->index, 2);
|
memcpy(ctr, chunk->index, 2);
|
||||||
return 0;
|
return 0;
|
||||||
@ -136,7 +136,7 @@ u32 BuildFakeTicket(Ticket* ticket, u8* title_id) {
|
|||||||
// fill ticket values
|
// fill ticket values
|
||||||
memcpy(ticket->sig_type, sig_type, 4);
|
memcpy(ticket->sig_type, sig_type, 4);
|
||||||
memset(ticket->signature, 0xFF, 0x100);
|
memset(ticket->signature, 0xFF, 0x100);
|
||||||
snprintf((char*) ticket->issuer, 0x40, "Root-CA00000003-XS0000000c");
|
snprintf((char*) ticket->issuer, 0x40, CIA_TICKET_ISSUER);
|
||||||
memset(ticket->ecdsa, 0xFF, 0x3C);
|
memset(ticket->ecdsa, 0xFF, 0x3C);
|
||||||
ticket->version = 0x01;
|
ticket->version = 0x01;
|
||||||
memset(ticket->titlekey, 0xFF, 16);
|
memset(ticket->titlekey, 0xFF, 16);
|
||||||
@ -157,7 +157,7 @@ u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents) {
|
|||||||
// file TMD values
|
// file TMD values
|
||||||
memcpy(tmd->sig_type, sig_type, 4);
|
memcpy(tmd->sig_type, sig_type, 4);
|
||||||
memset(tmd->signature, 0xFF, 0x100);
|
memset(tmd->signature, 0xFF, 0x100);
|
||||||
snprintf((char*) tmd->issuer, 0x40, "Root-CA00000003-CP0000000b");
|
snprintf((char*) tmd->issuer, 0x40, CIA_TMD_ISSUER);
|
||||||
tmd->version = 0x01;
|
tmd->version = 0x01;
|
||||||
memcpy(tmd->title_id, title_id, 8);
|
memcpy(tmd->title_id, title_id, 8);
|
||||||
tmd->title_type[3] = 0x40; // whatever
|
tmd->title_type[3] = 0x40; // whatever
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#define CIA_TICKET_ISSUER "Root-CA00000003-XS0000000c"
|
||||||
|
#define CIA_TMD_ISSUER "Root-CA00000003-CP0000000b"
|
||||||
|
|
||||||
#define CIA_MAX_CONTENTS (100+1) // theme CIAs contain maximum 100 themes + 1 index content
|
#define CIA_MAX_CONTENTS (100+1) // theme CIAs contain maximum 100 themes + 1 index content
|
||||||
#define CIA_HEADER_SIZE sizeof(CiaHeader)
|
#define CIA_HEADER_SIZE sizeof(CiaHeader)
|
||||||
#define CIA_CERT_SIZE 0xA00
|
#define CIA_CERT_SIZE 0xA00
|
||||||
@ -9,6 +12,7 @@
|
|||||||
#define CIA_TICKET_SIZE sizeof(Ticket)
|
#define CIA_TICKET_SIZE sizeof(Ticket)
|
||||||
#define CIA_TMD_SIZE_MIN sizeof(TitleMetaData)
|
#define CIA_TMD_SIZE_MIN sizeof(TitleMetaData)
|
||||||
#define CIA_TMD_SIZE_MAX (sizeof(TitleMetaData) + (CIA_MAX_CONTENTS*sizeof(TmdContentChunk)))
|
#define CIA_TMD_SIZE_MAX (sizeof(TitleMetaData) + (CIA_MAX_CONTENTS*sizeof(TmdContentChunk)))
|
||||||
|
#define CIA_TMD_SIZE_N(n) (sizeof(TitleMetaData) + (n*sizeof(TmdContentChunk)))
|
||||||
|
|
||||||
// see: https://www.3dbrew.org/wiki/CIA#Meta
|
// see: https://www.3dbrew.org/wiki/CIA#Meta
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -147,7 +151,7 @@ u32 ValidateCiaHeader(CiaHeader* header);
|
|||||||
u32 GetCiaInfo(CiaInfo* info, CiaHeader* header);
|
u32 GetCiaInfo(CiaInfo* info, CiaHeader* header);
|
||||||
u32 GetCiaContentInfo(CiaContentInfo* contents, TitleMetaData* tmd);
|
u32 GetCiaContentInfo(CiaContentInfo* contents, TitleMetaData* tmd);
|
||||||
u32 GetTitleKey(u8* titlekey, Ticket* ticket);
|
u32 GetTitleKey(u8* titlekey, Ticket* ticket);
|
||||||
u32 GetCiaCtr(u8* ctr, TmdContentChunk* chunk);
|
u32 GetTmdCtr(u8* ctr, TmdContentChunk* chunk);
|
||||||
|
|
||||||
u32 BuildCiaCert(u8* ciacert);
|
u32 BuildCiaCert(u8* ciacert);
|
||||||
u32 BuildFakeTicket(Ticket* ticket, u8* title_id);
|
u32 BuildFakeTicket(Ticket* ticket, u8* title_id);
|
||||||
|
@ -197,7 +197,26 @@ u32 LoadCiaStub(CiaStub* stub, const char* path) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 VerifyCiaContent(const char* path, u64 offset, TmdContentChunk* chunk, const u8* titlekey) {
|
u32 LoadTmdFile(TitleMetaData* tmd, const char* path) {
|
||||||
|
FIL file;
|
||||||
|
UINT btr;
|
||||||
|
|
||||||
|
if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// full TMD file
|
||||||
|
f_lseek(&file, 0);
|
||||||
|
if ((fx_read(&file, tmd, CIA_TMD_SIZE_MAX, &btr) != FR_OK) ||
|
||||||
|
(btr < CIA_TMD_SIZE_N(getbe16(tmd->content_count)))) {
|
||||||
|
fx_close(&file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_close(&file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 VerifyTmdContent(const char* path, u64 offset, TmdContentChunk* chunk, const u8* titlekey) {
|
||||||
u8 hash[32];
|
u8 hash[32];
|
||||||
u8 ctr[16];
|
u8 ctr[16];
|
||||||
FIL file;
|
FIL file;
|
||||||
@ -206,6 +225,7 @@ u32 VerifyCiaContent(const char* path, u64 offset, TmdContentChunk* chunk, const
|
|||||||
u64 size = getbe64(chunk->size);
|
u64 size = getbe64(chunk->size);
|
||||||
bool encrypted = getbe16(chunk->type) & 0x1;
|
bool encrypted = getbe16(chunk->type) & 0x1;
|
||||||
|
|
||||||
|
if (!ShowProgress(0, 0, path)) return 1;
|
||||||
if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
||||||
return 1;
|
return 1;
|
||||||
if (offset + size > f_size(&file)) {
|
if (offset + size > f_size(&file)) {
|
||||||
@ -214,9 +234,8 @@ u32 VerifyCiaContent(const char* path, u64 offset, TmdContentChunk* chunk, const
|
|||||||
}
|
}
|
||||||
f_lseek(&file, offset);
|
f_lseek(&file, offset);
|
||||||
|
|
||||||
GetCiaCtr(ctr, chunk);
|
GetTmdCtr(ctr, chunk);
|
||||||
sha_init(SHA256_MODE);
|
sha_init(SHA256_MODE);
|
||||||
ShowProgress(0, 0, path);
|
|
||||||
for (u32 i = 0; i < size; i += MAIN_BUFFER_SIZE) {
|
for (u32 i = 0; i < size; i += MAIN_BUFFER_SIZE) {
|
||||||
u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i));
|
u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i));
|
||||||
UINT bytes_read;
|
UINT bytes_read;
|
||||||
@ -253,7 +272,7 @@ u32 VerifyCiaFile(const char* path) {
|
|||||||
u64 next_offset = info.offset_content;
|
u64 next_offset = info.offset_content;
|
||||||
for (u32 i = 0; (i < content_count) && (i < CIA_MAX_CONTENTS); i++) {
|
for (u32 i = 0; (i < content_count) && (i < CIA_MAX_CONTENTS); i++) {
|
||||||
TmdContentChunk* chunk = &(cia->content_list[i]);
|
TmdContentChunk* chunk = &(cia->content_list[i]);
|
||||||
if (VerifyCiaContent(path, next_offset, chunk, titlekey) != 0) {
|
if (VerifyTmdContent(path, next_offset, chunk, titlekey) != 0) {
|
||||||
ShowPrompt(false, "%s\nID %08lX (%08llX@%08llX)\nVerification failed",
|
ShowPrompt(false, "%s\nID %08lX (%08llX@%08llX)\nVerification failed",
|
||||||
pathstr, getbe32(chunk->id), getbe64(chunk->size), next_offset, i);
|
pathstr, getbe32(chunk->id), getbe64(chunk->size), next_offset, i);
|
||||||
return 1;
|
return 1;
|
||||||
@ -264,6 +283,44 @@ u32 VerifyCiaFile(const char* path) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 VerifyTmdFile(const char* path) {
|
||||||
|
TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER;
|
||||||
|
TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1);
|
||||||
|
|
||||||
|
// path string
|
||||||
|
char pathstr[32 + 1];
|
||||||
|
TruncateString(pathstr, path, 32, 8);
|
||||||
|
|
||||||
|
// content path string
|
||||||
|
char path_content[256];
|
||||||
|
char* name_content;
|
||||||
|
strncpy(path_content, path, 256);
|
||||||
|
name_content = strrchr(path_content, '/');
|
||||||
|
if (!name_content) return 1; // will not happen
|
||||||
|
name_content++;
|
||||||
|
|
||||||
|
// load TMD file
|
||||||
|
if (LoadTmdFile(tmd, path) != 0) {
|
||||||
|
ShowPrompt(false, "%s\nError: TMD probably corrupted", pathstr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify contents
|
||||||
|
u32 content_count = getbe16(tmd->content_count);
|
||||||
|
for (u32 i = 0; (i < content_count) && (i < CIA_MAX_CONTENTS); i++) {
|
||||||
|
TmdContentChunk* chunk = &(content_list[i]);
|
||||||
|
chunk->type[1] &= ~0x01; // remove crypto flag
|
||||||
|
snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(chunk->id));
|
||||||
|
TruncateString(pathstr, path_content, 32, 8);
|
||||||
|
if (VerifyTmdContent(path_content, 0, chunk, NULL) != 0) {
|
||||||
|
ShowPrompt(false, "%s\nVerification failed", pathstr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
u32 VerifyGameFile(const char* path) {
|
u32 VerifyGameFile(const char* path) {
|
||||||
u32 filetype = IdentifyFileType(path);
|
u32 filetype = IdentifyFileType(path);
|
||||||
if (filetype == GAME_CIA)
|
if (filetype == GAME_CIA)
|
||||||
@ -272,5 +329,7 @@ u32 VerifyGameFile(const char* path) {
|
|||||||
return VerifyNcsdFile(path);
|
return VerifyNcsdFile(path);
|
||||||
else if (filetype == GAME_NCCH)
|
else if (filetype == GAME_NCCH)
|
||||||
return VerifyNcchFile(path, 0, 0);
|
return VerifyNcchFile(path, 0, 0);
|
||||||
|
else if (filetype == GAME_TMD)
|
||||||
|
return VerifyTmdFile(path);
|
||||||
else return 1;
|
else return 1;
|
||||||
}
|
}
|
||||||
|
@ -667,13 +667,12 @@ u32 GodMode() {
|
|||||||
u32 filetype = IdentifyFileType(curr_entry->path);
|
u32 filetype = IdentifyFileType(curr_entry->path);
|
||||||
u32 drvtype = DriveType(curr_entry->path);
|
u32 drvtype = DriveType(curr_entry->path);
|
||||||
|
|
||||||
int mountable = (filetype && (drvtype & DRV_FAT) &&
|
int mountable = ((filetype & FTYPE_MOUNTABLE) && (drvtype & DRV_FAT) &&
|
||||||
!(drvtype & (DRV_IMAGE|DRV_RAMDRIVE))) ?
|
!(drvtype & (DRV_IMAGE|DRV_RAMDRIVE))) ?
|
||||||
++n_opt : -1;
|
++n_opt : -1;
|
||||||
int hexviewer = ++n_opt;
|
int hexviewer = ++n_opt;
|
||||||
int calcsha = ++n_opt;
|
int calcsha = ++n_opt;
|
||||||
int verificable = ((filetype == GAME_CIA) || (filetype == GAME_NCSD) ||
|
int verificable = (filetype & FYTPE_VERIFICABLE) ? ++n_opt : -1;
|
||||||
(filetype == GAME_NCCH)) ? ++n_opt : -1;
|
|
||||||
int injectable = ((clipboard->n_entries == 1) &&
|
int injectable = ((clipboard->n_entries == 1) &&
|
||||||
(clipboard->entry[0].type == T_FILE) &&
|
(clipboard->entry[0].type == T_FILE) &&
|
||||||
(drvtype & DRV_FAT) &&
|
(drvtype & DRV_FAT) &&
|
||||||
@ -721,12 +720,15 @@ u32 GodMode() {
|
|||||||
n_other++;
|
n_other++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((filetype != GAME_CIA) && !ShowProgress(n_processed++, n_marked, path)) break;
|
if (!(filetype & (GAME_CIA|GAME_TMD)) &&
|
||||||
if (VerifyGameFile(path) == 0) n_success++;
|
!ShowProgress(n_processed++, n_marked, path)) break;
|
||||||
else if (filetype != GAME_CIA) ShowProgress(0, 0, path); // redraw progress bar
|
|
||||||
current_dir->entry[i].marked = false;
|
current_dir->entry[i].marked = false;
|
||||||
|
if (VerifyGameFile(path) == 0) n_success++;
|
||||||
|
else { // on failure: set cursor on failed title, break;
|
||||||
|
cursor = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (filetype != GAME_CIA) ShowProgress(1, 1, ""); // CIA verification has progress bar handling
|
|
||||||
if (n_other) ShowPrompt(false, "%lu/%lu files verified ok\n%lu/%lu not of same type",
|
if (n_other) ShowPrompt(false, "%lu/%lu files verified ok\n%lu/%lu not of same type",
|
||||||
n_success, n_marked, n_other, n_marked);
|
n_success, n_marked, n_other, n_marked);
|
||||||
else ShowPrompt(false, "%lu/%lu files verified ok", n_success, n_marked);
|
else ShowPrompt(false, "%lu/%lu files verified ok", n_success, n_marked);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user