Skip to content

Instantly share code, notes, and snippets.

@Linblow
Last active October 2, 2023 03:25
Show Gist options
  • Save Linblow/8639cb76ad6f813dc56d15cd40cf48ab to your computer and use it in GitHub Desktop.
Save Linblow/8639cb76ad6f813dc56d15cd40cf48ab to your computer and use it in GitHub Desktop.
Sony ~PSP file header clean reversed
/**
* @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