Last active
October 2, 2023 03:25
-
-
Save Linblow/8639cb76ad6f813dc56d15cd40cf48ab to your computer and use it in GitHub Desktop.
Sony ~PSP file header clean reversed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file pspheader.h | |
* @author Linblow (dev at linblow dot com) | |
* @version 1.0.7 | |
* @date 2023-09-05 | |
* | |
* Defines the structures ScePSPHeader, etc; for ~PSP header file data. | |
*/ | |
#ifndef _SCE_KERNEL_PSP_HEADER_H | |
#define _SCE_KERNEL_PSP_HEADER_H | |
/* No include for the u64/u32/u16/u8 types, that's up to the caller. */ | |
#if 0 | |
typedef unsigned long long u64; | |
typedef unsigned int u32; | |
typedef unsigned short u16; | |
typedef unsigned char u8; | |
#endif | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
typedef struct ScePSPHeaderModule ScePSPHeaderModule; | |
typedef struct ScePSPHeaderKirkMeta ScePSPHeaderKirkMeta; | |
typedef struct ScePSPHeaderDataSize ScePSPHeaderDataSize; | |
typedef struct ScePSPHeaderCore ScePSPHeaderCore; | |
typedef union ScePSPModuleTag ScePSPModuleTag; | |
typedef struct ScePSPHeaderADType3KirkMeta ScePSPHeaderADType3KirkMeta; | |
typedef struct ScePSPModuleType3DNASData ScePSPModuleType3DNASData; | |
typedef struct ScePSPHeaderADType3 ScePSPHeaderADType3; | |
typedef struct ScePSPHeaderADType4 ScePSPHeaderADType4; | |
typedef struct ScePSPHeaderADType9 ScePSPHeaderADType9; | |
typedef struct ScePSPHeaderADType10 ScePSPHeaderADType10; | |
typedef union ScePSPHeaderAD ScePSPHeaderAD; | |
typedef union ScePSPHeaderSig ScePSPHeaderSig; | |
typedef struct ScePSPHeaderEcdsaSig ScePSPHeaderEcdsaSig; | |
typedef struct ScePSPHeader ScePSPHeader; | |
/* IMPORTANT: | |
This removes padding added by the compiler. | |
I spent quite some time understanding why some reversed code didn't work. | |
The cause: 4 bytes of padding were added in an union. */ | |
#pragma pack(push, 1) | |
/* NOTE: | |
Unless prefixed with "off", all the offsets in the inline comments | |
are relative to the start of the ScePSPHeader structure. | |
*/ | |
#define SCE_PSP_HEADER_MAGIC (0x7E505350u) /* "~PSP" */ | |
#define SCE_PSP_HEADER_MAGIC_ARRAY {0x7E,0x50,0x53,0x50} | |
#define SCE_PSP_HEADER_TAG_SIZE (4) | |
#define SCE_PSP_HEADER_ECDSA_SIG_SIZE (0x28) | |
#define SCE_PSP_HEADER_CMAC_SIG_SIZE (0x10) | |
/** | |
* Encrypted ~PSP/PRX module information. | |
*/ | |
struct ScePSPHeaderModule | |
{ | |
/** Magic signature ("~PSP"). */ | |
u8 magic[4]; // 0 | |
/** Module attributes (one or more of SceModulePrivilegeLevel). */ | |
u16 modAttribute; // 4 | |
/** Compression attributes (one of SceExecFileAttr). */ | |
u16 compAttribute; // 6 | |
/** Module version minor, and major. */ | |
u8 modVersion[2]; // 8 | |
/** Module name including null-terminator. */ | |
char modName[28]; // 0A | |
/** Structure version (normally set to 1). */ | |
u8 version; // 26 | |
/** Number of segments in the ELF/PRX. */ | |
u8 nsegments; // 27 | |
/** Size of the plain & decompressed ELF/PRX data. */ | |
int elfSize; // 28 | |
/** Size of the encrypted (possibly compressed first) ELF/PRX data + the ScePSPHeader structure. */ | |
int pspSize; // 2C | |
/** Module entrypoint function offset (relative to start of .text section). */ | |
u32 entry; // 30 | |
/** Module sceModuleInfo structure offset (relative to start of .text section). */ | |
u32 modinfoOffset; // 34 | |
/** ELF/PRX .bss section size. */ | |
int bssSize; // 38 | |
/** ELF/PRX segments alignment. */ | |
u16 segAlign[4]; // 3C | |
/** ELF/PRX segments start address. */ | |
u32 segAddress[4]; // 44 | |
/** ELF/PRX segments size. */ | |
int segSize[4]; // 54 | |
/** Reserved (set to zero). */ | |
u32 reserved[5]; // 64 | |
/** Development kit version the module was compiled with. */ | |
u32 devkitVersion; // 78 | |
/** Decryption mode (one of SceExecFileDecryptMode). */ | |
u8 decryptMode; // 7C | |
/** Set to zero. */ | |
u8 padding; // 7D | |
/** Size of the GZIP compression overlap. */ | |
u16 overlapSize; // 7E | |
}; // size: 0x80 | |
/** | |
* Data for KIRK command 1 header. | |
*/ | |
struct ScePSPHeaderKirkMeta | |
{ | |
/** | |
* AES key for encrypting/decrypting the module data. | |
* This key is encrypted with KIRK 1 static key. | |
*/ | |
u8 aesKey[0x10]; // 80 | |
/** | |
* AES-CMAC hash key for the two CMAC signatures: | |
* (1) H1 = AES_CMAC(PLAIN(cmacKey), M(kirk_header_btm_t)) | |
* (2) H2 = AES_CMAC(PLAIN(cmacKey), M(kirk_header_btm_t || salt || data)) | |
* This key is encrypted with KIRK 1 static key. | |
*/ | |
u8 cmacKey[0x10]; // 90 | |
/** | |
* Signature of KIRK command 1 header bottom structure. | |
* H1 = CMAC(PLAIN(cmacKey), M(kirk_header_btm_t)) | |
*/ | |
u8 bottomHash[0x10]; // A0 | |
}; // size: 0x30 | |
/** | |
* Module salt/data size. | |
*/ | |
struct ScePSPHeaderDataSize | |
{ | |
/** Size of the plain ELF data size, possibly compressed. When not compressed, equal to ScePSPHeader.mod.elfSize. */ | |
int compSize; // B0 | |
/** Salt data size. Set to 0 zero when there is no salt following the KIRK command 1 header. */ | |
int saltSize; // B4 | |
/** Unused. Set to zero. */ | |
int reserved[2]; // B8 | |
}; // size: 0x10 | |
/** | |
* Essential information. | |
*/ | |
struct ScePSPHeaderCore | |
{ | |
/** KIRK command 1 header data. */ | |
ScePSPHeaderKirkMeta kirk; // 80 | |
/** Module salt/data size information. */ | |
ScePSPHeaderDataSize size; // B0 | |
/** | |
* Signature of KIRK command 1 header bottom structure, salt, and data. | |
* H2 = AES_CMAC(PLAIN(kirk.cmacKey), M(kirk_header_btm_t || salt || data)) | |
* The salt data is typically the ScePSPHeaderModule structure data. | |
*/ | |
u8 cmacDataHash[0x10]; // C0 | |
}; // size: 0x50 | |
/** | |
* Encrypted ~PSP/PRX module tag. | |
* This is basically an ID that determines the PRX type. | |
*/ | |
union ScePSPModuleTag | |
{ | |
u32 val; // D0 | |
u8 buf[4]; | |
}; // size: 4 | |
/** | |
* Input/output header data for KIRK command 2/3 (type 3 PRX only). | |
* >>> WARNING: THIS NEEDS MORE RESEARCH. MEMBERS ARE NOT CERTAIN. | |
*/ | |
struct ScePSPHeaderADType3KirkMeta | |
{ | |
/** Supposedly, AES key for data encryption/decryption. */ | |
u8 _aesDataKey[0x10]; // EC | |
// Note: data from this point is a struct kirk_header_cmac_sig_t (size 0x30). | |
/** Supposedly, AES-CMAC hash key. */ | |
u8 _hashHey[0x10]; // FC | |
/** Supposedly, AES-CMAC signature of KIRK command 2/3 header. */ | |
u8 _bottomHash[0x10]; // 10C | |
/** Supposedly, AES-CMAC signature of KIRK command 2/3 header + data. */ | |
u8 _dataHash[0x10]; // 11C | |
}; // size: 0x40 | |
/** | |
* Input/output data for KIRK command 2/3 (type 3 PRX only). | |
* This is the user data that comes after the KIRK command 2/3 header. | |
* The data here is somehow signed, with the hash/key being somewhere in the | |
* ScePSPHeaderADType3KirkMeta structure. | |
* KIRK 2 will first check this signature, and will fail when it doesn't match. | |
* When the signature matches, KIRK 2 will proceed to encrypt/re-sign the data. | |
* The resulting output data is then encrypted & tied to this specific device, | |
* which KIRK 3 can verify, and decrypt. | |
*/ | |
struct ScePSPModuleType3DNASData | |
{ | |
/** KIRK 1 command header data. */ | |
ScePSPHeaderKirkMeta kirk; // off: 0 | |
/** Signature of KIRK command 1 header bottom structure, salt, and data. */ | |
u8 cmacDataHash[0x10]; // off: 30 | |
/** | |
* SHA-1 signature of the ScePSPHeader structure. | |
* Note this is only the first 0x10 bytes of the original SHA-1, | |
* the last 4 bytes are not part of this. | |
*/ | |
u8 sha1Hash[0x10]; // off: 40; size is indeed 0x10 bytes, not 0x14! | |
}; // size: 0x50 | |
/** | |
* PRX type 3 additional data. | |
* This is the DNAS data required for KIRK command 2/3. | |
*/ | |
struct ScePSPHeaderADType3 | |
{ | |
/** Should be zero. */ | |
u8 scheck[0x18]; // D4 | |
/** Input & output header data for KIRK command 2/3. */ | |
ScePSPHeaderADType3KirkMeta kirk; // EC | |
}; // size: 0x58 | |
/** | |
* PRX type 4 additional data. | |
* Known tags: 0xADF305F0, 0x279D05F0 (SCE_MODULE_APP module attribute). | |
* Module API type 323 or 340, with decrypt mode DECRYPT_MODE_APP_MODULE (14). | |
* See sceMesgLed_driver_9E3C79D9() in mesg_led module, and CheckTick() at | |
* https://github.com/uofw/uofw/blob/master/src/loadcore/loadelf.c | |
*/ | |
struct ScePSPHeaderADType4 | |
{ | |
/** Unknown. */ | |
u8 unk[0x10]; // D4 | |
/** | |
* Module expiration time in RTC ticks (SceRtcTick). | |
* Set to zero when unused. | |
*/ | |
union { // E4 | |
u64 val; | |
struct { /* SceRtcTick */ | |
u32 lo; // LSB | |
u32 hi; // MSB | |
}; | |
} expireTime; // size: 8 | |
/** Unknown. */ | |
u8 unk18[0x40]; // EC | |
}; // size: 0x58 | |
/** | |
* PRX type 9 additional data. | |
*/ | |
struct ScePSPHeaderADType9 | |
{ | |
/** Should be zero. */ | |
u8 scheck[0x30]; // D4 | |
/** ECDSA signature. */ | |
ScePSPHeaderEcdsaSig sig; // 104 | |
}; // size: 0x58 | |
/** | |
* PRX type 10 additional data. | |
*/ | |
struct ScePSPHeaderADType10 | |
{ | |
/** Unknown flag. Related to the decryption process. */ | |
u8 unk_d4; // D4 | |
/** Should be zero. */ | |
u8 scheck[0x2f]; // D5 | |
/** ECDSA signature. */ | |
ScePSPHeaderEcdsaSig sig; // 104 | |
}; // size: 0x58 | |
/** | |
* Associated Data with the encrypted ~PSP module. | |
* This data is specific to each module type. | |
* Only a few types or tags have associated data. | |
*/ | |
union ScePSPHeaderAD | |
{ | |
/** Raw data buffer. */ | |
u8 buf[0x58]; // D4 | |
/** DNAS data for KIRK command 2/3. */ | |
ScePSPHeaderADType3 type3; // D4 | |
/** Application/demo module data. */ | |
ScePSPHeaderADType4 type4; // D4 | |
/** Type 9 data. */ | |
ScePSPHeaderADType9 type9; // D4 | |
/** Type 10 data. */ | |
ScePSPHeaderADType10 type10; // D4 | |
}; // size: 0x58 | |
/** | |
* Signature of a ScePSPHeader structure. | |
* The message to digest consists of the ScePSPHeader structure data copied | |
* into a temporary buffer, and scrambled in a way specific to each PRX type. | |
*/ | |
union ScePSPHeaderSig | |
{ | |
/** M33 oe tag (CFW). */ | |
u32 oeTag; // 12C | |
/** SHA-1 message digest. */ | |
u8 hash[0x14]; // 12C | |
}; // size: 0x14 | |
/** | |
* ECDSA signature of the ~PSP buffer along with the encrypted module data. | |
* The signature starts at offset 4 of the ScePSPHeader (ie. ignore "~PSP" magic). | |
* See PRX type 9/10 additional data. | |
*/ | |
struct ScePSPHeaderEcdsaSig | |
{ | |
u8 r[0x14]; | |
u8 s[0x14]; | |
}; // size: 0x28 | |
/** | |
* Encrypted ~PSP module header. | |
* | |
* The decrypted module data is GZIP-compressed when the compression attribute | |
* SCE_EXEC_FILE_COMPRESSED (1) is set. Sony's KL4E is used instead of Deflate | |
* when SCE_EXEC_FILE_KL4E_COMPRESSED (0x200) is also set. | |
* The compressed data always starts at offset 0x80 of the plain data buffer | |
* when the decrypt mode is 0. For all the other modes, it starts at 0. | |
* | |
* KL4E compressed data always begins with the "KL4E" magic bytes, | |
* followed by the actual compressed data. | |
*/ | |
struct ScePSPHeader | |
{ | |
ScePSPHeaderModule mod; // 0 | |
union { // 80 | |
/** | |
* When the module is "sign-checked", all the data here is scrambled and, | |
* AES encrypted with KIRK command 5 that uses a per-device unique key. | |
* See memlmd_6192F715() for the decryption and un-scrambling process | |
* of VSH and user module (decrypt mode 3 or 4). | |
*/ | |
u8 scramble[0xd0]; // 80 | |
/** | |
* When the structure data is *not* scrambled: | |
*/ | |
struct { | |
/** Essential information (keys, CMAC hashes, sizes). */ | |
ScePSPHeaderCore core; // 80 | |
/** Tag ID that determines the PRX type for the mesg_led decryption process. */ | |
ScePSPModuleTag tag; // D0 | |
/** Additional data specific to each PRX type. */ | |
ScePSPHeaderAD ad; // D4 | |
/** Signature of the ScePSPHeader structure. */ | |
ScePSPHeaderSig sig; // 12C | |
/** btncnf ID that uniquely identifies this module. */ | |
u8 btcnfId[16]; // 140 | |
}; // size: 0xD0 | |
}; // size: 0x150 - sizeof(ScePSPHeaderModule) = 0x150 - 0x80 = 0xD0 | |
/* The encrypted ELF/PRX module data immediately follows the structure. */ | |
}; // size: 0x150 | |
#pragma pack(pop) | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif /* _SCE_KERNEL_PSP_HEADER_H */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment