diff --git a/arm9/source/filesys/filetype.c b/arm9/source/filesys/filetype.c index 893653e..479ea73 100644 --- a/arm9/source/filesys/filetype.c +++ b/arm9/source/filesys/filetype.c @@ -20,7 +20,7 @@ u64 IdentifyFileType(const char* path) { const u8 png_magic[] = { PNG_MAGIC }; if (!path) return 0; // safety - u8 ALIGN(32) header[0x200]; // minimum required size + u8 ALIGN(32) header[0x2B8]; // minimum required size void* data = (void*) header; size_t fsize = FileGetSize(path); char* fname = strrchr(path, '/'); @@ -36,7 +36,7 @@ u64 IdentifyFileType(const char* path) { } else { ext = ""; } - if (FileGetData(path, header, 0x200, 0) < min(0x200, fsize)) return 0; + if (FileGetData(path, header, 0x2B8, 0) < min(0x2B8, fsize)) return 0; if (!fsize) return 0; if (fsize >= 0x200) { diff --git a/arm9/source/game/bdri.c b/arm9/source/game/bdri.c index 9d0417a..3de4649 100644 --- a/arm9/source/game/bdri.c +++ b/arm9/source/game/bdri.c @@ -669,12 +669,13 @@ u32 ReadTitleInfoEntryFromDB(const char* path, const u8* title_id, TitleInfoEntr return 0; } -u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket* ticket) +u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket** ticket) { FIL file; TickDBPreHeader pre_header; - TicketEntry te; - + TicketEntry* te = NULL; + u32 entry_size; + if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) return 1; @@ -682,8 +683,12 @@ u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket* ticket) if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) || !CheckDBMagic((u8*) &pre_header, true) || - (ReadBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id, (u8*) &te, + (GetBDRIEntrySize(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id, &entry_size) != 0) || + entry_size < sizeof(TicketEntry) + 0x14 || + (te = (TicketEntry*)malloc(entry_size), te == NULL) || + (ReadBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id, (u8*) te, sizeof(TicketEntry)) != 0)) { + free(te); // if allocated fvx_close(bdrifp); bdrifp = NULL; return 1; @@ -692,11 +697,21 @@ u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket* ticket) fvx_close(bdrifp); bdrifp = NULL; - if (te.ticket_size != sizeof(Ticket)) + if (te->ticket_size != GetTicketSize(&te->ticket)) { + free(te); return 1; + } - if (ticket) *ticket = te.ticket; + if (ticket) { + u32 size = te->ticket_size; + memmove(te, &te->ticket, size); // recycle this memory, instead of allocating another + Ticket* tik = realloc(te, size); + if(!tik) tik = (Ticket*)te; + *ticket = tik; + return 0; + } + free(te); return 0; } @@ -770,26 +785,35 @@ u32 AddTitleInfoEntryToDB(const char* path, const u8* title_id, const TitleInfoE u32 AddTicketToDB(const char* path, const u8* title_id, const Ticket* ticket, bool replace) { FIL file; TickDBPreHeader pre_header; + u32 entry_size = sizeof(TicketEntry) + GetTicketContentIndexSize(ticket); - TicketEntry te; - te.unknown = 1; - te.ticket_size = sizeof(Ticket); - te.ticket = *ticket; - - if (fvx_open(&file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK) + TicketEntry* te = (TicketEntry*)malloc(entry_size); + if (!te) { return 1; + } + + te->unknown = 1; + te->ticket_size = GetTicketSize(ticket); + memcpy(&te->ticket, ticket, te->ticket_size); + + if (fvx_open(&file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK) { + free(te); + return 1; + } bdrifp = &file; if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) || !CheckDBMagic((u8*) &pre_header, true) || (AddBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id, - (const u8*) &te, sizeof(TicketEntry), replace) != 0)) { + (const u8*) te, entry_size, replace) != 0)) { + free(te); fvx_close(bdrifp); bdrifp = NULL; return 1; } + free(te); fvx_close(bdrifp); bdrifp = NULL; return 0; diff --git a/arm9/source/game/bdri.h b/arm9/source/game/bdri.h index aac014e..28d68c6 100644 --- a/arm9/source/game/bdri.h +++ b/arm9/source/game/bdri.h @@ -91,7 +91,7 @@ typedef struct { typedef struct { u32 unknown; // usually (assuming always) == 1 - u32 ticket_size; // == 0x350 == sizeof(Ticket) + u32 ticket_size; // commonly == 0x350 Ticket ticket; } __attribute__((packed, aligned(4))) TicketEntry; @@ -100,7 +100,7 @@ u32 GetNumTickets(const char* path); u32 ListTitleInfoEntryTitleIDs(const char* path, u8* title_ids, u32 max_title_ids); u32 ListTicketTitleIDs(const char* path, u8* title_ids, u32 max_title_ids); u32 ReadTitleInfoEntryFromDB(const char* path, const u8* title_id, TitleInfoEntry* tie); -u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket* ticket); +u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket** ticket); u32 RemoveTitleInfoEntryFromDB(const char* path, const u8* title_id); u32 RemoveTicketFromDB(const char* path, const u8* title_id); u32 AddTitleInfoEntryToDB(const char* path, const u8* title_id, const TitleInfoEntry* tie, bool replace); diff --git a/arm9/source/game/cia.c b/arm9/source/game/cia.c index 62f4b58..62b3b02 100644 --- a/arm9/source/game/cia.c +++ b/arm9/source/game/cia.c @@ -8,7 +8,7 @@ u32 ValidateCiaHeader(CiaHeader* header) { if ((header->size_header != CIA_HEADER_SIZE) || (header->size_cert != CIA_CERT_SIZE) || - (header->size_ticket != TICKET_SIZE) || + (header->size_ticket != TICKET_COMMON_SIZE) || (header->size_tmd < TMD_SIZE_MIN) || (header->size_tmd > TMD_SIZE_MAX) || (header->size_content == 0)) @@ -95,12 +95,12 @@ u32 BuildCiaMeta(CiaMeta* meta, void* exthdr, void* smdh) { return 0; } -u32 BuildCiaHeader(CiaHeader* header) { +u32 BuildCiaHeader(CiaHeader* header, u32 ticket_size) { memset(header, 0, sizeof(CiaHeader)); // sizes in header - fill only known sizes, others zero header->size_header = sizeof(CiaHeader); header->size_cert = CIA_CERT_SIZE; - header->size_ticket = sizeof(Ticket); + header->size_ticket = ticket_size; header->size_tmd = 0; header->size_meta = 0; header->size_content = 0; diff --git a/arm9/source/game/cia.h b/arm9/source/game/cia.h index 6f83dc6..d8c690b 100644 --- a/arm9/source/game/cia.h +++ b/arm9/source/game/cia.h @@ -34,8 +34,8 @@ typedef struct { u8 header_padding[0x40 - (CIA_HEADER_SIZE % 0x40)]; u8 cert[CIA_CERT_SIZE]; // cert is aligned and needs no padding - Ticket ticket; - u8 ticket_padding[0x40 - (TICKET_SIZE % 0x40)]; + TicketCommon ticket; + u8 ticket_padding[0x40 - (TICKET_COMMON_SIZE % 0x40)]; TitleMetaData tmd; TmdContentChunk content_list[TMD_MAX_CONTENTS]; } PACKED_ALIGN(16) CiaStub; @@ -66,7 +66,7 @@ u32 FixCiaHeaderForTmd(CiaHeader* header, TitleMetaData* tmd); u32 BuildCiaCert(u8* ciacert); u32 BuildCiaMeta(CiaMeta* meta, void* exthdr, void* smdh); -u32 BuildCiaHeader(CiaHeader* header); +u32 BuildCiaHeader(CiaHeader* header, u32 ticket_size); u32 DecryptCiaContentSequential(void* data, u32 size, u8* ctr, const u8* titlekey); u32 EncryptCiaContentSequential(void* data, u32 size, u8* ctr, const u8* titlekey); diff --git a/arm9/source/game/ticket.c b/arm9/source/game/ticket.c index 239c7c1..34035cc 100644 --- a/arm9/source/game/ticket.c +++ b/arm9/source/game/ticket.c @@ -10,7 +10,11 @@ u32 ValidateTicket(Ticket* ticket) { if ((memcmp(ticket->sig_type, magic, sizeof(magic)) != 0) || ((strncmp((char*) ticket->issuer, TICKET_ISSUER, 0x40) != 0) && (strncmp((char*) ticket->issuer, TICKET_ISSUER_DEV, 0x40) != 0)) || - (ticket->commonkey_idx >= 6)) + (ticket->commonkey_idx >= 6) || + (getbe32(&ticket->content_index[0]) != 0x10014) || + (getbe32(&ticket->content_index[4]) < 0x14) || + (getbe32(&ticket->content_index[12]) != 0x10014) || + (getbe32(&ticket->content_index[16]) != 0)) return 1; return 0; } @@ -28,7 +32,7 @@ u32 ValidateTicketSignature(Ticket* ticket) { } if (!RSA_setKey2048(3, mod, exp) || - !RSA_verify2048((void*) &(ticket->signature), (void*) &(ticket->issuer), 0x210)) + !RSA_verify2048((void*) &(ticket->signature), (void*) &(ticket->issuer), GetTicketSize(ticket) - 0x140)) return 1; return 0; @@ -44,7 +48,7 @@ u32 BuildFakeTicket(Ticket* ticket, u8* title_id) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // set ticket all zero for a clean start - memset(ticket, 0x00, sizeof(Ticket)); + memset(ticket, 0x00, TICKET_COMMON_SIZE); // 0xAC being size of this fake ticket's content index // fill ticket values memcpy(ticket->sig_type, sig_type, 4); memset(ticket->signature, 0xFF, 0x100); @@ -60,6 +64,14 @@ u32 BuildFakeTicket(Ticket* ticket, u8* title_id) { return 0; } +u32 GetTicketContentIndexSize(const Ticket* ticket) { + return getbe32(&ticket->content_index[4]); +} + +u32 GetTicketSize(const Ticket* ticket) { + return sizeof(Ticket) + GetTicketContentIndexSize(ticket); +} + u32 BuildTicketCert(u8* tickcert) { const u8 cert_hash_expected[0x20] = { 0xDC, 0x15, 0x3C, 0x2B, 0x8A, 0x0A, 0xC8, 0x74, 0xA9, 0xDC, 0x78, 0x61, 0x0E, 0x6A, 0x8F, 0xE3, diff --git a/arm9/source/game/ticket.h b/arm9/source/game/ticket.h index 3c0a5bc..0752269 100644 --- a/arm9/source/game/ticket.h +++ b/arm9/source/game/ticket.h @@ -2,7 +2,8 @@ #include "common.h" -#define TICKET_SIZE sizeof(Ticket) +#define TICKET_COMMON_SIZE sizeof(TicketCommon) +#define TICKET_MINIMUM_SIZE sizeof(TicketMinimum) #define TICKET_CDNCERT_SIZE 0x700 #define TICKET_ISSUER "Root-CA00000003-XS0000000c" @@ -13,37 +14,54 @@ // from: https://github.com/profi200/Project_CTR/blob/02159e17ee225de3f7c46ca195ff0f9ba3b3d3e4/ctrtool/tik.h#L15-L39 // all numbers in big endian + +#define TICKETBASE \ + u8 sig_type[4]; \ + u8 signature[0x100]; \ + u8 padding1[0x3C]; \ + u8 issuer[0x40]; \ + u8 ecdsa[0x3C]; \ + u8 version; \ + u8 ca_crl_version; \ + u8 signer_crl_version; \ + u8 titlekey[0x10]; \ + u8 reserved0; \ + u8 ticket_id[8]; \ + u8 console_id[4]; \ + u8 title_id[8]; \ + u8 sys_access[2]; \ + u8 ticket_version[2]; \ + u8 time_mask[4]; \ + u8 permit_mask[4]; \ + u8 title_export; \ + u8 commonkey_idx; \ + u8 reserved1[0x2A]; \ + u8 eshop_id[4]; \ + u8 reserved2; \ + u8 audit; \ + u8 content_permissions[0x40]; \ + u8 reserved3[2]; \ + u8 timelimits[0x40] + typedef struct { - u8 sig_type[4]; - u8 signature[0x100]; - u8 padding1[0x3C]; - u8 issuer[0x40]; - u8 ecdsa[0x3C]; - u8 version; - u8 ca_crl_version; - u8 signer_crl_version; - u8 titlekey[0x10]; - u8 reserved0; - u8 ticket_id[8]; - u8 console_id[4]; - u8 title_id[8]; - u8 sys_access[2]; - u8 ticket_version[2]; - u8 time_mask[4]; - u8 permit_mask[4]; - u8 title_export; - u8 commonkey_idx; - u8 reserved1[0x2A]; - u8 eshop_id[4]; - u8 reserved2; - u8 audit; - u8 content_permissions[0x40]; - u8 reserved3[2]; - u8 timelimits[0x40]; - u8 content_index[0xAC]; + TICKETBASE; + u8 content_index[]; } __attribute__((packed, aligned(4))) Ticket; +typedef struct { + TICKETBASE; + u8 content_index[0xAC]; +} __attribute__((packed, aligned(4))) TicketCommon; + +// minimum allowed content_index is 0x14 +typedef struct { + TICKETBASE; + u8 content_index[0x14]; +} __attribute__((packed, aligned(4))) TicketMinimum; + u32 ValidateTicket(Ticket* ticket); u32 ValidateTicketSignature(Ticket* ticket); u32 BuildFakeTicket(Ticket* ticket, u8* title_id); +u32 GetTicketContentIndexSize(const Ticket* ticket); +u32 GetTicketSize(const Ticket* ticket); u32 BuildTicketCert(u8* tickcert); diff --git a/arm9/source/game/ticketdb.c b/arm9/source/game/ticketdb.c index a99cfd0..0005ce0 100644 --- a/arm9/source/game/ticketdb.c +++ b/arm9/source/game/ticketdb.c @@ -55,7 +55,7 @@ u32 GetTitleKey(u8* titlekey, Ticket* ticket) { Ticket* TicketFromTickDbChunk(u8* chunk, u8* title_id, bool legit_pls) { // chunk must be aligned to 0x200 byte in file and at least 0x400 byte big Ticket* tick = (Ticket*) (void*) (chunk + 0x18); - if ((getle32(chunk + 0x10) == 0) || (getle32(chunk + 0x14) != sizeof(Ticket))) return NULL; + if ((getle32(chunk + 0x10) == 0) || (getle32(chunk + 0x14) != GetTicketSize(tick))) return NULL; if (ValidateTicket(tick) != 0) return NULL; // ticket not validated if (title_id && (memcmp(title_id, tick->title_id, 8) != 0)) return NULL; // title id not matching if (legit_pls && (ValidateTicketSignature(tick) != 0)) return NULL; // legit check using RSA sig @@ -63,7 +63,7 @@ Ticket* TicketFromTickDbChunk(u8* chunk, u8* title_id, bool legit_pls) { return tick; } -u32 FindTicket(Ticket* ticket, u8* title_id, bool force_legit, bool emunand) { +u32 FindTicket(Ticket** ticket, u8* title_id, bool force_legit, bool emunand) { const char* path_db = TICKDB_PATH(emunand); // EmuNAND / SysNAND u8* data = (u8*) malloc(TICKDB_AREA_SIZE); if (!data) return 1; @@ -79,7 +79,11 @@ u32 FindTicket(Ticket* ticket, u8* title_id, bool force_legit, bool emunand) { for (u32 i = 0; !found && (i <= TICKDB_AREA_SIZE - 0x400); i += 0x200) { Ticket* tick = TicketFromTickDbChunk(data + i, title_id, force_legit); if (!tick) continue; - memcpy(ticket, tick, sizeof(Ticket)); + u32 size = GetTicketSize(tick); + Ticket* newtick = (Ticket*)malloc(size); + if (!newtick) break; + memcpy(newtick, tick, size); + *ticket = newtick; found = true; } diff --git a/arm9/source/game/ticketdb.h b/arm9/source/game/ticketdb.h index 41dc53b..2a6d512 100644 --- a/arm9/source/game/ticketdb.h +++ b/arm9/source/game/ticketdb.h @@ -36,7 +36,7 @@ typedef struct { u32 GetTitleKey(u8* titlekey, Ticket* ticket); Ticket* TicketFromTickDbChunk(u8* chunk, u8* title_id, bool legit_pls); -u32 FindTicket(Ticket* ticket, u8* title_id, bool force_legit, bool emunand); +u32 FindTicket(Ticket** ticket, u8* title_id, bool force_legit, bool emunand); u32 FindTitleKey(Ticket* ticket, u8* title_id); u32 AddTitleKeyToInfo(TitleKeysInfo* tik_info, TitleKeyEntry* tik_entry, bool decrypted_in, bool decrypted_out, bool devkit); u32 AddTicketToInfo(TitleKeysInfo* tik_info, Ticket* ticket, bool decrypt); diff --git a/arm9/source/utils/gameutil.c b/arm9/source/utils/gameutil.c index 193750f..ab96fc0 100644 --- a/arm9/source/utils/gameutil.c +++ b/arm9/source/utils/gameutil.c @@ -199,7 +199,8 @@ u32 LoadTmdFile(TitleMetaData* tmd, const char* path) { return 0; } -u32 LoadCdnTicketFile(Ticket* ticket, const char* path_cnt) { +u32 LoadCdnTicketFile(Ticket** ticket, const char* path_cnt) { + if(!ticket) return 1; // path points to CDN content file char path_cetk[256]; strncpy(path_cetk, path_cnt, 256); @@ -211,10 +212,22 @@ u32 LoadCdnTicketFile(Ticket* ticket, const char* path_cnt) { snprintf(ext_cetk, 256 - (ext_cetk - path_cetk), "cetk"); // load and check ticket + TicketMinimum tmp; UINT br; - if ((fvx_qread(path_cetk, ticket, 0, TICKET_SIZE, &br) != FR_OK) || (br != TICKET_SIZE) || - (ValidateTicket(ticket) != 0)) return 1; - + if ((fvx_qread(path_cetk, &tmp, 0, TICKET_MINIMUM_SIZE, &br) != FR_OK) || (br != TICKET_MINIMUM_SIZE) || + ValidateTicket((Ticket*)&tmp) != 0) return 1; + + u32 tik_size = GetTicketSize((Ticket*)&tmp); + + Ticket* tik = (Ticket*)malloc(tik_size); + if (!tik) return 1; + + if ((fvx_qread(path_cetk, tik, 0, tik_size, &br) != FR_OK) || (br != tik_size)) { + free(tik); + return 1; + } + + *ticket = tik; return 0; } @@ -546,7 +559,7 @@ u32 VerifyCiaFile(const char* path) { // load CIA stub if ((LoadCiaStub(cia, path) != 0) || (GetCiaInfo(&info, &(cia->header)) != 0) || - (GetTitleKey(titlekey, &(cia->ticket)) != 0)) { + (GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0)) { ShowPrompt(false, "%s\nError: Probably not a CIA file", pathstr); free(cia); return 1; @@ -607,15 +620,18 @@ u32 VerifyTmdFile(const char* path, bool cdn) { u8 titlekey[0x10] = { 0xFF }; if (cdn) { // load / build ticket (for titlekey / CDN only) - Ticket ticket; + Ticket* ticket = NULL; if (!((LoadCdnTicketFile(&ticket, path) == 0) || - ((BuildFakeTicket(&ticket, tmd->title_id) == 0) && - (FindTitleKey(&ticket, tmd->title_id) == 0))) || - (GetTitleKey(titlekey, &ticket) != 0)) { + ((ticket = (Ticket*)malloc(TICKET_COMMON_SIZE), ticket != NULL) && + (BuildFakeTicket(ticket, tmd->title_id) == 0) && + (FindTitleKey(ticket, tmd->title_id) == 0))) || + (GetTitleKey(titlekey, ticket) != 0)) { ShowPrompt(false, "%s\nError: CDN titlekey not found", pathstr); + free(ticket); free(tmd); return 1; } + free(ticket); } // verify contents @@ -1010,7 +1026,7 @@ u32 CryptCiaFile(const char* orig, const char* dest, u16 crypto) { if (!cia) return 1; if ((LoadCiaStub(cia, orig) != 0) || (GetCiaInfo(&info, &(cia->header)) != 0) || - (GetTitleKey(titlekey, &(cia->ticket)) != 0)) { + (GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0)) { free(cia); return 1; } @@ -1106,16 +1122,27 @@ u32 CryptCdnFileBuffered(const char* orig, const char* dest, u16 crypto, void* b } else tmd = NULL; // load or build ticket - Ticket ticket; + Ticket* ticket = NULL; if (LoadCdnTicketFile(&ticket, orig) != 0) { - if (!tmd || (BuildFakeTicket(&ticket, tmd->title_id) != 0)) return 1; - if (FindTitleKey(&ticket, tmd->title_id) != 0) return 1; + if (!tmd || (ticket = (Ticket*)malloc(TICKET_COMMON_SIZE), !ticket)) return 1; + if ((BuildFakeTicket(ticket, tmd->title_id) != 0)) { + free(ticket); + return 1; + } + if (FindTitleKey(ticket, tmd->title_id) != 0) { + free(ticket); + return 1; + } } // get titlekey u8 titlekey[0x10] = { 0xFF }; - if (GetTitleKey(titlekey, &ticket) != 0) + if (GetTitleKey(titlekey, ticket) != 0) { + free(ticket); return 1; + } + + free(ticket); // find (build fake) content chunk TmdContentChunk* chunk = NULL; @@ -1311,11 +1338,11 @@ u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool // build the CIA stub memset(cia, 0, sizeof(CiaStub)); - if ((BuildCiaHeader(&(cia->header)) != 0) || + if ((BuildCiaHeader(&(cia->header), TICKET_COMMON_SIZE) != 0) || (LoadTmdFile(&(cia->tmd), path_tmd) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (BuildCiaCert(cia->cert) != 0) || - (BuildFakeTicket(&(cia->ticket), cia->tmd.title_id) != 0)) { + (BuildFakeTicket((Ticket*)&(cia->ticket), cia->tmd.title_id) != 0)) { return 1; } @@ -1328,17 +1355,25 @@ u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool if (!content_count) return 1; // get (legit) ticket - Ticket* ticket = &(cia->ticket); + Ticket* ticket = (Ticket*)&(cia->ticket); bool src_emunand = ((*path_tmd == 'B') || (*path_tmd == '4')); if (force_legit) { - Ticket ticket_fake; // backup of the fake ticket - memcpy(&ticket_fake, ticket, sizeof(Ticket)); - if ((cdn && (LoadCdnTicketFile(ticket, path_tmd) != 0)) || - (!cdn && (FindTicket(ticket, title_id, true, src_emunand) != 0))) { + Ticket* ticket_tmp = NULL; + if ((cdn && (LoadCdnTicketFile(&ticket_tmp, path_tmd) != 0)) || + (!cdn && (FindTicket(&ticket_tmp, title_id, true, src_emunand) != 0))) { ShowPrompt(false, "ID %016llX\nLegit ticket not found.", getbe64(title_id)); + free(ticket_tmp); return 1; } - if (getbe32(ticket->console_id)) { + // either, it's a ticket without ways to check ownership data, smaller sized + // or, it's title ticket with > 1024 contents, of which can't make it work with current CiaStub + if (GetTicketSize(ticket_tmp) != TICKET_COMMON_SIZE) { + ShowPrompt(false, "ID %016llX\nLegit ticket of unsupported size.", getbe64(title_id)); + free(ticket_tmp); + return 1; + } + bool copy = true; + if (getbe32(ticket_tmp->console_id)) { static u32 default_action = 0; const char* optionstr[2] = {"Use personalized ticket (legit)", "Use generic ticket (not legit)"}; @@ -1347,27 +1382,36 @@ u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool "ID %016llX\nLegit ticket is personalized.\nCIA may not be installable.\nChoose default action:", getbe64(title_id)); ShowProgress(0, 0, path_tmd); } - if (!default_action) return 1; + if (!default_action) { + free(ticket_tmp); + return 1; + } else if (default_action == 2) { - memcpy(ticket_fake.titlekey, ticket->titlekey, 0x10); - ticket_fake.commonkey_idx = ticket->commonkey_idx; - memcpy(ticket, &ticket_fake, sizeof(Ticket)); + memcpy(ticket->titlekey, ticket_tmp->titlekey, 0x10); + ticket->commonkey_idx = ticket_tmp->commonkey_idx; + copy = false; } } + if (copy) memcpy(ticket, ticket_tmp, TICKET_COMMON_SIZE); + free(ticket_tmp); } else if (cdn) { - if ((LoadCdnTicketFile(ticket, path_tmd) != 0) && - (FindTitleKey(ticket, title_id) != 0)) { + Ticket* ticket_tmp = NULL; + if ((LoadCdnTicketFile(&ticket_tmp, path_tmd) != 0) && + (FindTitleKey(ticket_tmp, title_id) != 0)) { ShowPrompt(false, "ID %016llX\nTitlekey not found.", getbe64(title_id)); + free(ticket_tmp); return 1; } + free(ticket_tmp); } else { - Ticket ticket_tmp; + Ticket* ticket_tmp; if ((FindTitleKey(ticket, title_id) != 0) && (FindTicket(&ticket_tmp, title_id, false, src_emunand) == 0)) { // we just copy the titlekey from a valid ticket (if we can) - memcpy(ticket->titlekey, ticket_tmp.titlekey, 0x10); - ticket->commonkey_idx = ticket_tmp.commonkey_idx; + memcpy(ticket->titlekey, ticket_tmp->titlekey, 0x10); + ticket->commonkey_idx = ticket_tmp->commonkey_idx; } + free(ticket_tmp); } // content path string @@ -1399,7 +1443,7 @@ u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool // insert contents u8 titlekey[16] = { 0xFF }; - if ((GetTitleKey(titlekey, &(cia->ticket)) != 0) && force_legit) return 1; + if ((GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0) && force_legit) return 1; if (WriteCiaStub(cia, path_cia) != 0) return 1; for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) { TmdContentChunk* chunk = &(content_list[i]); @@ -1472,9 +1516,9 @@ u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) { CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); if (!cia) return 1; memset(cia, 0, sizeof(CiaStub)); - if ((BuildCiaHeader(&(cia->header)) != 0) || + if ((BuildCiaHeader(&(cia->header), TICKET_COMMON_SIZE) != 0) || (BuildCiaCert(cia->cert) != 0) || - (BuildFakeTicket(&(cia->ticket), title_id) != 0) || + (BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) || (BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, 0)) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (WriteCiaStub(cia, path_cia) != 0)) { @@ -1499,7 +1543,7 @@ u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) { free(meta); // write the CIA stub (take #2) - FindTitleKey((&cia->ticket), title_id); + FindTitleKey((Ticket*)(&cia->ticket), title_id); if ((FixTmdHashes(&(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (WriteCiaStub(cia, path_cia) != 0)) { @@ -1538,9 +1582,9 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); if (!cia) return 1; memset(cia, 0, sizeof(CiaStub)); - if ((BuildCiaHeader(&(cia->header)) != 0) || + if ((BuildCiaHeader(&(cia->header), TICKET_COMMON_SIZE) != 0) || (BuildCiaCert(cia->cert) != 0) || - (BuildFakeTicket(&(cia->ticket), title_id) != 0) || + (BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) || (BuildFakeTmd(&(cia->tmd), title_id, content_count, save_size, 0)) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (WriteCiaStub(cia, path_cia) != 0)) { @@ -1572,7 +1616,7 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) { if (meta) free(meta); // write the CIA stub (take #2) - FindTitleKey(&(cia->ticket), title_id); + FindTitleKey((Ticket*)&(cia->ticket), title_id); if ((FixTmdHashes(&(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (WriteCiaStub(cia, path_cia) != 0)) { @@ -1616,9 +1660,9 @@ u32 BuildCiaFromNdsFile(const char* path_nds, const char* path_cia) { CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub)); if (!cia) return 1; memset(cia, 0, sizeof(CiaStub)); - if ((BuildCiaHeader(&(cia->header)) != 0) || + if ((BuildCiaHeader(&(cia->header), TICKET_COMMON_SIZE) != 0) || (BuildCiaCert(cia->cert) != 0) || - (BuildFakeTicket(&(cia->ticket), title_id) != 0) || + (BuildFakeTicket((Ticket*)&(cia->ticket), title_id) != 0) || (BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, privsave_size)) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (WriteCiaStub(cia, path_cia) != 0)) { @@ -1635,7 +1679,7 @@ u32 BuildCiaFromNdsFile(const char* path_nds, const char* path_cia) { } // write the CIA stub (take #2) - FindTitleKey((&cia->ticket), title_id); + FindTitleKey((Ticket*)(&cia->ticket), title_id); if ((FixTmdHashes(&(cia->tmd)) != 0) || (FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) || (WriteCiaStub(cia, path_cia) != 0)) { @@ -2030,8 +2074,8 @@ u32 ShowCiaCheckerInfo(const char* path) { bool is_dlc = ((title_id >> 32) == 0x0004008C); // check ticket - if (ValidateTicket(&(cia->ticket)) == 0) - state_ticket = (ValidateTicketSignature(&(cia->ticket)) == 0) ? 2 : 1; + if (ValidateTicket((Ticket*)&(cia->ticket)) == 0) + state_ticket = (ValidateTicketSignature((Ticket*)&(cia->ticket)) == 0) ? 2 : 1; // check tmd if (ValidateTmd(&(cia->tmd)) == 0) @@ -2275,10 +2319,10 @@ u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump) { u64 filetype = path_in ? IdentifyFileType(path_in) : 0; if (filetype & GAME_TICKET) { - Ticket ticket; - if ((fvx_qread(path_in, &ticket, 0, TICKET_SIZE, NULL) != FR_OK) || + TicketCommon ticket; + if ((fvx_qread(path_in, &ticket, 0, TICKET_COMMON_SIZE, NULL) != FR_OK) || (TIKDB_SIZE(tik_info) + 32 > STD_BUFFER_SIZE) || - (AddTicketToInfo(tik_info, &ticket, dec) != 0)) { + (AddTicketToInfo(tik_info, (Ticket*)&ticket, dec) != 0)) { return 1; } } else if (filetype & SYS_TICKDB) { @@ -2476,7 +2520,7 @@ u32 LoadNcchFromGameFile(const char* path, NcchHeader* ncch) { u8 titlekey[16]; u8 ctr[16]; GetTmdCtr(ctr, chunk); - if (GetTitleKey(titlekey, &(cia->ticket)) != 0) { + if (GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0) { free(cia); return 1; } diff --git a/arm9/source/virtual/vgame.c b/arm9/source/virtual/vgame.c index 1171188..544e9a5 100644 --- a/arm9/source/virtual/vgame.c +++ b/arm9/source/virtual/vgame.c @@ -848,7 +848,7 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) { (ReadImageBytes((u8*) cia, 0, info.offset_content) != 0)) return false; offset_cia = vdir->offset; // always zero(!) - GetTitleKey(cia_titlekey, &(cia->ticket)); + GetTitleKey(cia_titlekey, (Ticket*)&(cia->ticket)); if (!BuildVGameCiaDir()) return false; } else if ((vdir->flags & VFLAG_NCSD) && (offset_ncsd != vdir->offset)) { if ((ReadImageBytes((u8*) ncsd, 0, sizeof(NcsdHeader)) != 0) || diff --git a/arm9/source/virtual/vtickdb.c b/arm9/source/virtual/vtickdb.c index ccc8079..0028e76 100644 --- a/arm9/source/virtual/vtickdb.c +++ b/arm9/source/virtual/vtickdb.c @@ -19,6 +19,7 @@ typedef struct { u32 commonkey_idx; u32 offset; + u32 size; u8 title_id[8]; u8 titlekey[16]; u8 ticket_id[8]; @@ -54,6 +55,7 @@ u32 AddTickDbInfo(TickDbInfo* info, Ticket* ticket, u32 offset, bool replace) { TickDbEntry* entry = info->entries + info->n_entries; entry->commonkey_idx = ticket->commonkey_idx; entry->offset = offset; + entry->size = GetTicketSize(ticket); memcpy(entry->title_id, ticket->title_id, 8); memcpy(entry->titlekey, ticket->titlekey, 16); memcpy(entry->ticket_id, ticket->ticket_id, 8); @@ -178,7 +180,7 @@ bool ReadVTickDbDir(VirtualFile* vfile, VirtualDir* vdir) { memset(vfile, 0, sizeof(VirtualFile)); snprintf(vfile->name, 32, NAME_TIK, getbe64(tick_entry->title_id), getbe32(tick_entry->console_id)); vfile->offset = tick_entry->offset & ~OFLAG_RAW; - vfile->size = sizeof(Ticket); + vfile->size = tick_entry->size; vfile->keyslot = 0xFF; vfile->flags = (vdir->flags | VFLAG_READONLY) & ~VFLAG_DIR;