From aa9f6713380ae414bba4d1afc9876513a14f11e9 Mon Sep 17 00:00:00 2001 From: ebenders Date: Thu, 8 Sep 2011 11:27:53 +0300 Subject: [PATCH] * Added the ELFFile class - top-level interface to ELF files * Added exceptions classes --- elftools/construct/adapters.py | 6 ++- elftools/elf/constants.py | 7 +++ elftools/elf/elffile.py | 89 ++++++++++++++++++++++++++++++++++ elftools/elf/enums.py | 8 ++- elftools/elf/structs.py | 30 +++++++++++- elftools/exceptions.py | 15 ++++++ z.py | 12 +++-- 7 files changed, 159 insertions(+), 8 deletions(-) create mode 100644 elftools/elf/elffile.py create mode 100644 elftools/exceptions.py diff --git a/elftools/construct/adapters.py b/elftools/construct/adapters.py index 98fda72..69c84d9 100644 --- a/elftools/construct/adapters.py +++ b/elftools/construct/adapters.py @@ -85,7 +85,8 @@ class MappingAdapter(Adapter): return self.encoding[obj] except (KeyError, TypeError): if self.encdefault is NotImplemented: - raise MappingError("no encoding mapping for %r" % (obj,)) + raise MappingError("no encoding mapping for %r [%s]" % ( + obj, self.subcon.name)) if self.encdefault is Pass: return obj return self.encdefault @@ -94,7 +95,8 @@ class MappingAdapter(Adapter): return self.decoding[obj] except (KeyError, TypeError): if self.decdefault is NotImplemented: - raise MappingError("no decoding mapping for %r" % (obj,)) + raise MappingError("no decoding mapping for %r [%s]" % ( + obj, self.subcon.name)) if self.decdefault is Pass: return obj return self.decdefault diff --git a/elftools/elf/constants.py b/elftools/elf/constants.py index 1c64a8f..5af8372 100644 --- a/elftools/elf/constants.py +++ b/elftools/elf/constants.py @@ -1,4 +1,11 @@ +#------------------------------------------------------------------------------- +# elftools: elf/constants.py +# # Constants and flags, placed into classes for namespacing +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- class SHN_INDICES(object): """ Special section indices diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py new file mode 100644 index 0000000..29dc095 --- /dev/null +++ b/elftools/elf/elffile.py @@ -0,0 +1,89 @@ +#------------------------------------------------------------------------------- +# elftools: elf/elffile.py +# +# ELFFile - main class for accessing ELF files +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- + +from .structs import ELFStructs +from ..exceptions import ELFError, ELFParseError +from ..construct import ConstructError + + +class ELFFile(object): + """ Accessible attributes: + + elfclass: + 32 or 64 - specifies the word size of the target machine + + little_endian: + boolean - specifies the target machine's endianness + + header: + the complete ELF file header + """ + def __init__(self, stream): + self.stream = stream + self._identify_file() + self.structs = ELFStructs( + little_endian=self.little_endian, + elfclass=self.elfclass) + self.header = self._parse_elf_header() + + def __getitem__(self, name): + """ Implement dict-like access to header entries + """ + return self.header[name] + + def _identify_file(self): + """ Verify the ELF file and identify its class and endianness. + """ + # Note: this code reads the stream directly, without using ELFStructs, + # since we don't yet know its exact format. ELF was designed to be + # read like this - its e_ident field is word-size and endian agnostic. + # + self.stream.seek(0) + magic = self.stream.read(4) + self._assert(magic == '\x7fELF', 'Magic number does not match') + + ei_class = self.stream.read(1) + if ei_class == '\x01': + self.elfclass = 32 + elif ei_class == '\x02': + self.elfclass = 64 + else: + raise ELFError('Invalid EI_CLASS %s' % repr(ei_class)) + + ei_data = self.stream.read(1) + if ei_data == '\x01': + self.little_endian = True + elif ei_data == '\x02': + self.little_endian = False + else: + raise ELFError('Invalid EI_DATA %s' % repr(ei_data)) + + def _parse_elf_header(self): + """ Parses the ELF file header and assigns the result to attributes + of this object. + """ + self.stream.seek(0) + return self._struct_parse(self.structs.Elf_Ehdr) + + def _struct_parse(self, struct): + """ Convenience method for parsing at the current stream location with + the given struct. Also wraps the error thrown by construct with our + own error. + """ + try: + return struct.parse_stream(self.stream) + except ConstructError as e: + raise ELFParseError(e.message) + + def _assert(self, cond, msg=''): + """ Assert that cond is True, otherwise raise ELFError(msg) + """ + if not cond: + raise ELFError(msg) + diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py index 0762979..1217d74 100644 --- a/elftools/elf/enums.py +++ b/elftools/elf/enums.py @@ -1,5 +1,11 @@ -# Mappings of enum names<->values to be inserted into construct's Enum adapter +#------------------------------------------------------------------------------- +# elftools: elf/enums.py # +# Mappings of enum names to values +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- # e_ident[EI_CLASS] in the ELF header ENUM_EI_CLASS = dict( diff --git a/elftools/elf/structs.py b/elftools/elf/structs.py index 8c610dc..0b9f58f 100644 --- a/elftools/elf/structs.py +++ b/elftools/elf/structs.py @@ -1,3 +1,13 @@ +#------------------------------------------------------------------------------- +# elftools: elf/structs.py +# +# Encapsulation of Construct structs for parsing an ELF file, adjusted for +# correct endianness and word-size. +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- + from ..construct import ( UBInt8, UBInt16, UBInt32, UBInt64, ULInt8, ULInt16, ULInt32, ULInt64, @@ -8,7 +18,25 @@ from ..construct import ( from .enums import * -class ELFStructs(object): +class ELFStructs(object): + """ Accessible attributes: + + Elf_{byte|half|word|addr|offset|sword|xword|xsword}: + Data chunks, as specified by the ELF standard, adjusted for + correct endianness and word-size. + + Elf_Ehdr: + ELF file header + + Elf_Phdr: + Program header + + Elf_Shdr: + Section header + + Elf_Sym: + Symbol table entry + """ def __init__(self, little_endian=True, elfclass=32): assert elfclass == 32 or elfclass == 64 self.little_endian = little_endian diff --git a/elftools/exceptions.py b/elftools/exceptions.py new file mode 100644 index 0000000..b61b463 --- /dev/null +++ b/elftools/exceptions.py @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------------- +# elftools: exceptions.py +# +# Exception classes for elftools +# +# Eli Bendersky (eliben@gmail.com) +# This code is in the public domain +#------------------------------------------------------------------------------- + +class ELFError(Exception): + pass + +class ELFParseError(ELFError): + pass + diff --git a/z.py b/z.py index aa4672e..e959632 100644 --- a/z.py +++ b/z.py @@ -1,16 +1,20 @@ import sys from elftools.elf.structs import ELFStructs +from elftools.elf.elffile import ELFFile # read a little-endian, 64-bit file es = ELFStructs(True, 64) stream = open('binfiles/z.elf', 'rb') -eheader = es.Elf_Ehdr.parse_stream(stream) -print eheader +efile = ELFFile(stream) -shtable_offset = eheader.e_shoff -strtable_section_offset = shtable_offset + eheader.e_shstrndx * eheader.e_shentsize +#~ print efile.header +#~ print dir(efile) +#~ print efile['e_type'] + +shtable_offset = efile['e_shoff'] +strtable_section_offset = shtable_offset + efile['e_shstrndx'] * efile['e_shentsize'] # get to the section header for the sh string table print strtable_section_offset -- 2.30.2