mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Added CIA Checker Tool
This commit is contained in:
parent
9467bd6050
commit
3446a43127
@ -52,6 +52,7 @@
|
|||||||
#define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB))
|
#define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB))
|
||||||
#define FTYPE_KEYBUILD(tp) (tp&(BIN_KEYDB|BIN_LEGKEY))
|
#define FTYPE_KEYBUILD(tp) (tp&(BIN_KEYDB|BIN_LEGKEY))
|
||||||
#define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS|GAME_GBA|GAME_TAD|GAME_3DSX))
|
#define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS|GAME_GBA|GAME_TAD|GAME_3DSX))
|
||||||
|
#define FTYPE_CIACHECK(tp) (tp&GAME_CIA)
|
||||||
#define FTYPE_RENAMABLE(tp) (tp&(GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_NDS|GAME_GBA))
|
#define FTYPE_RENAMABLE(tp) (tp&(GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_NDS|GAME_GBA))
|
||||||
#define FTYPE_TRIMABLE(tp) (tp&(IMG_NAND|GAME_NCCH|GAME_NCSD|GAME_NDS|SYS_FIRM))
|
#define FTYPE_TRIMABLE(tp) (tp&(IMG_NAND|GAME_NCCH|GAME_NCSD|GAME_NDS|SYS_FIRM))
|
||||||
#define FTYPE_TRANSFERABLE(tp) ((u64) (tp&(IMG_FAT|FLAG_CTR)) == (u64) (IMG_FAT|FLAG_CTR))
|
#define FTYPE_TRANSFERABLE(tp) ((u64) (tp&(IMG_FAT|FLAG_CTR)) == (u64) (IMG_FAT|FLAG_CTR))
|
||||||
|
21
arm9/source/game/cert.c
Normal file
21
arm9/source/game/cert.c
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "cert.h"
|
||||||
|
#include "ff.h"
|
||||||
|
|
||||||
|
u32 LoadCertFromCertDb(u64 offset, Certificate* cert, u8* mod, u32* exp) {
|
||||||
|
Certificate cert_local;
|
||||||
|
FIL db;
|
||||||
|
UINT bytes_read;
|
||||||
|
|
||||||
|
// not much in terms of error checking here
|
||||||
|
if (f_open(&db, "1:/dbs/certs.db", FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
||||||
|
return 1;
|
||||||
|
f_lseek(&db, offset);
|
||||||
|
if (!cert) cert = &cert_local;
|
||||||
|
f_read(&db, cert, CERT_SIZE, &bytes_read);
|
||||||
|
f_close(&db);
|
||||||
|
|
||||||
|
if (mod) memcpy(mod, cert->mod, 0x100);
|
||||||
|
if (exp) *exp = getle32(cert->exp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
22
arm9/source/game/cert.h
Normal file
22
arm9/source/game/cert.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define CERT_SIZE sizeof(Certificate)
|
||||||
|
|
||||||
|
// from: http://3dbrew.org/wiki/Certificates
|
||||||
|
// all numbers in big endian
|
||||||
|
typedef struct {
|
||||||
|
u8 sig_type[4]; // expected: 0x010004 / RSA_2048 SHA256
|
||||||
|
u8 signature[0x100];
|
||||||
|
u8 padding0[0x3C];
|
||||||
|
u8 issuer[0x40];
|
||||||
|
u8 keytype[4]; // expected: 0x01 / RSA_2048
|
||||||
|
u8 name[0x40];
|
||||||
|
u8 unknown[4];
|
||||||
|
u8 mod[0x100];
|
||||||
|
u8 exp[0x04];
|
||||||
|
u8 padding1[0x34];
|
||||||
|
} __attribute__((packed)) Certificate;
|
||||||
|
|
||||||
|
u32 LoadCertFromCertDb(u64 offset, Certificate* cert, u8* mod, u32* exp);
|
@ -1,5 +1,6 @@
|
|||||||
#include "ticket.h"
|
#include "ticket.h"
|
||||||
#include "unittype.h"
|
#include "unittype.h"
|
||||||
|
#include "cert.h"
|
||||||
#include "sha.h"
|
#include "sha.h"
|
||||||
#include "rsa.h"
|
#include "rsa.h"
|
||||||
#include "ff.h"
|
#include "ff.h"
|
||||||
@ -19,19 +20,11 @@ u32 ValidateTicketSignature(Ticket* ticket) {
|
|||||||
static u8 mod[0x100] = { 0 };
|
static u8 mod[0x100] = { 0 };
|
||||||
static u32 exp = 0;
|
static u32 exp = 0;
|
||||||
|
|
||||||
// grab cert from cert.db
|
|
||||||
if (!got_modexp) {
|
if (!got_modexp) {
|
||||||
Certificate cert;
|
// grab mod/exp from cert from cert.db
|
||||||
FIL db;
|
if (LoadCertFromCertDb(0x3F10, NULL, mod, &exp) == 0)
|
||||||
UINT bytes_read;
|
|
||||||
if (f_open(&db, "1:/dbs/certs.db", FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
|
||||||
return 1;
|
|
||||||
f_lseek(&db, 0x3F10);
|
|
||||||
f_read(&db, &cert, CERT_SIZE, &bytes_read);
|
|
||||||
f_close(&db);
|
|
||||||
memcpy(mod, cert.mod, 0x100);
|
|
||||||
exp = getle32(cert.exp);
|
|
||||||
got_modexp = true;
|
got_modexp = true;
|
||||||
|
else return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!RSA_setKey2048(3, mod, exp) ||
|
if (!RSA_setKey2048(3, mod, exp) ||
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#define TICKET_SIZE sizeof(Ticket)
|
#define TICKET_SIZE sizeof(Ticket)
|
||||||
#define CERT_SIZE sizeof(Certificate)
|
|
||||||
#define TICKET_CDNCERT_SIZE 0x700
|
#define TICKET_CDNCERT_SIZE 0x700
|
||||||
|
|
||||||
#define TICKET_ISSUER "Root-CA00000003-XS0000000c"
|
#define TICKET_ISSUER "Root-CA00000003-XS0000000c"
|
||||||
@ -44,21 +43,6 @@ typedef struct {
|
|||||||
u8 content_index[0xAC];
|
u8 content_index[0xAC];
|
||||||
} __attribute__((packed)) Ticket;
|
} __attribute__((packed)) Ticket;
|
||||||
|
|
||||||
// from: http://3dbrew.org/wiki/Certificates
|
|
||||||
// all numbers in big endian
|
|
||||||
typedef struct {
|
|
||||||
u8 sig_type[4]; // expected: 0x010004 / RSA_2048 SHA256
|
|
||||||
u8 signature[0x100];
|
|
||||||
u8 padding0[0x3C];
|
|
||||||
u8 issuer[0x40];
|
|
||||||
u8 keytype[4]; // expected: 0x01 / RSA_2048
|
|
||||||
u8 name[0x40];
|
|
||||||
u8 unknown[4];
|
|
||||||
u8 mod[0x100];
|
|
||||||
u8 exp[0x04];
|
|
||||||
u8 padding1[0x34];
|
|
||||||
} __attribute__((packed)) Certificate;
|
|
||||||
|
|
||||||
u32 ValidateTicket(Ticket* ticket);
|
u32 ValidateTicket(Ticket* ticket);
|
||||||
u32 ValidateTicketSignature(Ticket* ticket);
|
u32 ValidateTicketSignature(Ticket* ticket);
|
||||||
u32 BuildFakeTicket(Ticket* ticket, u8* title_id);
|
u32 BuildFakeTicket(Ticket* ticket, u8* title_id);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "tmd.h"
|
#include "tmd.h"
|
||||||
#include "unittype.h"
|
#include "unittype.h"
|
||||||
|
#include "cert.h"
|
||||||
#include "sha.h"
|
#include "sha.h"
|
||||||
|
#include "rsa.h"
|
||||||
#include "ff.h"
|
#include "ff.h"
|
||||||
|
|
||||||
u32 ValidateTmd(TitleMetaData* tmd) {
|
u32 ValidateTmd(TitleMetaData* tmd) {
|
||||||
@ -12,6 +14,25 @@ u32 ValidateTmd(TitleMetaData* tmd) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 ValidateTmdSignature(TitleMetaData* tmd) {
|
||||||
|
static bool got_modexp = false;
|
||||||
|
static u8 mod[0x100] = { 0 };
|
||||||
|
static u32 exp = 0;
|
||||||
|
|
||||||
|
if (!got_modexp) {
|
||||||
|
// grab mod/exp from cert from cert.db
|
||||||
|
if (LoadCertFromCertDb(0x3C10, NULL, mod, &exp) == 0)
|
||||||
|
got_modexp = true;
|
||||||
|
else return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RSA_setKey2048(3, mod, exp) ||
|
||||||
|
!RSA_verify2048((void*) &(tmd->signature), (void*) &(tmd->issuer), 0xC4))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
u32 GetTmdCtr(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);
|
||||||
|
@ -58,6 +58,7 @@ typedef struct {
|
|||||||
} __attribute__((packed)) TitleMetaData;
|
} __attribute__((packed)) TitleMetaData;
|
||||||
|
|
||||||
u32 ValidateTmd(TitleMetaData* tmd);
|
u32 ValidateTmd(TitleMetaData* tmd);
|
||||||
|
u32 ValidateTmdSignature(TitleMetaData* tmd);
|
||||||
u32 GetTmdCtr(u8* ctr, TmdContentChunk* chunk);
|
u32 GetTmdCtr(u8* ctr, TmdContentChunk* chunk);
|
||||||
u32 FixTmdHashes(TitleMetaData* tmd);
|
u32 FixTmdHashes(TitleMetaData* tmd);
|
||||||
u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size);
|
u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size);
|
||||||
|
@ -1028,6 +1028,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
|||||||
bool tik_buildable = (FTYPE_TIKBUILD(filetype)) && !in_output_path;
|
bool tik_buildable = (FTYPE_TIKBUILD(filetype)) && !in_output_path;
|
||||||
bool key_buildable = (FTYPE_KEYBUILD(filetype)) && !in_output_path && !((drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND));
|
bool key_buildable = (FTYPE_KEYBUILD(filetype)) && !in_output_path && !((drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND));
|
||||||
bool titleinfo = (FTYPE_TITLEINFO(filetype));
|
bool titleinfo = (FTYPE_TITLEINFO(filetype));
|
||||||
|
bool ciacheckable = (FTYPE_CIACHECK(filetype));
|
||||||
bool renamable = (FTYPE_RENAMABLE(filetype));
|
bool renamable = (FTYPE_RENAMABLE(filetype));
|
||||||
bool trimable = (FTYPE_TRIMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) &&
|
bool trimable = (FTYPE_TRIMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) &&
|
||||||
!(drvtype & DRV_CTRNAND) && !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_IMAGE);
|
!(drvtype & DRV_CTRNAND) && !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_IMAGE);
|
||||||
@ -1230,6 +1231,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
|||||||
// stuff for special menu starts here
|
// stuff for special menu starts here
|
||||||
n_opt = 0;
|
n_opt = 0;
|
||||||
int show_info = (titleinfo) ? ++n_opt : -1;
|
int show_info = (titleinfo) ? ++n_opt : -1;
|
||||||
|
int ciacheck = (ciacheckable) ? ++n_opt : -1;
|
||||||
int mount = (mountable) ? ++n_opt : -1;
|
int mount = (mountable) ? ++n_opt : -1;
|
||||||
int restore = (restorable) ? ++n_opt : -1;
|
int restore = (restorable) ? ++n_opt : -1;
|
||||||
int ebackup = (ebackupable) ? ++n_opt : -1;
|
int ebackup = (ebackupable) ? ++n_opt : -1;
|
||||||
@ -1266,6 +1268,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
|||||||
if (ebackup > 0) optionstr[ebackup-1] = "Update embedded backup";
|
if (ebackup > 0) optionstr[ebackup-1] = "Update embedded backup";
|
||||||
if (ncsdfix > 0) optionstr[ncsdfix-1] = "Rebuild NCSD header";
|
if (ncsdfix > 0) optionstr[ncsdfix-1] = "Rebuild NCSD header";
|
||||||
if (show_info > 0) optionstr[show_info-1] = "Show title info";
|
if (show_info > 0) optionstr[show_info-1] = "Show title info";
|
||||||
|
if (ciacheck > 0) optionstr[ciacheck-1] = "CIA checker tool";
|
||||||
if (decrypt > 0) optionstr[decrypt-1] = (cryptable_inplace) ? "Decrypt file (...)" : "Decrypt file (" OUTPUT_PATH ")";
|
if (decrypt > 0) optionstr[decrypt-1] = (cryptable_inplace) ? "Decrypt file (...)" : "Decrypt file (" OUTPUT_PATH ")";
|
||||||
if (encrypt > 0) optionstr[encrypt-1] = (cryptable_inplace) ? "Encrypt file (...)" : "Encrypt file (" OUTPUT_PATH ")";
|
if (encrypt > 0) optionstr[encrypt-1] = (cryptable_inplace) ? "Encrypt file (...)" : "Encrypt file (" OUTPUT_PATH ")";
|
||||||
if (cia_build > 0) optionstr[cia_build-1] = (cia_build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)";
|
if (cia_build > 0) optionstr[cia_build-1] = (cia_build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)";
|
||||||
@ -1647,6 +1650,10 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
|||||||
ShowPrompt(false, "Title info: not found");
|
ShowPrompt(false, "Title info: not found");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
else if (user_select == ciacheck) { // -> CIA checker tool
|
||||||
|
ShowCiaCheckerInfo(file_path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
else if (user_select == hsinject) { // -> Inject to Health & Safety
|
else if (user_select == hsinject) { // -> Inject to Health & Safety
|
||||||
char* destdrv[2] = { NULL };
|
char* destdrv[2] = { NULL };
|
||||||
n_opt = 0;
|
n_opt = 0;
|
||||||
|
@ -1816,6 +1816,73 @@ u32 ShowGameFileTitleInfo(const char* path) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 ShowCiaCheckerInfo(const char* path) {
|
||||||
|
CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub));
|
||||||
|
if (!cia) return 1;
|
||||||
|
|
||||||
|
// path string
|
||||||
|
char pathstr[32 + 1];
|
||||||
|
TruncateString(pathstr, path, 32, 8);
|
||||||
|
|
||||||
|
// load CIA stub
|
||||||
|
if (LoadCiaStub(cia, path) != 0) {
|
||||||
|
ShowPrompt(false, "%s\nError: Probably not a CIA file", pathstr);
|
||||||
|
free(cia);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// states: 0 -> invalid / 1 -> valid / badsig / 2 -> valid / goodsig
|
||||||
|
u32 state_ticket = 0;
|
||||||
|
u32 state_tmd = 0;
|
||||||
|
u32 content_count = getbe16(cia->tmd.content_count);
|
||||||
|
u32 content_found = 0;
|
||||||
|
u64 title_id = getbe64(cia->ticket.title_id);
|
||||||
|
u32 console_id = getbe32(cia->ticket.console_id);
|
||||||
|
bool missing_first = false;
|
||||||
|
bool is_dlc = ((title_id >> 32) == 0x0004008C);
|
||||||
|
|
||||||
|
// check ticket
|
||||||
|
if (ValidateTicket(&(cia->ticket)) == 0)
|
||||||
|
state_ticket = (ValidateTicketSignature(&(cia->ticket)) == 0) ? 2 : 1;
|
||||||
|
|
||||||
|
// check tmd
|
||||||
|
if (ValidateTmd(&(cia->tmd)) == 0)
|
||||||
|
state_tmd = (ValidateTmdSignature(&(cia->tmd)) == 0) ? 2 : 1;
|
||||||
|
|
||||||
|
// check for available contents
|
||||||
|
u8* cnt_index = cia->header.content_index;
|
||||||
|
for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) {
|
||||||
|
TmdContentChunk* chunk = &(cia->content_list[i]);
|
||||||
|
u16 index = getbe16(chunk->index);
|
||||||
|
if (cnt_index[index/8] & (1 << (7-(index%8)))) content_found++;
|
||||||
|
else if (i == 0) missing_first = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CIA type string
|
||||||
|
char typestr[32];
|
||||||
|
if (!state_ticket || !state_tmd || missing_first || (!is_dlc && (content_found != content_count)))
|
||||||
|
snprintf(typestr, 32, "Possibly Broken");
|
||||||
|
else snprintf(typestr, 32, "%s %s%s",
|
||||||
|
console_id ? "Personal" : "Universal",
|
||||||
|
(state_ticket == 2) ? "Legit" : (state_tmd == 2) ? "Pirate Legit" : "Custom",
|
||||||
|
is_dlc ? " DLC" : "");
|
||||||
|
|
||||||
|
// output results
|
||||||
|
s32 state_verify = -1;
|
||||||
|
while (true) {
|
||||||
|
if (!ShowPrompt(state_verify < 0, "%s\n%s CIA File\n \nTitle ID: %016llX\nConsole ID: %08lX\nContents in CIA: %lu/%lu\nTicket/TMD: %s/%s\nVerification: %s",
|
||||||
|
pathstr, typestr, title_id, console_id,
|
||||||
|
content_found, content_count,
|
||||||
|
(state_ticket == 0) ? "invalid" : (state_ticket == 2) ? "legit" : "illegit",
|
||||||
|
(state_tmd == 0) ? "invalid" : (state_tmd == 2) ? "legit" : "illegit",
|
||||||
|
(state_verify < 0) ? "pending\n \nProceed with verification?" : (state_verify == 0) ? "passed" : "failed") ||
|
||||||
|
(state_verify >= 0)) break;
|
||||||
|
state_verify = VerifyCiaFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (state_ticket && state_tmd) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
u32 BuildNcchInfoXorpads(const char* destdir, const char* path) {
|
u32 BuildNcchInfoXorpads(const char* destdir, const char* path) {
|
||||||
FIL fp_info;
|
FIL fp_info;
|
||||||
FIL fp_xorpad;
|
FIL fp_xorpad;
|
||||||
|
@ -12,6 +12,7 @@ u32 ExtractDataFromDisaDiff(const char* path);
|
|||||||
u64 GetGameFileTrimmedSize(const char* path);
|
u64 GetGameFileTrimmedSize(const char* path);
|
||||||
u32 TrimGameFile(const char* path);
|
u32 TrimGameFile(const char* path);
|
||||||
u32 ShowGameFileTitleInfo(const char* path);
|
u32 ShowGameFileTitleInfo(const char* path);
|
||||||
|
u32 ShowCiaCheckerInfo(const char* path);
|
||||||
u32 GetTmdContentPath(char* path_content, const char* path_tmd);
|
u32 GetTmdContentPath(char* path_content, const char* path_tmd);
|
||||||
u32 BuildNcchInfoXorpads(const char* destdir, const char* path);
|
u32 BuildNcchInfoXorpads(const char* destdir, const char* path);
|
||||||
u32 CheckHealthAndSafetyInject(const char* hsdrv);
|
u32 CheckHealthAndSafetyInject(const char* hsdrv);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user