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_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_CIACHECK(tp) (tp&GAME_CIA)
|
||||
#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_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 "unittype.h"
|
||||
#include "cert.h"
|
||||
#include "sha.h"
|
||||
#include "rsa.h"
|
||||
#include "ff.h"
|
||||
@ -19,19 +20,11 @@ u32 ValidateTicketSignature(Ticket* ticket) {
|
||||
static u8 mod[0x100] = { 0 };
|
||||
static u32 exp = 0;
|
||||
|
||||
// grab cert from cert.db
|
||||
if (!got_modexp) {
|
||||
Certificate cert;
|
||||
FIL db;
|
||||
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);
|
||||
// grab mod/exp from cert from cert.db
|
||||
if (LoadCertFromCertDb(0x3F10, NULL, mod, &exp) == 0)
|
||||
got_modexp = true;
|
||||
else return 1;
|
||||
}
|
||||
|
||||
if (!RSA_setKey2048(3, mod, exp) ||
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include "common.h"
|
||||
|
||||
#define TICKET_SIZE sizeof(Ticket)
|
||||
#define CERT_SIZE sizeof(Certificate)
|
||||
#define TICKET_CDNCERT_SIZE 0x700
|
||||
|
||||
#define TICKET_ISSUER "Root-CA00000003-XS0000000c"
|
||||
@ -44,21 +43,6 @@ typedef struct {
|
||||
u8 content_index[0xAC];
|
||||
} __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 ValidateTicketSignature(Ticket* ticket);
|
||||
u32 BuildFakeTicket(Ticket* ticket, u8* title_id);
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "tmd.h"
|
||||
#include "unittype.h"
|
||||
#include "cert.h"
|
||||
#include "sha.h"
|
||||
#include "rsa.h"
|
||||
#include "ff.h"
|
||||
|
||||
u32 ValidateTmd(TitleMetaData* tmd) {
|
||||
@ -12,6 +14,25 @@ u32 ValidateTmd(TitleMetaData* tmd) {
|
||||
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) {
|
||||
memset(ctr, 0, 16);
|
||||
memcpy(ctr, chunk->index, 2);
|
||||
|
@ -58,6 +58,7 @@ typedef struct {
|
||||
} __attribute__((packed)) TitleMetaData;
|
||||
|
||||
u32 ValidateTmd(TitleMetaData* tmd);
|
||||
u32 ValidateTmdSignature(TitleMetaData* tmd);
|
||||
u32 GetTmdCtr(u8* ctr, TmdContentChunk* chunk);
|
||||
u32 FixTmdHashes(TitleMetaData* tmd);
|
||||
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 key_buildable = (FTYPE_KEYBUILD(filetype)) && !in_output_path && !((drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND));
|
||||
bool titleinfo = (FTYPE_TITLEINFO(filetype));
|
||||
bool ciacheckable = (FTYPE_CIACHECK(filetype));
|
||||
bool renamable = (FTYPE_RENAMABLE(filetype));
|
||||
bool trimable = (FTYPE_TRIMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) &&
|
||||
!(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
|
||||
n_opt = 0;
|
||||
int show_info = (titleinfo) ? ++n_opt : -1;
|
||||
int ciacheck = (ciacheckable) ? ++n_opt : -1;
|
||||
int mount = (mountable) ? ++n_opt : -1;
|
||||
int restore = (restorable) ? ++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 (ncsdfix > 0) optionstr[ncsdfix-1] = "Rebuild NCSD header";
|
||||
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 (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)";
|
||||
@ -1647,6 +1650,10 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
||||
ShowPrompt(false, "Title info: not found");
|
||||
return 0;
|
||||
}
|
||||
else if (user_select == ciacheck) { // -> CIA checker tool
|
||||
ShowCiaCheckerInfo(file_path);
|
||||
return 0;
|
||||
}
|
||||
else if (user_select == hsinject) { // -> Inject to Health & Safety
|
||||
char* destdrv[2] = { NULL };
|
||||
n_opt = 0;
|
||||
|
@ -1816,6 +1816,73 @@ u32 ShowGameFileTitleInfo(const char* path) {
|
||||
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) {
|
||||
FIL fp_info;
|
||||
FIL fp_xorpad;
|
||||
|
@ -12,6 +12,7 @@ u32 ExtractDataFromDisaDiff(const char* path);
|
||||
u64 GetGameFileTrimmedSize(const char* path);
|
||||
u32 TrimGameFile(const char* path);
|
||||
u32 ShowGameFileTitleInfo(const char* path);
|
||||
u32 ShowCiaCheckerInfo(const char* path);
|
||||
u32 GetTmdContentPath(char* path_content, const char* path_tmd);
|
||||
u32 BuildNcchInfoXorpads(const char* destdir, const char* path);
|
||||
u32 CheckHealthAndSafetyInject(const char* hsdrv);
|
||||
|
Loading…
x
Reference in New Issue
Block a user