Skip to content

Instantly share code, notes, and snippets.

@moyix
Created November 22, 2021 19:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moyix/7345bb494ed0cd90b9c8725f03098642 to your computer and use it in GitHub Desktop.
Save moyix/7345bb494ed0cd90b9c8725f03098642 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import sys
import struct
from pprint import pprint
# Parser for ELF executables
def parse_elf_header(f):
# Parse ELF header
f.seek(0)
magic = f.read(4)
if magic != b'\x7fELF':
raise Exception('Not an ELF file')
ei_class = f.read(1)
ei_data = f.read(1)
ei_version = f.read(1)
ei_osabi = f.read(1)
ei_abiversion = f.read(1)
pad = f.read(7)
e_type = f.read(2)
e_machine = f.read(2)
e_version = f.read(4)
if ei_class == b'\x02':
e_entry = f.read(8)
e_phoff = f.read(8)
e_shoff = f.read(8)
else:
e_entry = f.read(4)
e_phoff = f.read(4)
e_shoff = f.read(4)
e_flags = f.read(4)
e_ehsize = f.read(2)
e_phentsize = f.read(2)
e_phnum = f.read(2)
e_shentsize = f.read(2)
e_shnum = f.read(2)
e_shstrndx = f.read(2)
ei_class = int.from_bytes(ei_class, byteorder='little')
ei_data = int.from_bytes(ei_data, byteorder='little')
ei_version = int.from_bytes(ei_version, byteorder='little')
ei_osabi = int.from_bytes(ei_osabi, byteorder='little')
ei_abiversion = int.from_bytes(ei_abiversion, byteorder='little')
e_type = int.from_bytes(e_type, byteorder='little')
e_machine = int.from_bytes(e_machine, byteorder='little')
e_version = int.from_bytes(e_version, byteorder='little')
e_entry = int.from_bytes(e_entry, byteorder='little')
e_phoff = int.from_bytes(e_phoff, byteorder='little')
e_shoff = int.from_bytes(e_shoff, byteorder='little')
e_flags = int.from_bytes(e_flags, byteorder='little')
e_ehsize = int.from_bytes(e_ehsize, byteorder='little')
e_phentsize = int.from_bytes(e_phentsize, byteorder='little')
e_phnum = int.from_bytes(e_phnum, byteorder='little')
e_shentsize = int.from_bytes(e_shentsize, byteorder='little')
e_shnum = int.from_bytes(e_shnum, byteorder='little')
e_shstrndx = int.from_bytes(e_shstrndx, byteorder='little')
return {
'magic': magic,
'ei_class': ei_class,
'ei_data': ei_data,
'ei_version': ei_version,
'ei_osabi': ei_osabi,
'ei_abiversion': ei_abiversion,
'e_type': e_type,
'e_machine': e_machine,
'e_version': e_version,
'e_entry': e_entry,
'e_phoff': e_phoff,
'e_shoff': e_shoff,
'e_flags': e_flags,
'e_ehsize': e_ehsize,
'e_phentsize': e_phentsize,
'e_phnum': e_phnum,
'e_shentsize': e_shentsize,
'e_shnum': e_shnum,
'e_shstrndx': e_shstrndx
}
def parse_program_headers(f, header):
# Parse program header
f.seek(header['e_phoff'])
ph_headers = []
for i in range(header['e_phnum']):
ph_header = {}
if header['ei_class'] == 2:
ph_header['p_type'] = f.read(4)
ph_header['p_flags'] = f.read(4)
ph_header['p_offset'] = f.read(8)
ph_header['p_vaddr'] = f.read(8)
ph_header['p_paddr'] = f.read(8)
ph_header['p_filesz'] = f.read(8)
ph_header['p_memsz'] = f.read(8)
ph_header['p_align'] = f.read(8)
else:
ph_header['p_type'] = f.read(4)
ph_header['p_offset'] = f.read(4)
ph_header['p_vaddr'] = f.read(4)
ph_header['p_paddr'] = f.read(4)
ph_header['p_filesz'] = f.read(4)
ph_header['p_memsz'] = f.read(4)
ph_header['p_flags'] = f.read(4)
ph_header['p_align'] = f.read(4)
# Convert to ints
for k in ph_header:
ph_header[k] = int.from_bytes(ph_header[k], byteorder='little')
ph_headers.append(ph_header)
return ph_headers
def parse_section_headers(f, header):
# Parse section header
f.seek(header['e_shoff'])
sh_headers = []
for i in range(header['e_shnum']):
sh_header = {}
if header['ei_class'] == 2:
sh_header['sh_name'] = f.read(4)
sh_header['sh_type'] = f.read(4)
sh_header['sh_flags'] = f.read(8)
sh_header['sh_addr'] = f.read(8)
sh_header['sh_offset'] = f.read(8)
sh_header['sh_size'] = f.read(8)
sh_header['sh_link'] = f.read(4)
sh_header['sh_info'] = f.read(4)
sh_header['sh_addralign'] = f.read(8)
sh_header['sh_entsize'] = f.read(8)
else:
sh_header['sh_name'] = f.read(4)
sh_header['sh_type'] = f.read(4)
sh_header['sh_flags'] = f.read(4)
sh_header['sh_addr'] = f.read(4)
sh_header['sh_offset'] = f.read(4)
sh_header['sh_size'] = f.read(4)
sh_header['sh_link'] = f.read(4)
sh_header['sh_info'] = f.read(4)
sh_header['sh_addralign'] = f.read(4)
sh_header['sh_entsize'] = f.read(4)
# Convert to ints
for k in sh_header:
sh_header[k] = int.from_bytes(sh_header[k], byteorder='little')
sh_headers.append(sh_header)
return sh_headers
# Only care about i386, x86_64, ARM, and ARM64
elf_machine_types = {
0x03: 'i386',
0x3e: 'x86_64',
0x28: 'ARM',
0xB7: 'ARM64'
}
e_type_names = {
0x00: 'ET_NONE',
0x01: 'ET_REL',
0x02: 'ET_EXEC',
0x03: 'ET_DYN',
0x04: 'ET_CORE',
0xfe00: 'ET_LOPROC',
0xffff: 'ET_HIPROC'
}
def print_elf_header(header):
print('ELF Header:')
print(' Magic: ' + header['magic'].hex())
print(' Class: ' + 'ELF64' if header['ei_class'] == 2 else 'ELF32')
print(' Data: ' + 'LE' if header['ei_data'] == 1 else 'BE')
print(' Version: ' + str(header['ei_version']))
print(' OS/ABI: ' + str(header['ei_osabi']))
print(' ABI Version: ' + str(header['ei_abiversion']))
print(' Type: ' + e_type_names[header['e_type']])
print(' Machine: ' + elf_machine_types[header['e_machine']])
print(' Version: ' + str(header['e_version']))
print(' Entry point: ' + hex(header['e_entry']))
print(' Program header offset: ' + hex(header['e_phoff']))
print(' Section header offset: ' + hex(header['e_shoff']))
print(' Flags: ' + hex(header['e_flags']))
print(' Size of this header: ' + hex(header['e_ehsize']))
print(' Size of program header: ' + hex(header['e_phentsize']))
print(' Number of program headers: ' + hex(header['e_phnum']))
print(' Size of section header: ' + hex(header['e_shentsize']))
print(' Number of section headers: ' + hex(header['e_shnum']))
print(' Section header string table index: ' + hex(header['e_shstrndx']))
program_header_names = {
0x00: 'PT_NULL',
0x01: 'PT_LOAD',
0x02: 'PT_DYNAMIC',
0x03: 'PT_INTERP',
0x04: 'PT_NOTE',
0x05: 'PT_SHLIB',
0x06: 'PT_PHDR',
0x07: 'PT_TLS',
0x60000000: 'PT_LOOS',
0x6fffffff: 'PT_HIOS',
0x70000000: 'PT_LOPROC',
0x7fffffff: 'PT_HIPROC',
}
program_header_names_reverse = {v: k for k, v in program_header_names.items()}
program_header_names[program_header_names_reverse['PT_LOOS'] + 0x474e550] = 'PT_GNU_EH_FRAME'
program_header_names[program_header_names_reverse['PT_LOOS'] + 0x474e551] = 'PT_GNU_STACK'
program_header_names[program_header_names_reverse['PT_LOOS'] + 0x474e552] = 'PT_GNU_RELRO'
program_header_names[program_header_names_reverse['PT_LOOS'] + 0x474e553] = 'PT_GNU_PROPERTY'
program_header_names[program_header_names_reverse['PT_LOOS'] + 0x5a3dbe6] = 'PT_OPENBSD_RANDOMIZE'
program_header_names[program_header_names_reverse['PT_LOOS'] + 0x5a3dbe7] = 'PT_OPENBSD_WXNEEDED'
program_header_names[program_header_names_reverse['PT_LOOS'] + 0x5a41be6] = 'PT_OPENBSD_BOOTDATA'
def print_program_header(ph_header):
print('Program Header:')
if ph_header['p_type'] in program_header_names:
print(' Type: ' + program_header_names[ph_header['p_type']])
else:
print(' Type: ' + hex(ph_header['p_type']))
print(' Offset: ' + hex(ph_header['p_offset']))
print(' Virtual Address: ' + hex(ph_header['p_vaddr']))
print(' Physical Address: ' + hex(ph_header['p_paddr']))
print(' File Size: ' + hex(ph_header['p_filesz']))
print(' Memory Size: ' + hex(ph_header['p_memsz']))
print(' Flags: ' + hex(ph_header['p_flags']))
print(' Alignment: ' + hex(ph_header['p_align']))
def parse_string_table(f, header, sh_headers):
# Parse string table
sh_strtab = sh_headers[header['e_shstrndx']]
f.seek(sh_strtab['sh_offset'])
string_table = f.read(sh_strtab['sh_size'])
return string_table
section_header_types = {
0x00: 'SHT_NULL',
0x01: 'SHT_PROGBITS',
0x02: 'SHT_SYMTAB',
0x03: 'SHT_STRTAB',
0x04: 'SHT_RELA',
0x05: 'SHT_HASH',
0x06: 'SHT_DYNAMIC',
0x07: 'SHT_NOTE',
0x08: 'SHT_NOBITS',
0x09: 'SHT_REL',
0x0a: 'SHT_SHLIB',
0x0b: 'SHT_DYNSYM',
0x0c: 'SHT_INIT_ARRAY',
0x0d: 'SHT_FINI_ARRAY',
0x0e: 'SHT_PREINIT_ARRAY',
0x0f: 'SHT_GROUP',
0x10: 'SHT_SYMTAB_SHNDX',
0x11: 'SHT_NUM',
0x12: 'SHT_LOOS',
0x13: 'SHT_GNU_ATTRIBUTES',
0x14: 'SHT_GNU_HASH',
0x15: 'SHT_GNU_LIBLIST',
0x16: 'SHT_CHECKSUM',
0x17: 'SHT_LOSUNW',
0x18: 'SHT_SUNW_move',
0x19: 'SHT_SUNW_COMDAT',
0x1a: 'SHT_SUNW_syminfo',
0x1b: 'SHT_GNU_verdef',
0x1c: 'SHT_GNU_verneed',
0x1d: 'SHT_GNU_versym',
0x1e: 'SHT_HISUNW',
0x1f: 'SHT_HIOS',
0x40: 'SHT_LOPROC',
0x7f: 'SHT_HIPROC',
0x80: 'SHT_LOUSER',
0xff: 'SHT_HIUSER'
}
def print_section_header(sh_header, string_table):
print('Section Header:')
print(' Name: ' + string_table[sh_header['sh_name']:].decode('utf-8').split('\x00')[0])
if sh_header['sh_type'] in section_header_types:
print(' Type: ' + section_header_types[sh_header['sh_type']])
else:
print(' Type: ' + hex(sh_header['sh_type']))
print(' Flags: ' + hex(sh_header['sh_flags']))
print(' Address: ' + hex(sh_header['sh_addr']))
print(' Offset: ' + hex(sh_header['sh_offset']))
print(' Size: ' + hex(sh_header['sh_size']))
print(' Link: ' + hex(sh_header['sh_link']))
print(' Info: ' + hex(sh_header['sh_info']))
print(' Address alignment: ' + hex(sh_header['sh_addralign']))
print(' Entry size: ' + hex(sh_header['sh_entsize']))
elf_file = open(sys.argv[1], 'rb')
header = parse_elf_header(elf_file)
print_elf_header(header)
ph_headers = parse_program_headers(elf_file, header)
for ph_header in ph_headers:
print_program_header(ph_header)
sh_headers = parse_section_headers(elf_file, header)
string_table = parse_string_table(elf_file, header, sh_headers)
for sh_header in sh_headers:
print_section_header(sh_header, string_table)
elf_file.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment