# -*- coding: utf-8 -*-
# This code is part of Amoco
# Copyright (C) 2006-2019 Axel Tillequin (bdcht3@gmail.com)
# published under GPLv2 license
"""
system/elf.py
=============
The system elf module implements Elf classes for both 32/64bits executable format.
"""
from amoco.system.core import BinFormat
from amoco.system.structs import Consts, StructDefine
from amoco.system.structs import StructFormatter, token_constant_fmt, token_address_fmt
from amoco.logger import Log
logger = Log(__name__)
logger.debug("loading module")
[docs]class ElfError(Exception):
"""
ElfError is raised whenever Elf object instance fails
to decode required structures.
"""
def __init__(self, message):
self.message = message
def __str__(self):
return str(self.message)
# ------------------------------------------------------------------------------
[docs]class Elf(BinFormat):
"""
This class takes a DataIO object (ie an opened file of BytesIO instance)
and decodes all ELF structures found in it.
Attributes:
entrypoints (list of int): list of entrypoint addresses.
filename (str): binary file name.
Ehdr (Ehdr): the ELF header structure.
Phdr (list of Phdr): the list of ELF Program header structures.
Shdr (list of Shdr): the list of ELF Section header structures.
dynamic (Bool): True if the binary wants to load dynamic libs.
basemap (int): base address for this ELF image.
functions (list): a list of function names gathered from internal
definitions (if not stripped) and import names.
variables (list): a list of global variables' names (if found.)
"""
is_ELF = True
@property
def entrypoints(self):
return [self.Ehdr.e_entry]
@property
def filename(self):
return self.__file.name
@property
def header(self):
return self.Ehdr
@property
def dataio(self):
return self.__file
def __init__(self, f):
self.__file = f
self.Ehdr = Ehdr(f)
x64 = self.Ehdr.e_ident.EI_CLASS == ELFCLASS64
lbe = ">" if (self.Ehdr.e_ident.EI_DATA == ELFDATA2MSB) else None
self.dynamic = False
# read program header table: should not raise any errors
self.Phdr = []
if self.Ehdr.e_phoff:
offset = self.Ehdr.e_phoff
n, l = self.Ehdr.e_phnum, self.Ehdr.e_phentsize
for pht in range(n):
P = Phdr(f, offset, lbe, x64)
offset += l
if P.p_type == PT_LOAD:
if not self.basemap:
self.basemap = P.p_vaddr
self.Phdr.append(P)
elif P.p_type == PT_INTERP:
self.dynamic = True
self.Phdr.append(P)
elif not P.p_type in Consts.All["p_type"].keys():
logger.verbose("invalid segment detected (removed)")
else:
self.Phdr.append(P)
# read section header table: unused by loader, can raise error
self.Shdr = []
if self.Ehdr.e_shoff:
try:
offset = self.Ehdr.e_shoff
n, l = self.Ehdr.e_shnum, self.Ehdr.e_shentsize
for sht in range(n):
S = Shdr(f, offset, lbe, x64)
offset += l
if S.sh_type in Consts.All["sh_type"].keys():
self.Shdr.append(S)
else:
logger.verbose("unknown sh_type: %d" % S.sh_type)
except Exception:
logger.verbose("exception raised while parsing section(s)")
# read section's name string table:
for i, s in enumerate(self.Shdr):
s.name = ".s%d" % i
n = self.Ehdr.e_shstrndx
if n != SHN_UNDEF and n in range(len(self.Shdr)):
S = self.Shdr[self.Ehdr.e_shstrndx]
if S.sh_type != SHT_STRTAB:
logger.verbose("section names not a string table")
else:
from codecs import decode
offset = S.sh_offset
data = f[offset : offset + S.sh_size]
for s in self.Shdr:
name = data[s.sh_name :].split(b"\0")[0]
s.name = decode(name)
self.__sections = {}
self.functions = self.__functions()
self.variables = self.__variables()
[docs] def getsize(self):
"total file size of all the Program headers"
total = sum([s.p_filesz for s in self.Phdr])
return total
[docs] def getinfo(self, target):
"""
target is either an address provided as str or int,
or a symbol str searched in the functions dictionary.
Returns a triplet with:
- section index (0 is error, -1 is a dynamic call)
- offset into section (idem)
- base virtual address (0 for dynamic calls)
"""
addr = None
if isinstance(target, str):
try:
addr = int(target, 16)
except ValueError:
for a, f in iter(self.functions.items()):
if f[0] == target:
addr = int(a, 16)
break
elif isinstance(target, int):
addr = target
if addr is None:
# target is propably a symbol not found in functions
return None, 0, 0
# now we have addr so we can see in which section/segment it is...
# sections are smaller than segments so we try first with Shdr
# but this may lead to errors because what really matters are segments
# loaded by the kernel binfmt_elf.c loader.
if self.Shdr:
for s in reversed(self.Shdr):
if s.sh_type != SHT_PROGBITS:
continue
if s.sh_addr <= addr < s.sh_addr + s.sh_size:
return s, addr - s.sh_addr, s.sh_addr
elif self.Phdr:
for s in reversed(self.Phdr):
if s.p_type != PT_LOAD:
continue
if s.p_vaddr <= addr < s.p_vaddr + s.p_filesz:
return s, addr - s.p_vaddr, s.p_vaddr
return None, 0, 0
[docs] def data(self, target, size):
"returns 'size' bytes located at target virtual address"
return self._readcode(target, size)[0]
def _readcode(self, target, size=None):
s, offset, base = self.getinfo(target)
data = b""
if s:
if isinstance(s, Phdr):
c = self.readsegment(s)
else:
c = self.readsection(s)
if c:
if size != None:
if isinstance(c, Str):
c = c.data
data = c[offset : offset + size]
else:
data = c[offset:]
return data, 0, base + offset
[docs] def getfileoffset(self, target):
"converts given target virtual address back to offset in file"
s, offset, base = self.getinfo(target)
if s != None:
result = s.p_offset + offset
else:
result = None
return result
[docs] def readsegment(self, S):
"returns segment S data padded to S.p_memsz"
self.__file.seek(S.p_offset)
return self.__file.read(S.p_filesz).ljust(S.p_memsz, b"\x00")
[docs] def loadsegment(self, S, pagesize=None):
"""
If S is of type PT_LOAD, returns a dict {base: bytes}
indicating that segment data bytes (extended to pagesize boundary)
need to be mapped at virtual base address.
(Returns None if not a PT_LOAD segment.)
"""
if S.p_type == PT_LOAD:
self.__file.seek(S.p_offset)
if S.p_align > 1 and (S.p_offset != (S.p_vaddr % S.p_align)):
logger.verbose(
"wrong p_vaddr/p_align [%08x/%0d]" % (S.p_vaddr, S.p_align)
)
base = S.p_vaddr
bytes_ = self.__file.read(S.p_filesz).ljust(S.p_memsz, b"\x00")
if pagesize:
# note: bytes are not truncated, only extended if needed...
bytes_ = bytes_.ljust(pagesize, b"\x00")
return {base: bytes_}
else:
logger.error("segment not a PT_LOAD [%08x/%0d]" % (S.p_vaddr, S.p_align))
return None
[docs] def readsection(self, sect):
"returns the given section data bytes from file."
S = None
if isinstance(sect, str):
for st in self.Shdr:
if st.name == sect:
S = st
break
elif isinstance(sect, int):
S = self.Shdr[sect]
else:
S = sect
if S:
if S.name in self.__sections:
return self.__sections[S.name]
if S.sh_type in (SHT_SYMTAB, SHT_DYNSYM):
s = self.__read_symtab(S)
elif S.sh_type == SHT_STRTAB:
s = self.__read_strtab(S)
elif S.sh_type in (SHT_REL, SHT_RELA):
s = self.__read_relocs(S)
elif S.sh_type == SHT_DYNAMIC:
s = self.__read_dynamic(S)
else:
self.__file.seek(S.sh_offset)
s = self.__file.read(S.sh_size)
self.__sections[S.name] = s
return s
def __read_symtab(self, section):
if section.sh_type not in (SHT_SYMTAB, SHT_DYNSYM):
logger.warning("not a symbol table section")
return None
x64 = self.Ehdr.e_ident.EI_CLASS == ELFCLASS64
lbe = ">" if (self.Ehdr.e_ident.EI_DATA == ELFDATA2MSB) else None
# read the section:
self.__file.seek(section.sh_offset)
data = self.__file.read(section.sh_size)
# and parse it into Sym objects:
l = section.sh_entsize
if (section.sh_size % l) != 0:
raise ElfError("symbol table size mismatch")
else:
n = section.sh_size // l
symtab = []
offset = 0
for i in range(n):
symtab.append(Sym(data, offset, lbe, x64))
offset += l
return symtab
def __read_strtab(self, section):
if section.sh_type != SHT_STRTAB:
raise ElfError("not a string table section")
self.__file.seek(section.sh_offset)
data = self.__file.read(section.sh_size)
x64 = self.Ehdr.e_ident.EI_CLASS == ELFCLASS64
strtab = StrTable(data, x64)
return strtab
def __read_relocs(self, section):
if section.sh_type not in (SHT_REL, SHT_RELA):
logger.warning("not a relocation table section")
return None
self.__file.seek(section.sh_offset)
data = self.__file.read(section.sh_size)
l = section.sh_entsize
if (section.sh_size % l) != 0:
raise ElfError("relocation table size mismatch")
else:
n = section.sh_size // l
reltab = []
x64 = self.Ehdr.e_ident.EI_CLASS == ELFCLASS64
lbe = ">" if (self.Ehdr.e_ident.EI_DATA == ELFDATA2MSB) else None
offset = 0
if section.sh_type == SHT_REL:
rcls = Rel
elif section.sh_type == SHT_RELA:
rcls = Rela
for i in range(n):
reltab.append(rcls(data, offset, lbe, x64))
offset += l
return reltab
def __read_dynamic(self, section):
if section.sh_type != SHT_DYNAMIC:
logger.warning("not a dynamic linking section")
return None
# read the section:
self.__file.seek(section.sh_offset)
data = self.__file.read(section.sh_size)
# and parse it into Dyn objects:
l = section.sh_entsize
if (section.sh_size % l) != 0:
raise ElfError("dynamic linking size mismatch")
else:
n = section.sh_size // l
dyntab = []
x64 = self.Ehdr.e_ident.EI_CLASS == ELFCLASS64
lbe = ">" if (self.Ehdr.e_ident.EI_DATA == ELFDATA2MSB) else None
offset = 0
for i in range(n):
dyntab.append(Dyn(data, offset, lbe, x64))
offset += l
return dyntab
def __read_note(self, section):
if section.sh_type != SHT_NOTE:
logger.warning("not a note section")
return None
self.__file.seek(section.sh_offset)
data = self.__file.read(section.sh_size)
x64 = self.Ehdr.e_ident.EI_CLASS == ELFCLASS64
lbe = ">" if (self.Ehdr.e_ident.EI_DATA == ELFDATA2MSB) else None
note = Note(data, lbe, x64)
return note
def __functions(self, fltr=None):
D = self.__symbols(STT_FUNC)
# fltr applies to section name only :
if fltr:
for k, v in iter(D.items()):
if self.Shdr[v[2]].name != fltr:
D.pop(k)
if self.dynamic:
D.update(self.__dynamic(STT_FUNC))
return D
def __variables(self, fltr=None):
D = self.__symbols(STT_OBJECT)
# fltr applies also to section name :
if fltr:
for k, v in iter(D.items()):
if self.Shdr[v[2]].name != fltr:
D.pop(k)
return D
def __symbols(self, t):
D = {}
symtab = self.readsection(".symtab") or []
strtab = self.readsection(".strtab")
if strtab:
for sym in symtab:
if sym.st_type == t and sym.st_value:
D[sym.st_value] = (
str(strtab[sym.st_name].decode()),
sym.st_size,
sym.st_info,
sym.st_shndx,
)
return D
def __dynamic(self, type=None):
D = {}
self.readsection(".dynamic")
dynsym = self.readsection(".dynsym") or []
dynstr = self.readsection(".dynstr")
if dynstr:
for s in self.Shdr:
if s.sh_type in (SHT_REL, SHT_RELA):
for r in self.readsection(s):
if r.r_offset:
sym = dynsym[r.r_sym]
D[r.r_offset] = str(dynstr[sym.st_name].decode())
return D
[docs] def checksec(self):
"check for usual security features."
R = {}
R["Canary"] = 0
R["Fortify"] = 0
for f in iter(self.functions.values()):
if isinstance(f, tuple):
f = f[0]
if f.startswith("__stack_chk_fail"):
R["Canary"] = 1
elif f.endswith("_chk@GLIBC"):
R["Fortify"] = 1
R["NX"] = 0
R["Partial RelRO"] = 0
for p in self.Phdr:
if p.p_type == PT_GNU_STACK:
if not (p.p_flags & PF_X):
R["NX"] = 1
elif p.p_type == PT_GNU_RELRO:
R["Partial RelRO"] = 1
R["PIE"] = 0
if self.Ehdr.e_type != ET_EXEC:
R["PIE"] = 1
R["Full RelRO"] = 0
for d in self.readsection(".dynamic") or []:
if d.d_tag == DT_BIND_NOW or\
(d.d_tag == DT_FLAGS and d.d_un==DF_BIND_NOW):
R["Full RelRO"] = 1
break
return R
def __str__(self):
ss = ["ELF header:"]
tmp = self.Ehdr.pfx
self.Ehdr.pfx = "\t"
ss.append(self.Ehdr.__str__())
self.Ehdr.pfx = tmp
ss += ["\nSections:"]
for s in self.Shdr:
tmp = s.pfx
s.pfx = "\t"
ss.append(s.__str__())
ss.append("---")
s.pfx = tmp
ss += ["\nSegments:"]
for s in self.Phdr:
tmp = s.pfx
s.pfx = "\t"
ss.append(s.__str__())
ss.append("---")
s.pfx = tmp
return "\n".join(ss)
# ------------------------------------------------------------------------------
[docs]@StructDefine(
"""
B : ELFMAG0
c*3: ELFMAG
B : EI_CLASS
B : EI_DATA
B : EI_VERSION
B : EI_OSABI
B : EI_ABIVERSION
b*7: unused
"""
)
class IDENT(StructFormatter):
def __init__(self, data=None):
self.name_formatter("EI_CLASS", "EI_DATA", "EI_OSABI")
if data:
self.unpack(data)
def unpack(self, data, offset=0):
StructFormatter.unpack(self, data, offset)
if self.ELFMAG0 != 0x7F or self.ELFMAG != b"ELF":
raise ElfError("Wrong magic number, not an ELF file ?")
if self.EI_DATA not in (ELFDATA2LSB, ELFDATA2MSB):
logger.info("No endianess specified in ELF header.")
return self
# EI_CLASS values:
with Consts("EI_CLASS"):
ELFCLASSNONE = 0
ELFCLASS32 = 1
ELFCLASS64 = 2
ELFCLASSNUM = 3
# EI_DATA values:
with Consts("EI_DATA"):
ELFDATANONE = 0
ELFDATA2LSB = 1
ELFDATA2MSB = 2
ELFDATANUM = 3
# EI_OSABI values:
with Consts("EI_OSABI"):
ELFOSABI_NONE = 0
ELFOSABI_SYSV = 0
ELFOSABI_HPUX = 1
ELFOSABI_NETBSD = 2
ELFOSABI_LINUX = 3
ELFOSABI_SOLARIS = 6
ELFOSABI_AIX = 7
ELFOSABI_IRIX = 8
ELFOSABI_FREEBSD = 9
ELFOSABI_TRU64 = 10
ELFOSABI_MODESTO = 11
ELFOSABI_OPENBSD = 12
ELFOSABI_ARM = 97
ELFOSABI_STANDALONE = 255
[docs]@StructDefine(
"""
IDENT :< e_ident
H : e_type
H : e_machine
I : e_version
I : e_entry
I : e_phoff
I : e_shoff
I : e_flags
H : e_ehsize
H : e_phentsize
H : e_phnum
H : e_shentsize
H : e_shnum
H : e_shstrndx
"""
)
class Ehdr(StructFormatter):
def __init__(self, data=None):
self.name_formatter("e_type", "e_machine", "e_version")
self.address_formatter("e_entry")
self.flag_formatter("e_flags")
if data:
self.unpack(data)
def unpack(self, data, offset=0):
f0 = self.fields[0]
self._v.e_ident = f0.unpack(data, offset)
offset += f0.size()
# change endianness if necessary:
if self._v.e_ident.EI_DATA == ELFDATA2MSB:
for f in self.fields[1:]:
f.order = ">"
# change pointers format if necessary:
if self._v.e_ident.EI_CLASS == ELFCLASS64:
self.fields[4].typename = "Q"
self.fields[5].typename = "Q"
self.fields[6].typename = "Q"
for f in self.fields[1:]:
setattr(self._v, f.name, f.unpack(data, offset))
offset += f.size()
return self
# legal values for e_type (object file type):
with Consts("e_type"):
ET_NONE = 0
ET_REL = 1
ET_EXEC = 2
ET_DYN = 3
ET_CORE = 4
ET_NUM = 5
ET_LOOS = 0xFE00
ET_HIOS = 0xFEFF
ET_LOPROC = 0xFF00
ET_HIPROC = 0xFFFF
# legal values for e_machine (architecture):
with Consts("e_machine"):
EM_NONE = 0
EM_M32 = 1
EM_SPARC = 2
EM_386 = 3
EM_68K = 4
EM_88K = 5
EM_860 = 7
EM_MIPS = 8
EM_S370 = 9
EM_MIPS_RS3_LE = 10
EM_PARISC = 15
EM_VPP500 = 17
EM_SPARC32PLUS = 18
EM_960 = 19
EM_PPC = 20
EM_PPC64 = 21
EM_S390 = 22
EM_V800 = 36
EM_FR20 = 37
EM_RH32 = 38
EM_RCE = 39
EM_ARM = 40
EM_FAKE_ALPHA = 41
EM_SH = 42
EM_SPARCV9 = 43
EM_TRICORE = 44
EM_ARC = 45
EM_H8_300 = 46
EM_H8_300H = 47
EM_H8S = 48
EM_H8_500 = 49
EM_IA_64 = 50
EM_MIPS_X = 51
EM_COLDFIRE = 52
EM_68HC12 = 53
EM_MMA = 54
EM_PCP = 55
EM_NCPU = 56
EM_NDR1 = 57
EM_STARCORE = 58
EM_ME16 = 59
EM_ST100 = 60
EM_TINYJ = 61
EM_X86_64 = 62
EM_PDSP = 63
EM_FX66 = 66
EM_ST9PLUS = 67
EM_ST7 = 68
EM_68HC16 = 69
EM_68HC11 = 70
EM_68HC08 = 71
EM_68HC05 = 72
EM_SVX = 73
EM_ST19 = 74
EM_VAX = 75
EM_CRIS = 76
EM_JAVELIN = 77
EM_FIREPATH = 78
EM_ZSP = 79
EM_MMIX = 80
EM_HUANY = 81
EM_PRISM = 82
EM_AVR = 83
EM_FR30 = 84
EM_D10V = 85
EM_D30V = 86
EM_V850 = 87
EM_M32R = 88
EM_MN10300 = 89
EM_MN10200 = 90
EM_PJ = 91
EM_OPENRISC = 92
EM_ARC_A5 = 93
EM_XTENSA = 94
EM_NUM = 95
EM_ST200 = 100
EM_MSP430 = 105
EM_SEP = 108
EM_M16C = 117
EM_DSPIC30F = 118
EM_CE = 119
EM_M32C = 120
EM_R32C = 162
EM_QDSP6 = 164
EM_AARCH64 = 183
EM_AVR32 = 185
EM_STM8 = 186
EM_CUDA = 190
EM_Z80 = 220
EM_AMDGPU = 224
EM_RISCV = 243
EM_BPF = 247
# unofficial values should pick large index:
EM_ALPHA = 0x9026
EM_WEBASSEMBLY = 0x4157
# legal values for e_version (version):
with Consts("e_version"):
EV_NONE = 0
EV_CURRENT = 1
EV_NUM = 2
[docs]@StructDefine(
"""
I : sh_name
I : sh_type
I : sh_flags
I : sh_addr
I : sh_offset
I : sh_size
I : sh_link
I : sh_info
I : sh_addralign
I : sh_entsize
"""
)
class Shdr(StructFormatter):
def __init__(self, data=None, offset=0, order=None, x64=False):
if order:
for f in self.fields:
f.order = order
if x64:
for i in (2, 3, 4, 5, 8, 9):
self.fields[i].typename = "Q"
self.name_formatter("sh_name", "sh_type")
self.address_formatter("sh_addr")
self.flag_formatter("sh_flags")
self.func_formatter(sh_addralign=token_constant_fmt)
if data:
self.unpack(data, offset)
with Consts("sh_name"):
SHN_UNDEF = 0
SHN_LORESERVE = 0xFF00
SHN_LOPROC = 0xFF00
SHN_BEFORE = 0xFF00
SHN_AFTER = 0xFF01
SHN_HIPROC = 0xFF1F
SHN_LOOS = 0xFF20
SHN_HIOS = 0xFF3F
SHN_ABS = 0xFFF1
SHN_COMMON = 0xFFF2
SHN_XINDEX = 0xFFFF
SHN_HIRESERVE = 0xFFFF
# legal values for sh_type (section type):
with Consts("sh_type"):
SHT_NULL = 0
SHT_PROGBITS = 1
SHT_SYMTAB = 2
SHT_STRTAB = 3
SHT_RELA = 4
SHT_HASH = 5
SHT_DYNAMIC = 6
SHT_NOTE = 7
SHT_NOBITS = 8
SHT_REL = 9
SHT_SHLIB = 10
SHT_DYNSYM = 11
SHT_INIT_ARRAY = 14
SHT_FINI_ARRAY = 15
SHT_PREINIT_ARRAY = 16
SHT_GROUP = 17
SHT_SYMTAB_SHNDX = 18
SHT_NUM = 19
SHT_LOOS = 0x60000000
SHT_GNU_HASH = 0x6FFFFFF6
SHT_GNU_LIBLIST = 0x6FFFFFF7
SHT_CHECKSUM = 0x6FFFFFF8
SHT_LOSUNW = 0x6FFFFFFA
SHT_SUNW_move = 0x6FFFFFFA
SHT_SUNW_COMDAT = 0x6FFFFFFB
SHT_SUNW_syminfo = 0x6FFFFFFC
SHT_GNU_verdef = 0x6FFFFFFD
SHT_GNU_verneed = 0x6FFFFFFE
SHT_GNU_versym = 0x6FFFFFFF
SHT_HISUNW = 0x6FFFFFFF
SHT_HIOS = 0x6FFFFFFF
SHT_LOPROC = 0x70000000
SHT_HIPROC = 0x7FFFFFFF
SHT_LOUSER = 0x80000000
SHT_HIUSER = 0x8FFFFFFF
SHT_ARM_EXIDX = SHT_LOPROC + 1
SHT_ARM_PREEMPTMAP = SHT_LOPROC + 2
SHT_ARM_ATTRIBUTES = SHT_LOPROC + 3
# legal values for sh_flags (section flags):
with Consts("sh_flags"):
SHF_WRITE = 1 << 0
SHF_ALLOC = 1 << 1
SHF_EXECINSTR = 1 << 2
SHF_MERGE = 1 << 4
SHF_STRINGS = 1 << 5
SHF_INFO_LINK = 1 << 6
SHF_LINK_ORDER = 1 << 7
SHF_OS_NONCONFORMING = 1 << 8
SHF_GROUP = 1 << 9
SHF_TLS = 1 << 10
SHF_MASKOS = 0x0FF00000
SHF_MASKPROC = 0xF0000000
SHF_ORDERED = 1 << 30
SHF_EXCLUDE = 1 << 31
# section group handling:
GRP_COMDAT = 0x1
[docs]@StructDefine(
"""
I : st_name
I : st_value
I : st_size
B : st_info
B : st_other
H : st_shndx
"""
)
class Sym(StructFormatter):
def __init__(self, data=None, offset=0, order=None, x64=False):
if order:
for f in self.fields:
f.order = order
if x64: # need to reorder fields...
fvalue = self.fields.pop(1)
fsize = self.fields.pop(1)
fvalue.typename = fsize.typename = "Q"
self.fields.append(fvalue)
self.fields.append(fsize)
self.name_formatter("st_name", "st_bind", "st_type", "st_visibility")
if data:
self.unpack(data, offset)
def ELF32_ST_BIND(self):
return self.st_info >> 4
st_bind = property(ELF32_ST_BIND)
def ELF32_ST_TYPE(self):
return self.st_info & 0xF
st_type = property(ELF32_ST_TYPE)
def ELF32_ST_INFO(self, bind, type):
self._v.st_info = bind << 4 + (type & 0xF)
def ELF32_ST_VISIBILITY(self):
return self.st_other & 0x03
st_visibility = property(ELF32_ST_VISIBILITY)
def __str__(self):
s = super().__str__() + "\n"
cname = self.__class__.__name__
s += self.strkey("st_bind", cname) + "\n"
s += self.strkey("st_type", cname) + "\n"
s += self.strkey("st_visibility", cname)
return s
# legal values for st_bind:
with Consts("st_bind"):
STB_LOCAL = 0
STB_GLOBAL = 1
STB_WEAK = 2
STB_NUM = 3
STB_LOOS = 10
STB_HIOS = 12
STB_LOPROC = 13
STB_HIPROC = 15
# legal values for st_type:
with Consts("st_type"):
STT_NOTYPE = 0
STT_OBJECT = 1
STT_FUNC = 2
STT_SECTION = 3
STT_FILE = 4
STT_COMMON = 5
STT_TLS = 6
STT_NUM = 7
STT_LOOS = 10
STT_HIOS = 12
STT_LOPROC = 13
STT_HIPROC = 15
# special index indicating the end end of a chain:
STN_UNDEF = 0
with Consts("st_visibility"):
STV_DEFAULT = 0
STV_INTERNAL = 1
STV_HIDDEN = 2
STV_PROTECTED = 3
[docs]@StructDefine(
"""
I : r_offset
I : r_info
"""
)
class Rel(StructFormatter):
def __init__(self, data=None, offset=0, order=None, x64=False):
if order:
for f in self.fields:
f.order = order
if x64:
for f in self.fields:
f.typename = "Q"
self.name_formatter("r_type")
self.func_formatter(r_sym=token_address_fmt)
if data:
self.unpack(data, offset)
def ELF_R_SYM(self):
return self.r_info >> (8 if self.fields[1].typename == "I" else 32)
r_sym = property(ELF_R_SYM)
def ELF_R_TYPE(self):
return self.r_info & (0xFF if self.fields[1].typename == "I" else 0xFFFFFFFF)
r_type = property(ELF_R_TYPE)
def ELF_R_INFO(self, sym, type):
if self.fields[1].typename == "I":
self._v.r_info = sym << 8 + (type & 0xFF)
else:
self._v.r_info = sym << 32 + (type & 0xFFFFFFFF)
def __str__(self):
s = StructFormatter.__str__(self) + "\n"
cname = self.__class__.__name__
s += self.strkey("r_type", cname)
return s
[docs]@StructDefine(
"""
I : r_offset
I : r_info
I : r_addend
"""
)
class Rela(Rel):
def __init__(self, data=None, offset=0, order=None, x64=False):
if order:
for f in self.fields:
f.order = order
if x64:
for f in self.fields:
f.typename = "Q"
if data:
self.unpack(data, offset)
[docs]@StructDefine(
"""
I : p_type
I : p_offset
I : p_vaddr
I : p_paddr
I : p_filesz
I : p_memsz
I : p_flags
I : p_align
"""
)
class Phdr(StructFormatter):
def __init__(self, data=None, offset=0, order=None, x64=False):
if order:
for f in self.fields:
f.order = order
if x64:
pflags = self.fields.pop(6)
self.fields.insert(1, pflags)
for f in self.fields[2:]:
f.typename = "Q"
self.name_formatter("p_type")
self.address_formatter("p_vaddr", "p_paddr")
self.flag_formatter("p_flags")
if data:
self.unpack(data, offset)
# legal values for p_type (segment type):
with Consts("p_type"):
PT_NULL = 0
PT_LOAD = 1
PT_DYNAMIC = 2
PT_INTERP = 3
PT_NOTE = 4
PT_SHLIB = 5
PT_PHDR = 6
PT_TLS = 7
PT_NUM = 8
PT_LOOS = 0x60000000
PT_GNU_EH_FRAME = 0x6474E550
PT_GNU_STACK = 0x6474E551
PT_GNU_RELRO = 0x6474E552
PT_LOSUNW = 0x6FFFFFFA
PT_SUNWBSS = 0x6FFFFFFA
PT_SUNWSTACK = 0x6FFFFFFB
PT_HISUNW = 0x6FFFFFFF
PT_HIOS = 0x6FFFFFFF
PT_LOPROC = 0x70000000
PT_HIPROC = 0x7FFFFFFF
PT_ARM_EXIDX = PT_LOPROC + 1
# legal values for p_flags (segment flags):
with Consts("p_flags"):
PF_X = 1 << 0
PF_W = 1 << 1
PF_R = 1 << 2
PF_MASKOS = 0x0FF00000
PF_MASKPROC = 0xF0000000
PF_ARM_SB = 0x10000000
PF_ARM_PI = 0x20000000
PF_ARM_ABS = 0x40000000
[docs]@StructDefine(
"""
I : namesz
I : descsz
I : n_type
"""
)
class Note(StructFormatter):
def __init__(self, data=None, offset=0, order=None, x64=False):
if order:
for f in self.fields:
f.order = order
if x64:
for f in self.fields:
f.typename = "Q"
self.name_formatter("n_type")
if data:
self.unpack(data, offset)
offset += self.size()
self.name = data[offset : offset + self.namesz]
offset += self.namesz
if offset % 4 != 0:
offset = ((offset + 4) // 4) * 4
self.desc = data[offset : offset + self.descsz]
# legal values for note segment descriptor types for core files:
with Consts("n_type"):
NT_PRSTATUS = 1
NT_FPREGSET = 2
NT_PRPSINFO = 3
NT_PRXREG = 4
NT_TASKSTRUCT = 4
NT_PLATFORM = 5
NT_AUXV = 6
NT_GWINDOWS = 7
NT_ASRS = 8
NT_PSTATUS = 10
NT_PSINFO = 13
NT_PRCRED = 14
NT_UTSNAME = 15
NT_LWPSTATUS = 16
NT_LWPSINFO = 17
NT_PRFPXREG = 20
NT_VERSION = 1
[docs]@StructDefine(
"""
I : d_tag
I : d_un
"""
)
class Dyn(StructFormatter):
def __init__(self, data=None, offset=0, order=None, x64=False):
if order:
for f in self.fields:
f.order = order
if x64:
for f in self.fields:
f.typename = "Q"
self.name_formatter("d_tag")
self.address_formatter("d_un")
if data:
self.unpack(data, offset)
def DT_VALTAGIDX(self, tag):
self.d_un = DT_VALRNGHI - tag
def DT_ADDRTAGIDX(self, tag):
self.d_un = DT_ADDRRNGHI - tag
# legal values for d_tag (dynamic entry type):
with Consts("d_tag"):
DT_NULL = 0
DT_NEEDED = 1
DT_PLTRELSZ = 2
DT_PLTGOT = 3
DT_HASH = 4
DT_STRTAB = 5
DT_SYMTAB = 6
DT_RELA = 7
DT_RELASZ = 8
DT_RELAENT = 9
DT_STRSZ = 10
DT_SYMENT = 11
DT_INIT = 12
DT_FINI = 13
DT_SONAME = 14
DT_RPATH = 15
DT_SYMBOLIC = 16
DT_REL = 17
DT_RELSZ = 18
DT_RELENT = 19
DT_PLTREL = 20
DT_DEBUG = 21
DT_TEXTREL = 22
DT_JMPREL = 23
DT_BIND_NOW = 24
DT_INIT_ARRAY = 25
DT_FINI_ARRAY = 26
DT_INIT_ARRAYSZ = 27
DT_FINI_ARRAYSZ = 28
DT_RUNPATH = 29
DT_FLAGS = 30
DT_ENCODING = 32
DT_PREINIT_ARRAY = 32
DT_PREINIT_ARRAYSZ = 33
DT_NUM = 34
DT_LOOS = 0x6000000D
DT_HIOS = 0x6FFFF000
DT_LOPROC = 0x70000000
DT_HIPROC = 0x7FFFFFFF
DT_GNU_HASH = 0x6FFFFEF5
DT_VERDEF = 0x6FFFFFFC
DT_VERDEFNUM = 0x6FFFFFFD
DT_VERNEED = 0x6FFFFFFE
DT_VERNEEDNUM = 0x6FFFFFFF
DT_VERSYM = 0x6FFFFFF0
DT_RELACOUNT = 0x6FFFFFF9
DT_RELCOUNT = 0x6FFFFFFA
DT_FLAGS_1 = 0x6FFFFFFB
# legal values for d_un (union type use here value):
with Consts("d_un"):
DT_VALRNGLO = 0x6FFFFD00
DT_GNU_PRELINKED = 0x6FFFFDF5
DT_GNU_CONFLICTSZ = 0x6FFFFDF6
DT_GNU_LIBLISTSZ = 0x6FFFFDF7
DT_CHECKSUM = 0x6FFFFDF8
DT_PLTPADSZ = 0x6FFFFDF9
DT_MOVEENT = 0x6FFFFDFA
DT_MOVESZ = 0x6FFFFDFB
DT_FEATURE_1 = 0x6FFFFDFC
DT_POSFLAG_1 = 0x6FFFFDFD
DT_SYMINSZ = 0x6FFFFDFE
DT_SYMINENT = 0x6FFFFDFF
DT_VALRNGHI = 0x6FFFFDFF
DT_VALNUM = 12
# legal values for d_un (union type use here address):
DT_ADDRRNGLO = 0x6FFFFE00
DT_TLSDESC_PLT = 0x6FFFFEF6
DT_TLSDESC_GOT = 0x6FFFFEF7
DT_GNU_CONFLICT = 0x6FFFFEF8
DT_GNU_LIBLIST = 0x6FFFFEF9
DT_CONFIG = 0x6FFFFEFA
DT_DEPAUDIT = 0x6FFFFEFB
DT_AUDIT = 0x6FFFFEFC
DT_PLTPAD = 0x6FFFFEFD
DT_MOVETAB = 0x6FFFFEFE
DT_SYMINFO = 0x6FFFFEFF
DT_ADDRRNGHI = 0x6FFFFEFF
DT_ADDRNUM = 10
DF_ORIGIN = 0x1
DF_SYMBOLIC = 0x2
DF_TEXTREL = 0x4
DF_BIND_NOW = 0x8
DF_STATIC_TLS = 0x10
[docs]@StructDefine(
"""
I: l_name
I: l_time_stamp
I: l_checksum
I: l_version
I: l_flags
"""
)
class Lib(StructFormatter):
def __init__(self, data=None, offset=0, order=None, x64=False):
if order:
for f in self.fields:
f.order = order
self.flag_formatter("l_flags")
if data:
self.unpack(data, offset)
with Consts("l_flags"):
LL_IGNORE_INT_VER = 1 << 1
LL_EXACT_MATCH = 1 << 0
LL_REQUIRE_MINOR = 1 << 2
LL_NONE = 0x0
LL_DELTA = 1 << 5
LL_EXPORTS = 1 << 3
LL_DELAY_LOAD = 1 << 4
# String Table entry; provided to deal with C strings and char indexed
# string table sections. This is not a standard structure, it is more
# like a C-string Array class for python.
# ------------------------------------------------------------------------------
class StrTable(object):
def __init__(self, data, x64=False):
self.data = data
self.x64 = x64
def __getitem__(self, i):
z = self.data[i:].index(b"\0")
return self.data[i : i + z]
def as_dict(self):
D = {}
cstrings = self.data.split(b"\0")
p = 0
for cs in cstrings:
D[p] = cs
p += len(cs) + 1
return D
def __str__(self):
fmt = "0x%" + "%02dx: %%s" % (16 if self.x64 else 8)
return "\n".join((fmt % (k, v) for (k, v) in iter(self.as_dict().items())))