Created
November 24, 2016 11:52
-
-
Save mrzechonek/b843be2d1b48236e40a07193272309c0 to your computer and use it in GitHub Desktop.
This file contains 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
#!/usr/bin/env python3 | |
from collections import OrderedDict | |
import enum | |
import struct | |
class Field: | |
def __init__(self, format, type=int): | |
self.format = format | |
self.type = type | |
class StructMeta(type): | |
@classmethod | |
def __prepare__(self, name, bases): | |
# preserve field order | |
return OrderedDict() | |
def __new__(mcs, name, bases, attrs, **kwargs): | |
cls = super().__new__(mcs, name, bases, attrs, **kwargs) | |
cls._fields = OrderedDict((k, v) for k, v in attrs.items() if isinstance(v, Field)) | |
return cls | |
class Struct(metaclass=StructMeta): | |
class Endian(enum.Enum): | |
BIG = '>' | |
LITTLE = '<' | |
NATIVE = '=' | |
def __init__(self, *args, **kwargs): | |
for name in self._fields.keys(): | |
setattr(self, name, kwargs.get(name)) | |
def __str__(self): | |
values = ('{}={}'.format(i, getattr(self, i)) for i in self._fields) | |
return '{}({})'.format(type(self).__name__, ', '.join(values)) | |
@classmethod | |
def read(cls, fp, endian=Endian.NATIVE.value): | |
format = endian + ''.join(i.format for i in cls._fields.values()) | |
data = fp.read(struct.calcsize(format)) | |
values = struct.unpack(format, data) | |
kwargs = { name: field.type(value) | |
for (name, field), value | |
in zip(cls._fields.items(), values)} | |
return cls(**kwargs) | |
class RangedEnumMeta(enum.EnumMeta): | |
def __call__(cls, value, *args, **kwargs): | |
if cls._range(value): | |
return value | |
return super().__call__(value, *args, **kwargs) | |
class ELFIdent(Struct): | |
class Bits(enum.Enum): | |
B32 = 1 | |
B64 = 2 | |
class Endian(enum.Enum): | |
LITTLE = 1 | |
BIG = 2 | |
class OperatingSystem(enum.Enum): | |
SYSTEM_V = 0x0 | |
HP_UX = 0x1 | |
NETBSD = 0x2 | |
LINUX = 0x3 | |
SOLARIS = 0x6 | |
AIX = 0x7 | |
IRIX = 0x8 | |
FREEBSD = 0x9 | |
OPENBSD = 0xc | |
# there are more... | |
magic = Field('4s', bytes) | |
bits = Field('B', Bits) | |
endian = Field('B', Endian) | |
version = Field('B') | |
os = Field('B', OperatingSystem) | |
abiversion = Field('B') | |
pad = Field('7x', None) | |
class ELFHeader64(Struct): | |
class Type(enum.Enum): | |
RELOCATABLE = 1 | |
EXECUTABLE = 2 | |
SHARED = 3 | |
CORE = 4 | |
class Machine(enum.Enum): | |
UNSPEC = 0x00 | |
SPARC = 0x02 | |
X86 = 0x03 | |
MIPS = 0x08 | |
PPC = 0x14 | |
ARM = 0x28 | |
SH = 0x2A | |
IA64 = 0x32 | |
X86_64 = 0x3E | |
AARCH64 = 0xB7 | |
type = Field('H', Type) | |
machine = Field('H', Machine) | |
version = Field('L') | |
entry = Field('Q') | |
phoff = Field('Q') | |
shoff = Field('Q') | |
flags = Field('L') | |
ehsize = Field('H') | |
phentsize = Field('H') | |
phnum = Field('H') | |
shentsize = Field('H') | |
shnum = Field('H') | |
shstrndx = Field('H') | |
class ProgramHeader64(Struct): | |
class Type(enum.Enum, metaclass=RangedEnumMeta): | |
NULL = 0x00000000 | |
LOAD = 0x00000001 | |
DYNAMIC = 0x00000002 | |
INTERP = 0x00000003 | |
NOTE = 0x00000004 | |
SHLIB = 0x00000005 | |
PHDR = 0x00000006 | |
@classmethod | |
def _range(self, value): | |
OS = (0x60000000, 0x6FFFFFFF) | |
PROC = (0x70000000, 0x7FFFFFFF) | |
return OS[0] <= value <= OS[1] or \ | |
PROC[0] <= value <= PROC[1] | |
type = Field('L', Type) | |
flags = Field('L') | |
offset = Field('Q') | |
vaddr = Field('Q') | |
paddr = Field('Q') | |
filesz = Field('Q') | |
memsz = Field('Q') | |
align = Field('Q') | |
class SectionHeader64(Struct): | |
class Type(enum.Enum, metaclass=RangedEnumMeta): | |
NULL = 0x0 | |
PROGBITS = 0x1 | |
SYMTAB = 0x2 | |
STRTAB = 0x3 | |
RELA = 0x4 | |
HASH = 0x5 | |
DYNAMIC = 0x6 | |
NOTE = 0x7 | |
NOBITS = 0x8 | |
REL = 0x9 | |
SHLIB = 0xa | |
DYNSYM = 0xb | |
@classmethod | |
def _range(self, value): | |
OS = (0x60000000, 0x6FFFFFFF) | |
PROC = (0x70000000, 0x7FFFFFFF) | |
return OS[0] <= value <= OS[1] or \ | |
PROC[0] <= value <= PROC[1] | |
name = Field('L') | |
type = Field('L', Type) | |
flags = Field('Q') | |
addr = Field('Q') | |
offset = Field('Q') | |
link = Field('L') | |
info = Field('L') | |
addralign = Field('Q') | |
entsize = Field('Q') | |
if __name__ == "__main__": | |
import sys | |
f = open(sys.argv[1], 'rb') | |
ident = ELFIdent.read(f) | |
print(ident) | |
assert ident.bits == ELFIdent.Bits.B64 | |
header = ELFHeader64.read(f) | |
print(header) | |
f.seek(header.phoff) | |
for i in range(header.phnum): | |
program_header = ProgramHeader64.read(f) | |
print(program_header) | |
f.seek(header.shoff) | |
for i in range(header.shnum): | |
section_header = SectionHeader64.read(f) | |
print(section_header) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment