SELF File Format: Difference between revisions
CelesteBlue (talk | contribs) No edit summary |
CelesteBlue (talk | contribs) |
||
Line 256: | Line 256: | ||
===Comments=== | ===Comments=== | ||
== | == Section Information == | ||
=== Struct === | === Struct === | ||
<source lang="C"> | |||
typedef struct { | |||
uint64_t offset; | uint64_t offset; | ||
uint64_t size; | uint64_t size; | ||
uint32_t | uint32_t compression; | ||
uint32_t | uint32_t unk1; | ||
uint32_t | uint32_t encryption; | ||
uint32_t | uint32_t unk2; | ||
} __attribute__((packed)) SECTION_INFO; | |||
</source> | |||
=== Table === | === Table === | ||
Line 274: | Line 276: | ||
! field !! offset !! type !! notes | ! field !! offset !! type !! notes | ||
|- | |- | ||
| | | Offset || 0x00 || u64 || Offset of data | ||
|- | |- | ||
| | | Size || 0x08 || u64 || Size of data | ||
|- | |- | ||
| Compression || 0x10 || u32 || 1 = uncompressed, 2 = compressed | | Compression || 0x10 || u32 || 1 = uncompressed, 2 = compressed | ||
|- | |- | ||
| | | unknown 1 || 0x14 || u32 || | ||
|- | |- | ||
| Encryption|| 0x18 || u32 || 1 = encrypted, 2 = unencrypted | | Encryption || 0x18 || u32 || 1 = encrypted, 2 = unencrypted | ||
|- | |- | ||
| | | unknown 2 || 0x1C || u32 || | ||
|} | |} | ||
=== Comments === | === Comments === | ||
There is one of these entries ( | There is one of these entries (Section Information) for each phdr entry (Program Header) in the ELF file so that the console knows where to decrypt the data from (because it might also be compressed). | ||
== SCE Version Info == | == SCE Version Info == |
Revision as of 16:58, 22 December 2019
SELF stands for Signed Executable and Linkable Format.
It is the format used by the executables on the PS3 (SCE Header Version 2), PS Vita (SCE Header Version 3).
It consists of an SELF header followed by the encapsulated ELF file. ELF segments can be compressed using gzip.
System and NPDRM SELFs are encrypted and signed and are decrypted on PSVita to memory by F00D.
ELF sections might be encrypted using AES CTR and signed using ECDSA + HMAC-SHA1.
SELF file has a specific header called SCE header where it stores all the parameters for this program.
File Format
Notes:
- Numbers are stored in little endian format because of ARM endianness (whilst PS3 used big endian because of PowerPC endianness).
- Encapsulated ELF header fields are useless (only the EI_CLASS EI_DATA and EI_VERSION fields are checked)
SELF/SCE Header
Struct
typedef struct { uint32_t m_magic; /* 53434500 = SCE\0 */ uint32_t m_version; /* header version: 3 for PSVita */ uint16_t sdk_type; /* key revision */ uint16_t m_category; /* 1 self, 2 srvk, 3 spkg, 4 spp */ uint32_t m_ext_header_size; /* metadata offset */ uint64_t m_file_offset; /* SELF header length */ uint64_t elf_filesize; /* ELF file length */ uint64_t self_filesize; /* SELF file length */ uint64_t unknown; /* UNKNOWN */ uint64_t self_offset; /* SELF offset */ uint64_t appinfo_offset; /* app info offset */ uint64_t elf_offset; /* ELF #1 offset */ uint64_t phdr_offset; /* program header offset */ uint64_t shdr_offset; /* section header offset */ uint64_t section_info_offset; /* section info offset */ uint64_t sceversion_offset; /* version offset */ uint64_t controlinfo_offset; /* control info offset */ uint64_t controlinfo_size; /* control info size */ uint64_t padding; } __attribute__((packed)) SELF_header;
Table
field | offset | type | notes |
---|---|---|---|
Magic | 0x0 | u32 | Must be "SCE\0". |
Version | 0x4 | u32 | Must be 3 on PSVita. |
SDK type | 0x8 | u16 | Corresponds to the revision of the decryption key. |
Header type | 0xA | u16 | 1 self, 2 rvk, 3 pkg, 4 spp |
Metadata offset | 0xC | u32 | Offset to the checksums. Must be at least 20 bytes before the end of the header. |
Header length | 0x10 | u64 | Length of the header (including the fake ELF header) (usually 0x100). |
Encrypted size | 0x18 | u64 | Size of the encrypted part of the SELF file. |
File size | 0x20 | u64 | Size of the SELF file. |
unknown | 0x28 | u64 | Must be 0. |
SELF offset | 0x30 | u64 | Offset to the SELF (usually 0x4). |
App Info offset | 0x38 | u64 | Offset to AppInfo, (usually 0x80). See App info struct. |
ELF offset | 0x40 | u64 | Offset to the fake ELF header (usually 0xA0). |
Program header offset | 0x48 | u64 | Offset to Program Header. |
Segments header offset | 0x50 | u64 | Offset to Segments Header. |
encrypted phdr sizes/offsets table offset | 0x58 | u64 | Offset to a table which maps phdr entries to the actual offset/size within the encrypted fself.
Because fselfs can be compressed, they might not match the values listed within the elf. |
sceversion header offset | 0x60 | u64 | Offset to a header which contains some version information, including an offset to the .sceversion section of the encrypted elf. |
Control Information | 0x68 | u64 | Several information containers. |
Control Information Size | 0x70 | u64 |
App Info
Struct
typedef struct { uint64_t authority_id; /* authority id */ uint32_t vendorid; /* vendor id */ uint32_t selftype; /* SELF type */ uint64_t sceversion; /* sdk version */ uint64_t padding; /* UNKNOWN */ } __attribute__((packed)) APP_INFO;
Table
field | offset | type | notes | example |
---|---|---|---|---|
authority_id | 0x00 | u64 | AuthorityId | ex: 21 00 00 10 1C CA 01 30 |
vendorid | 0x08 | u32 | VendorId (strangely always 00 00 00 00) | ex: 00 00 00 00 |
selftype | 0x0C | u32 | SELF type | ex: 08 00 00 00 |
version | 0x10 | u64 | version | ex: 0x0000036000000000 (3.600.011), 0x0000009450000000 (0.945.040) |
padding | 0x18 | u64 | 00 00 00 00 00 00 00 00 |
Comments
Aligned to 0x10 bytes.
SELF type
value | name | files |
---|---|---|
0x7 | KERNEL | kernel module (.skprx) |
0x8 | APP | NPDRM SELF and SDK fSELF (eboot.bin, .suprx) |
0x9 | BOOT | kernel_boot_loader.self |
0xB | SECURE | SM SELF (os0:sm/*.self and slb2:kprx_auth_sm.self) |
0xD | USER | system userland SELF (.self, eboot.bin, .suprx) |
ELF Header
Struct
typedef struct { uint8_t e_ident[16]; /* ELF identification */ uint16_t e_type; /* object file type */ uint16_t e_machine; /* machine type */ uint32_t e_version; /* object file version */ uint32_t e_entry; /* entry point address */ uint32_t e_phoff; /* program header offset */ uint32_t e_shoff; /* section header offset */ uint32_t e_flags; /* processor-specific flags */ uint16_t e_ehsize; /* ELF header size */ uint16_t e_phentsize; /* size of program header entry */ uint16_t e_phnum; /* number of program header entries */ uint16_t e_shentsize; /* size of section header entry */ uint16_t e_shnum; /* number of section header entries */ uint16_t e_shstrndx; /* section name string table index */ } __attribute__((packed)) ELF;
Table
Name of the variable | Offset | Size | Notes |
---|---|---|---|
e_ident[0..3] | elf_offset+(0,1,2,3) | 4 | Magic |
e_ident[4] | elf_offset+4 | 1 | Class Type must be [ELFCLASS32 = 0x01] |
e_ident[5] | elf_offset+5 | 1 | Data Type must be [ELFDATA2LSB (i.e. le) = 0x01] |
e_ident[6] | elf_offset+6 | 1 | File version (must be 0x1) |
e_ident[7..15] | elf_offset+(6->15) | 1 | unused |
e_type | elf_offset+0x10 | 2 | SCE-specific e_type |
e_machine | elf_offset+0x12 | 2 | Machine type must be [EM_ARM = 0x0028] |
e_version | elf_offset+0x14 | 4 | elf version (must be 0x00000001) |
e_entry | elf_offset+0x18 | 4 | Address to jump to in order to start program |
e_phoff | elf_offset+0x1c | 4 | boundary checked, but unused (already given by SELF header) |
e_shoff | elf_offset+0x20 | 4 | unused |
e_flags | elf_offset+0x24 | 4 | unused |
e_ehsize | elf_offset+0x28 | 2 | Must be sizeof(Elf32_Ehdr) = 0x0034 |
e_phentsize | elf_offset+0x2a | 2 | Must be sizeof(Elf32_Phdr) = 0x0020 |
e_phnum | elf_offset+0x2c | 2 | Count of Program Header in this elf |
e_shentsize | elf_offset+0x2e | 2 | unused |
e_shnum | elf_offset+0x30 | 2 | unused |
e_shstrndx | elf_offset+0x32 | 2 | unused |
SCE-specific e_type
/* SCE-specific definitions for e_type: */
#define ET_SCE_ELF 0x2 /* SCE Executable file */
#define ET_SCE_EXEC 0xFE00 /* SCE Executable file */
#define ET_SCE_RELEXEC 0xFE04 /* SCE Relocatable Executable file */
#define ET_SCE_STUBLIB 0xFE0C /* SCE SDK Stubs */
#define ET_SCE_DYNAMIC 0xFE18 /* Unused */
#define ET_SCE_PSPRELEXEC 0xFFA0 /* Unused (PSP ELF only) */
#define ET_SCE_PPURELEXEC 0xFFA4 /* Unused (SPU ELF only) */
#define ET_SCE_UNK 0xFFA5 /* Unknown */
See Specifications here: ELF Header ELF-64 Object File Format
ELF Program Headers
Struct
typedef struct { uint32_t p_type; /* type of segment */ uint32_t p_flags; /* segment attributes */ uint64_t p_offset; /* offset in file */ uint64_t p_vaddr; /* virtual address in memory */ uint64_t p_paddr; /* reserved */ uint64_t p_filesz; /* size of segment in file */ uint64_t p_memsz; /* size of segment in memory */ uint64_t p_align; /* alignment of segment */ } __attribute__((packed)) ELF_PHDR;
Table
Comments
See Spec here: ELF Program Headers
ELF Section Headers
Struct
typedef struct { uint32_t sh_name; /* section name */ uint32_t sh_type; /* section type */ uint64_t sh_flags; /* section attributes */ uint64_t sh_addr; /* virtual address in memory */ uint64_t sh_offset; /* offset in file */ uint64_t sh_size; /* size of section */ uint32_t sh_link; /* link to other section */ uint32_t sh_info; /* miscellaneous information */ uint64_t sh_addralign; /* address alignment boundary */ uint64_t sh_entsize; /* size of entries, if section has table */ } __attribute__((packed)) ELF_SHDR;
Table
Comments
Section Information
Struct
typedef struct {
uint64_t offset;
uint64_t size;
uint32_t compression;
uint32_t unk1;
uint32_t encryption;
uint32_t unk2;
} __attribute__((packed)) SECTION_INFO;
Table
field | offset | type | notes |
---|---|---|---|
Offset | 0x00 | u64 | Offset of data |
Size | 0x08 | u64 | Size of data |
Compression | 0x10 | u32 | 1 = uncompressed, 2 = compressed |
unknown 1 | 0x14 | u32 | |
Encryption | 0x18 | u32 | 1 = encrypted, 2 = unencrypted |
unknown 2 | 0x1C | u32 |
Comments
There is one of these entries (Section Information) for each phdr entry (Program Header) in the ELF file so that the console knows where to decrypt the data from (because it might also be compressed).
SCE Version Info
Struct
typedef struct { uint32_t unknown1; // 0x1 uint32_t unknown2; uint32_t unknown3; // 0x10 ?size? uint32_t unknown4; } __attribute__((packed)) SCEVERSION_INFO;
Table
Comment
Control Information
Struct
typedef struct { uint32_t type; // 4==PSVita ELF digest info; 5==PSVita NPDRM info; 6==PSVita boot param info; 7==PSVita shared secret info uint32_t size; uint64_t next; // 1 if another Control Info structure follows else 0 union { // type 4, 0x50 bytes struct { // 0x40 bytes of data uint8_t constant[0x14]; // same for every PSVita/PS3 SELF, hardcoded in make_fself.exe: 627CB1808AB938E32C8C091708726A579E2586E4 uint8_t elf_digest[0x20]; // on PSVita: SHA-256 of source ELF file, on PS3: SHA-1 uint8_t padding[8]; uint32_t min_required_fw; // ex: 0x363 for 3.63 } PSVita_elf_digest_info; // type 5, 0x110 bytes struct { // 0x80 bytes of data uint32_t magic; // 7F 44 52 4D (".DRM") uint32_t finalized_flag; // ex: 80 00 00 01 uint32_t drm_type; // license_type ex: 2 local, 0xD free with license uint32_t padding; uint8_t content_id[0x30]; uint8_t digest[0x10]; // ?sha-1 hash of debug self/sprx created using make_fself_npdrm? uint8_t padding_78[0x78]; uint8_t hash_signature[0x38]; // unknown hash/signature } PSVita_npdrm_info; // type 6, 0x110 bytes struct { // 0x100 bytes of data uint32_t is_used; // 0=false, 1=true uint8_t boot_param[0x9C]; // ex: starting with 02 00 00 00 } PSVita_boot_param_info; // type 7, 0x50 bytes struct { // 0x40 bytes of data uint8_t shared_secret_0[0x10]; // ex: 0x7E7FD126A7B9614940607EE1BF9DDF5E or full of zeroes uint8_t shared_secret_1[0x10]; // ex: full of zeroes uint8_t shared_secret_2[0x10]; // ex: full of zeroes uint8_t shared_secret_3[0x10]; // ex: full of zeroes } PSVita_shared_secret_info; }; } __attribute__((packed)) PSVita_CONTROL_INFO;
Table
Comments
Relocations
Offset | Size | Description |
---|---|---|
0x0 | 0x4 | Relocation Type |
0x4 | 0x4 | Long entry: Addend |
0x4 | 0x4 | Short entry: 0-19 = Bits 10-29 of offset, 20-31 = addend |
0x8 | 0x4 | Long entry: Offset |
Relocations can be of two types: 8 byte "short" entries or 12 byte "long" entries. The relocation code is the same as ARM ELF format.
Segment start = Buffer address of segment indexed at "Patch segment"
Symbol start = Buffer address of segment indexed at "Symbol segment"
Address to patch = segment start + offset
P = address to patch
S = "Symbol segment" == 15 ? 0 : symbol start
A = addend
Relocation Type
Start | End | Description |
---|---|---|
0 | 3 | Short entry if set |
4 | 7 | Symbol segment |
8 | 15 | Relocation code |
16 | 19 | Patch segment |
20 | 27 | Long entry: Optional relocation code 2 |
28 | 31 | Long entry: Optional distance 2 |
20 | 31 | Short entry: Lower 12 bits of offset |
Supported Relocation Codes (1.69)
Code | Description |
---|---|
0 | R_ARM_NONE |
2 | R_ARM_ABS32 |
3 | R_ARM_REL32 |
10 | R_ARM_THM_CALL |
28 | R_ARM_CALL |
29 | R_ARM_JUMP24 |
38 | R_ARM_TARGET1 (same as R_ARM_ABS32) |
40 | R_ARM_V4BX (same as R_ARM_NONE) |
41 | R_ARM_TARGET2 (same as R_ARM_REL32) |
42 | R_ARM_PREL31 |
43 | R_ARM_MOVW_ABS_NC |
44 | R_ARM_MOVT_ABS |
47 | R_ARM_THM_MOVW_ABS_NC |
48 | R_ARM_THM_MOVT_ABS |