Skip to content

Instantly share code, notes, and snippets.

@SocraticBliss
Last active October 13, 2023 13:59
Show Gist options
  • Save SocraticBliss/bc1af97116825a19021146d9ec96e06d to your computer and use it in GitHub Desktop.
Save SocraticBliss/bc1af97116825a19021146d9ec96e06d to your computer and use it in GitHub Desktop.
PS4 Signed ELF Parser
# PS4 Signed ELF Parser
# SocraticBliss (R)
# Thanks to Znullptr and flatz <3
from binascii import hexlify as hx
import struct
import sys
def self_header(file):
# MAGIC = 4F 15 3D 1D
# MAGIC, VERSION, MODE, ENDIAN, ATTRIBUTES
FMT = '<4s4B'
MAGIC, VERSION, MODE, ENDIAN, ATTRIBUTES = struct.unpack(FMT, file.read(struct.calcsize(FMT)))
print('\n[SELF Header]')
print('Magic: 0x%s' % hx(MAGIC).upper())
print('Version: %d' % VERSION)
print('Mode: 0x%X' % MODE)
print('Endian: %d (%s)' % (ENDIAN, 'Little Endian' if ENDIAN == 1 else 'Unknown'))
print('Attributes: 0x%X' % ATTRIBUTES)
# KEY TYPE, HEADER SIZE, META SIZE, FILE SIZE, ENTRY COUNT, FLAG, 4x PADDING
EXTENDED_FMT = '<I2HQ2H4x'
KEY_TYPE, HEADER_SIZE, META_SIZE, FILE_SIZE, ENTRY_COUNT, FLAG = struct.unpack(EXTENDED_FMT, file.read(struct.calcsize(EXTENDED_FMT)))
print('\n[SELF Extended Header]')
print('Key Type: 0x%X' % KEY_TYPE)
print('Header Size: 0x%X' % HEADER_SIZE)
print('Meta Size: %d Bytes' % META_SIZE)
print('File Size: %d Bytes' % FILE_SIZE)
print('Entry Count: %d' % ENTRY_COUNT)
print('Flag: 0x%X' % FLAG)
return ENTRY_COUNT
def self_entry(entry, file):
# PROPS, FILE_OFFSET, FILE_SIZE, MEMORY_SIZE
FMT = '<4Q'
PROPS, FILE_OFFSET, FILE_SIZE, MEMORY_SIZE = struct.unpack(FMT, file.read(struct.calcsize(FMT)))
print('\n[SELF Entry #%d]' % entry)
print('Properties: 0x%X' % PROPS)
# PROPS = [ORDER, ENCRYPTED, SIGNED, COMPRESSED, WINDOW BITS, HAS BLOCK, BLOCK SIZE, HAS DIGEST, HAS EXTENT, HAS META, SEGMENT INDEX]
PROPERTIES = [
['Order', 0, 0x1],
['Encrypted', 1, 0x1],
['Signed', 2, 0x1],
['Compressed', 3, 0x1],
['Window Bits', 8, 0x7],
['Has Block', 11, 0x1],
['Block Size', 12, 0xF],
['Has Digest', 16, 0x1],
['Has Extent', 17, 0x1],
['Has Meta', 20, 0x1],
['Segment Index', 20, 0xFFFF]
]
for property in PROPERTIES:
if property[0] == 'Block Size':
if ((PROPS >> property[1]) & property[2]) != 0:
size = 1 << (12 + (PROPS >> property[1]) & property[2])
else:
size = 0x1000
print(' %s: 0x%X' % (property[0], size))
else:
print(' %s: %s' % (property[0], (PROPS >> property[1]) & property[2]))
print('File Offset: 0x%X' % FILE_OFFSET)
print('File Size: %s Bytes' % FILE_SIZE)
print('Memory Size: %s Bytes' % MEMORY_SIZE)
def elf_header(file):
# 7F 45 4C 46
# MAGIC, ARCHITECTURE, ENDIAN, VERSION, OS/ABI, ABI VERSION, 6x PADDING, NID SIZE
FMT = '<4s5B6xB'
MAGIC, ARCHITECTURE, ENDIAN, VERSION, OS_ABI, ABI_VERSION, NID_SIZE = struct.unpack(FMT, file.read(struct.calcsize(FMT)))
print('\n[ELF Header]')
print('Magic: %s' % hx(MAGIC).upper())
print('Architecture: %d (%s)' % (ARCHITECTURE, 'ELF64' if ARCHITECTURE == 2 else 'Unknown'))
print('Endian: %d (%s)' % (ENDIAN, 'Little Endian' if ENDIAN == 1 else 'Unknown'))
print('Version: %d (%s)' % (VERSION, 'Current' if VERSION == 1 else 'None'))
print('OS/ABI: %d (%s)' % (OS_ABI, 'FreeBSD' if OS_ABI == 9 else 'Unknown'))
print('ABI Version: %d' % ABI_VERSION)
print('NID Size: %d' % NID_SIZE)
# TYPE, MACHINE, VERSION, ENTRY POINT ADDRESS, PROGRAM HEADER OFFSET, SECTION HEADER OFFSET, FLAG, HEADER SIZE, PROGRAM HEADER SIZE, PROGRAM HEADER COUNT, SECTION HEADER SIZE, SECTION HEADER COUNT, SECTION HEADER STRING INDEX
EX_FMT = '<2HI3QI6H'
TYPE, MACHINE, VERSION, ENTRY_POINT_ADDRESS, PROGRAM_HEADER_OFFSET, SECTION_HEADER_OFFSET, FLAG, HEADER_SIZE, PROGRAM_HEADER_SIZE, PROGRAM_HEADER_COUNT, SECTION_HEADER_SIZE, SECTION_HEADER_COUNT, SECTION_HEADER_STRING_INDEX = struct.unpack(EX_FMT, file.read(struct.calcsize(EX_FMT)))
TYPES = {
0x0: 'ET_NONE',
0x1: 'ET_REL',
0x2: 'ET_EXEC',
0x3: 'ET_DYN',
0x4: 'ET_CORE',
0xFE00: 'ET_SCE_EXEC',
0xFE0C: 'ET_SCE_STUBLIB',
0xFE10: 'ET_SCE_DYNEXEC',
0xFE18: 'ET_SCE_DYNAMIC',
}
print('\n[ELF Extension Header]')
print('Type: 0x%X (%s)' % (TYPE, TYPES.get(TYPE, 'Unknown')))
print('Machine: 0x%X (%s)' % (MACHINE, 'AMD_X86_64' if MACHINE == 0x3E else 'Unknown'))
print('Version: %d' % VERSION)
print('Entry Point Address: 0x%X' % ENTRY_POINT_ADDRESS)
print('Program Header Offset: 0x%X' % PROGRAM_HEADER_OFFSET)
print('Section Header Offset: 0x%X' % SECTION_HEADER_OFFSET)
print('Flag: 0x%X' % FLAG)
print('Header Size: %d Bytes' % HEADER_SIZE)
print('Program Header Size: %d Bytes' % PROGRAM_HEADER_SIZE)
print('Program Header Count: %d' % PROGRAM_HEADER_COUNT)
print('Section Header Size: %d' % SECTION_HEADER_SIZE)
print('Section Header Count: %d' % SECTION_HEADER_COUNT)
print('Section Header String Index: 0x%X' % SECTION_HEADER_STRING_INDEX)
return PROGRAM_HEADER_COUNT, SECTION_HEADER_COUNT
def elf_program_header(program, file):
# TYPE, FLAG, OFFSET, VIRTUAL ADDRESS, PHYSICAL ADDRESS, FILE SIZE, MEMORY SIZE, ALIGNMENT
FMT = '<2I6Q'
TYPE, FLAG, OFFSET, VIRTUAL_ADDRESS, PHYSICAL_ADDRESS, FILE_SIZE, MEMORY_SIZE, ALIGNMENT = struct.unpack(FMT, file.read(struct.calcsize(FMT)))
TYPES = {
0x0: 'PT_NULL',
0x1: 'PT_LOAD',
0x2: 'PT_DYNAMIC',
0x3: 'PT_INTERP',
0x4: 'PT_NOTE',
0x5: 'PT_SHLIB',
0x6: 'PT_PHDR',
0x7: 'PT_TLS',
0x6474E550: 'PT_GNU_EH_FRAME',
0x6474E551: 'PT_GNU_STACK',
0x6474E552: 'PT_GNU_RELRO',
0x60000000: 'PT_SCE_RELA',
0x61000000: 'PT_SCE_DYNLIBDATA',
0x61000001: 'PT_SCE_PROCPARAM',
0x61000002: 'PT_SCE_MODULE_PARAM',
0x61000010: 'PT_SCE_RELRO',
0x6FFFFF00: 'PT_SCE_COMMENT',
0x6FFFFF01: 'PT_SCE_LIBVERSION'
}
FLAGS = {
0x0: 'None',
0x1: 'Execute',
0x2: 'Write',
0x4: 'Read',
0x5: 'Read, Execute',
0x6: 'Read, Write',
0x7: 'Read, Write, Execute'
}
print('\n[ELF Program Header #%d]' % program)
print('Type: 0x%X (%s)' % (TYPE, TYPES.get(TYPE, 'Unknown')))
print('Flag: 0x%X (%s)' % (FLAG, FLAGS.get(FLAG, 'Unknown')))
print('Offset: 0x%X' % OFFSET)
print('Virtual Address: 0x%X' % VIRTUAL_ADDRESS)
print('Physical Address: 0x%X' % PHYSICAL_ADDRESS)
print('File Size: 0x%X' % FILE_SIZE)
print('Memory Size: 0x%X' % MEMORY_SIZE)
print('Alignment: 0x%X' % ALIGNMENT)
if TYPE == 0x6FFFFF01:
print('\n[SELF Version]')
print('Version: %d' % FILE_SIZE)
def elf_section_header(section, input):
# NAME, TYPE, FLAG, ADDRESS, OFFSET, SIZE, LINK, INFORMATION, ALIGNMENT, ENTRY SIZE
FMT = '<2I4Q2I2Q'
NAME, TYPE, FLAG, ADDRESS, OFFSET, SIZE, LINK, INFORMATION, ALIGNMENT, ENTRY_SIZE = struct.unpack(FMT, file.read(struct.calcsize(FMT)))
TYPES = {
0x0: 'SHT_NULL',
0x1: 'SHT_PROGBITS',
0x2: 'SHT_SYMTAB',
0x3: 'SHT_STRTAB',
0x4: 'SHT_RELA',
0x5: 'SHT_HASH',
0x6: 'SHT_DYNAMIC',
0x7: 'SHT_NOTE',
0x8: 'SHT_NOBITS',
0x9: 'SHT_REL',
0xA: 'SHT_SHLIB',
0xB: 'SHT_DYNSYM',
0xE: 'SHT_INIT_ARRAY',
0xF: 'SHT_FINI_ARRAY',
0x10: 'SHT_PREINIT_ARRAY',
0x11: 'SHT_GROUP',
0x12: 'SHT_SYMTAB_SHNDX',
0x61000001: 'SHT_SCE_NID'
}
FLAGS = {
0x1: 'SHF_WRITE',
0x2: 'SHF_ALLOC',
0x4: 'SHF_EXECINSTR',
0x10: 'SHF_MERGE',
0x20: 'SHF_STRINGS',
0x40: 'SHF_INFO_LINK',
0x80: 'SHF_LINK_ORDER',
0x100: 'SHF_OS_NONCONFORMING',
0x200: 'SHF_GROUP',
0x400: 'SHF_TLS'
}
print('\n[ELF Section Header #%d]' % section)
print('Name: %s' % NAME)
print('Type: 0x%X (%s)' % (TYPE, TYPES.get(TYPE, 'Unknown')))
print('Flag: 0x%X (%s)' % (FLAG, FLAGS.get(FLAG, 'Unknown')))
print('Address: 0x%X' % ADDRESS)
print('Offset: 0x%X' % OFFSET)
print('Size: %d Bytes' % SIZE)
print('Link: %s' % LINK)
print('Information: %s' % INFORMATION)
print('Alignment: 0x%X' % ALIGNMENT)
print('Entry Size: %d Bytes' % ENTRY_SIZE)
def self_extended_information(file):
# AUTHENTICATION ID, PROGRAM TYPE, APPLICATION VERSION, FIRMWARE VERSION, DIGEST
FMT = '<4Q32s'
AUTHENTICATION_ID, TYPE, APPLICATION_VERSION, FIRMWARE_VERSION, DIGEST = struct.unpack(FMT, file.read(struct.calcsize(FMT)))
TYPES = {
0x1: 'PT_FAKE',
0x4: 'PT_NPDRM_EXEC',
0x5: 'PT_NPDRM_DYNLIB',
0x8: 'PT_SYSTEM_EXEC',
0x9: 'PT_SYSTEM_DYNLIB',
0xC: 'PT_HOST_KERNEL',
0xE: 'PT_SECURE_MODULE',
0xF: 'PT_SECURE_KERNEL'
}
print('\n[SELF Extended Information]')
print('Authentication ID: 0x%X' % AUTHENTICATION_ID)
print('Type: 0x%X (%s)' % (TYPE, TYPES.get(TYPE, 'Unknown')))
print('Application Version: 0x%X' % APPLICATION_VERSION)
print('Firmware Version: 0x%X' % FIRMWARE_VERSION)
print('Digest: %s' % hx(DIGEST).upper())
if TYPE in {0x4, 0x5}:
return True
return False
def self_npdrm_control_block(file):
# TYPE, 14x PADDING, CONTENT ID, RANDOM PADDING
FMT = '<H14x19s13s'
TYPE, CONTENT_ID, RANDOM_PADDING = struct.unpack(FMT, file.read(struct.calcsize(FMT)))
print('\n[SELF NPDRM Control Block]')
print('Type: 0x%X (NPDRM)' % TYPE)
print('Content ID: 0x%X' % hx(CONTENT_ID).upper())
print('Random Padding: 0x%X' % hx(RANDOM_PADDING).upper())
def self_meta_block(entry, file):
# 80x PADDING
FMT = '<80x'
_ = struct.unpack(FMT, file.read(struct.calcsize(FMT)))
def self_meta_footer(file):
# 48x PADDING, UNKNOWN, 28x PADDING, SIGNATURE
FMT = '<48xI28x32s'
UNKNOWN, SIGNATURE = struct.unpack(FMT, file.read(struct.calcsize(FMT)))
print('\n[SELF Meta Footer]')
print('Unknown: %s' % UNKNOWN)
print('Signature: %s' % hx(SIGNATURE).upper())
def main(argc, argv):
if argc != 2:
raise SystemExit('\nUsage : python %s <self file>' % argv[0])
try:
with open(argv[1]) as input:
print('\nParsing PS4 SELF Header...')
entry_count = self_header(input)
if entry_count > 0:
print('\nParsing PS4 SELF Entries...')
for entry in xrange(entry_count):
self_entry(entry, input)
print('\nParsing PS4 ELF Header...')
program_header_count, section_header_count = elf_header(input)
if program_header_count > 0:
print('\nParsing PS4 ELF Program Headers...')
for program in xrange(program_header_count):
elf_program_header(program, input)
if section_header_count > 0:
print('\nParsing PS4 ELF Section Headers...')
for section in xrange(section_header_count):
elf_section_header(section, input)
print('\nParsing PS4 SELF Extended Information...')
has_NPDRM = self_extended_information(input)
'''
if has_NPDRM:
print('\nParsing PS4 SELF NPDRM Control Block...')
self_npdrm_control_block(input)
if entry_count > 0:
print('\nParsing PS4 SELF Meta Blocks...')
for entry in xrange(entry_count):
self_meta_block(entry, input)
print('\nParsing PS4 SELF Meta Footer...')
self_meta_footer(input)
'''
print('\nDone!')
except:
raise SystemExit('\nError: Unable to load PS4 signed elf file!')
if __name__ == '__main__':
main(len(sys.argv), sys.argv)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment