mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Support for variable sized tickets
Except for cia building or loading cia just yet. Added more checks on ticket content index, mainly due to having effects in the ticket format itself, and are unknown still. Ability to determine ticket size. Verify signature with ticket's proper size. Changes to use the new Ticket struct with the flexible array member.
This commit is contained in:
parent
e0e72142bc
commit
4e04849860
@ -20,7 +20,7 @@ u64 IdentifyFileType(const char* path) {
|
|||||||
const u8 png_magic[] = { PNG_MAGIC };
|
const u8 png_magic[] = { PNG_MAGIC };
|
||||||
|
|
||||||
if (!path) return 0; // safety
|
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;
|
void* data = (void*) header;
|
||||||
size_t fsize = FileGetSize(path);
|
size_t fsize = FileGetSize(path);
|
||||||
char* fname = strrchr(path, '/');
|
char* fname = strrchr(path, '/');
|
||||||
@ -36,7 +36,7 @@ u64 IdentifyFileType(const char* path) {
|
|||||||
} else {
|
} else {
|
||||||
ext = "";
|
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) return 0;
|
||||||
|
|
||||||
if (fsize >= 0x200) {
|
if (fsize >= 0x200) {
|
||||||
|
@ -669,11 +669,12 @@ u32 ReadTitleInfoEntryFromDB(const char* path, const u8* title_id, TitleInfoEntr
|
|||||||
return 0;
|
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;
|
FIL file;
|
||||||
TickDBPreHeader pre_header;
|
TickDBPreHeader pre_header;
|
||||||
TicketEntry te;
|
TicketEntry* te = NULL;
|
||||||
|
u32 entry_size;
|
||||||
|
|
||||||
if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
||||||
return 1;
|
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) ||
|
if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) ||
|
||||||
!CheckDBMagic((u8*) &pre_header, true) ||
|
!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)) {
|
sizeof(TicketEntry)) != 0)) {
|
||||||
|
free(te); // if allocated
|
||||||
fvx_close(bdrifp);
|
fvx_close(bdrifp);
|
||||||
bdrifp = NULL;
|
bdrifp = NULL;
|
||||||
return 1;
|
return 1;
|
||||||
@ -692,11 +697,21 @@ u32 ReadTicketFromDB(const char* path, const u8* title_id, Ticket* ticket)
|
|||||||
fvx_close(bdrifp);
|
fvx_close(bdrifp);
|
||||||
bdrifp = NULL;
|
bdrifp = NULL;
|
||||||
|
|
||||||
if (te.ticket_size != sizeof(Ticket))
|
if (te->ticket_size != GetTicketSize(&te->ticket)) {
|
||||||
|
free(te);
|
||||||
return 1;
|
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;
|
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) {
|
u32 AddTicketToDB(const char* path, const u8* title_id, const Ticket* ticket, bool replace) {
|
||||||
FIL file;
|
FIL file;
|
||||||
TickDBPreHeader pre_header;
|
TickDBPreHeader pre_header;
|
||||||
|
u32 entry_size = sizeof(TicketEntry) + GetTicketContentIndexSize(ticket);
|
||||||
|
|
||||||
TicketEntry te;
|
TicketEntry* te = (TicketEntry*)malloc(entry_size);
|
||||||
te.unknown = 1;
|
if (!te) {
|
||||||
te.ticket_size = sizeof(Ticket);
|
|
||||||
te.ticket = *ticket;
|
|
||||||
|
|
||||||
if (fvx_open(&file, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK)
|
|
||||||
return 1;
|
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;
|
bdrifp = &file;
|
||||||
|
|
||||||
if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) ||
|
if ((BDRIRead(0, sizeof(TickDBPreHeader), &pre_header) != FR_OK) ||
|
||||||
!CheckDBMagic((u8*) &pre_header, true) ||
|
!CheckDBMagic((u8*) &pre_header, true) ||
|
||||||
(AddBDRIEntry(&(pre_header.fs_header), sizeof(TickDBPreHeader) - sizeof(BDRIFsHeader), title_id,
|
(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);
|
fvx_close(bdrifp);
|
||||||
bdrifp = NULL;
|
bdrifp = NULL;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(te);
|
||||||
fvx_close(bdrifp);
|
fvx_close(bdrifp);
|
||||||
bdrifp = NULL;
|
bdrifp = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -91,7 +91,7 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 unknown; // usually (assuming always) == 1
|
u32 unknown; // usually (assuming always) == 1
|
||||||
u32 ticket_size; // == 0x350 == sizeof(Ticket)
|
u32 ticket_size; // commonly == 0x350
|
||||||
Ticket ticket;
|
Ticket ticket;
|
||||||
} __attribute__((packed, aligned(4))) TicketEntry;
|
} __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 ListTitleInfoEntryTitleIDs(const char* path, u8* title_ids, u32 max_title_ids);
|
||||||
u32 ListTicketTitleIDs(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 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 RemoveTitleInfoEntryFromDB(const char* path, const u8* title_id);
|
||||||
u32 RemoveTicketFromDB(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);
|
u32 AddTitleInfoEntryToDB(const char* path, const u8* title_id, const TitleInfoEntry* tie, bool replace);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
u32 ValidateCiaHeader(CiaHeader* header) {
|
u32 ValidateCiaHeader(CiaHeader* header) {
|
||||||
if ((header->size_header != CIA_HEADER_SIZE) ||
|
if ((header->size_header != CIA_HEADER_SIZE) ||
|
||||||
(header->size_cert != CIA_CERT_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_MIN) ||
|
||||||
(header->size_tmd > TMD_SIZE_MAX) ||
|
(header->size_tmd > TMD_SIZE_MAX) ||
|
||||||
(header->size_content == 0))
|
(header->size_content == 0))
|
||||||
@ -95,12 +95,12 @@ u32 BuildCiaMeta(CiaMeta* meta, void* exthdr, void* smdh) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 BuildCiaHeader(CiaHeader* header) {
|
u32 BuildCiaHeader(CiaHeader* header, u32 ticket_size) {
|
||||||
memset(header, 0, sizeof(CiaHeader));
|
memset(header, 0, sizeof(CiaHeader));
|
||||||
// sizes in header - fill only known sizes, others zero
|
// sizes in header - fill only known sizes, others zero
|
||||||
header->size_header = sizeof(CiaHeader);
|
header->size_header = sizeof(CiaHeader);
|
||||||
header->size_cert = CIA_CERT_SIZE;
|
header->size_cert = CIA_CERT_SIZE;
|
||||||
header->size_ticket = sizeof(Ticket);
|
header->size_ticket = ticket_size;
|
||||||
header->size_tmd = 0;
|
header->size_tmd = 0;
|
||||||
header->size_meta = 0;
|
header->size_meta = 0;
|
||||||
header->size_content = 0;
|
header->size_content = 0;
|
||||||
|
@ -34,8 +34,8 @@ typedef struct {
|
|||||||
u8 header_padding[0x40 - (CIA_HEADER_SIZE % 0x40)];
|
u8 header_padding[0x40 - (CIA_HEADER_SIZE % 0x40)];
|
||||||
u8 cert[CIA_CERT_SIZE];
|
u8 cert[CIA_CERT_SIZE];
|
||||||
// cert is aligned and needs no padding
|
// cert is aligned and needs no padding
|
||||||
Ticket ticket;
|
TicketCommon ticket;
|
||||||
u8 ticket_padding[0x40 - (TICKET_SIZE % 0x40)];
|
u8 ticket_padding[0x40 - (TICKET_COMMON_SIZE % 0x40)];
|
||||||
TitleMetaData tmd;
|
TitleMetaData tmd;
|
||||||
TmdContentChunk content_list[TMD_MAX_CONTENTS];
|
TmdContentChunk content_list[TMD_MAX_CONTENTS];
|
||||||
} PACKED_ALIGN(16) CiaStub;
|
} PACKED_ALIGN(16) CiaStub;
|
||||||
@ -66,7 +66,7 @@ u32 FixCiaHeaderForTmd(CiaHeader* header, TitleMetaData* tmd);
|
|||||||
|
|
||||||
u32 BuildCiaCert(u8* ciacert);
|
u32 BuildCiaCert(u8* ciacert);
|
||||||
u32 BuildCiaMeta(CiaMeta* meta, void* exthdr, void* smdh);
|
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 DecryptCiaContentSequential(void* data, u32 size, u8* ctr, const u8* titlekey);
|
||||||
u32 EncryptCiaContentSequential(void* data, u32 size, u8* ctr, const u8* titlekey);
|
u32 EncryptCiaContentSequential(void* data, u32 size, u8* ctr, const u8* titlekey);
|
||||||
|
@ -10,7 +10,11 @@ u32 ValidateTicket(Ticket* ticket) {
|
|||||||
if ((memcmp(ticket->sig_type, magic, sizeof(magic)) != 0) ||
|
if ((memcmp(ticket->sig_type, magic, sizeof(magic)) != 0) ||
|
||||||
((strncmp((char*) ticket->issuer, TICKET_ISSUER, 0x40) != 0) &&
|
((strncmp((char*) ticket->issuer, TICKET_ISSUER, 0x40) != 0) &&
|
||||||
(strncmp((char*) ticket->issuer, TICKET_ISSUER_DEV, 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 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -28,7 +32,7 @@ u32 ValidateTicketSignature(Ticket* ticket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!RSA_setKey2048(3, mod, exp) ||
|
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 1;
|
||||||
|
|
||||||
return 0;
|
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
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||||
};
|
};
|
||||||
// set ticket all zero for a clean start
|
// 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
|
// 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);
|
||||||
@ -60,6 +64,14 @@ u32 BuildFakeTicket(Ticket* ticket, u8* title_id) {
|
|||||||
return 0;
|
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) {
|
u32 BuildTicketCert(u8* tickcert) {
|
||||||
const u8 cert_hash_expected[0x20] = {
|
const u8 cert_hash_expected[0x20] = {
|
||||||
0xDC, 0x15, 0x3C, 0x2B, 0x8A, 0x0A, 0xC8, 0x74, 0xA9, 0xDC, 0x78, 0x61, 0x0E, 0x6A, 0x8F, 0xE3,
|
0xDC, 0x15, 0x3C, 0x2B, 0x8A, 0x0A, 0xC8, 0x74, 0xA9, 0xDC, 0x78, 0x61, 0x0E, 0x6A, 0x8F, 0xE3,
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#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_CDNCERT_SIZE 0x700
|
||||||
|
|
||||||
#define TICKET_ISSUER "Root-CA00000003-XS0000000c"
|
#define TICKET_ISSUER "Root-CA00000003-XS0000000c"
|
||||||
@ -13,37 +14,54 @@
|
|||||||
|
|
||||||
// from: https://github.com/profi200/Project_CTR/blob/02159e17ee225de3f7c46ca195ff0f9ba3b3d3e4/ctrtool/tik.h#L15-L39
|
// from: https://github.com/profi200/Project_CTR/blob/02159e17ee225de3f7c46ca195ff0f9ba3b3d3e4/ctrtool/tik.h#L15-L39
|
||||||
// all numbers in big endian
|
// 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 {
|
typedef struct {
|
||||||
u8 sig_type[4];
|
TICKETBASE;
|
||||||
u8 signature[0x100];
|
u8 content_index[];
|
||||||
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];
|
|
||||||
} __attribute__((packed, aligned(4))) Ticket;
|
} __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 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);
|
||||||
|
u32 GetTicketContentIndexSize(const Ticket* ticket);
|
||||||
|
u32 GetTicketSize(const Ticket* ticket);
|
||||||
u32 BuildTicketCert(u8* tickcert);
|
u32 BuildTicketCert(u8* tickcert);
|
||||||
|
@ -55,7 +55,7 @@ u32 GetTitleKey(u8* titlekey, Ticket* ticket) {
|
|||||||
Ticket* TicketFromTickDbChunk(u8* chunk, u8* title_id, bool legit_pls) {
|
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
|
// chunk must be aligned to 0x200 byte in file and at least 0x400 byte big
|
||||||
Ticket* tick = (Ticket*) (void*) (chunk + 0x18);
|
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 (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 (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
|
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;
|
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
|
const char* path_db = TICKDB_PATH(emunand); // EmuNAND / SysNAND
|
||||||
u8* data = (u8*) malloc(TICKDB_AREA_SIZE);
|
u8* data = (u8*) malloc(TICKDB_AREA_SIZE);
|
||||||
if (!data) return 1;
|
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) {
|
for (u32 i = 0; !found && (i <= TICKDB_AREA_SIZE - 0x400); i += 0x200) {
|
||||||
Ticket* tick = TicketFromTickDbChunk(data + i, title_id, force_legit);
|
Ticket* tick = TicketFromTickDbChunk(data + i, title_id, force_legit);
|
||||||
if (!tick) continue;
|
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;
|
found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ typedef struct {
|
|||||||
|
|
||||||
u32 GetTitleKey(u8* titlekey, Ticket* ticket);
|
u32 GetTitleKey(u8* titlekey, Ticket* ticket);
|
||||||
Ticket* TicketFromTickDbChunk(u8* chunk, u8* title_id, bool legit_pls);
|
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 FindTitleKey(Ticket* ticket, u8* title_id);
|
||||||
u32 AddTitleKeyToInfo(TitleKeysInfo* tik_info, TitleKeyEntry* tik_entry, bool decrypted_in, bool decrypted_out, bool devkit);
|
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);
|
u32 AddTicketToInfo(TitleKeysInfo* tik_info, Ticket* ticket, bool decrypt);
|
||||||
|
@ -199,7 +199,8 @@ u32 LoadTmdFile(TitleMetaData* tmd, const char* path) {
|
|||||||
return 0;
|
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
|
// path points to CDN content file
|
||||||
char path_cetk[256];
|
char path_cetk[256];
|
||||||
strncpy(path_cetk, path_cnt, 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");
|
snprintf(ext_cetk, 256 - (ext_cetk - path_cetk), "cetk");
|
||||||
|
|
||||||
// load and check ticket
|
// load and check ticket
|
||||||
|
TicketMinimum tmp;
|
||||||
UINT br;
|
UINT br;
|
||||||
if ((fvx_qread(path_cetk, ticket, 0, TICKET_SIZE, &br) != FR_OK) || (br != TICKET_SIZE) ||
|
if ((fvx_qread(path_cetk, &tmp, 0, TICKET_MINIMUM_SIZE, &br) != FR_OK) || (br != TICKET_MINIMUM_SIZE) ||
|
||||||
(ValidateTicket(ticket) != 0)) return 1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,7 +559,7 @@ u32 VerifyCiaFile(const char* path) {
|
|||||||
// load CIA stub
|
// load CIA stub
|
||||||
if ((LoadCiaStub(cia, path) != 0) ||
|
if ((LoadCiaStub(cia, path) != 0) ||
|
||||||
(GetCiaInfo(&info, &(cia->header)) != 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);
|
ShowPrompt(false, "%s\nError: Probably not a CIA file", pathstr);
|
||||||
free(cia);
|
free(cia);
|
||||||
return 1;
|
return 1;
|
||||||
@ -607,15 +620,18 @@ u32 VerifyTmdFile(const char* path, bool cdn) {
|
|||||||
|
|
||||||
u8 titlekey[0x10] = { 0xFF };
|
u8 titlekey[0x10] = { 0xFF };
|
||||||
if (cdn) { // load / build ticket (for titlekey / CDN only)
|
if (cdn) { // load / build ticket (for titlekey / CDN only)
|
||||||
Ticket ticket;
|
Ticket* ticket = NULL;
|
||||||
if (!((LoadCdnTicketFile(&ticket, path) == 0) ||
|
if (!((LoadCdnTicketFile(&ticket, path) == 0) ||
|
||||||
((BuildFakeTicket(&ticket, tmd->title_id) == 0) &&
|
((ticket = (Ticket*)malloc(TICKET_COMMON_SIZE), ticket != NULL) &&
|
||||||
(FindTitleKey(&ticket, tmd->title_id) == 0))) ||
|
(BuildFakeTicket(ticket, tmd->title_id) == 0) &&
|
||||||
(GetTitleKey(titlekey, &ticket) != 0)) {
|
(FindTitleKey(ticket, tmd->title_id) == 0))) ||
|
||||||
|
(GetTitleKey(titlekey, ticket) != 0)) {
|
||||||
ShowPrompt(false, "%s\nError: CDN titlekey not found", pathstr);
|
ShowPrompt(false, "%s\nError: CDN titlekey not found", pathstr);
|
||||||
|
free(ticket);
|
||||||
free(tmd);
|
free(tmd);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
free(ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify contents
|
// verify contents
|
||||||
@ -1010,7 +1026,7 @@ u32 CryptCiaFile(const char* orig, const char* dest, u16 crypto) {
|
|||||||
if (!cia) return 1;
|
if (!cia) return 1;
|
||||||
if ((LoadCiaStub(cia, orig) != 0) ||
|
if ((LoadCiaStub(cia, orig) != 0) ||
|
||||||
(GetCiaInfo(&info, &(cia->header)) != 0) ||
|
(GetCiaInfo(&info, &(cia->header)) != 0) ||
|
||||||
(GetTitleKey(titlekey, &(cia->ticket)) != 0)) {
|
(GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0)) {
|
||||||
free(cia);
|
free(cia);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1106,16 +1122,27 @@ u32 CryptCdnFileBuffered(const char* orig, const char* dest, u16 crypto, void* b
|
|||||||
} else tmd = NULL;
|
} else tmd = NULL;
|
||||||
|
|
||||||
// load or build ticket
|
// load or build ticket
|
||||||
Ticket ticket;
|
Ticket* ticket = NULL;
|
||||||
if (LoadCdnTicketFile(&ticket, orig) != 0) {
|
if (LoadCdnTicketFile(&ticket, orig) != 0) {
|
||||||
if (!tmd || (BuildFakeTicket(&ticket, tmd->title_id) != 0)) return 1;
|
if (!tmd || (ticket = (Ticket*)malloc(TICKET_COMMON_SIZE), !ticket)) return 1;
|
||||||
if (FindTitleKey(&ticket, tmd->title_id) != 0) 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
|
// get titlekey
|
||||||
u8 titlekey[0x10] = { 0xFF };
|
u8 titlekey[0x10] = { 0xFF };
|
||||||
if (GetTitleKey(titlekey, &ticket) != 0)
|
if (GetTitleKey(titlekey, ticket) != 0) {
|
||||||
|
free(ticket);
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ticket);
|
||||||
|
|
||||||
// find (build fake) content chunk
|
// find (build fake) content chunk
|
||||||
TmdContentChunk* chunk = NULL;
|
TmdContentChunk* chunk = NULL;
|
||||||
@ -1311,11 +1338,11 @@ u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool
|
|||||||
|
|
||||||
// build the CIA stub
|
// build the CIA stub
|
||||||
memset(cia, 0, sizeof(CiaStub));
|
memset(cia, 0, sizeof(CiaStub));
|
||||||
if ((BuildCiaHeader(&(cia->header)) != 0) ||
|
if ((BuildCiaHeader(&(cia->header), TICKET_COMMON_SIZE) != 0) ||
|
||||||
(LoadTmdFile(&(cia->tmd), path_tmd) != 0) ||
|
(LoadTmdFile(&(cia->tmd), path_tmd) != 0) ||
|
||||||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
||||||
(BuildCiaCert(cia->cert) != 0) ||
|
(BuildCiaCert(cia->cert) != 0) ||
|
||||||
(BuildFakeTicket(&(cia->ticket), cia->tmd.title_id) != 0)) {
|
(BuildFakeTicket((Ticket*)&(cia->ticket), cia->tmd.title_id) != 0)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1328,17 +1355,25 @@ u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool
|
|||||||
if (!content_count) return 1;
|
if (!content_count) return 1;
|
||||||
|
|
||||||
// get (legit) ticket
|
// get (legit) ticket
|
||||||
Ticket* ticket = &(cia->ticket);
|
Ticket* ticket = (Ticket*)&(cia->ticket);
|
||||||
bool src_emunand = ((*path_tmd == 'B') || (*path_tmd == '4'));
|
bool src_emunand = ((*path_tmd == 'B') || (*path_tmd == '4'));
|
||||||
if (force_legit) {
|
if (force_legit) {
|
||||||
Ticket ticket_fake; // backup of the fake ticket
|
Ticket* ticket_tmp = NULL;
|
||||||
memcpy(&ticket_fake, ticket, sizeof(Ticket));
|
if ((cdn && (LoadCdnTicketFile(&ticket_tmp, path_tmd) != 0)) ||
|
||||||
if ((cdn && (LoadCdnTicketFile(ticket, path_tmd) != 0)) ||
|
(!cdn && (FindTicket(&ticket_tmp, title_id, true, src_emunand) != 0))) {
|
||||||
(!cdn && (FindTicket(ticket, title_id, true, src_emunand) != 0))) {
|
|
||||||
ShowPrompt(false, "ID %016llX\nLegit ticket not found.", getbe64(title_id));
|
ShowPrompt(false, "ID %016llX\nLegit ticket not found.", getbe64(title_id));
|
||||||
|
free(ticket_tmp);
|
||||||
return 1;
|
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;
|
static u32 default_action = 0;
|
||||||
const char* optionstr[2] =
|
const char* optionstr[2] =
|
||||||
{"Use personalized ticket (legit)", "Use generic ticket (not legit)"};
|
{"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));
|
"ID %016llX\nLegit ticket is personalized.\nCIA may not be installable.\nChoose default action:", getbe64(title_id));
|
||||||
ShowProgress(0, 0, path_tmd);
|
ShowProgress(0, 0, path_tmd);
|
||||||
}
|
}
|
||||||
if (!default_action) return 1;
|
if (!default_action) {
|
||||||
else if (default_action == 2) {
|
free(ticket_tmp);
|
||||||
memcpy(ticket_fake.titlekey, ticket->titlekey, 0x10);
|
|
||||||
ticket_fake.commonkey_idx = ticket->commonkey_idx;
|
|
||||||
memcpy(ticket, &ticket_fake, sizeof(Ticket));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (cdn) {
|
|
||||||
if ((LoadCdnTicketFile(ticket, path_tmd) != 0) &&
|
|
||||||
(FindTitleKey(ticket, title_id) != 0)) {
|
|
||||||
ShowPrompt(false, "ID %016llX\nTitlekey not found.", getbe64(title_id));
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
else if (default_action == 2) {
|
||||||
|
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) {
|
||||||
|
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 {
|
} else {
|
||||||
Ticket ticket_tmp;
|
Ticket* ticket_tmp;
|
||||||
if ((FindTitleKey(ticket, title_id) != 0) &&
|
if ((FindTitleKey(ticket, title_id) != 0) &&
|
||||||
(FindTicket(&ticket_tmp, title_id, false, src_emunand) == 0)) {
|
(FindTicket(&ticket_tmp, title_id, false, src_emunand) == 0)) {
|
||||||
// we just copy the titlekey from a valid ticket (if we can)
|
// we just copy the titlekey from a valid ticket (if we can)
|
||||||
memcpy(ticket->titlekey, ticket_tmp.titlekey, 0x10);
|
memcpy(ticket->titlekey, ticket_tmp->titlekey, 0x10);
|
||||||
ticket->commonkey_idx = ticket_tmp.commonkey_idx;
|
ticket->commonkey_idx = ticket_tmp->commonkey_idx;
|
||||||
}
|
}
|
||||||
|
free(ticket_tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// content path string
|
// content path string
|
||||||
@ -1399,7 +1443,7 @@ u32 BuildCiaFromTmdFileBuffered(const char* path_tmd, const char* path_cia, bool
|
|||||||
|
|
||||||
// insert contents
|
// insert contents
|
||||||
u8 titlekey[16] = { 0xFF };
|
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;
|
if (WriteCiaStub(cia, path_cia) != 0) return 1;
|
||||||
for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) {
|
for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) {
|
||||||
TmdContentChunk* chunk = &(content_list[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));
|
CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub));
|
||||||
if (!cia) return 1;
|
if (!cia) return 1;
|
||||||
memset(cia, 0, sizeof(CiaStub));
|
memset(cia, 0, sizeof(CiaStub));
|
||||||
if ((BuildCiaHeader(&(cia->header)) != 0) ||
|
if ((BuildCiaHeader(&(cia->header), TICKET_COMMON_SIZE) != 0) ||
|
||||||
(BuildCiaCert(cia->cert) != 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)) ||
|
(BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, 0)) ||
|
||||||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
||||||
(WriteCiaStub(cia, path_cia) != 0)) {
|
(WriteCiaStub(cia, path_cia) != 0)) {
|
||||||
@ -1499,7 +1543,7 @@ u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) {
|
|||||||
free(meta);
|
free(meta);
|
||||||
|
|
||||||
// write the CIA stub (take #2)
|
// write the CIA stub (take #2)
|
||||||
FindTitleKey((&cia->ticket), title_id);
|
FindTitleKey((Ticket*)(&cia->ticket), title_id);
|
||||||
if ((FixTmdHashes(&(cia->tmd)) != 0) ||
|
if ((FixTmdHashes(&(cia->tmd)) != 0) ||
|
||||||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
||||||
(WriteCiaStub(cia, path_cia) != 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));
|
CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub));
|
||||||
if (!cia) return 1;
|
if (!cia) return 1;
|
||||||
memset(cia, 0, sizeof(CiaStub));
|
memset(cia, 0, sizeof(CiaStub));
|
||||||
if ((BuildCiaHeader(&(cia->header)) != 0) ||
|
if ((BuildCiaHeader(&(cia->header), TICKET_COMMON_SIZE) != 0) ||
|
||||||
(BuildCiaCert(cia->cert) != 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)) ||
|
(BuildFakeTmd(&(cia->tmd), title_id, content_count, save_size, 0)) ||
|
||||||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
||||||
(WriteCiaStub(cia, path_cia) != 0)) {
|
(WriteCiaStub(cia, path_cia) != 0)) {
|
||||||
@ -1572,7 +1616,7 @@ u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) {
|
|||||||
if (meta) free(meta);
|
if (meta) free(meta);
|
||||||
|
|
||||||
// write the CIA stub (take #2)
|
// write the CIA stub (take #2)
|
||||||
FindTitleKey(&(cia->ticket), title_id);
|
FindTitleKey((Ticket*)&(cia->ticket), title_id);
|
||||||
if ((FixTmdHashes(&(cia->tmd)) != 0) ||
|
if ((FixTmdHashes(&(cia->tmd)) != 0) ||
|
||||||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
||||||
(WriteCiaStub(cia, path_cia) != 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));
|
CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub));
|
||||||
if (!cia) return 1;
|
if (!cia) return 1;
|
||||||
memset(cia, 0, sizeof(CiaStub));
|
memset(cia, 0, sizeof(CiaStub));
|
||||||
if ((BuildCiaHeader(&(cia->header)) != 0) ||
|
if ((BuildCiaHeader(&(cia->header), TICKET_COMMON_SIZE) != 0) ||
|
||||||
(BuildCiaCert(cia->cert) != 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)) ||
|
(BuildFakeTmd(&(cia->tmd), title_id, 1, save_size, privsave_size)) ||
|
||||||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
||||||
(WriteCiaStub(cia, path_cia) != 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)
|
// write the CIA stub (take #2)
|
||||||
FindTitleKey((&cia->ticket), title_id);
|
FindTitleKey((Ticket*)(&cia->ticket), title_id);
|
||||||
if ((FixTmdHashes(&(cia->tmd)) != 0) ||
|
if ((FixTmdHashes(&(cia->tmd)) != 0) ||
|
||||||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
|
||||||
(WriteCiaStub(cia, path_cia) != 0)) {
|
(WriteCiaStub(cia, path_cia) != 0)) {
|
||||||
@ -2030,8 +2074,8 @@ u32 ShowCiaCheckerInfo(const char* path) {
|
|||||||
bool is_dlc = ((title_id >> 32) == 0x0004008C);
|
bool is_dlc = ((title_id >> 32) == 0x0004008C);
|
||||||
|
|
||||||
// check ticket
|
// check ticket
|
||||||
if (ValidateTicket(&(cia->ticket)) == 0)
|
if (ValidateTicket((Ticket*)&(cia->ticket)) == 0)
|
||||||
state_ticket = (ValidateTicketSignature(&(cia->ticket)) == 0) ? 2 : 1;
|
state_ticket = (ValidateTicketSignature((Ticket*)&(cia->ticket)) == 0) ? 2 : 1;
|
||||||
|
|
||||||
// check tmd
|
// check tmd
|
||||||
if (ValidateTmd(&(cia->tmd)) == 0)
|
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;
|
u64 filetype = path_in ? IdentifyFileType(path_in) : 0;
|
||||||
if (filetype & GAME_TICKET) {
|
if (filetype & GAME_TICKET) {
|
||||||
Ticket ticket;
|
TicketCommon ticket;
|
||||||
if ((fvx_qread(path_in, &ticket, 0, TICKET_SIZE, NULL) != FR_OK) ||
|
if ((fvx_qread(path_in, &ticket, 0, TICKET_COMMON_SIZE, NULL) != FR_OK) ||
|
||||||
(TIKDB_SIZE(tik_info) + 32 > STD_BUFFER_SIZE) ||
|
(TIKDB_SIZE(tik_info) + 32 > STD_BUFFER_SIZE) ||
|
||||||
(AddTicketToInfo(tik_info, &ticket, dec) != 0)) {
|
(AddTicketToInfo(tik_info, (Ticket*)&ticket, dec) != 0)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else if (filetype & SYS_TICKDB) {
|
} else if (filetype & SYS_TICKDB) {
|
||||||
@ -2476,7 +2520,7 @@ u32 LoadNcchFromGameFile(const char* path, NcchHeader* ncch) {
|
|||||||
u8 titlekey[16];
|
u8 titlekey[16];
|
||||||
u8 ctr[16];
|
u8 ctr[16];
|
||||||
GetTmdCtr(ctr, chunk);
|
GetTmdCtr(ctr, chunk);
|
||||||
if (GetTitleKey(titlekey, &(cia->ticket)) != 0) {
|
if (GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0) {
|
||||||
free(cia);
|
free(cia);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -848,7 +848,7 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
|
|||||||
(ReadImageBytes((u8*) cia, 0, info.offset_content) != 0))
|
(ReadImageBytes((u8*) cia, 0, info.offset_content) != 0))
|
||||||
return false;
|
return false;
|
||||||
offset_cia = vdir->offset; // always zero(!)
|
offset_cia = vdir->offset; // always zero(!)
|
||||||
GetTitleKey(cia_titlekey, &(cia->ticket));
|
GetTitleKey(cia_titlekey, (Ticket*)&(cia->ticket));
|
||||||
if (!BuildVGameCiaDir()) return false;
|
if (!BuildVGameCiaDir()) return false;
|
||||||
} else if ((vdir->flags & VFLAG_NCSD) && (offset_ncsd != vdir->offset)) {
|
} else if ((vdir->flags & VFLAG_NCSD) && (offset_ncsd != vdir->offset)) {
|
||||||
if ((ReadImageBytes((u8*) ncsd, 0, sizeof(NcsdHeader)) != 0) ||
|
if ((ReadImageBytes((u8*) ncsd, 0, sizeof(NcsdHeader)) != 0) ||
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
u32 commonkey_idx;
|
u32 commonkey_idx;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
|
u32 size;
|
||||||
u8 title_id[8];
|
u8 title_id[8];
|
||||||
u8 titlekey[16];
|
u8 titlekey[16];
|
||||||
u8 ticket_id[8];
|
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;
|
TickDbEntry* entry = info->entries + info->n_entries;
|
||||||
entry->commonkey_idx = ticket->commonkey_idx;
|
entry->commonkey_idx = ticket->commonkey_idx;
|
||||||
entry->offset = offset;
|
entry->offset = offset;
|
||||||
|
entry->size = GetTicketSize(ticket);
|
||||||
memcpy(entry->title_id, ticket->title_id, 8);
|
memcpy(entry->title_id, ticket->title_id, 8);
|
||||||
memcpy(entry->titlekey, ticket->titlekey, 16);
|
memcpy(entry->titlekey, ticket->titlekey, 16);
|
||||||
memcpy(entry->ticket_id, ticket->ticket_id, 8);
|
memcpy(entry->ticket_id, ticket->ticket_id, 8);
|
||||||
@ -178,7 +180,7 @@ bool ReadVTickDbDir(VirtualFile* vfile, VirtualDir* vdir) {
|
|||||||
memset(vfile, 0, sizeof(VirtualFile));
|
memset(vfile, 0, sizeof(VirtualFile));
|
||||||
snprintf(vfile->name, 32, NAME_TIK, getbe64(tick_entry->title_id), getbe32(tick_entry->console_id));
|
snprintf(vfile->name, 32, NAME_TIK, getbe64(tick_entry->title_id), getbe32(tick_entry->console_id));
|
||||||
vfile->offset = tick_entry->offset & ~OFLAG_RAW;
|
vfile->offset = tick_entry->offset & ~OFLAG_RAW;
|
||||||
vfile->size = sizeof(Ticket);
|
vfile->size = tick_entry->size;
|
||||||
vfile->keyslot = 0xFF;
|
vfile->keyslot = 0xFF;
|
||||||
vfile->flags = (vdir->flags | VFLAG_READONLY) & ~VFLAG_DIR;
|
vfile->flags = (vdir->flags | VFLAG_READONLY) & ~VFLAG_DIR;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user