Ejemplo n.º 1
0
def get_relocations(fd):
    """ 
	Return a dict with the relocations contained in a file
	"""
    elffile = ELFFile(fd)
    relocations = {}
    has_relocation_sections = False
    for section in elffile.iter_sections():
        if not isinstance(section, RelocationSection):
            continue

        has_relocation_sections = True
        # The symbol table section pointed to in sh_link
        symtable = elffile.get_section(section['sh_link'])

        for rel in section.iter_relocations():
            offset = rel['r_offset']

            symbol = symtable.get_symbol(rel['r_info_sym'])
            # Some symbols have zero 'st_name', so instead what's used is
            # the name of the section they point at
            if symbol['st_name'] == 0:
                symsec = elffile.get_section(symbol['st_shndx'])
                symbol_name = symsec.name
            else:
                symbol_name = symbol.name
                relocations[offset] = bytes2str(symbol_name)

    return relocations
Ejemplo n.º 2
0
def get_relocations(fd):
	""" 
	Return a dict with the relocations contained in a file
	"""
	elffile = ELFFile(fd)
	relocations = {}
	has_relocation_sections = False
	for section in elffile.iter_sections():
	    if not isinstance(section, RelocationSection):
		continue

	    has_relocation_sections = True
	    # The symbol table section pointed to in sh_link
	    symtable = elffile.get_section(section['sh_link'])

	    for rel in section.iter_relocations():
		offset = rel['r_offset'] 

		symbol = symtable.get_symbol(rel['r_info_sym'])
		# Some symbols have zero 'st_name', so instead what's used is
		# the name of the section they point at
		if symbol['st_name'] == 0:
		    symsec = elffile.get_section(symbol['st_shndx'])
		    symbol_name = symsec.name
		else:
		    symbol_name = symbol.name
		    relocations[offset] = bytes2str(symbol_name)

	return relocations
Ejemplo n.º 3
0
    def get_relocations(self):
        relocations = []
        with open(self.filename, "rb") as elf_file:
            elf = ELFFile(elf_file)
            for section in elf.iter_sections():
                if not isinstance(section, RelocationSection):
                    continue
                symbols = elf.get_section(section["sh_link"])
                for relocation in section.iter_relocations():
                    if relocation["r_info_sym"] == 0:
                        continue
                    relocation_data = {}
                    relocation_data["offset"] = relocation["r_offset"]
                    relocation_data["info"] = relocation["r_info"]
                    relocation_data["info_type"] = describe_reloc_type(relocation["r_info_type"], elf)
                    relocation_data["symbol"] = relocation["r_info_sym"]
                    symbol = symbols.get_symbol(relocation_data["symbol"])
                    if symbol["st_name"] == 0:
                        symbol_section = elf.get_section(symbol["st_shndx"])
                        relocation_data["symbol_name"] = symbol_section.name
                    else:
                        relocation_data["symbol_name"] = symbol.name

                    relocation_data["symbol_value"] = symbol["st_value"]
                    relocation_data["section_index"] = symbol["st_shndx"]
                    relocations.append(relocation_data)
        return relocations
Ejemplo n.º 4
0
Archivo: elf.py Proyecto: f0wl/REHelper
def print_basic_info(filename: str) -> None:
    with open(filename, "rb") as f:
        elffile = ELFFile(f)  # ELF object

        # variables
        sections = ""
        debug = RED + "No" + RESET
        fileMD5 = file_MD5sum(filename)
        filesha1 = file_sha1sum(filename)
        filesha256 = file_sha256sum(filename)
        fileSSDEEP = file_ssdeepsum(filename)
        vtlink = tinyurl("https://www.virustotal.com/gui/file/" + filesha256)

        # logic
        if not vtlink:
            vtlink = "https://www.virustotal.com/gui/file/" + filesha256
        for x in range(elffile.num_sections()):
            if len(elffile.get_section(x).name) > 0:
                sections += "{}{} {}({}) ".format(
                    GREEN,
                    elffile.get_section(x).name, RESET,
                    hex(elffile.get_section(x).data_size))
            if x % 4 == 0 and x > 0:
                sections += "\n"

        if not sections:
            sections = RED + "No sections found" + RESET
        # has debug info?
        if elffile.has_dwarf_info():
            debug = GREEN + "Yes" + RESET

        info_table = [
            ["Filename:", filename], ["Filesize:",
                                      file_size(filename)],
            [
                "Filetype:",
                GREEN + "ELF " + str(elffile.get_machine_arch()) + RESET
            ],
            [
                "Subsystem:",
                GREEN + describe_e_type(elffile.header['e_type']) + RESET
            ], ["MD5: ", fileMD5], ["SHA1: ", filesha1],
            ["SHA256: ", filesha256], ["SSDEEP:", fileSSDEEP],
            ["VT link:", vtlink], ["Symbols:", debug],
            ["Entropy:", str(file_entropy(filename))],
            ["Sections:\n(with size)", sections],
            ["Entrypoint:", "{}".format(hex(elffile.header["e_entry"]))]
        ]

        print("")
        print(
            AsciiTable(
                title="Basic Information",
                table_data=info_table,
            ).table)
        print("")
def process_file(filename):
    print('Processing file:', filename)
    print("###############################################################")
    functions = {}
    with open(filename, 'rb') as f:
        p_vaddr = []
        p_offset = []
        p_filesz = []
        p_memsz = []
        elffile = ELFFile(f)

        for seg in elffile.iter_segments():
            if seg.header.p_type == 'PT_LOAD':

                print(seg.header)
                p_vaddr.append(seg.header.p_vaddr)
                p_offset.append(seg.header.p_offset)
                p_memsz.append(seg.header.p_memsz)
                p_filesz.append(seg.header.p_filesz)
                #print("p_offset : %d p_vaddr : %d p_memsz : %d p_filesz : %d" %(p_offset,p_vaddr,p_memsz,p_filesz))

        section_inter = elffile.get_section_by_name('.interp')
        section_text = elffile.get_section_by_name('.text')
        section_fini = elffile.get_section_by_name('.fini')

        mem = 0
        print(elffile['e_shnum'])
        for i in range(1, elffile['e_shnum']):
            print(elffile.get_section(i))

            section_numbering = elffile.get_section(i).header.sh_size
            mem = mem + section_numbering

        if not section_text:
            print('Is it rightA?')
            return
        if not section_fini:
            print('Is it right B?')
            return

        print(section_text)
        print(elffile.header)

        print("All section - :", hex(section_inter.header.sh_offset))
        print("All section -  size :", mem)
        print("section - text:", hex(section_text.header.sh_offset))
        print("section - text size :", hex(section_text.header.sh_size))
        print("section - fini:", hex(section_fini.header.sh_offset))
        print("section - fini size :", hex(section_fini.header.sh_size))

        functions = {'value': section_inter.header.sh_offset, 'size': mem}
        print(hex(section_text.header.sh_offset),
              section_text.header.sh_entsize, section_text.header.sh_size)
    #print(functions)
    return functions
Ejemplo n.º 6
0
    def find_ecalls_elf(self):
        t_section = None
        t_vaddr = None
        elf = ELFFile(open(self.filename, 'rb')) 
        # find the symbols table(s)
        for section in elf.iter_sections():
            if section.header['sh_type'] == 'SHT_SYMTAB':
                # find the g_ecall_table symbol
                for symbol in section.iter_symbols():
                    if symbol.name == 'g_ecall_table':
                        t_section=symbol.entry['st_shndx']
                        t_vaddr=symbol.entry['st_value']
                        break
            if t_section and t_vaddr:
                break

        if t_section and t_vaddr:
            # we got it, go calculate the table address 
            section = elf.get_section(t_section)
            # calculate the symbol offset from the section start
            sym_offset = t_vaddr - section.header['sh_addr']
            # return the physical address of the symbol
            return section.header['sh_offset'] + sym_offset

        return None
Ejemplo n.º 7
0
 def init_mem(self):
     print("init_mem")
     sw_image = cocotb.plusargs["SW_IMAGE"]
     with open(sw_image, "rb") as f:
         elffile = ELFFile(f)
         symtab = elffile.get_section_by_name('.symtab')
         
         begin_signature = symtab.get_symbol_by_name("begin_signature")[0]["st_value"]
         end_signature = symtab.get_symbol_by_name("end_signature")[0]["st_value"]
         
         addr = begin_signature
         
         # Find the section that contains the data we need
         section = None
         for i in range(elffile.num_sections()):
             shdr = elffile._get_section_header(i)
             if begin_signature >= shdr['sh_addr'] and begin_signature <= (shdr['sh_addr'] + shdr['sh_size']):
                 section = elffile.get_section(i)
                 begin_signature_offset = begin_signature - shdr['sh_addr']
                 break
             
         data = section.data()
         for addr in range(begin_signature, end_signature, 4):
             word = (
                 (data[begin_signature_offset+0] << (8*0))
                 | (data[begin_signature_offset+1] << (8*1))
                 | (data[begin_signature_offset+2] << (8*2))
                 | (data[begin_signature_offset+3] << (8*3))
                 );
             self.mem[(addr & 0xFFFF) >> 2] = word
             
             begin_signature_offset += 4
Ejemplo n.º 8
0
def get_func_insts(prog, func):
    f = open(prog, 'rb')
    e = ELFFile(f)

    sym = None
    for sec in e.iter_sections():
        if sec.name == '.symtab':
            syms = sec.get_symbol_by_name(func)
            if syms is not None:
                sym = syms[0]

    if sym is None:
        raise ValueError('Cannot find function %s:%s' % (file, func))

    sec_idx = sym['st_shndx']
    sym_size = sym['st_size']
    sym_value = sym['st_value']

    sec = e.get_section(sec_idx)
    if sec.name != '.text':
        raise ValueError('Symbol section is not .text')

    sec_offset = sec['sh_offset']
    sec_size = sec['sh_size']
    sec_addr = sec['sh_addr']

    if sym_value < sec_addr or sym_value + sym_size > sec_addr + sec_size:
        raise ValueError('Symbol not in section')

    file_offset = sym_value - sec_addr + sec_offset
    f.seek(file_offset)
    return sym_value, f.read(sym_size)
Ejemplo n.º 9
0
class ElfParser(object):
    def __init__(self, f):
        self.elffile = ELFFile(open(f, "rb"))
        self.text_section = self.elffile.get_section_by_name(".text")
        self.code = self.text_section.data()
        self.code_len = len(self.code)
        self.text_offset = self.text_section.header.sh_addr
        self.funcs = FunctionsList()
        self.init_functions_list()

    def get_functions_list(self):
        return self.funcs

    def get_code_and_funcs(self):
        return self.get_binary_code(), self.get_functions_list()

    def get_functions_num(self):
        return len(funcs)

    def get_code_len(self):
        return self.code_len

    def get_binary_code(self):
        return self.code

    def get_section_idx(self, section):
        for i in xrange(self.elffile.num_sections()):
            if self.elffile.get_section(i) == section:
                return i

    def va_to_offset(self, va):
        return va - self.text_offset

    def offset_to_va(self, offset):
        return offset + self.text_offset

    @staticmethod
    def is_function_symbol(symbol, section_idx):
        if symbol.entry.st_info.type == "STT_FUNC":
            if symbol.entry.st_shndx == section_idx:
                if symbol.entry.st_size > 0:
                    return True
        return False

    def init_functions_list(self):
        symtab = self.elffile.get_section_by_name(".symtab")
        text_section_idx = self.get_section_idx(self.text_section)
        if not isinstance(symtab, SymbolTableSection):
            raise Exception
        for symbol in symtab.iter_symbols():
            if self.is_function_symbol(symbol, text_section_idx):
                sym_offset = self.va_to_offset(symbol.entry.st_value)
                self.funcs.append(symbol.name, sym_offset,
                                  symbol.entry.st_size)

    def print_functions_list(self):
        print "%-30s\t%8s\t%8s" % ("Name", "Offset", "Size")
        print "-" * 58
        for func in self.funcs:
            print func
Ejemplo n.º 10
0
    def get_vtable_offset(self, image_path_name, class_name):
        """return vtable offset in image
            image_path_name: string like /foo/bar/liba.so
            class_name: c++ class name, like CPlayer
        """
        with open(image_path_name, 'rb') as f:
            elffile = ELFFile(f)
            section = elffile.get_section_by_name('.rela.dyn')

            if not isinstance(section, RelocationSection):
                return

            # cxx demangle name, like "vtable for CPlayer"
            vtable_name = "vtable for {}".format(class_name)

            # The symbol table section pointed to in sh_link
            symtable = elffile.get_section(section['sh_link'])
            for rel in section.iter_relocations():
                if rel['r_info_sym'] == 0:
                    continue

                symbol = symtable.get_symbol(rel['r_info_sym'])
                if symbol['st_name'] == 0:
                    continue

                symbol_name = symbol.name
                try:
                    symbol_name = cxxfilt.demangle(symbol_name)
                except cxxfilt.InvalidName:
                    continue

                if symbol_name == vtable_name:
                    return symbol['st_value']
Ejemplo n.º 11
0
def load_binary(static):
  elf = ELFFile(open(static.path))

  # TODO: replace with elf['e_machine']
  progdat = open(static.path).read(0x20)
  fb = struct.unpack("H", progdat[0x12:0x14])[0]   # e_machine
  static['arch'] = get_arch(fb)
  static['entry'] = elf['e_entry']

  ncount = 0
  for section in elf.iter_sections():
    addr = section['sh_addr']
    slen = section['sh_size']
    if addr != 0 and slen > 0:
      static.add_memory_chunk(addr, section.data())

    if isinstance(section, RelocationSection):
      symtable = elf.get_section(section['sh_link'])
      for rel in section.iter_relocations():
        symbol = symtable.get_symbol(rel['r_info_sym'])
        if static.debug >= 1: #suppress output for testing
          print "Relocation",rel, symbol.name
        if rel['r_offset'] != 0 and symbol.name != "":
          static[rel['r_offset']]['name'] = "__"+symbol.name
          ncount += 1

    if isinstance(section, SymbolTableSection):
      for nsym, symbol in enumerate(section.iter_symbols()):
        if symbol['st_value'] != 0 and symbol.name != "" and symbol['st_info']['type'] == "STT_FUNC":
          if static.debug >= 1:
            print "Symbol",symbol['st_value'], symbol.name
          static[symbol['st_value']]['name'] = symbol.name
          ncount += 1
  if static.debug >= 1:
    print "** found %d names" % ncount
Ejemplo n.º 12
0
def load_binary(static):
    elf = ELFFile(open(static.path))

    # TODO: replace with elf['e_machine']
    progdat = open(static.path).read(0x20)
    fb = struct.unpack("H", progdat[0x12:0x14])[0]  # e_machine
    static['arch'] = get_arch(fb)
    static['entry'] = elf['e_entry']

    ncount = 0
    for section in elf.iter_sections():
        addr = section['sh_addr']
        slen = section['sh_size']
        if addr != 0 and slen > 0:
            static.add_memory_chunk(addr, section.data())

        if isinstance(section, RelocationSection):
            symtable = elf.get_section(section['sh_link'])
            for rel in section.iter_relocations():
                symbol = symtable.get_symbol(rel['r_info_sym'])
                if static.debug:
                    print "Relocation", rel, symbol.name
                if rel['r_offset'] != 0 and symbol.name != "":
                    static[rel['r_offset']]['name'] = "__" + symbol.name
                    ncount += 1

        if isinstance(section, SymbolTableSection):
            for nsym, symbol in enumerate(section.iter_symbols()):
                if symbol['st_value'] != 0 and symbol.name != "" and symbol[
                        'st_info']['type'] == "STT_FUNC":
                    if static.debug:
                        print "Symbol", symbol['st_value'], symbol.name
                    static[symbol['st_value']]['name'] = symbol.name
                    ncount += 1
    print "** found %d names" % ncount
Ejemplo n.º 13
0
class ElfParser(object):
    def __init__(self, f):
        self.elffile = ELFFile(open(f, "rb"))
        self.text_section = self.elffile.get_section_by_name(".text")
        self.code = self.text_section.data()
        self.code_len = len(self.code)
        self.text_offset = self.text_section.header.sh_addr
        self.funcs = FunctionsList() 
        self.init_functions_list()

    def get_functions_list(self):
        return self.funcs

    def get_code_and_funcs(self):
        return self.get_binary_code(), self.get_functions_list()

    def get_functions_num(self):
        return len(funcs)

    def get_code_len(self):
        return self.code_len

    def get_binary_code(self):
        return self.code

    def get_section_idx(self, section):
        for i in xrange(self.elffile.num_sections()):
            if self.elffile.get_section(i) == section:
                return i

    def va_to_offset(self, va):
        return va - self.text_offset

    def offset_to_va(self, offset):
        return offset + self.text_offset

    @staticmethod
    def is_function_symbol(symbol, section_idx):
            if symbol.entry.st_info.type == "STT_FUNC":
                if symbol.entry.st_shndx == section_idx:
                    if symbol.entry.st_size > 0:
                        return True
            return False

    def init_functions_list(self):
        symtab = self.elffile.get_section_by_name(".symtab")
        text_section_idx = self.get_section_idx(self.text_section)
        if not isinstance(symtab, SymbolTableSection):
            raise Exception
        for symbol in symtab.iter_symbols():
            if self.is_function_symbol(symbol, text_section_idx):
                sym_offset = self.va_to_offset(symbol.entry.st_value)
                self.funcs.append(symbol.name, sym_offset, symbol.entry.st_size)

    def print_functions_list(self):
        print "%-30s\t%8s\t%8s" % ("Name", "Offset", "Size")
        print "-" * 58
        for func in self.funcs:
            print func
Ejemplo n.º 14
0
 def dump_sections(self):
     with open(self.filename, "rb") as f:
         elf = ELFFile(f)
         sections = []
         for i in range(elf.num_sections()):
             sec = elf.get_section(i)
             sections.append(sec)
     return sections
Ejemplo n.º 15
0
class RelReader:
    def __init__(self, rel_name):
        self.rel_elf = ELFFile(open(rel_name, "rb"))
        self.rel_symbol_table = self.rel_elf.get_section_by_name(".symtab")

    def __del__(self):
        self.rel_elf.stream.close()

    def fetch_rel_sections(self, expected_flags):
        """
        fetch_rel_sections returns SHF_ALLOC sections that satisfy given expected flag and are not empty.
        :param expected_flags: combination of SHF_WRITE and SHF_EXECINSTR
        """
        sections = []
        for section in self.rel_elf.iter_sections():
            flags = section.header["sh_flags"]
            section_size = section.header["sh_size"]
            if flags & SHF_ALLOC and section_size > 0:
                cur_flags = flags & (SHF_WRITE + SHF_EXECINSTR)
                if cur_flags == expected_flags:
                    sections.append(section)
        return sections

    def get_relocations(self):
        """
        get_relocations returns list of pairs (relocation's section name, relocation).
        """
        relocations = []
        for section in self.rel_elf.iter_sections():
            if isinstance(section, RelocationSection):
                for relocation in section.iter_relocations():
                    relocations.append((section.name[5:], relocation))
        return relocations

    def get_stream_content(self, offset, size):
        """
        returns content of stream based on offset and size.
        :param offset: beginning of content
        :param size:   content size
        """
        self.rel_elf.stream.seek(offset)
        return self.rel_elf.stream.read(size)

    def get_symbol(self, index):
        return self.rel_symbol_table.get_symbol(index)

    def get_section_name(self, index):
        return self.rel_elf.get_section(index).name

    def get_symbol_offset(self, symbol_name):
        """
        get_symbol_offset returns given symbol offset or None, if such symbol does not exist.
        """
        symbols = self.rel_symbol_table.get_symbol_by_name(symbol_name)
        if symbols is None:
            return None
        symbol = symbols[0]
        return symbol["st_value"]
Ejemplo n.º 16
0
def get_relocations(filename):
    """ 
    Return a dict with the relocations contained in a file
    Taken and modified from 
    https://github.com/eliben/pyelftools/blob/master/scripts/readelf.py
    """
    #print '[*] Getting relocations'
    relocations = {}
    if not filename:
        print '[*] Relocations not found'
        return relocations
	    
    try:
        pe = pefile.PE(filename)
        for entry in pe.DIRECTORY_ENTRY_IMPORT:
            for imp in entry.imports:
                relocations[imp.address] = imp.name
                        
    except pefile.PEFormatError:
        with file(filename,'r') as fd:
            elffile = ELFFile(fd)

            has_relocation_sections = False
            for section in elffile.iter_sections():
                if not isinstance(section, RelocationSection):
                    continue

                has_relocation_sections = True
                # The symbol table section pointed to in sh_link
                symtable = elffile.get_section(section['sh_link'])

                for rel in section.iter_relocations():
                    offset = rel['r_offset'] 

                    symbol = symtable.get_symbol(rel['r_info_sym'])
                    # Some symbols have zero 'st_name', so instead what's used is
                    # the name of the section they point at
                    if symbol['st_name'] == 0:
                        symsec = elffile.get_section(symbol['st_shndx'])
                        symbol_name = symsec.name
                    else:
                        symbol_name = symbol.name
                        relocations[offset] = bytes2str(symbol_name)
    #print '[*] Getting relocations DONE'                        
    return relocations
Ejemplo n.º 17
0
def get_relocations(filename):
    """ 
    Return a dict with the relocations contained in a file
    Taken and modified from 
    https://github.com/eliben/pyelftools/blob/master/scripts/readelf.py
    """
    #print '[*] Getting relocations'
    relocations = {}
    if not filename:
        print '[*] Relocations not found'
        return relocations

    try:
        pe = pefile.PE(filename)
        for entry in pe.DIRECTORY_ENTRY_IMPORT:
            for imp in entry.imports:
                relocations[imp.address] = imp.name

    except pefile.PEFormatError:
        with file(filename, 'r') as fd:
            elffile = ELFFile(fd)

            has_relocation_sections = False
            for section in elffile.iter_sections():
                if not isinstance(section, RelocationSection):
                    continue

                has_relocation_sections = True
                # The symbol table section pointed to in sh_link
                symtable = elffile.get_section(section['sh_link'])

                for rel in section.iter_relocations():
                    offset = rel['r_offset']

                    symbol = symtable.get_symbol(rel['r_info_sym'])
                    # Some symbols have zero 'st_name', so instead what's used is
                    # the name of the section they point at
                    if symbol['st_name'] == 0:
                        symsec = elffile.get_section(symbol['st_shndx'])
                        symbol_name = symsec.name
                    else:
                        symbol_name = symbol.name
                        relocations[offset] = bytes2str(symbol_name)
    #print '[*] Getting relocations DONE'
    return relocations
Ejemplo n.º 18
0
def relocations(file):
    with open(file, 'rb') as f:
        e = ELFFile(f)
        for section in e.iter_sections():
            if isinstance(section, RelocationSection):
                print(f'{section.name}:')
                symbol_table = e.get_section(section['sh_link'])
                for relocation in section.iter_relocations():
                    symbol = symbol_table.get_symbol(relocation['r_info_sym'])
                    addr = hex(relocation['r_offset'])
                    print(f'{symbol.name} {addr}')
Ejemplo n.º 19
0
def get_relocations_in_elf(path, symbol_objects):
    rels = []
    with open(path, "rb") as f:
        elf = ELFFile(f)
        for section in elf.iter_sections():
            if isinstance(section, RelocationSection):
                symtable = elf.get_section(section["sh_link"])
                for r in section.iter_relocations():
                    if r["r_info_sym"] == 0:
                        continue
                    rels.append(Relocation(elf, section, symtable, symbol_objects, r))
    return rels
Ejemplo n.º 20
0
async def test(top):
    await pybfms.init()

    u_bram = pybfms.find_bfm(".*u_bram")
    u_dbg_bfm: RiscvDebugBfm = pybfms.find_bfm(".*u_dbg_bfm")
    u_uart_bfm: UartBfm = pybfms.find_bfm(".*u_uart_bfm")

    uart_bfm_sw: UartBfmSwAPI = UartBfmSwAPI([u_uart_bfm])

    sw_image = cocotb.plusargs["sw.image"]
    u_dbg_bfm.load_elf(sw_image)

    u_dbg_bfm.register_export_api(UartBfmSwAPI)
    u_dbg_bfm.set_export_impl(UartBfmSwAPI, uart_bfm_sw)

    print("Note: loading image " + sw_image)
    with open(sw_image, "rb") as f:
        elffile = ELFFile(f)

        symtab = elffile.get_section_by_name('.symtab')

        # Find the section that contains the data we need
        section = None
        for i in range(elffile.num_sections()):
            shdr = elffile._get_section_header(i)
            #            print("sh_addr=" + hex(shdr['sh_addr']) + " sh_size=" + hex(shdr['sh_size']) + " flags=" + hex(shdr['sh_flags']))
            #            print("  keys=" + str(shdr.keys()))
            if shdr['sh_size'] != 0 and (shdr['sh_flags'] & 0x2) == 0x2:
                section = elffile.get_section(i)
                data = section.data()
                addr = shdr['sh_addr']
                j = 0
                while j < len(data):
                    word = (data[j + 0] << (8 * 0))
                    word |= (data[j + 1] <<
                             (8 * 1)) if j + 1 < len(data) else 0
                    word |= (data[j + 2] <<
                             (8 * 2)) if j + 2 < len(data) else 0
                    word |= (data[j + 3] <<
                             (8 * 3)) if j + 3 < len(data) else 0
                    #                    print("Write: " + hex(addr) + "(" + hex(int((addr & 0xFFFFF)/4)) + ") " + hex(word))
                    u_bram.write_nb(int((addr & 0xFFFFF) / 4), word, 0xF)
                    addr += 4
                    j += 4

    # Wait for the main function to exit
    print("--> wait main")
    await u_dbg_bfm.on_exit("main")
    print("<-- wait main")

    # Wait for all objections to be dropped
    await pybfms.objection.inst().wait()
Ejemplo n.º 21
0
    def parse_elf(self):
        self.f = open(self.f, 'rb')  #read binary form commad line

        elff = ELFFile(self.f)

        arch = elff.get_machine_arch()
        if arch == "x64":
            cs_arch = CS_ARCH_X86
            cs_mode = CS_MODE_64
        elif arch == "x86":
            cs_arch = CS_ARCH_X86
            cs_mode = CS_MODE_32
        else:
            print("ELF architecture '%s' currently not supported" % arch)
            return
        """ Initialize capstone """
        self.md = Cs(cs_arch, cs_mode)

        s = elff.get_section(1)
        self.align = s['sh_addr'] - s['sh_offset']

        s = elff.get_section_by_name('.plt')
        if s:
            print('.plt')
            self.plt_start, self.plt_end = s[
                'sh_addr'], s['sh_addr'] + s['sh_size']
            self.plti = self.plt_start + 16
            print('0x%x 0x%x' % (self.plt_start, self.plt_end))

            s = elff.get_section_by_name('.dynsym')
            if s:
                print(s.name)
                syms = self.parse_symbols(s)
                if self.to_look:
                    self.f.close()
                    return syms
            else:
                print('No Dynamic Symbols table (.dynsym)')

            s = elff.get_section_by_name('.symtab')
            if s:
                print(s.name)
                self.parse_symbols(s)
            else:
                print('No Symbols Table (.symtab)')
        else:
            print('No plt table (.plt)')

        self.f.close()
        return self.plts
Ejemplo n.º 22
0
def get_relocations(filename):
    ret = set()
    elffile = ELFFile(open(filename, 'r'))
    for section in elffile.iter_sections():
        if not isinstance(section, RelocationSection):
            continue

        # The symbol table section pointed to in sh_link
        symtable = elffile.get_section(section['sh_link'])

        for rel in section.iter_relocations():
            symbol = symtable.get_symbol(rel['r_info_sym'])
            if symbol['st_info']['type'] != 'STT_FUNC':
                continue
            # Some symbols have zero 'st_name', so instead what's used is
            # the name of the section they point at
            if symbol['st_name'] == 0:
                symsec = elffile.get_section(symbol['st_shndx'])
                symbol_name = symsec.name
            else:
                symbol_name = symbol.name
            ret.add(symbol_name)
    return ret
Ejemplo n.º 23
0
    def check(self):
        status = True
        sw_image = hpi.get_plusarg("SW_IMAGE")
        print("SW_IMAGE=" + sw_image)

        with open(sw_image, "rb") as f:
            elffile = ELFFile(f)

            symtab = elffile.get_section_by_name('.symtab')
            start_expected = symtab.get_symbol_by_name(
                "start_expected")[0]["st_value"]
            end_expected = symtab.get_symbol_by_name(
                "end_expected")[0]["st_value"]

            section = None
            for i in range(elffile.num_sections()):
                shdr = elffile._get_section_header(i)
                if (start_expected >= shdr['sh_addr']) and (
                        end_expected <= (shdr['sh_addr'] + shdr['sh_size'])):
                    start_expected -= shdr['sh_addr']
                    end_expected -= shdr['sh_addr']
                    section = elffile.get_section(i)
                    break

            data = section.data()

            exp_l = []

            for i in range(start_expected, end_expected, 8):
                reg = data[i + 0] | (data[i + 1] << 8) | (
                    data[i + 2] << 16) | (data[i + 3] << 24)
                exp = data[i + 4] | (data[i + 5] << 8) | (
                    data[i + 6] << 16) | (data[i + 7] << 24)

                exp_l.append([reg, exp])

            # Now, check results
            for exp in exp_l:
                print("Expect: R[" + str(exp[0]) + "] = " + str(exp[1]))

            for exp in exp_l:
                if not self.regs[exp[0]][1]:
                    print("Error: R[" + str(exp[0]) + "] not written")
                    status = False

                if self.regs[exp[0]][0] != exp[1]:
                    print("Error: R[" + str(exp[0]) + "] has unexpected value")

        return status
Ejemplo n.º 24
0
def collect_patches(patchfile):
    with open(patchfile, 'rb') as file:
        elf = ELFFile(file)

        symbol_tables = [
            s for s in elf.iter_sections()
            if isinstance(s, SymbolTableSection)
        ]

        patches = []

        # Gather symbols.
        for section in symbol_tables:
            # We need to find the length of each patched section. However,
            # because these are annotated using symbols, symbols have no
            # length and thus we can't look at st_size. Instead, we define
            # pairs of symbols (using "patch" and "endpatch" macros),
            # and use those to find the length of each patch.

            # Get all the "start" and "end" symbols, in sorted order.
            patch_start_syms = sorted([
                s for s in section.iter_symbols()
                if s.name.startswith("__patch$")
            ],
                                      key=lambda x: x['st_value'])
            patch_end_syms = sorted([
                s for s in section.iter_symbols()
                if s.name.startswith("__endpatch$")
            ],
                                    key=lambda x: x['st_value'])

            for symbol in patch_start_syms:
                section_index = symbol['st_shndx']
                start_addr = symbol['st_value']
                # For this "start" symbol, get the closest "end".
                closest = next(
                    filter(lambda x: start_addr < x['st_value'],
                           patch_end_syms))
                # Get the length, and store it in the array.
                end_addr = closest['st_value']
                patches.append(Patch(section_index, start_addr, end_addr))

        # Now, retrieve data.
        for patch in patches:
            section = elf.get_section(patch.section_index)
            data = section.data()[patch.start_addr:patch.end_addr]
            patch.data = data

    return patches
Ejemplo n.º 25
0
def get_relocations_in_elf(obj):
    rels = []
    with open(obj, "rb") as f:
        elf = ELFFile(f)
        for section in elf.iter_sections():
            if not isinstance(section, RelocationSection):
                continue
            symtable = elf.get_section(section['sh_link'])
            for rel in section.iter_relocations():
                if rel['r_info_sym'] == 0:
                    continue
                rdata = {}
                rdata["offset"] = int(rel['r_offset'])
                rdata["info"] = rel['r_info']
                rdata["type"] = describe_reloc_type(rel['r_info_type'], elf)
                symbol = symtable.get_symbol(rel['r_info_sym'])
                if symbol['st_name'] == 0:
                    symsec = elf.get_section(symbol['st_shndx'])
                    rdata["name"] = str(symsec.name)
                else:
                    rdata["name"] = str(symbol.name)
                rdata["value"] = symbol["st_value"]
                rels.append(rdata)
    return rels
Ejemplo n.º 26
0
    def test_existing_section(self):
        with open(os.path.join('test', 'testfiles_for_unittests',
                               'simple_gcc.elf.arm'), 'rb') as f:
            elf = ELFFile(f)

            # Find the symbol table.
            data_section_index = elf.get_section_index('.data')
            self.assertIsNotNone(data_section_index)

            # Test we can find a symbol by its name.
            data_section = elf.get_section(data_section_index)
            self.assertIsNotNone(data_section)

            # Test it is actually the symbol we expect.
            self.assertEqual(data_section.name, '.data')
Ejemplo n.º 27
0
def elf_extract_details(path):
    elf_details = {}
    try:
        with open(path, 'rb') as f:
            elf = ELFFile(f)

            elf_details["entrypoint"] = elf.header.e_entry
            elf_details["arch"] = elf.header.e_machine

            # extracting the sections
            elf_details["sections"] = []
            for section in elf.iter_sections():
                elf_details["sections"].append({
                    'name': section.name,
                    # this is the offset from the begining of the
                    # file in the filesystem!
                    'address': section['sh_addr'],
                    'size': section.data_size,
                })

            # extracting the symbols
            elf_details["symbols"] = []
            for section in elf.iter_sections():

                # only relocations are interesting for this one.
                if not isinstance(section, RelocationSection):
                    continue

                symbols_section = elf.get_section(section["sh_link"])
                for relocation in section.iter_relocations():
                    symbol = symbols_section.get_symbol(
                        relocation["r_info_sym"])
                    symbol_address = relocation['r_offset']
                    symbol_address = symbol_address

                    # ignore symbols with no name for now...
                    if symbol.name == "":
                        continue

                    elf_details["symbols"].append({
                        'address': symbol_address,
                        'name': symbol.name
                    })

    except Exception as e:
        return None

    return elf_details
Ejemplo n.º 28
0
 def process(self, data):
     try:
         elf = ELFFile(io.BytesIO(data))
     except Exception:
         elf = None
     if not elf:
         raise ValueError('unable to parse input as ELF file')
     if not self.args.offset.section:
         return data[self._slice(self._data_offset(elf, self.args.offset.address))]
     for k in range(elf.num_sections()):
         section = elf.get_section(k)
         if self.args.offset.section == section.name:
             section_data = section.get_data()
             return section_data[self._slice(self.args.offset.address)]
     else:
         raise ValueError(F'unable to find section {self.args.offset.section}')
Ejemplo n.º 29
0
Archivo: elf.py Proyecto: yzz127/qiling
    def lkm_get_init(self, ql):
        elffile = ELFFile(open(ql.path, 'rb'))
        symbol_tables = [
            s for s in elffile.iter_sections()
            if isinstance(s, SymbolTableSection)
        ]
        for section in symbol_tables:
            for nsym, symbol in enumerate(section.iter_symbols()):
                if symbol.name == 'init_module':
                    addr = symbol.entry.st_value + elffile.get_section(
                        symbol['st_shndx'])['sh_offset']
                    ql.nprint("init_module = 0x%x" % addr)
                    return addr

        # not found. FIXME: report error on invalid module??
        return -1
Ejemplo n.º 30
0
    def test_basic(self):
        with open(os.path.join('test', 'testfiles_for_unittests',
                               'simple_gcc.elf.mips'), 'rb') as f:
            elf = ELFFile(f)
            self.assertEqual(elf.get_machine_arch(), 'MIPS')

            # Check some other properties of this ELF file derived from readelf
            self.assertEqual(elf['e_entry'], 0x0)
            self.assertEqual(elf.num_sections(), 25)
            self.assertEqual(elf.num_segments(), 0)

            # Test that Mips-specific section types work; these types are
            # available only when the file is identified as MIPS in the
            # e_machine header field.
            sec9 = elf.get_section(9)
            self.assertEqual(sec9['sh_type'], 'SHT_MIPS_DWARF')
Ejemplo n.º 31
0
    def check(self):
        reg_data = []

        sw_image = cocotb.plusargs["SW_IMAGE"]
        testname = cocotb.plusargs["TESTNAME"]
        print("SW_IMAGE=" + sw_image)

        with open(sw_image, "rb") as f:
            elffile = ELFFile(f)

            symtab = elffile.get_section_by_name('.symtab')
            start_expected = symtab.get_symbol_by_name(
                "start_expected")[0]["st_value"]
            end_expected = symtab.get_symbol_by_name(
                "end_expected")[0]["st_value"]

            section = None
            for i in range(elffile.num_sections()):
                shdr = elffile._get_section_header(i)
                if (start_expected >= shdr['sh_addr']) and (
                        end_expected <= (shdr['sh_addr'] + shdr['sh_size'])):
                    start_expected -= shdr['sh_addr']
                    end_expected -= shdr['sh_addr']
                    section = elffile.get_section(i)
                    break

            data = section.data()

            exp_l = []

            for i in range(start_expected, end_expected, 8):
                reg = data[i + 0] | (data[i + 1] << 8) | (
                    data[i + 2] << 16) | (data[i + 3] << 24)
                exp = data[i + 4] | (data[i + 5] << 8) | (
                    data[i + 6] << 16) | (data[i + 7] << 24)

                exp_l.append([reg, exp])

        for i in range(64):
            info = yield self.tracer_bfm.get_reg_info(i)
            reg_data.append(info)

        if not self.complete:
            print("FAIL: " + testname)
        else:
            print("PASS: " + testname)
    def test_basic(self):
        with open(
                os.path.join('test', 'testfiles_for_unittests',
                             'simple_gcc.elf.mips'), 'rb') as f:
            elf = ELFFile(f)
            self.assertEqual(elf.get_machine_arch(), 'MIPS')

            # Check some other properties of this ELF file derived from readelf
            self.assertEqual(elf['e_entry'], 0x0)
            self.assertEqual(elf.num_sections(), 25)
            self.assertEqual(elf.num_segments(), 0)

            # Test that Mips-specific section types work; these types are
            # available only when the file is identified as MIPS in the
            # e_machine header field.
            sec9 = elf.get_section(9)
            self.assertEqual(sec9['sh_type'], 'SHT_MIPS_DWARF')
Ejemplo n.º 33
0
    def lkm_get_init(self, elffile: ELFFile) -> int:
        """Get file offset of the init_module function.
        """

        symbol_tables = (sec for sec in elffile.iter_sections()
                         if type(sec) is SymbolTableSection)

        for sec in symbol_tables:
            syms = sec.get_symbol_by_name('init_module')

            if syms:
                sym = syms[0]
                addr = sym['st_value'] + elffile.get_section(
                    sym['st_shndx'])['sh_offset']
                self.ql.log.info(f'init_module = {addr:#x}')
                return addr

        raise QlErrorELFFormat('invalid module: symbol init_module not found')
Ejemplo n.º 34
0
async def test(top):
    await pybfms.init()

    u_bram = pybfms.find_bfm(".*u_bram")
    u_dbg_bfm: RiscvDebugBfm = pybfms.find_bfm(".*u_dbg_bfm")

    sw_image = cocotb.plusargs["sw.image"]
    u_dbg_bfm.load_elf(sw_image)

    print("Note: loading image " + sw_image)
    with open(sw_image, "rb") as f:
        elffile = ELFFile(f)

        # Find the section that contains the data we need
        section = None
        for i in range(elffile.num_sections()):
            shdr = elffile._get_section_header(i)
            #            print("sh_addr=" + hex(shdr['sh_addr']) + " sh_size=" + hex(shdr['sh_size']) + " flags=" + hex(shdr['sh_flags']))
            #            print("  keys=" + str(shdr.keys()))
            print("sh_size=" + hex(shdr['sh_size']) + " sh_flags=" +
                  hex(shdr['sh_flags']))
            if shdr['sh_size'] != 0 and (shdr['sh_flags'] & 0x2) == 0x2:
                section = elffile.get_section(i)
                data = section.data()
                addr = shdr['sh_addr']
                j = 0
                while j < len(data):
                    word = (data[j + 0] << (8 * 0))
                    word |= (data[j + 1] <<
                             (8 * 1)) if j + 1 < len(data) else 0
                    word |= (data[j + 2] <<
                             (8 * 2)) if j + 2 < len(data) else 0
                    word |= (data[j + 3] <<
                             (8 * 3)) if j + 3 < len(data) else 0
                    print("Write: " + hex(addr) + "(" +
                          hex(int((addr & 0xFFFFF) / 4)) + ") " + hex(word))
                    u_bram.write_nb(int((addr & 0xFFFFF) / 4), word, 0xF)
                    addr += 4
                    j += 4

    print("Hello")
    print("--> wait main")
    await u_dbg_bfm.on_exit("done")
    print("<-- wait main")
Ejemplo n.º 35
0
def build_glob_data_table(elf: ELFFile) -> Dict[int, int]:
    table: Dict[int, int] = dict()
    section = elf.get_section_by_name(".rela.dyn")
    assert isinstance(section, RelocationSection)

    symtab = elf.get_section(section["sh_link"])
    offset = symtab["sh_offset"]
    entsize = symtab["sh_entsize"]

    for reloc in section.iter_relocations():
        symtab.stream.seek(offset + reloc["r_info_sym"] * entsize)
        sym_value = _ElfSym.parse(symtab.stream.read(_ElfSymFormat.size)).st_value
        info_type = reloc["r_info_type"]
        if info_type == R_AARCH64_GLOB_DAT:
            table[reloc["r_offset"]] = sym_value + reloc["r_addend"]
        elif info_type == R_AARCH64_RELATIVE:
            # FIXME: this should be Delta(S) + A
            table[reloc["r_offset"]] = sym_value + reloc["r_addend"]

    return table
Ejemplo n.º 36
0
 def get_swo_db(self, f):
     elf = ELFFile(f)
     # Find SWO section
     trace_sec = None
     for secnum, sec in enumerate(elf.iter_sections()):
         if TRACE_SECTION_NAME in sec.name:
             trace_sec = elf.get_section(secnum)
             break
     if trace_sec is None:
         raise ValueError(
             "Trace sections not found in elf file. Ensure that the linker file is correct and that "
             "there is at least one module and level enabled.")
     logger.critical(
         "Creating dictionary of strings and functions from elf file...")
     logger.debug(
         "SWO TRACE ======================================================="
     )
     # Build SWO trace database by searching in symbol table
     for sym in elf.get_section_by_name('.symtab').iter_symbols():
         if sym.entry.st_value & TRACE_BASE_ADDR == TRACE_BASE_ADDR and "SWOSymbol" in sym.name:
             # Find offset into section by subtracting section base address
             offset = trace_sec.header['sh_offset'] + (sym.entry.st_value -
                                                       TRACE_BASE_ADDR)
             # Seek to offset in ELF
             trace_sec.stream.seek(offset)
             # Read until end of section
             value = trace_sec.stream.read(trace_sec.header['sh_size'] -
                                           (offset -
                                            trace_sec.header['sh_offset']))
             # Truncate output at null character and remove quotes
             value = value.decode("utf-8").split("\0")[0].replace("\"", "")
             # Create new ElfString to store in dictionary
             elf_string = ElfString(value)
             # Add to relevant database
             if elf_string.opcode is SWOOpcode.EVENT_CREATION:
                 self.eventDB[elf_string.logModule +
                              elf_string.event] = elf_string
             else:
                 self.traceDB[sym.entry.st_value] = elf_string
             logger.debug("{} --> {}".format(hex(sym.entry.st_value),
                                             value))
Ejemplo n.º 37
0
 def getnames(self):
   from elftools.elf.elffile import ELFFile
   from elftools.elf.sections import SymbolTableSection
   from elftools.elf.relocation import RelocationSection
   elf = ELFFile(open(self.program))
   ncount = 0
   for section in elf.iter_sections():
     if isinstance(section, RelocationSection):
       symtable = elf.get_section(section['sh_link'])
       for rel in section.iter_relocations():
         symbol = symtable.get_symbol(rel['r_info_sym'])
         #print rel, symbol.name
         if rel['r_offset'] != 0 and symbol.name != "":
           self.tags[rel['r_offset']]['name'] = "__"+symbol.name
           ncount += 1
     if isinstance(section, SymbolTableSection):
       for nsym, symbol in enumerate(section.iter_symbols()):
         if symbol['st_value'] != 0 and symbol.name != "" and symbol['st_info']['type'] == "STT_FUNC":
           #print symbol['st_value'], symbol.name
           self.tags[symbol['st_value']]['name'] = symbol.name
           ncount += 1
   print "** found %d names" % ncount
Ejemplo n.º 38
0
Archivo: dwarf.py Proyecto: alon/emolog
class FileParser:
    def __init__(self, filename: str) -> None:
        self.all_dies = {}
        self.elf_file = None
        self.symbol_table = None
        f = open(filename, 'rb')
        logger.debug('Processing file: {}'.format(filename))
        self.elf_file = ELFFile(f)
        self.read_dies_from_dwarf_file()
        # the following assumes there's just one symbol table (ELF format allows more than one):
        self.symbol_tables = [x for x in self.elf_file.iter_sections() if isinstance(x, SymbolTableSection)]
        self.symbol_table = {x.name: x for x in chain(*[s.iter_symbols() for s in self.symbol_tables])}
        var_dies = {offset: die for offset, die in self.all_dies.items() if die.tag == 'DW_TAG_variable' and 'DW_AT_type' in die.attributes}
        logger.debug("read %d DIEs which include %d variable DIEs" % (len(self.all_dies), len(var_dies)))
        self.var_descriptors = var_descriptors = []
        for offset, var_die in var_dies.items():
            var_descriptors.append(VarDescriptor(self, self.all_dies, var_die, None))
        self.interesting_vars = [v for v in var_descriptors if v.is_interesting()]
        # note the file is intentionally kept open, otherwise some functions would fail later

    def read_dies_from_dwarf_file(self) -> None:
        if not self.elf_file.has_dwarf_info():
            logger.error('file has no DWARF info')
            return
        dwarfinfo = self.elf_file.get_dwarf_info()
        for CU in dwarfinfo.iter_CUs():
            top_DIE = CU.get_top_DIE()
            self.read_die_rec(top_DIE)

    def read_die_rec(self, die: DIE) -> None:
        self.all_dies[die.offset] = die
        for child in die.iter_children():
            self.read_die_rec(child)

    def visit_interesting_vars_tree_leafs(self) -> Generator['VarDescriptor', None, None]:
        for v in self.interesting_vars:
            yield from v.visit_leafs()

    def pretty_print(self, children=None, tab=0):
        if children is None:
            children = self.interesting_vars
        for v in children:
            print("{}{!s}".format('   ' * tab, v))
            self.pretty_print(children=v.children, tab=tab + 1)

    def read_value_at_address(self, address, size, section_name='st_shndx'):
        """
        """
        return None
        section_num = symbol.entry[section_name]
        section = self.elf_file.get_section(section_num)
        section_start_addr = section['sh_addr']
        return section.data()[address - section_start_addr : address - section_start_addr + size]

    def get_value_by_name(self, name, var_descriptor=None):
        if self.symbol_table is None:
            return None  # TODO more meaningful error return values?
        if name not in self.symbol_table:
            return None
        symbol = self.symbol_table[name]
        if symbol is None:
            return None  # TODO more meaningful error return values?
        if not isinstance(symbol, Symbol):
            symbol = symbol[0]
        section_num = symbol.entry['st_shndx']
        if not isinstance(section_num, int):  # several special cases are possible
            if section_num == 'SHN_ABS':  # special case, means symbol['st_value'] isn't an address but an actual value
                return symbol['st_value']
            else:  # other special cases are not implemented
                return None  # TODO more meaningful error return values?
        address = symbol['st_value']
        # size = symbol['st_size']  # NOT GOOD, rounded to multiple of 4 or something.
        # have to look up size in DWARF data (var_descriptor):
        if var_descriptor is None: # hack - mixed use cases. should fix
            var_descriptor = [x for x in self.var_descriptors if x.name == name]
            if var_descriptor is None or len(var_descriptor) > 1:
                return None  # TODO more meaningful error return values?
            var_descriptor = var_descriptor[0]
        size = var_descriptor.size
        if size is None:
            return None  # TODO more meaningful error return values?
        section = self.elf_file.get_section(section_num)
        section_start_addr = section['sh_addr']
        return section.data()[address - section_start_addr : address - section_start_addr + size]
Ejemplo n.º 39
0
class ReadElf(object):
    """ display_* methods are used to emit output into the output stream
    """

    def __init__(self, file, output):
        """ file:
                stream object with the ELF file to read

            output:
                output stream to write to
        """
        self.elffile = ELFFile(file)
        self.output = output

        # Lazily initialized if a debug dump is requested
        self._dwarfinfo = None

        self._versioninfo = None

    def _section_from_spec(self, spec):
        """ Retrieve a section given a "spec" (either number or name).
            Return None if no such section exists in the file.
        """
        try:
            num = int(spec)
            if num < self.elffile.num_sections():
                return self.elffile.get_section(num)
            else:
                return None
        except ValueError:
            # Not a number. Must be a name then
            return self.elffile.get_section_by_name(str2bytes(spec))

    def pretty_print_pmdinfo(self, pmdinfo):
        global pcidb

        for i in pmdinfo["pci_ids"]:
            vendor = pcidb.find_vendor(i[0])
            device = vendor.find_device(i[1])
            subdev = device.find_subid(i[2], i[3])
            print("%s (%s) : %s (%s) %s" %
                  (vendor.name, vendor.ID, device.name,
                   device.ID, subdev.name))

    def parse_pmd_info_string(self, mystring):
        global raw_output
        global pcidb

        optional_pmd_info = [{'id': 'params', 'tag': 'PMD PARAMETERS'}]

        i = mystring.index("=")
        mystring = mystring[i + 2:]
        pmdinfo = json.loads(mystring)

        if raw_output:
            print(json.dumps(pmdinfo))
            return

        print("PMD NAME: " + pmdinfo["name"])
        for i in optional_pmd_info:
            try:
                print("%s: %s" % (i['tag'], pmdinfo[i['id']]))
            except KeyError as e:
                continue

        if (len(pmdinfo["pci_ids"]) != 0):
            print("PMD HW SUPPORT:")
            if pcidb is not None:
                self.pretty_print_pmdinfo(pmdinfo)
            else:
                print("VENDOR\t DEVICE\t SUBVENDOR\t SUBDEVICE")
                for i in pmdinfo["pci_ids"]:
                    print("0x%04x\t 0x%04x\t 0x%04x\t\t 0x%04x" %
                          (i[0], i[1], i[2], i[3]))

        print("")

    def display_pmd_info_strings(self, section_spec):
        """ Display a strings dump of a section. section_spec is either a
            section number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            return

        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            while (dataptr < len(data) and
                    not (32 <= byte2int(data[dataptr]) <= 127)):
                dataptr += 1

            if dataptr >= len(data):
                break

            endptr = dataptr
            while endptr < len(data) and byte2int(data[endptr]) != 0:
                endptr += 1

            mystring = bytes2str(data[dataptr:endptr])
            rc = mystring.find("PMD_INFO_STRING")
            if (rc != -1):
                self.parse_pmd_info_string(mystring)

            dataptr = endptr

    def find_librte_eal(self, section):
        for tag in section.iter_tags():
            if tag.entry.d_tag == 'DT_NEEDED':
                if "librte_eal" in tag.needed:
                    return tag.needed
        return None

    def search_for_autoload_path(self):
        scanelf = self
        scanfile = None
        library = None

        section = self._section_from_spec(".dynamic")
        try:
            eallib = self.find_librte_eal(section)
            if eallib is not None:
                ldlibpath = os.environ.get('LD_LIBRARY_PATH')
                if ldlibpath is None:
                    ldlibpath = ""
                dtr = self.get_dt_runpath(section)
                library = search_file(eallib,
                                      dtr + ":" + ldlibpath +
                                      ":/usr/lib64:/lib64:/usr/lib:/lib")
                if library is None:
                    return (None, None)
                if raw_output is False:
                    print("Scanning for autoload path in %s" % library)
                scanfile = open(library, 'rb')
                scanelf = ReadElf(scanfile, sys.stdout)
        except AttributeError:
            # Not a dynamic binary
            pass
        except ELFError:
            scanfile.close()
            return (None, None)

        section = scanelf._section_from_spec(".rodata")
        if section is None:
            if scanfile is not None:
                scanfile.close()
            return (None, None)

        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            while (dataptr < len(data) and
                    not (32 <= byte2int(data[dataptr]) <= 127)):
                dataptr += 1

            if dataptr >= len(data):
                break

            endptr = dataptr
            while endptr < len(data) and byte2int(data[endptr]) != 0:
                endptr += 1

            mystring = bytes2str(data[dataptr:endptr])
            rc = mystring.find("DPDK_PLUGIN_PATH")
            if (rc != -1):
                rc = mystring.find("=")
                return (mystring[rc + 1:], library)

            dataptr = endptr
        if scanfile is not None:
            scanfile.close()
        return (None, None)

    def get_dt_runpath(self, dynsec):
        for tag in dynsec.iter_tags():
            if tag.entry.d_tag == 'DT_RUNPATH':
                return tag.runpath
        return ""

    def process_dt_needed_entries(self):
        """ Look to see if there are any DT_NEEDED entries in the binary
            And process those if there are
        """
        global raw_output
        runpath = ""
        ldlibpath = os.environ.get('LD_LIBRARY_PATH')
        if ldlibpath is None:
            ldlibpath = ""

        dynsec = self._section_from_spec(".dynamic")
        try:
            runpath = self.get_dt_runpath(dynsec)
        except AttributeError:
            # dynsec is None, just return
            return

        for tag in dynsec.iter_tags():
            if tag.entry.d_tag == 'DT_NEEDED':
                rc = tag.needed.find("librte_pmd")
                if (rc != -1):
                    library = search_file(tag.needed,
                                          runpath + ":" + ldlibpath +
                                          ":/usr/lib64:/lib64:/usr/lib:/lib")
                    if library is not None:
                        if raw_output is False:
                            print("Scanning %s for pmd information" % library)
                        with open(library, 'rb') as file:
                            try:
                                libelf = ReadElf(file, sys.stdout)
                            except ELFError as e:
                                print("%s is no an ELF file" % library)
                                continue
                            libelf.process_dt_needed_entries()
                            libelf.display_pmd_info_strings(".rodata")
                            file.close()
Ejemplo n.º 40
0
class ELF:
    def __init__(self, mem, classbinary, filename):
        import capstone as CAPSTONE

        fd = open(filename, "rb")
        self.elf = ELFFile(fd)
        self.classbinary = classbinary
        self.mem = mem

        self.arch_lookup = {
            "x86": CAPSTONE.CS_ARCH_X86,
            "x64": CAPSTONE.CS_ARCH_X86,
            "ARM": CAPSTONE.CS_ARCH_ARM,
            "MIPS": CAPSTONE.CS_ARCH_MIPS,
        }

        self.arch_mode_lookup = {
            "x86": CAPSTONE.CS_MODE_32,
            "x64": CAPSTONE.CS_MODE_64,
            "ARM": CAPSTONE.CS_ARCH_ARM,
            "MIPS": {
                32: CAPSTONE.CS_MODE_MIPS32,
                64: CAPSTONE.CS_MODE_MIPS64,
            }
        }

        self.sym_type_lookup = {
            "STT_FUNC": MEM_FUNC,
        }

        self.__sections = {} # start address -> elf section

        for s in self.elf.iter_sections():
            if not s.name:
                continue

            start = s.header.sh_addr

            if s.header.sh_flags & 0xf != 0:
                bisect.insort_left(classbinary._sorted_sections, start)

            self.__sections[start] = s
            is_data = self.__section_is_data(s)
            is_exec = self.__section_is_exec(s)
            data = s.data()

            classbinary._abs_sections[start] = SectionAbs(
                    s.name.decode(),
                    start,
                    s.header.sh_size,
                    len(data),
                    is_exec,
                    is_data,
                    data)


    def load_section_names(self):
        # Used for the auto-completion
        for s in self.elf.iter_sections():
            if s.header.sh_flags & 0xf != 0:
                ad = s.header.sh_addr
                name = s.name.decode()
                self.classbinary.section_names[name] = ad


    def load_static_sym(self):
        symtab = self.elf.get_section_by_name(b".symtab")
        if symtab is None:
            return
        dont_save = [b"$a", b"$t", b"$d"]
        arch = self.elf.get_machine_arch()
        is_arm = arch == "ARM"

        for sy in symtab.iter_symbols():
            if is_arm and sy.name in dont_save:
                continue

            ad = sy.entry.st_value
            if ad != 0 and sy.name != b"":
                name = sy.name.decode()
                if name in self.classbinary.symbols:
                    name = self.classbinary.rename_sym(name)

                self.classbinary.reverse_symbols[ad] = name
                self.classbinary.symbols[name] = ad

                ty = self.sym_type_lookup.get(sy.entry.st_info.type, MEM_UNK)
                self.mem.add(ad, 1, ty)


    def __x86_resolve_reloc(self, rel, symtab, plt, got_plt, addr_size):
        # Save all got offsets with the corresponding symbol
        got_off = {}
        for r in rel.iter_relocations():
            sym = symtab.get_symbol(r.entry.r_info_sym)
            name = sym.name.decode()
            ad = r.entry.r_offset
            if name and ad:
                ty = self.sym_type_lookup.get(sym.entry.st_info.type, MEM_UNK)
                got_off[ad] = [name + "@plt", ty]

        data = got_plt.data()

        unpack_str = "<" if self.elf.little_endian else ">"
        unpack_str += str(int(len(data) / addr_size))
        unpack_str += "Q" if addr_size == 8 else "I"

        got_values = struct.unpack(unpack_str, data)
        plt_data = plt.data()
        wrong_jump_opcode = False
        off = got_plt.header.sh_addr

        # Read the .got.plt and for each address in the plt, substract 6
        # to go at the begining of the plt entry.

        opcode_jmp = [b"\xff\x25", b"\xff\xa3"]

        for jump_in_plt in got_values:
            if off in got_off:
                plt_start = jump_in_plt - 6
                plt_off = plt_start - plt.header.sh_addr

                # Check "jmp *(ADDR)" opcode.
                if plt_data[plt_off:plt_off+2] not in opcode_jmp:
                    wrong_jump_opcode = True
                    continue

                name, ty = got_off[off]
                if name in self.classbinary.symbols:
                    continue

                self.classbinary.imports[plt_start] = True
                self.classbinary.reverse_symbols[plt_start] = name
                self.classbinary.symbols[name] = plt_start

                self.mem.add(plt_start, 1, ty)

            off += addr_size

        if wrong_jump_opcode:
            warning("I'm expecting to see a jmp *(ADDR) on each plt entry")
            warning("opcode \\xff\\x25 was not found, please report")


    def __resolve_symtab(self, rel, symtab, arch):
        # TODO: don't know why st_value is not 0 like x86
        # In some executables I've tested, it seems that st_value
        # is the address of the plt entry

        # TODO: really useful to iter on relocations and get the symbol
        # from the symtab ?
        # for r in rel.iter_relocations():
            # sym = symtab.get_symbol(r.entry.r_info_sym)

        for sym in symtab.iter_symbols():
            ad = sym.entry.st_value
            if ad != 0:
                name = sym.name.decode()
                if arch == "ARM":
                    name += "@plt"

                if name in self.classbinary.symbols:
                    continue

                self.classbinary.imports[ad] = True
                self.classbinary.reverse_symbols[ad] = name
                self.classbinary.symbols[name] = ad

                ty = self.sym_type_lookup.get(sym.entry.st_info.type, MEM_UNK)
                self.mem.add(ad, 1, ty)


    def __iter_reloc(self):
        for rel in self.elf.iter_sections():
            if rel.header.sh_type in ["SHT_RELA", "SHT_REL"]:
                symtab = self.elf.get_section(rel.header.sh_link)
                if symtab is None:
                    continue
                yield (rel, symtab)


    def load_dyn_sym(self):
        arch = self.elf.get_machine_arch()

        if arch == "ARM" or arch == "MIPS":
            for (rel, symtab) in self.__iter_reloc():
                self.__resolve_symtab(rel, symtab, arch)
            return

        # x86/x64

        # TODO: .plt can be renamed ?
        plt = self.elf.get_section_by_name(b".plt")

        if plt is None:
            warning(".plt section not found")
            return

        # TODO: .got.plt can be renamed or may be removed ?
        got_plt = self.elf.get_section_by_name(b".got.plt")
        addr_size = 8 if arch == "x64" else 4

        if got_plt is None:
            warning(".got.plt section not found")
            return

        for (rel, symtab) in self.__iter_reloc():
            self.__x86_resolve_reloc(rel, symtab, plt, got_plt, addr_size)


    def __section_is_data(self, s):
        mask = SH_FLAGS.SHF_WRITE | SH_FLAGS.SHF_ALLOC
        return s.header.sh_flags & mask and not self.__section_is_exec(s)


    def __section_is_exec(self, s):
        if s is None:
            return 0
        return s.header.sh_flags & SH_FLAGS.SHF_EXECINSTR


    def section_stream_read(self, addr, size):
        s = self.classbinary.get_section(addr)
        if s is None:
            return b""
        s = self.__sections[s.start]
        off = addr - s.header.sh_addr
        end = s.header.sh_addr + s.header.sh_size
        s.stream.seek(s.header.sh_offset + off)
        return s.stream.read(min(size, end - addr))


    def get_arch(self):
        import capstone as CAPSTONE
        arch = self.arch_lookup.get(self.elf.get_machine_arch(), None)
        mode = self.arch_mode_lookup.get(self.elf.get_machine_arch(), None)

        if arch is None:
            return None, None

        # If one arch name has multiple "word size"
        if isinstance(mode, dict):
            mode = mode[self.elf.elfclass]

        if self.elf.little_endian:
            mode |= CAPSTONE.CS_MODE_LITTLE_ENDIAN
        else:
            mode |= CAPSTONE.CS_MODE_BIG_ENDIAN

        return arch, mode


    def get_arch_string(self):
        return self.elf.get_machine_arch()


    def get_entry_point(self):
        return self.elf.header['e_entry']
Ejemplo n.º 41
0
class Image(object):
    def __init__(self, fname):
        if platform.system() == "Windows":
            elf_data = open(fname, "r")
        else:     
            with open(fname, "r") as f:
                elf_data = StringIO(f.read())
        
        self.elf = ELFFile(elf_data)
        if self.elf.has_dwarf_info():
            self.dwarf = self.elf.get_dwarf_info()
            set_global_machine_arch(self.elf.get_machine_arch())
            self.__tame_dwarf()
            self.get_expr_evaluator = lambda: ExprLiveEval(self)

    @property
    def executable(self):
        try:
            return self._exe
        except:
            self._exe = self._build_executable()
        return self._exe

    def _build_executable(self):
        s = self.elf.get_section(1)
        assert s.header["sh_flags"] & 2 and s.header["sh_type"] == "SHT_PROGBITS"        
        base_addr = s.header["sh_addr"]
        
        img = s.data()

        s = self.elf.get_section(2)
        if s.header["sh_flags"] & 2 and s.header["sh_type"] == "SHT_PROGBITS":
            if s.header["sh_addr"] != base_addr + len(img):
                raise Exception("bad section vaddr - #2 should follow #1")

            img += s.data()

            s = self.elf.get_section(3)
            print "%s" % str(s.header)
            if s.header["sh_flags"] & 2 and s.header["sh_type"] == "SHT_PROGBITS":
                if s.header["sh_addr"] != base_addr + len(img):
                    raise Exception("bad section vaddr - #3 should follow #2")

                img += s.data()

        return (base_addr, img)

    def __tame_dwarf(self):
        dw = self.dwarf
        self._compile_units = {}
        self._addresses = {}
        self._lowest_known_address = None
        
        location_lists = dw.location_lists()
            
        
        cfi = None
        if dw.has_EH_CFI():
            cfi = dw.EH_CFI_entries()
            print "we have EH CFI entries"
        elif dw.has_CFI():
            cfi = dw.CFI_entries()
            print "we have CFI entries"
        
        else:
            print "no (EH) CFI"

        if None is not cfi:
            self._cfa_rule = {}
            for c in cfi:
                try:
                    decoded = c.get_decoded()
                except:
                    print "CFI decoding exception"
                    break

                for entry in decoded.table:
                    if entry["pc"] in self._cfa_rule:
                        print "duplicate cfa rule found at pc %x" % entry["pc"]
                        print "\t%s" % str(self._cfa_rule[entry["pc"]])
                        print "\t%s" % str(entry)
                        print
                    #assert (not entry["pc"] in self._cfa_rule) or (self._cfa_rule[entry["pc"]] == entry)
                    self._cfa_rule[entry["pc"]] = entry


            
        
        for c in dw.iter_CUs():
            functions = {}  
            variables = {}

            td = c.get_top_DIE()

            for d in td.iter_children():
                if d.tag == 'DW_TAG_subprogram':
                    if 'DW_AT_declaration' in d.attributes:
                        continue
                    lpc = d.attributes['DW_AT_low_pc'].value
                    hpc = d.attributes['DW_AT_high_pc'].value
                    if hpc < lpc:
                        hpc += lpc

                    function_name = d.attributes['DW_AT_name'].value
                    f = {}
                    f["lpc"] = lpc
                    f["hpc"] = hpc
                    f["args"] = {}
                    f["vars"] = {}
                    if 'DW_AT_frame_base' in d.attributes:
                        a = d.attributes['DW_AT_frame_base']
                        if a.form == 'DW_FORM_data4' or a.form == 'DW_FORM_sec_offset':
                            f["fb"] = location_lists.get_location_list_at_offset(a.value)
                        else:
                            f["fb"] = a.value
                    
                    for child in d.iter_children():
                        if child.tag == "DW_TAG_formal_parameter":
                            name = child.attributes['DW_AT_name'].value
                            v = {}
                            try:
                                if child.attributes['DW_AT_location'].form in ['DW_FORM_sec_offset', 'DW_FORM_data4']:
                                    v["location"] = location_lists.get_location_list_at_offset(child.attributes['DW_AT_location'].value)
                                else:
                                    v["location"] = child.attributes['DW_AT_location'].value
                            except:
                                v["location"] = []
                            f["args"][name] = v
                        if child.tag == "DW_TAG_variable":
                            name = child.attributes['DW_AT_name'].value
                            v = {}
                            try:
                                if child.attributes['DW_AT_location'].form in ['DW_FORM_sec_offset', 'DW_FORM_data4']:
                                    v["location"] = location_lists.get_location_list_at_offset(child.attributes['DW_AT_location'].value)
                                else:
                                    v["location"] = child.attributes['DW_AT_location'].value
                            except:
                                v["location"] = []
                            f["vars"][name] = v

                    functions[function_name] = f
                elif d.tag == 'DW_TAG_variable':
                    if d.attributes['DW_AT_decl_file'].value == 1:
                        try:
                            name = d.attributes['DW_AT_name'].value
                        except:
                            name = '(%s)' % str(d.attributes['DW_AT_name'])
                            
                        v = {}
                        try:
                            v["location"] = d.attributes['DW_AT_location'].value
                        except:
                            v["location"] = []
                        variables[name] = v

            x = {}

            fname = td.attributes['DW_AT_name'].value
            x["line_program"] = dw.line_program_for_CU(c).get_entries()
            x["lpc"] = td.attributes['DW_AT_low_pc'].value
            x["hpc"] = td.attributes['DW_AT_high_pc'].value
            x["comp_dir"] = td.attributes['DW_AT_comp_dir'].value
            x["functions"] = functions
            x["variables"] = variables

            self._compile_units[fname] = x
            if ((self._lowest_known_address is None) or
                    (self._lowest_known_address > x["lpc"])):
                self._lowest_known_address = x["lpc"]

            
        for c in self._compile_units:
            self._compile_units[c]["lines"] = {}
            for line in self._compile_units[c]["line_program"]:
                state = line.state
                if state is not None and not (state.end_sequence or state.basic_block or state.epilogue_begin or state.prologue_end):
                    cl = "%s+%d" % (c, state.line)
                    if state.address in self._addresses and self._addresses[state.address] != cl:
                        raise Exception("addr %x is both \"%s\" and \"%s+%d\"" % (state.address, self._addresses[state.address], c, state.line))
                    self._addresses[state.address] = cl
                    try: self._compile_units[c]["lines"][state.line] += [state.address]
                    except: self._compile_units[c]["lines"][state.line] = [state.address]
        
        if not cfi is None:
            print "CFA table:"
            for pc in sorted(self._cfa_rule.keys()):
                print "%x: %s\t\t(%s)" % (pc, str(self._cfa_rule[pc]), self.addr2line(pc))

    def addr2line(self, addr):
        try: return self._addresses[addr]
        except: return ''

    def loc_at(self, addr):
        line = self.addr2line(addr)
        while '' == line and addr >= self._lowest_known_address:
            addr -= 4
            line = self.addr2line(addr)
        if '' == line:
            return ("unknown", "", 0, "")

        cuname, culine = line.split("+")
        fname = ""
        c = self._compile_units[cuname]
        for f in c["functions"]:
            if ((c["functions"][f]["lpc"] <= addr) and
                    (c["functions"][f]["hpc"] >= addr)):
                fname = f
                break
        return (fname, cuname, culine, c["comp_dir"])

    def line2addr(self, fname, line):
        return self._compile_units[fname]["lines"][line]	
Ejemplo n.º 42
0
class ELF:
    def __init__(self, classbinary, filename):
        import capstone as CAPSTONE

        fd = open(filename, "rb")
        self.elf = ELFFile(fd)
        self.classbinary = classbinary
        self.__data_sections = []
        self.__data_sections_content = []
        self.__exec_sections = []

        self.arch_lookup = {
            "x86": CAPSTONE.CS_ARCH_X86,
            "x64": CAPSTONE.CS_ARCH_X86,
            "ARM": CAPSTONE.CS_ARCH_ARM,
            "MIPS": CAPSTONE.CS_ARCH_MIPS,
        }

        self.arch_mode_lookup = {
            "x86": CAPSTONE.CS_MODE_32,
            "x64": CAPSTONE.CS_MODE_64,
            "ARM": CAPSTONE.CS_ARCH_ARM,
            "MIPS": {
                32: CAPSTONE.CS_MODE_MIPS32,
                64: CAPSTONE.CS_MODE_MIPS64,
            }
        }


    def load_section_names(self):
        # Used for the auto-completion
        for s in self.elf.iter_sections():
            if s.header.sh_flags & 0xf != 0:
                ad = s.header.sh_addr
                name = s.name.decode()
                self.classbinary.section_names[name] = ad


    def load_static_sym(self):
        symtab = self.elf.get_section_by_name(b".symtab")
        if symtab is None:
            return
        dont_save = [b"$a", b"$t", b"$d"]
        arch = self.elf.get_machine_arch()
        is_arm = arch == "ARM"
        for sy in symtab.iter_symbols():
            if is_arm and sy.name in dont_save:
                continue
            if sy.entry.st_value != 0 and sy.name != b"":
                self.classbinary.reverse_symbols[sy.entry.st_value] = sy.name.decode()
                self.classbinary.symbols[sy.name.decode()] = sy.entry.st_value


    def __x86_resolve_reloc(self, rel, symtab, plt, got_plt, addr_size):
        # Save all got offsets with the corresponding symbol
        got_off = {}
        for r in rel.iter_relocations():
            sym = symtab.get_symbol(r.entry.r_info_sym)
            got_off[r.entry.r_offset] = sym.name.decode() + "@plt"

        data = got_plt.data()

        unpack_str = "<" if self.elf.little_endian else ">"
        unpack_str += str(int(len(data) / addr_size))
        unpack_str += "Q" if addr_size == 8 else "I"

        got_values = struct.unpack(unpack_str, data)
        plt_data = plt.data()
        wrong_jump_opcode = False
        off = got_plt.header.sh_addr

        # Read the .got.plt and for each address in the plt, substract 6
        # to go at the begining of the plt entry.
        for jump_in_plt in got_values:
            if off in got_off:
                plt_start = jump_in_plt - 6
                plt_off = plt_start - plt.header.sh_addr

                # Check "jmp *(ADDR)" opcode.
                if plt_data[plt_off:plt_off+2] != b"\xff\x25":
                    wrong_jump_opcode = True
                    continue

                name = got_off[off]
                self.classbinary.reverse_symbols[plt_start] = name
                self.classbinary.symbols[name] = plt_start

            off += addr_size

        if wrong_jump_opcode:
            warning("I'm expecting to see a jmp *(ADDR) on each plt entry")
            warning("opcode \\xff\\x25 was not found, please report")


    def __arm_resolve_reloc(self, rel, symtab):
        # TODO: don't know why st_value is 0 in x86
        # for arm st_value is the address of the plt entry
        for r in rel.iter_relocations():
            sym = symtab.get_symbol(r.entry.r_info_sym)
            plt_start = sym.entry.st_value
            if plt_start != 0:
                name = sym.name.decode() + "@plt"
                self.classbinary.reverse_symbols[plt_start] = name
                self.classbinary.symbols[name] = plt_start


    def __iter_reloc(self):
        for rel in self.elf.iter_sections():
            if rel.header.sh_type in ["SHT_RELA", "SHT_REL"]:
                symtab = self.elf.get_section(rel.header.sh_link)
                if symtab is None:
                    continue
                yield (rel, symtab)


    def load_dyn_sym(self):
        arch = self.elf.get_machine_arch()

        if arch == "MIPS":
            return

        # TODO: .plt can be renamed ?
        plt = self.elf.get_section_by_name(b".plt")

        if plt is None:
            warning(".plt section not found")
            return

        if arch == "ARM":
            for (rel, symtab) in self.__iter_reloc():
                self.__arm_resolve_reloc(rel, symtab)
            return

        # x86/x64
        # TODO: .got.plt can be renamed or may be removed ?
        got_plt = self.elf.get_section_by_name(b".got.plt")
        addr_size = 8 if arch == "x64" else 4

        if got_plt is None:
            warning(".got.plt section not found")
            return

        for (rel, symtab) in self.__iter_reloc():
            self.__x86_resolve_reloc(rel, symtab, plt, got_plt, addr_size)


    def load_data_sections(self):
        for s in self.elf.iter_sections():
            if self.__section_is_data(s):
                self.__data_sections.append(s)
                self.__data_sections_content.append(s.data())


    def __get_data_section_idx(self, addr):
        for i, s in enumerate(self.__data_sections):
            start = s.header.sh_addr
            end = start + s.header.sh_size
            if start <= addr < end:
                return i
        return -1


    def __section_is_data(self, s):
        mask = SH_FLAGS.SHF_WRITE | SH_FLAGS.SHF_ALLOC
        return s.header.sh_flags & mask and not self.__section_is_exec(s)


    def is_address(self, imm):
        for s in self.elf.iter_sections():
            if s.header.sh_flags & 0xf == 0:
                continue
            start = s.header.sh_addr
            end = start + s.header.sh_size
            if  start <= imm < end:
                return s.name.decode(), self.__section_is_data(s)
        return None, False


    def __get_cached_exec_section(self, addr):
        for s in self.__exec_sections:
            start = s.header.sh_addr
            end = start + s.header.sh_size
            if start <= addr < end:
                return s
        return None


    def __find_section(self, addr):
        for s in self.elf.iter_sections():
            start = s.header.sh_addr
            end = start + s.header.sh_size
            if  start <= addr < end:
                return s
        return None


    def __get_section(self, addr):
        s = self.__get_cached_exec_section(addr)
        if s is not None:
            return s
        s = self.__find_section(addr)
        if s is None:
            return None
        self.__exec_sections.append(s)
        return s


    def check_addr(self, addr):
        s = self.__get_section(addr)
        return (s is not None, self.__section_is_exec(s))


    def get_section_meta(self, addr):
        s = self.__get_section(addr)
        if s is None:
            return None
        a = s.header.sh_addr
        return s.name.decode(), a, a + s.header.sh_size - 1


    def section_stream_read(self, addr, size):
        s = self.__get_section(addr)
        if s is None:
            return b""
        off = addr - s.header.sh_addr
        end = s.header.sh_addr + s.header.sh_size
        s.stream.seek(s.header.sh_offset + off)
        return s.stream.read(min(size, end - addr))


    def __section_is_exec(self, s):
        if s is None:
            return 0
        return s.header.sh_flags & SH_FLAGS.SHF_EXECINSTR


    def get_string(self, addr, max_data_size):
        i = self.__get_data_section_idx(addr)
        if i == -1:
            return ""

        s = self.__data_sections[i]
        data = self.__data_sections_content[i]
        off = addr - s.header.sh_addr
        txt = ['"']

        c = 0
        i = 0
        while i < max_data_size and \
              off < len(data):
            c = data[off]
            if c == 0:
                break
            txt.append(get_char(c))
            off += 1
            i += 1

        if c != 0 and off != len(data):
            txt.append("...")

        return ''.join(txt) + '"'


    def get_arch(self):
        import capstone as CAPSTONE
        arch = self.arch_lookup.get(self.elf.get_machine_arch(), None)
        mode = self.arch_mode_lookup.get(self.elf.get_machine_arch(), None)

        if arch is None:
            return None, None

        # If one arch name has multiple "word size"
        if isinstance(mode, dict):
            mode = mode[self.elf.elfclass]

        if self.elf.little_endian:
            mode |= CAPSTONE.CS_MODE_LITTLE_ENDIAN
        else:
            mode |= CAPSTONE.CS_MODE_BIG_ENDIAN

        return arch, mode


    def get_arch_string(self):
        return self.elf.get_machine_arch()


    def section_start(self, section_name):
        s = self.elf.get_section_by_name(section_name)
        if s is None:
            return -1
        return s.header.sh_addr


    def get_entry_point(self):
        return self.elf.header['e_entry']


    def iter_sections(self):
        for s in self.elf.iter_sections():
            start = s.header.sh_addr
            end = start + s.header.sh_size - 1
            if s.name != b"":
                yield (s.name.decode(), start, end)
Ejemplo n.º 43
0
class ElfExtractor(object):

  sh_type2name = {'SHT_SUNW_syminfo': 'syminfo', 'SHT_DYNSYM': 'symbols',
                  'SHT_GNU_verneed': 'verneed', 'SHT_GNU_verdef': 'verdef',
                  'SHT_GNU_versym': 'versym', 'SHT_DYNAMIC': 'dynamic'}

  def __init__(self, binary_path, debug=False):
    self.debug = debug
    self._binary_path = binary_path
    self.config = configuration.GetConfig()
    username, password = rest.GetUsernameAndPassword()
    self.rest_client = rest.RestClient(
        pkgdb_url=self.config.get('rest', 'pkgdb'),
        releases_url=self.config.get('rest', 'releases'),
        username=username,
        password=password)
    fd = open(self._binary_path, 'rb')
    self._mmap = mmap.mmap(fd.fileno(), 0, access=mmap.PROT_READ)
    self._elffile = ELFFile(self._mmap)

  def _compute_md5_sum(self):
    md5_hash = hashlib.md5()
    md5_hash.update(self._mmap)
    return md5_hash.hexdigest()

  def _get_sections_of_interest(self, *names):
    """ Find and returns the given sections based on their short names
    """
    sections = {}
    for section in self._elffile.iter_sections():
      if section.header['sh_type'] in ElfExtractor.sh_type2name:
        name = ElfExtractor.sh_type2name[section.header['sh_type']]
        if name in names:
          sections[name] = section

    return sections

  def _describe_symbol_shndx(self, shndx):
    """ We use our own instead of the one provided by pyelftools.
        This one resolves the section name if shndx is a section index
        and it outputs the same string as elfdump in the other cases.
    """
    if isinstance(shndx, int):
      try:
        return self._elffile.get_section(shndx).name
      except (ELFParseError, ValueError):
        # The elf file is a bit corrupt, the shndx refers
        # to a non-existing section. There are some existing
        # binaries with this issue is the repository so
        # we just skip the problem and return the section number
        return str(shndx)
    else:
      return shndx[4:]

  def _describe_symbol_boundto(self, syminfo):
    """ We use our own instead of the one provided by pyelftools.
        because we only want here to display the related library
        referenced in the dynamic section.
    """
    dynamic_section = self._elffile.get_section_by_name('.dynamic')
    if syminfo['si_flags'] & SUNW_SYMINFO_FLAGS.SYMINFO_FLG_FILTER:
      return dynamic_section.get_tag(syminfo['si_boundto']).sunw_filter
    else:
      return dynamic_section.get_tag(syminfo['si_boundto']).needed

  def CollectBinaryElfinfo(self):
    """Returns various informations symbol and versions present in elf header
    We will analyse 5 sections:
     - version definitions and
       version needed: contains version interface defined for this binary
                       and for each required soname, the version interfaces
                       required
     - symbol table: contains list of symbol name
     - version symbol table: maps the symbol against version interface
     - syminfo: contains special linking flags for each symbol
    The amount of data might be too large for it to fit in memory at one time,
    therefore the rest_client is passed to facilitate saving data.
    """
    md5_sum = self._compute_md5_sum()
    if self.rest_client.BlobExists('elfdump', md5_sum):
      logging.debug('We already have info about %r.', self._binary_path)
      return md5_sum

    sections = self._get_sections_of_interest('verneed', 'verdef',
                                              'syminfo', 'symbols')
    versions_needed = []
    if 'verneed' in sections:
      for verneed, vernaux_iter in sections['verneed'].iter_versions():
        versions_needed.extend([{'index': vernaux['vna_other'],
                                 'soname': verneed.name,
                                 'version': vernaux.name}
                                for vernaux in vernaux_iter])

      versions_needed.sort(key=lambda x: x['index'])
      for version in versions_needed:
        del version['index']

    version_definitions = []
    if 'verdef' in sections:
      for verdef, verdaux_iter in sections['verdef'].iter_versions():
        version_name = verdaux_iter.next().name
        dependencies = [x.name for x in verdaux_iter]
        version_definitions.append({'index': verdef['vd_ndx'],
                                    'version': version_name,
                                    'dependencies': dependencies})

      if version_definitions:
        version_definitions.sort(key=lambda x: x['index'])
        # the first "version definition" entry is the base soname
        # we don't care about this information
        version_definitions.pop(0)
        for version in version_definitions:
          del version['index']

    symbols = []
    if 'symbols' in sections:
      versions_info = (version_definitions + versions_needed)
      symbol_iter = sections['symbols'].iter_symbols()
      # We skip the first symbol which is always the 'UNDEF' symbol entry
      symbol_iter.next()
      for index, sym in enumerate(symbol_iter, start=1):

        symbol = {'bind': describe_symbol_bind(sym['st_info']['bind']),
                  'shndx': self._describe_symbol_shndx(sym['st_shndx']),
                  'symbol': sym.name,
                  'flags': None, 'soname': None, 'version': None}

        if 'versym' in sections:
          versym = sections['versym'].get_symbol(index)
          if not versym['ndx'] in ['VER_NDX_LOCAL', 'VER_NDX_GLOBAL']:
            # if versym is 2 or more, it's an index on the version
            # definition and version needed tables
            version = versions_info[versym['ndx'] - 2]
            symbol['version'] = version['version']
            if 'soname' in version:
              symbol['soname'] = version['soname']

        if 'syminfo' in sections:
          syminfo = sections['syminfo'].get_symbol(index)
          # We only use the information from syminfo if:
          # - there is at least one flag that uses the boundto value,
          # - boundto is an index and not special value (SYMINFO_BT_SELF...)
          if (syminfo['si_flags'] & (
                SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECT |
                SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECTBIND |
                SUNW_SYMINFO_FLAGS.SYMINFO_FLG_LAZYLOAD |
                SUNW_SYMINFO_FLAGS.SYMINFO_FLG_FILTER)
              and isinstance(syminfo['si_boundto'], int)):
            symbol['flags'] = describe_syminfo_flags(syminfo['si_flags'])
            symbol['soname'] = self._describe_symbol_boundto(syminfo)

        symbols.append(representations.ElfSymInfo(**symbol))

      symbols.sort(key=lambda m: m.symbol)

    binary_info = {'version definition': version_definitions,
                   'version needed': versions_needed,
                   'symbol table': symbols}
    self.rest_client.SaveBlob('elfdump', md5_sum, binary_info)
    return md5_sum

  def CollectBinaryDumpinfo(self):
    """Returns informations about soname and runpath located in
       the dynamic section.
    """
    binary_dump_info = {'needed_sonames': [],
                        'runpath': [],
                        'rpath': [],
                        'soname': None}

    sections = self._get_sections_of_interest('dynamic')
    if 'dynamic' in sections:

      for dyn_tag in sections['dynamic'].iter_tags():
        if dyn_tag['d_tag'] == 'DT_NEEDED':
          binary_dump_info['needed_sonames'].append(dyn_tag.needed)
        elif dyn_tag['d_tag'] == 'DT_RUNPATH':
          binary_dump_info['runpath'].extend(dyn_tag.runpath.split(':'))
        elif dyn_tag['d_tag'] == 'DT_RPATH':
          binary_dump_info['rpath'].extend(dyn_tag.rpath.split(':'))
        elif dyn_tag['d_tag'] == 'DT_SONAME':
          binary_dump_info['soname'] = dyn_tag.soname

    return binary_dump_info

  def GetMachineIdOfBinary(self):
    e_machine = self._elffile.header['e_machine']
    if e_machine not in ENUM_E_MACHINE:
      logging.warning('%r not found in ENUM_E_MACHINE in elftools; '
                      'resetting to EM_NONE', e_machine)
      e_machine = 'EM_NONE'
    return ENUM_E_MACHINE[e_machine]
Ejemplo n.º 44
0
def main():
    filename = sys.argv[1]
    elf = ELFFile(file(filename))
    print('[II] Object %s is a %s_%s elf' % (filename, elf.get_machine_arch(), elf.elfclass))
    assert elf.elfclass == 64 and elf.get_machine_arch() == 'x64'

    print "[II] Elf has %d sections."% elf.num_sections()

    selected_sections = []
    for section_prefix in ['.text', '.data', '.rodata', '.bss']:
        for section in elf.iter_sections():
            if section.name.startswith(section_prefix):
                selected_sections.append(section.name)

    offsets = {}
    shellcode = StringIO('')
    for section_name in selected_sections:
        offsets[section_name] = shellcode.len
        try:
            s = elf.get_section_by_name(section_name)
            if s['sh_type'] == 'SHT_NOBITS':
                data = chr(0) * s['sh_size']
            else:
                data = elf.get_section_by_name(section_name).data()
            print "[II] Section %s is %d bytes offset %d"%(section_name,len(data),offsets[section_name])
        except:
            data = ''
            print '[WW] No %s section'%section_name
        shellcode.write(data)
        # padding to 16
        shellcode.write(chr(0) * (16-shellcode.len % 16))

    print "[II] Total packed data size %d" % shellcode.len

    relocs = []
    for section_name in selected_sections:
        reloc_section = find_relocations_for_section(elf, section_name)
        if reloc_section is None:
            continue
        symtab = elf.get_section(reloc_section['sh_link'])
        for reloc in reloc_section.iter_relocations():
            #print reloc
            #assert elf.get_machine_arch() == 'x64' and not reloc.is_RELA()
            assert elf.get_machine_arch() == 'x64' and reloc.is_RELA()
            reloc_base = offsets[section_name]
            reloc_offset = reloc['r_offset']
            reloc_type = reloc['r_info_type']
            target_symbol = symtab.get_symbol(reloc['r_info_sym'])
            target_name = elf.get_section(target_symbol['st_shndx']).name
            target_base = offsets[target_name]
            target_offset = target_symbol['st_value']

            shellcode.seek(reloc_base+reloc_offset)
            value = struct.unpack("<l",shellcode.read(4))[0]     #+ reloc['r_addend']
            #print "RELOC:",section_name, '0x%x' % reloc_base, '0x%x' % reloc_offset, "=>", target_name, '0x%x' % target_base,'0x%x' % target_offset, value, '(%s)' % target_symbol.name
            if reloc_type == ENUM_RELOC_TYPE_x64['R_X86_64_32']:
                value = target_base + target_offset + value + reloc['r_addend']
                relocs.append(reloc_base+reloc_offset)
                print "[II] Offset ",reloc_base+reloc_offset, "added to reloc list"
            elif reloc_type == ENUM_RELOC_TYPE_x64['R_X86_64_PC32']:
                value = (target_base + target_offset) -  (reloc_base + reloc_offset) + value + reloc['r_addend']

            elif reloc_type == ENUM_RELOC_TYPE_x64['R_X86_64_32S']:
                value = target_base + target_offset + value+ reloc['r_addend']
                relocs.append(reloc_base+reloc_offset)
            else:
                assert reloc_type == ENUM_RELOC_TYPE_x64['R_X86_64_NONE']
            shellcode.seek(reloc_base + reloc_offset)
            shellcode.write(struct.pack("<L",value&0xffffffff))
        shellcode.seek(shellcode.len)

    def to_c_array(s):
        if len(s) % 4 != 0:
            s += chr(0) * (4 - len(s) % 4)
        bs = map(ord, s)
        result = ''
        for i in range(0, len(bs), 8):
            result += ' ' + ''.join(' 0x%02x,' % b for b in bs[i:i+8]) + '\n'
        return result

    def to_c_array2(arr):
        result = ''
        for i in range(0, len(arr), 10):
            result += ' ' + ''.join(' %d,' % x for x in arr[i:i+10]) + '\n'
        return result

    with file('bot_opt.cc', 'w') as fp:
        bss_size = elf.get_section_by_name('.bss')['sh_size']
        assert shellcode.getvalue()[-bss_size:] == chr(0) * bss_size

        pagesize = 4096

        fp.write('''#include <sys/mman.h>
#include "bot_opt.h"

static unsigned char code[%d] __attribute__((aligned(4096))) = {
%s};
static int patch[] = {
%s};
''' % (
        (shellcode.len + pagesize-1) / pagesize * pagesize,
        to_c_array(shellcode.getvalue()[:-bss_size]),
        to_c_array2(relocs),
        ))
    
            #fp.write('reloc %d\n' % rel)
        fp.write('''
void load_code() {
  if ((uintptr_t)code > 0xffffffffull)
    return;
  mprotect(code, sizeof(code), PROT_READ|PROT_WRITE|PROT_EXEC);
  for (unsigned int i = 0; i < sizeof(patch)/sizeof(patch[0]); i++) {
    *(uint32_t*)(void*)(code + patch[i]) += (uintptr_t)code;
  }
''')

    #with file('bot_opt.bin', 'wb') as fp:
    #    fp.write(shellcode.getvalue())

    
        # export symbols
        for entry in (
                'root_search_move',
                'init_bot',
                'max_lookahead',
                'maybe_dead_threshold',
                'search_threshold',
                'cache1_clear',
                ):
            symbol = None
            for s in elf.get_section_by_name('.symtab').iter_symbols():
                if s.name == entry:
                    symbol = s
            assert symbol
            section = elf.get_section(symbol['st_shndx']).name
            base = offsets[section]
            offset = symbol['st_value']
            
            start = base + offset
            print section, entry, start
            if section == '.text':
                fp.write('  %s_func = (%s_func_t)((char*)code + %d);\n' % (
                    entry, entry, start))
            else:
                fp.write('  %s_ptr = (%s_ptr_t)((char*)code + %d);\n' % (
                    entry, entry, start))
            #fp.write('%s %s %d\n' % (section, entry, start))


        fp.write('''}
''')
Ejemplo n.º 45
0
Archivo: loader.py Proyecto: 3a9LL/qira
def load_binary(static):
  try:
    elf = ELFFile(open(static.path))
  except ELFError:
    print "*** loader error: non-ELF detected"
    return

  # TODO: replace with elf['e_machine']
  progdat = open(static.path).read(0x20)
  fb = struct.unpack("H", progdat[0x12:0x14])[0]   # e_machine
  static['arch'] = get_arch(fb)
  static['entry'] = elf['e_entry']

  ncount = 0
  for segment in elf.iter_segments():
    addr = segment['p_vaddr']
    if segment['p_type'] == 'PT_LOAD':
      memsize = segment['p_memsz']
      static.add_memory_chunk(addr, segment.data().ljust(memsize, "\x00"))

  for section in elf.iter_sections():
    if static.debug >= 1:
      print "** found section", section.name, type(section)

    if isinstance(section, RelocationSection):
      symtable = elf.get_section(section['sh_link'])
      if symtable.is_null():
        continue

      for rel in section.iter_relocations():
        symbol = symtable.get_symbol(rel['r_info_sym'])
        if static.debug >= 1: #suppress output for testing
          print "Relocation",rel, symbol.name
        if rel['r_offset'] != 0 and symbol.name != "":
          static[rel['r_offset']]['name'] = "__"+symbol.name
          ncount += 1

      # hacks for PLT
      # TODO: this is f*****g terrible
      if section.name == '.rel.plt' or section.name == '.rela.plt':
        # first symbol is blank
        plt_symbols = []
        for rel in section.iter_relocations():
          symbol = symtable.get_symbol(rel['r_info_sym'])
          plt_symbols.append(symbol.name)

        # does this change?
        PLT_ENTRY_SIZE = 0x10

        for section in elf.iter_sections():
          if section.name == ".plt":
            for name, addr in zip(plt_symbols,
                     range(section['sh_addr'] + PLT_ENTRY_SIZE,
                           section['sh_addr'] + PLT_ENTRY_SIZE + PLT_ENTRY_SIZE*len(plt_symbols),
                           PLT_ENTRY_SIZE)):
              static[addr]['name'] = name
            #print plt_symbols, section['sh_addr']


    if isinstance(section, SymbolTableSection):
      for nsym, symbol in enumerate(section.iter_symbols()):
        #print symbol['st_info'], symbol.name, hex(symbol['st_value'])
        if symbol['st_value'] != 0 and symbol.name != "" and symbol['st_info']['type'] == "STT_FUNC":
          if static.debug >= 1:
            print "Symbol",hex(symbol['st_value']), symbol.name
          static[symbol['st_value']]['name'] = symbol.name
          ncount += 1

    # parse the DynamicSection to get the libraries
    #if isinstance(section, DynamicSection):
  if static.debug >= 1:
    print "** found %d names" % ncount
Ejemplo n.º 46
0
class ELF(object):
    def __init__(self, file_path):
        self.file_path = file_path
        self.elf = None
        self.result = {}

    def run(self):
        try:
            self.elf = ELFFile(open(self.file_path, "rb"))
            self.result["file_header"] = self._get_file_header()
            self.result["section_headers"] = self._get_section_headers()
            self.result["program_headers"] = self._get_program_headers()
            self.result["dynamic_tags"] = self._get_dynamic_tags()
            self.result["symbol_tables"] = self._get_symbol_tables()
            self.result["relocations"] = self._get_relocations()
            self.result["notes"] = self._get_notes()
            # TODO: add library name per import (see #807)
        except Exception as e:
            log.exception(e)

        return self.result

    def _get_file_header(self):
        return {
            "magic": convert_to_printable(self.elf.e_ident_raw[:4]),
            "class": describe_ei_class(self.elf.header.e_ident["EI_CLASS"]),
            "data": describe_ei_data(self.elf.header.e_ident["EI_DATA"]),
            "ei_version": describe_ei_version(self.elf.header.e_ident["EI_VERSION"]),
            "os_abi": describe_ei_osabi(self.elf.header.e_ident["EI_OSABI"]),
            "abi_version": self.elf.header.e_ident["EI_ABIVERSION"],
            "type": describe_e_type(self.elf.header["e_type"]),
            "machine": describe_e_machine(self.elf.header["e_machine"]),
            "version": describe_e_version_numeric(self.elf.header["e_version"]),
            "entry_point_address": self._print_addr(self.elf.header["e_entry"]),
            "start_of_program_headers": self.elf.header["e_phoff"],
            "start_of_section_headers": self.elf.header["e_shoff"],
            "flags": "{}{}".format(
                self._print_addr(self.elf.header["e_flags"]),
                self._decode_flags(self.elf.header["e_flags"])
            ),
            "size_of_this_header": self.elf.header["e_ehsize"],
            "size_of_program_headers": self.elf.header["e_phentsize"],
            "number_of_program_headers": self.elf.header["e_phnum"],
            "size_of_section_headers": self.elf.header["e_shentsize"],
            "number_of_section_headers": self.elf.header["e_shnum"],
            "section_header_string_table_index": self.elf.header["e_shstrndx"],
        }

    def _get_section_headers(self):
        section_headers = []
        for section in self.elf.iter_sections():
            section_headers.append({
                "name": section.name,
                "type": describe_sh_type(section["sh_type"]),
                "addr": self._print_addr(section["sh_addr"]),
                "size": section["sh_size"],
            })
        return section_headers

    def _get_program_headers(self):
        program_headers = []
        for segment in self.elf.iter_segments():
            program_headers.append({
                "type": describe_p_type(segment["p_type"]),
                "addr": self._print_addr(segment["p_vaddr"]),
                "flags": describe_p_flags(segment["p_flags"]).strip(),
                "size": segment["p_memsz"],
            })
        return program_headers

    def _get_dynamic_tags(self):
        dynamic_tags = []
        for section in self.elf.iter_sections():
            if not isinstance(section, DynamicSection):
                continue
            for tag in section.iter_tags():
                dynamic_tags.append({
                    "tag": self._print_addr(
                        ENUM_D_TAG.get(tag.entry.d_tag, tag.entry.d_tag)
                    ),
                    "type": tag.entry.d_tag[3:],
                    "value": self._parse_tag(tag),
                })
        return dynamic_tags

    def _get_symbol_tables(self):
        symbol_tables = []
        for section in self.elf.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            for nsym, symbol in enumerate(section.iter_symbols()):
                symbol_tables.append({
                    "value": self._print_addr(symbol["st_value"]),
                    "type": describe_symbol_type(symbol["st_info"]["type"]),
                    "bind": describe_symbol_bind(symbol["st_info"]["bind"]),
                    "ndx_name": symbol.name,
                })
        return symbol_tables

    def _get_relocations(self):
        relocations = []
        for section in self.elf.iter_sections():
            if not isinstance(section, RelocationSection):
                continue
            section_relocations = []
            for rel in section.iter_relocations():
                relocation = {
                    "offset": self._print_addr(rel["r_offset"]),
                    "info": self._print_addr(rel["r_info"]),
                    "type": describe_reloc_type(rel["r_info_type"], self.elf),
                    "value": "",
                    "name": ""
                }

                if rel["r_info_sym"] != 0:
                    symtable = self.elf.get_section(section["sh_link"])
                    symbol = symtable.get_symbol(rel["r_info_sym"])
                    # Some symbols have zero "st_name", so instead use
                    # the name of the section they point at
                    if symbol["st_name"] == 0:
                        symsec = self.elf.get_section(symbol["st_shndx"])
                        symbol_name = symsec.name
                    else:
                        symbol_name = symbol.name

                    relocation["value"] = self._print_addr(symbol["st_value"])
                    relocation["name"] = symbol_name

                if relocation not in section_relocations:
                    section_relocations.append(relocation)

            relocations.append({
                "name": section.name,
                "entries": section_relocations,
            })
        return relocations

    def _get_notes(self):
        notes = []
        for segment in self.elf.iter_segments():
            if not isinstance(segment, NoteSegment):
                continue
            for note in segment.iter_notes():
                notes.append({
                    "owner": note["n_name"],
                    "size": self._print_addr(note["n_descsz"]),
                    "note": describe_note(note),
                    "name": note["n_name"],
                })
        return notes

    def _print_addr(self, addr):
        fmt = "0x{0:08x}" if self.elf.elfclass == 32 else "0x{0:016x}"
        return fmt.format(addr)

    def _decode_flags(self, flags):
        description = ""
        if self.elf["e_machine"] == "EM_ARM":
            if flags & E_FLAGS.EF_ARM_HASENTRY:
                description = ", has entry point"

            version = flags & E_FLAGS.EF_ARM_EABIMASK
            if version == E_FLAGS.EF_ARM_EABI_VER5:
                description = ", Version5 EABI"
        elif self.elf["e_machine"] == "EM_MIPS":
            if flags & E_FLAGS.EF_MIPS_NOREORDER:
                description = ", noreorder"
            if flags & E_FLAGS.EF_MIPS_CPIC:
                description = ", cpic"
            if not (flags & E_FLAGS.EF_MIPS_ABI2) and not (flags & E_FLAGS.EF_MIPS_ABI_ON32):
                description = ", o32"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_1:
                description = ", mips1"

        return description

    def _parse_tag(self, tag):
        if tag.entry.d_tag == "DT_NEEDED":
            parsed = "Shared library: [%s]" % tag.needed
        elif tag.entry.d_tag == "DT_RPATH":
            parsed = "Library rpath: [%s]" % tag.rpath
        elif tag.entry.d_tag == "DT_RUNPATH":
            parsed = "Library runpath: [%s]" % tag.runpath
        elif tag.entry.d_tag == "DT_SONAME":
            parsed = "Library soname: [%s]" % tag.soname
        elif tag.entry.d_tag.endswith(("SZ", "ENT")):
            parsed = "%i (bytes)" % tag["d_val"]
        elif tag.entry.d_tag.endswith(("NUM", "COUNT")):
            parsed = "%i" % tag["d_val"]
        elif tag.entry.d_tag == "DT_PLTREL":
            s = describe_dyn_tag(tag.entry.d_val)
            if s.startswith("DT_"):
                s = s[3:]
            parsed = "%s" % s
        else:
            parsed = self._print_addr(tag["d_val"])

        return parsed
Ejemplo n.º 47
0
class ELFExecutable(BaseExecutable):
    def __init__(self, file_path):
        super(ELFExecutable, self).__init__(file_path)

        self.helper = ELFFile(self.binary)

        self.architecture = self._identify_arch()

        if self.architecture is None:
            raise Exception('Architecture is not recognized')

        logging.debug('Initialized {} {} with file \'{}\''.format(self.architecture, type(self).__name__, file_path))

        self.pack_endianness = '<' if self.helper.little_endian else '>'
        self.address_pack_type = 'I' if self.helper.elfclass == 32 else 'Q'

        self.sections = [section_from_elf_section(s) for s in self.helper.iter_sections()]

        self.executable_segment = [s for s in self.helper.iter_segments() if s['p_type'] == 'PT_LOAD' and s['p_flags'] & 0x1][0]

        dyn = self.helper.get_section_by_name('.dynamic')
        if dyn:
            self.libraries = [t.needed for t in dyn.iter_tags() if t['d_tag'] == 'DT_NEEDED']

        self.next_injection_offset = None
        self.next_injection_vaddr = None

    def _identify_arch(self):
        machine = self.helper.get_machine_arch()
        if machine == 'x86':
            return ARCHITECTURE.X86
        elif machine == 'x64':
            return ARCHITECTURE.X86_64
        elif machine == 'ARM':
            return ARCHITECTURE.ARM
        elif machine == 'AArch64':
            return ARCHITECTURE.ARM_64
        else:
            return None

    def entry_point(self):
        return self.helper['e_entry']

    def executable_segment_vaddr(self):
        return self.executable_segment['p_vaddr']

    def executable_segment_size(self):
        # TODO: Maybe limit this because we use this as part of our injection method?
        return self.executable_segment['p_memsz']

    def iter_string_sections(self):
        STRING_SECTIONS = ['.rodata', '.data', '.bss']
        for s in self.sections:
            if s.name in STRING_SECTIONS:
                yield s

    def _extract_symbol_table(self):
        # Add in symbols from the PLT/rela.plt
        # .rela.plt contains indexes to reference both .dynsym (symbol names) and .plt (jumps to GOT)
        if self.is_64_bit():
            reloc_section = self.helper.get_section_by_name('.rela.plt')
        else:
            reloc_section = self.helper.get_section_by_name('.rel.plt')

        if reloc_section:
            dynsym = self.helper.get_section(reloc_section['sh_link']) # .dynsym
            if isinstance(dynsym, SymbolTableSection):
                plt = self.helper.get_section_by_name('.plt')
                for idx, reloc in enumerate(reloc_section.iter_relocations()):
                    # Get the symbol's name from dynsym
                    symbol_name = dynsym.get_symbol(reloc['r_info_sym']).name

                    # The address of this function in the PLT is the base PLT offset + the index of the relocation.
                    # However, since there is the extra "trampoline" entity at the top of the PLT, we need to add one to the
                    # index to account for it.

                    # While sh_entsize is sometimes defined, it appears to be incorrect in some cases so we just ignore that
                    # and calculate it based off of the total size / num_relocations (plus the trampoline entity)
                    entsize = (plt['sh_size'] / (reloc_section.num_relocations() + 1))

                    plt_addr = plt['sh_addr'] + ((idx+1) * entsize)

                    logging.debug('Directly adding PLT function {} at vaddr {}'.format(symbol_name, hex(plt_addr)))

                    f = Function(plt_addr,
                                 entsize,
                                 symbol_name + '@PLT',
                                 self,
                                 type=Function.DYNAMIC_FUNC)
                    self.functions[plt_addr] = f
            else:
                logging.debug('Relocation section had sh_link to {}. Not parsing symbols...'.format(dynsym))



        # Some things in the symtab have st_size = 0 which confuses analysis later on. To solve this, we keep track of
        # where each address is in the `function_vaddrs` set and go back after all symbols have been iterated to compute
        # size by taking the difference between the current address and the next recorded address.

        # We do this for each executable section so that the produced functions cannot span multiple sections.

        for section in self.helper.iter_sections():
            if self.executable_segment.section_in_segment(section):
                name_for_addr = {}

                function_vaddrs = set([section['sh_addr'] + section['sh_size']])

                symbol_table = self.helper.get_section_by_name('.symtab')
                if symbol_table:
                    for symbol in symbol_table.iter_symbols():
                        if symbol['st_info']['type'] == 'STT_FUNC' and symbol['st_shndx'] != 'SHN_UNDEF':
                            if section['sh_addr'] <= symbol['st_value'] < section['sh_addr'] + section['sh_size']:
                                name_for_addr[symbol['st_value']] = symbol.name
                                function_vaddrs.add(symbol['st_value'])

                                if symbol['st_size']:
                                    logging.debug('Eagerly adding function {} from .symtab at vaddr {} with size {}'
                                                  .format(symbol.name, hex(symbol['st_value']), hex(symbol['st_size'])))
                                    f = Function(symbol['st_value'],
                                                 symbol['st_size'],
                                                 symbol.name,
                                                 self)
                                    self.functions[symbol['st_value']] = f


                function_vaddrs = sorted(list(function_vaddrs))

                for cur_addr, next_addr in zip(function_vaddrs[:-1], function_vaddrs[1:]):
                    # If st_size was set, we already added the function above, so don't add it again.
                    if cur_addr not in self.functions:
                        func_name = name_for_addr[cur_addr]
                        size = next_addr - cur_addr
                        logging.debug('Lazily adding function {} from .symtab at vaddr {} with size {}'
                                      .format(func_name, hex(cur_addr), hex(size)))
                        f = Function(cur_addr,
                                     next_addr - cur_addr,
                                     name_for_addr[cur_addr],
                                     self,
                                     type=Function.DYNAMIC_FUNC)
                        self.functions[cur_addr] = f

        # TODO: Automatically find and label main from call to libc_start_main

    def prepare_for_injection(self):
        """
        Derived from http://vxheavens.com/lib/vsc01.html
        """
        modified = StringIO(self.binary.getvalue())

        # Add INJECTION_SIZE to the section header list offset to make room for our injected code
        elf_hdr = self.helper.header.copy()
        elf_hdr.e_shoff += INJECTION_SIZE
        logging.debug('Changing e_shoff to {}'.format(elf_hdr.e_shoff))

        modified.seek(0)
        modified.write(self.helper.structs.Elf_Ehdr.build(elf_hdr))

        # Find the main RX LOAD segment and also adjust other segment offsets along the way
        executable_segment = None

        for segment_idx, segment in enumerate(self.helper.iter_segments()):
            segment_hdr = segment.header.copy()
            segment_hdr_offset = self.helper._segment_offset(segment_idx)

            if executable_segment is not None:
                # Already past the executable segment, so just update the offset if needed (i.e. don't update things
                # that come before the expanded section)
                if segment_hdr.p_offset > last_exec_section['sh_offset']:
                    segment_hdr.p_offset += INJECTION_SIZE

            elif segment['p_type'] == 'PT_LOAD' and segment['p_flags'] & P_FLAGS.PF_X:
                # Found the executable LOAD segment.
                # Make room for our injected code.

                logging.debug('Found executable LOAD segment at index {}'.format(segment_idx))
                executable_segment = segment

                last_exec_section_idx = max([idx for idx in range(self.helper.num_sections()) if
                                             executable_segment.section_in_segment(self.helper.get_section(idx))])
                last_exec_section = self.helper.get_section(last_exec_section_idx)

                segment_hdr.p_filesz += INJECTION_SIZE
                segment_hdr.p_memsz += INJECTION_SIZE

                logging.debug('Rewriting segment filesize and memsize to {} and {}'.format(
                    segment_hdr.p_filesz, segment_hdr.p_memsz)
                )

            modified.seek(segment_hdr_offset)
            modified.write(self.helper.structs.Elf_Phdr.build(segment_hdr))

        if executable_segment is None:
            logging.error("Could not locate an executable LOAD segment. Cannot continue injection.")
            return False

        logging.debug('Last section in executable LOAD segment is at index {} ({})'.format(last_exec_section_idx,
                                                                                           last_exec_section.name))

        self.next_injection_offset = last_exec_section['sh_offset'] + last_exec_section['sh_size']
        self.next_injection_vaddr = last_exec_section['sh_addr'] + last_exec_section['sh_size']

        # Update sh_size for the section we grew
        section_header_offset = self.helper._section_offset(last_exec_section_idx)
        section_header = last_exec_section.header.copy()

        section_header.sh_size += INJECTION_SIZE

        modified.seek(section_header_offset)
        modified.write(self.helper.structs.Elf_Shdr.build(section_header))

        # Update sh_offset for each section past the last section in the executable segment
        for section_idx in range(last_exec_section_idx + 1, self.helper.num_sections()):
            section_header_offset = self.helper._section_offset(section_idx)
            section_header = self.helper.get_section(section_idx).header.copy()

            section_header.sh_offset += INJECTION_SIZE
            logging.debug('Rewriting section {}\'s offset to {}'.format(section_idx, section_header.sh_offset))

            modified.seek(section_header_offset)
            modified.write(self.helper.structs.Elf_Shdr.build(section_header))

        # TODO: Architecture-specific padding
        # Should be something that won't immediately crash, but can be caught (e.g. SIGTRAP on x86)
        modified = StringIO(modified.getvalue()[:self.next_injection_offset] +
                            '\xCC'*INJECTION_SIZE +
                            modified.getvalue()[self.next_injection_offset:])

        self.binary = modified
        self.helper = ELFFile(self.binary)

        return True

    def inject(self, asm, update_entry=False):
        if self.next_injection_offset is None or self.next_injection_vaddr is None:
            logging.warning(
                'prepare_for_injection() was not called before inject(). This may cause unexpected behavior')
            self.prepare_for_injection()

        for segment in self.helper.iter_segments():
            if segment['p_type'] == 'PT_LOAD' and segment['p_flags'] & P_FLAGS.PF_X:
                injection_section_idx = max(i for i in range(self.helper.num_sections()) if segment.section_in_segment(self.helper.get_section(i)))
                break

        injection_section = self.helper.get_section(injection_section_idx)

        # If we haven't injected code before or need to expand the section again for this injection, go ahead and
        # shift stuff around.
        if injection_section['sh_size'] < INJECTION_SIZE or \
                        injection_section['sh_offset'] + injection_section['sh_size'] < self.next_injection_offset + len(asm):
            logging.debug('Automatically expanding injection section to accommodate for assembly')

            # NOTE: Could this change the destination address for the code that gets injected?
            self.prepare_for_injection()
        elif self.next_injection_offset == 0:
            used_code_len = len(injection_section.data().rstrip('\xCC'))
            self.next_injection_offset = injection_section['sh_offset'] + used_code_len
            self.next_injection_vaddr = injection_section['sh_addr'] + used_code_len

        # "Inject" the assembly
        logging.debug('Injecting {} bytes of assembly at offset {}'.format(len(asm), self.next_injection_offset))
        self.binary.seek(self.next_injection_offset)
        self.binary.write(asm)

        # Update e_entry if requested
        if update_entry:
            logging.debug('Rewriting ELF entry address to {}'.format(self.next_injection_vaddr))
            elf_hdr = self.helper.header
            elf_hdr.e_entry = self.next_injection_vaddr

            self.binary.seek(0)
            self.binary.write(self.helper.structs.Elf_Ehdr.build(elf_hdr))

        self.helper = ELFFile(self.binary)

        self.next_injection_vaddr += len(asm)
        self.next_injection_offset += len(asm)

        return self.next_injection_vaddr - len(asm)
Ejemplo n.º 48
0
class ReadElf(object):
    """ display_* methods are used to emit output into the output stream
    """
    def __init__(self, file, output):
        """ file:
                stream object with the ELF file to read

            output:
                output stream to write to
        """
        self.elffile = ELFFile(file)
        self.output = output

        # Lazily initialized if a debug dump is requested
        self._dwarfinfo = None

        self._versioninfo = None

    def display_file_header(self):
        """ Display the ELF file header
        """
        self._emitline('ELF Header:')
        self._emit('  Magic:   ')
        self._emitline(' '.join('%2.2x' % byte2int(b)
                                    for b in self.elffile.e_ident_raw))
        header = self.elffile.header
        e_ident = header['e_ident']
        self._emitline('  Class:                             %s' %
                describe_ei_class(e_ident['EI_CLASS']))
        self._emitline('  Data:                              %s' %
                describe_ei_data(e_ident['EI_DATA']))
        self._emitline('  Version:                           %s' %
                describe_ei_version(e_ident['EI_VERSION']))
        self._emitline('  OS/ABI:                            %s' %
                describe_ei_osabi(e_ident['EI_OSABI']))
        self._emitline('  ABI Version:                       %d' %
                e_ident['EI_ABIVERSION'])
        self._emitline('  Type:                              %s' %
                describe_e_type(header['e_type']))
        self._emitline('  Machine:                           %s' %
                describe_e_machine(header['e_machine']))
        self._emitline('  Version:                           %s' %
                describe_e_version_numeric(header['e_version']))
        self._emitline('  Entry point address:               %s' %
                self._format_hex(header['e_entry']))
        self._emit('  Start of program headers:          %s' %
                header['e_phoff'])
        self._emitline(' (bytes into file)')
        self._emit('  Start of section headers:          %s' %
                header['e_shoff'])
        self._emitline(' (bytes into file)')
        self._emitline('  Flags:                             %s%s' %
                (self._format_hex(header['e_flags']),
                self.decode_flags(header['e_flags'])))
        self._emitline('  Size of this header:               %s (bytes)' %
                header['e_ehsize'])
        self._emitline('  Size of program headers:           %s (bytes)' %
                header['e_phentsize'])
        self._emitline('  Number of program headers:         %s' %
                header['e_phnum'])
        self._emitline('  Size of section headers:           %s (bytes)' %
                header['e_shentsize'])
        self._emitline('  Number of section headers:         %s' %
                header['e_shnum'])
        self._emitline('  Section header string table index: %s' %
                header['e_shstrndx'])

    def decode_flags(self, flags):
        description = ""
        if self.elffile['e_machine'] == "EM_ARM":
            if flags & E_FLAGS.EF_ARM_HASENTRY:
                description += ", has entry point"

            version = flags & E_FLAGS.EF_ARM_EABIMASK
            if version == E_FLAGS.EF_ARM_EABI_VER5:
                description += ", Version5 EABI"
        return description

    def display_program_headers(self, show_heading=True):
        """ Display the ELF program headers.
            If show_heading is True, displays the heading for this information
            (Elf file type is...)
        """
        self._emitline()
        if self.elffile.num_segments() == 0:
            self._emitline('There are no program headers in this file.')
            return

        elfheader = self.elffile.header
        if show_heading:
            self._emitline('Elf file type is %s' %
                describe_e_type(elfheader['e_type']))
            self._emitline('Entry point is %s' %
                self._format_hex(elfheader['e_entry']))
            # readelf weirness - why isn't e_phoff printed as hex? (for section
            # headers, it is...)
            self._emitline('There are %s program headers, starting at offset %s' % (
                elfheader['e_phnum'], elfheader['e_phoff']))
            self._emitline()

        self._emitline('Program Headers:')

        # Now comes the table of program headers with their attributes. Note
        # that due to different formatting constraints of 32-bit and 64-bit
        # addresses, there are some conditions on elfclass here.
        #
        # First comes the table heading
        #
        if self.elffile.elfclass == 32:
            self._emitline('  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align')
        else:
            self._emitline('  Type           Offset             VirtAddr           PhysAddr')
            self._emitline('                 FileSiz            MemSiz              Flags  Align')

        # Now the entries
        #
        for segment in self.elffile.iter_segments():
            self._emit('  %-14s ' % describe_p_type(segment['p_type']))

            if self.elffile.elfclass == 32:
                self._emitline('%s %s %s %s %s %-3s %s' % (
                    self._format_hex(segment['p_offset'], fieldsize=6),
                    self._format_hex(segment['p_vaddr'], fullhex=True),
                    self._format_hex(segment['p_paddr'], fullhex=True),
                    self._format_hex(segment['p_filesz'], fieldsize=5),
                    self._format_hex(segment['p_memsz'], fieldsize=5),
                    describe_p_flags(segment['p_flags']),
                    self._format_hex(segment['p_align'])))
            else: # 64
                self._emitline('%s %s %s' % (
                    self._format_hex(segment['p_offset'], fullhex=True),
                    self._format_hex(segment['p_vaddr'], fullhex=True),
                    self._format_hex(segment['p_paddr'], fullhex=True)))
                self._emitline('                 %s %s  %-3s    %s' % (
                    self._format_hex(segment['p_filesz'], fullhex=True),
                    self._format_hex(segment['p_memsz'], fullhex=True),
                    describe_p_flags(segment['p_flags']),
                    # lead0x set to False for p_align, to mimic readelf.
                    # No idea why the difference from 32-bit mode :-|
                    self._format_hex(segment['p_align'], lead0x=False)))

            if isinstance(segment, InterpSegment):
                self._emitline('      [Requesting program interpreter: %s]' %
                    bytes2str(segment.get_interp_name()))

        # Sections to segments mapping
        #
        if self.elffile.num_sections() == 0:
            # No sections? We're done
            return

        self._emitline('\n Section to Segment mapping:')
        self._emitline('  Segment Sections...')

        for nseg, segment in enumerate(self.elffile.iter_segments()):
            self._emit('   %2.2d     ' % nseg)

            for section in self.elffile.iter_sections():
                if (    not section.is_null() and
                        segment.section_in_segment(section)):
                    self._emit('%s ' % bytes2str(section.name))

            self._emitline('')

    def display_section_headers(self, show_heading=True):
        """ Display the ELF section headers
        """
        elfheader = self.elffile.header
        if show_heading:
            self._emitline('There are %s section headers, starting at offset %s' % (
                elfheader['e_shnum'], self._format_hex(elfheader['e_shoff'])))

        self._emitline('\nSection Header%s:' % (
            's' if elfheader['e_shnum'] > 1 else ''))

        # Different formatting constraints of 32-bit and 64-bit addresses
        #
        if self.elffile.elfclass == 32:
            self._emitline('  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al')
        else:
            self._emitline('  [Nr] Name              Type             Address           Offset')
            self._emitline('       Size              EntSize          Flags  Link  Info  Align')

        # Now the entries
        #
        for nsec, section in enumerate(self.elffile.iter_sections()):
            self._emit('  [%2u] %-17.17s %-15.15s ' % (
                nsec, bytes2str(section.name), describe_sh_type(section['sh_type'])))

            if self.elffile.elfclass == 32:
                self._emitline('%s %s %s %s %3s %2s %3s %2s' % (
                    self._format_hex(section['sh_addr'], fieldsize=8, lead0x=False),
                    self._format_hex(section['sh_offset'], fieldsize=6, lead0x=False),
                    self._format_hex(section['sh_size'], fieldsize=6, lead0x=False),
                    self._format_hex(section['sh_entsize'], fieldsize=2, lead0x=False),
                    describe_sh_flags(section['sh_flags']),
                    section['sh_link'], section['sh_info'],
                    section['sh_addralign']))
            else: # 64
                self._emitline(' %s  %s' % (
                    self._format_hex(section['sh_addr'], fullhex=True, lead0x=False),
                    self._format_hex(section['sh_offset'],
                        fieldsize=16 if section['sh_offset'] > 0xffffffff else 8,
                        lead0x=False)))
                self._emitline('       %s  %s %3s      %2s   %3s     %s' % (
                    self._format_hex(section['sh_size'], fullhex=True, lead0x=False),
                    self._format_hex(section['sh_entsize'], fullhex=True, lead0x=False),
                    describe_sh_flags(section['sh_flags']),
                    section['sh_link'], section['sh_info'],
                    section['sh_addralign']))

        self._emitline('Key to Flags:')
        self._emit('  W (write), A (alloc), X (execute), M (merge), S (strings)')
        if self.elffile['e_machine'] in ('EM_X86_64', 'EM_L10M'):
            self._emitline(', l (large)')
        else:
            self._emitline()
        self._emitline('  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
        self._emitline('  O (extra OS processing required) o (OS specific), p (processor specific)')

    def display_symbol_tables(self):
        """ Display the symbol tables contained in the file
        """
        self._init_versioninfo()

        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue

            if section['sh_entsize'] == 0:
                self._emitline("\nSymbol table '%s' has a sh_entsize of zero!" % (
                    bytes2str(section.name)))
                continue

            self._emitline("\nSymbol table '%s' contains %s entries:" % (
                bytes2str(section.name), section.num_symbols()))

            if self.elffile.elfclass == 32:
                self._emitline('   Num:    Value  Size Type    Bind   Vis      Ndx Name')
            else: # 64
                self._emitline('   Num:    Value          Size Type    Bind   Vis      Ndx Name')

            for nsym, symbol in enumerate(section.iter_symbols()):

                version_info = ''
                # readelf doesn't display version info for Solaris versioning
                if (section['sh_type'] == 'SHT_DYNSYM' and
                        self._versioninfo['type'] == 'GNU'):
                    version = self._symbol_version(nsym)
                    if (version['name'] != bytes2str(symbol.name) and
                        version['index'] not in ('VER_NDX_LOCAL',
                                                 'VER_NDX_GLOBAL')):
                        if version['filename']:
                            # external symbol
                            version_info = '@%(name)s (%(index)i)' % version
                        else:
                            # internal symbol
                            if version['hidden']:
                                version_info = '@%(name)s' % version
                            else:
                                version_info = '@@%(name)s' % version

                # symbol names are truncated to 25 chars, similarly to readelf
                self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % (
                    nsym,
                    self._format_hex(
                        symbol['st_value'], fullhex=True, lead0x=False),
                    symbol['st_size'],
                    describe_symbol_type(symbol['st_info']['type']),
                    describe_symbol_bind(symbol['st_info']['bind']),
                    describe_symbol_visibility(symbol['st_other']['visibility']),
                    describe_symbol_shndx(symbol['st_shndx']),
                    bytes2str(symbol.name),
                    version_info))

    def display_dynamic_tags(self):
        """ Display the dynamic tags contained in the file
        """
        has_dynamic_sections = False
        for section in self.elffile.iter_sections():
            if not isinstance(section, DynamicSection):
                continue

            has_dynamic_sections = True
            self._emitline("\nDynamic section at offset %s contains %s entries:" % (
                self._format_hex(section['sh_offset']),
                section.num_tags()))
            self._emitline("  Tag        Type                         Name/Value")

            padding = 20 + (8 if self.elffile.elfclass == 32 else 0)
            for tag in section.iter_tags():
                if tag.entry.d_tag == 'DT_NEEDED':
                    parsed = 'Shared library: [%s]' % bytes2str(tag.needed)
                elif tag.entry.d_tag == 'DT_RPATH':
                    parsed = 'Library rpath: [%s]' % bytes2str(tag.rpath)
                elif tag.entry.d_tag == 'DT_RUNPATH':
                    parsed = 'Library runpath: [%s]' % bytes2str(tag.runpath)
                elif tag.entry.d_tag == 'DT_SONAME':
                    parsed = 'Library soname: [%s]' % bytes2str(tag.soname)
                elif (tag.entry.d_tag.endswith('SZ') or
                      tag.entry.d_tag.endswith('ENT')):
                    parsed = '%i (bytes)' % tag['d_val']
                elif (tag.entry.d_tag.endswith('NUM') or
                      tag.entry.d_tag.endswith('COUNT')):
                    parsed = '%i' % tag['d_val']
                elif tag.entry.d_tag == 'DT_PLTREL':
                    s = describe_dyn_tag(tag.entry.d_val)
                    if s.startswith('DT_'):
                        s = s[3:]
                    parsed = '%s' % s
                else:
                    parsed = '%#x' % tag['d_val']

                self._emitline(" %s %-*s %s" % (
                    self._format_hex(ENUM_D_TAG.get(tag.entry.d_tag, tag.entry.d_tag),
                        fullhex=True, lead0x=True),
                    padding,
                    '(%s)' % (tag.entry.d_tag[3:],),
                    parsed))
        if not has_dynamic_sections:
            # readelf only prints this if there is at least one segment
            if self.elffile.num_segments():
                self._emitline("\nThere is no dynamic section in this file.")

    def display_relocations(self):
        """ Display the relocations contained in the file
        """
        has_relocation_sections = False
        for section in self.elffile.iter_sections():
            if not isinstance(section, RelocationSection):
                continue

            has_relocation_sections = True
            self._emitline("\nRelocation section '%s' at offset %s contains %s entries:" % (
                bytes2str(section.name),
                self._format_hex(section['sh_offset']),
                section.num_relocations()))
            if section.is_RELA():
                self._emitline("  Offset          Info           Type           Sym. Value    Sym. Name + Addend")
            else:
                self._emitline(" Offset     Info    Type            Sym.Value  Sym. Name")

            # The symbol table section pointed to in sh_link
            symtable = self.elffile.get_section(section['sh_link'])

            for rel in section.iter_relocations():
                hexwidth = 8 if self.elffile.elfclass == 32 else 12
                self._emit('%s  %s %-17.17s' % (
                    self._format_hex(rel['r_offset'],
                        fieldsize=hexwidth, lead0x=False),
                    self._format_hex(rel['r_info'],
                        fieldsize=hexwidth, lead0x=False),
                    describe_reloc_type(
                        rel['r_info_type'], self.elffile)))

                if rel['r_info_sym'] == 0:
                    self._emitline()
                    continue

                symbol = symtable.get_symbol(rel['r_info_sym'])
                # Some symbols have zero 'st_name', so instead what's used is
                # the name of the section they point at
                if symbol['st_name'] == 0:
                    symsec = self.elffile.get_section(symbol['st_shndx'])
                    symbol_name = symsec.name
                else:
                    symbol_name = symbol.name
                self._emit(' %s %s%22.22s' % (
                    self._format_hex(
                        symbol['st_value'],
                        fullhex=True, lead0x=False),
                    '  ' if self.elffile.elfclass == 32 else '',
                    bytes2str(symbol_name)))
                if section.is_RELA():
                    self._emit(' %s %x' % (
                        '+' if rel['r_addend'] >= 0 else '-',
                        abs(rel['r_addend'])))
                self._emitline()

        if not has_relocation_sections:
            self._emitline('\nThere are no relocations in this file.')

    def display_version_info(self):
        """ Display the version info contained in the file
        """
        self._init_versioninfo()

        if not self._versioninfo['type']:
            self._emitline("\nNo version information found in this file.")
            return

        for section in self.elffile.iter_sections():
            if isinstance(section, GNUVerSymSection):
                self._print_version_section_header(
                    section, 'Version symbols', lead0x=False)

                num_symbols = section.num_symbols()
    
                # Symbol version info are printed four by four entries 
                for idx_by_4 in range(0, num_symbols, 4):

                    self._emit('  %03x:' % idx_by_4)

                    for idx in range(idx_by_4, min(idx_by_4 + 4, num_symbols)):

                        symbol_version = self._symbol_version(idx)
                        if symbol_version['index'] == 'VER_NDX_LOCAL':
                            version_index = 0
                            version_name = '(*local*)'
                        elif symbol_version['index'] == 'VER_NDX_GLOBAL':
                            version_index = 1
                            version_name = '(*global*)'
                        else:
                            version_index = symbol_version['index']
                            version_name = '(%(name)s)' % symbol_version

                        visibility = 'h' if symbol_version['hidden'] else ' '

                        self._emit('%4x%s%-13s' % (
                            version_index, visibility, version_name))

                    self._emitline()

            elif isinstance(section, GNUVerDefSection):
                self._print_version_section_header(
                    section, 'Version definition', indent=2)

                offset = 0
                for verdef, verdaux_iter in section.iter_versions():
                    verdaux = next(verdaux_iter)

                    name = verdaux.name
                    if verdef['vd_flags']:
                        flags = describe_ver_flags(verdef['vd_flags'])
                        # Mimic exactly the readelf output
                        flags += ' '
                    else:
                        flags = 'none'

                    self._emitline('  %s: Rev: %i  Flags: %s  Index: %i'
                                   '  Cnt: %i  Name: %s' % (
                            self._format_hex(offset, fieldsize=6,
                                             alternate=True),
                            verdef['vd_version'], flags, verdef['vd_ndx'],
                            verdef['vd_cnt'], bytes2str(name)))

                    verdaux_offset = (
                            offset + verdef['vd_aux'] + verdaux['vda_next'])
                    for idx, verdaux in enumerate(verdaux_iter, start=1):
                        self._emitline('  %s: Parent %i: %s' %
                            (self._format_hex(verdaux_offset, fieldsize=4),
                                              idx, bytes2str(verdaux.name)))
                        verdaux_offset += verdaux['vda_next']

                    offset += verdef['vd_next']

            elif isinstance(section, GNUVerNeedSection):
                self._print_version_section_header(section, 'Version needs')

                offset = 0
                for verneed, verneed_iter in section.iter_versions():

                    self._emitline('  %s: Version: %i  File: %s  Cnt: %i' % (
                            self._format_hex(offset, fieldsize=6,
                                             alternate=True),
                            verneed['vn_version'], bytes2str(verneed.name),
                            verneed['vn_cnt']))

                    vernaux_offset = offset + verneed['vn_aux']
                    for idx, vernaux in enumerate(verneed_iter, start=1):
                        if vernaux['vna_flags']:
                            flags = describe_ver_flags(vernaux['vna_flags'])
                            # Mimic exactly the readelf output
                            flags += ' '
                        else:
                            flags = 'none'

                        self._emitline(
                            '  %s:   Name: %s  Flags: %s  Version: %i' % (
                                self._format_hex(vernaux_offset, fieldsize=4),
                                bytes2str(vernaux.name), flags,
                                vernaux['vna_other']))

                        vernaux_offset += vernaux['vna_next']

                    offset += verneed['vn_next']

    def display_hex_dump(self, section_spec):
        """ Display a hex dump of a section. section_spec is either a section
            number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" % (
                section_spec))
            return

        self._emitline("\nHex dump of section '%s':" % bytes2str(section.name))
        self._note_relocs_for_section(section)
        addr = section['sh_addr']
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            bytesleft = len(data) - dataptr
            # chunks of 16 bytes per line
            linebytes = 16 if bytesleft > 16 else bytesleft

            self._emit('  %s ' % self._format_hex(addr, fieldsize=8))
            for i in range(16):
                if i < linebytes:
                    self._emit('%2.2x' % byte2int(data[dataptr + i]))
                else:
                    self._emit('  ')
                if i % 4 == 3:
                    self._emit(' ')

            for i in range(linebytes):
                c = data[dataptr + i : dataptr + i + 1]
                if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f:
                    self._emit(bytes2str(c))
                else:
                    self._emit(bytes2str(b'.'))

            self._emitline()
            addr += linebytes
            dataptr += linebytes

        self._emitline()

    def display_string_dump(self, section_spec):
        """ Display a strings dump of a section. section_spec is either a
            section number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" % (
                section_spec))
            return

        self._emitline("\nString dump of section '%s':" % bytes2str(section.name))

        found = False
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            while ( dataptr < len(data) and
                    not (32 <= byte2int(data[dataptr]) <= 127)):
                dataptr += 1

            if dataptr >= len(data):
                break

            endptr = dataptr
            while endptr < len(data) and byte2int(data[endptr]) != 0:
                endptr += 1

            found = True
            self._emitline('  [%6x]  %s' % (
                dataptr, bytes2str(data[dataptr:endptr])))

            dataptr = endptr

        if not found:
            self._emitline('  No strings found in this section.')
        else:
            self._emitline()

    def display_debug_dump(self, dump_what):
        """ Dump a DWARF section
        """
        self._init_dwarfinfo()
        if self._dwarfinfo is None:
            return

        set_global_machine_arch(self.elffile.get_machine_arch())

        if dump_what == 'info':
            self._dump_debug_info()
        elif dump_what == 'decodedline':
            self._dump_debug_line_programs()
        elif dump_what == 'frames':
            self._dump_debug_frames()
        elif dump_what == 'frames-interp':
            self._dump_debug_frames_interp()
        else:
            self._emitline('debug dump not yet supported for "%s"' % dump_what)

    def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True,
                    alternate=False):
        """ Format an address into a hexadecimal string.

            fieldsize:
                Size of the hexadecimal field (with leading zeros to fit the
                address into. For example with fieldsize=8, the format will
                be %08x
                If None, the minimal required field size will be used.

            fullhex:
                If True, override fieldsize to set it to the maximal size
                needed for the elfclass

            lead0x:
                If True, leading 0x is added

            alternate:
                If True, override lead0x to emulate the alternate
                hexadecimal form specified in format string with the #
                character: only non-zero values are prefixed with 0x.
                This form is used by readelf.
        """
        if alternate:
            if addr == 0:
                lead0x = False
            else:
                lead0x = True
                fieldsize -= 2

        s = '0x' if lead0x else ''
        if fullhex:
            fieldsize = 8 if self.elffile.elfclass == 32 else 16
        if fieldsize is None:
            field = '%x'
        else:
            field = '%' + '0%sx' % fieldsize
        return s + field % addr

    def _print_version_section_header(self, version_section, name, lead0x=True,
                                      indent=1):
        """ Print a section header of one version related section (versym,
            verneed or verdef) with some options to accomodate readelf
            little differences between each header (e.g. indentation
            and 0x prefixing).
        """
        if hasattr(version_section, 'num_versions'):
            num_entries = version_section.num_versions()
        else:
            num_entries = version_section.num_symbols()

        self._emitline("\n%s section '%s' contains %s entries:" %
            (name, bytes2str(version_section.name), num_entries))
        self._emitline('%sAddr: %s  Offset: %s  Link: %i (%s)' % (
            ' ' * indent,
            self._format_hex(
                version_section['sh_addr'], fieldsize=16, lead0x=lead0x),
            self._format_hex(
                version_section['sh_offset'], fieldsize=6, lead0x=True),
            version_section['sh_link'],
            bytes2str(
                self.elffile.get_section(version_section['sh_link']).name)
            )
        )

    def _init_versioninfo(self):
        """ Search and initialize informations about version related sections
            and the kind of versioning used (GNU or Solaris).
        """
        if self._versioninfo is not None:
            return

        self._versioninfo = {'versym': None, 'verdef': None,
                             'verneed': None, 'type': None}

        for section in self.elffile.iter_sections():
            if isinstance(section, GNUVerSymSection):
                self._versioninfo['versym'] = section
            elif isinstance(section, GNUVerDefSection):
                self._versioninfo['verdef'] = section
            elif isinstance(section, GNUVerNeedSection):
                self._versioninfo['verneed'] = section
            elif isinstance(section, DynamicSection):
                for tag in section.iter_tags():
                    if tag['d_tag'] == 'DT_VERSYM':
                        self._versioninfo['type'] = 'GNU'
                        break

        if not self._versioninfo['type'] and (
                self._versioninfo['verneed'] or self._versioninfo['verdef']):
            self._versioninfo['type'] = 'Solaris'

    def _symbol_version(self, nsym):
        """ Return a dict containing information on the
                   or None if no version information is available
        """
        self._init_versioninfo()

        symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden'))

        if (not self._versioninfo['versym'] or
                nsym >= self._versioninfo['versym'].num_symbols()):
            return None

        symbol = self._versioninfo['versym'].get_symbol(nsym)
        index = symbol.entry['ndx']
        if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
            index = int(index)

            if self._versioninfo['type'] == 'GNU':
                # In GNU versioning mode, the highest bit is used to
                # store wether the symbol is hidden or not
                if index & 0x8000:
                    index &= ~0x8000
                    symbol_version['hidden'] = True

            if (self._versioninfo['verdef'] and
                    index <= self._versioninfo['verdef'].num_versions()):
                _, verdaux_iter = \
                        self._versioninfo['verdef'].get_version(index)
                symbol_version['name'] = bytes2str(next(verdaux_iter).name)
            else:
                verneed, vernaux = \
                        self._versioninfo['verneed'].get_version(index)
                symbol_version['name'] = bytes2str(vernaux.name)
                symbol_version['filename'] = bytes2str(verneed.name)

        symbol_version['index'] = index
        return symbol_version

    def _section_from_spec(self, spec):
        """ Retrieve a section given a "spec" (either number or name).
            Return None if no such section exists in the file.
        """
        try:
            num = int(spec)
            if num < self.elffile.num_sections():
                return self.elffile.get_section(num)
            else:
                return None
        except ValueError:
            # Not a number. Must be a name then
            return self.elffile.get_section_by_name(str2bytes(spec))

    def _note_relocs_for_section(self, section):
        """ If there are relocation sections pointing to the givne section,
            emit a note about it.
        """
        for relsec in self.elffile.iter_sections():
            if isinstance(relsec, RelocationSection):
                info_idx = relsec['sh_info']
                if self.elffile.get_section(info_idx) == section:
                    self._emitline('  Note: This section has relocations against it, but these have NOT been applied to this dump.')
                    return

    def _init_dwarfinfo(self):
        """ Initialize the DWARF info contained in the file and assign it to
            self._dwarfinfo.
            Leave self._dwarfinfo at None if no DWARF info was found in the file
        """
        if self._dwarfinfo is not None:
            return

        if self.elffile.has_dwarf_info():
            self._dwarfinfo = self.elffile.get_dwarf_info()
        else:
            self._dwarfinfo = None

    def _dump_debug_info(self):
        """ Dump the debugging info section.
        """
        self._emitline('Contents of the .debug_info section:\n')

        # Offset of the .debug_info section in the stream
        section_offset = self._dwarfinfo.debug_info_sec.global_offset

        for cu in self._dwarfinfo.iter_CUs():
            self._emitline('  Compilation Unit @ offset %s:' %
                self._format_hex(cu.cu_offset))
            self._emitline('   Length:        %s (%s)' % (
                self._format_hex(cu['unit_length']),
                '%s-bit' % cu.dwarf_format()))
            self._emitline('   Version:       %s' % cu['version']),
            self._emitline('   Abbrev Offset: %s' % (
                self._format_hex(cu['debug_abbrev_offset']))),
            self._emitline('   Pointer Size:  %s' % cu['address_size'])

            # The nesting depth of each DIE within the tree of DIEs must be
            # displayed. To implement this, a counter is incremented each time
            # the current DIE has children, and decremented when a null die is
            # encountered. Due to the way the DIE tree is serialized, this will
            # correctly reflect the nesting depth
            #
            die_depth = 0
            for die in cu.iter_DIEs():
                self._emitline(' <%s><%x>: Abbrev Number: %s%s' % (
                    die_depth,
                    die.offset,
                    die.abbrev_code,
                    (' (%s)' % die.tag) if not die.is_null() else ''))
                if die.is_null():
                    die_depth -= 1
                    continue

                for attr in itervalues(die.attributes):
                    name = attr.name
                    # Unknown attribute values are passed-through as integers
                    if isinstance(name, int):
                        name = 'Unknown AT value: %x' % name
                    self._emitline('    <%2x>   %-18s: %s' % (
                        attr.offset,
                        name,
                        describe_attr_value(
                            attr, die, section_offset)))

                if die.has_children:
                    die_depth += 1

        self._emitline()

    def _dump_debug_line_programs(self):
        """ Dump the (decoded) line programs from .debug_line
            The programs are dumped in the order of the CUs they belong to.
        """
        self._emitline('Decoded dump of debug contents of section .debug_line:\n')

        for cu in self._dwarfinfo.iter_CUs():
            lineprogram = self._dwarfinfo.line_program_for_CU(cu)

            cu_filename = bytes2str(lineprogram['file_entry'][0].name)
            if len(lineprogram['include_directory']) > 0:
                dir_index = lineprogram['file_entry'][0].dir_index
                if dir_index > 0:
                    dir = lineprogram['include_directory'][dir_index - 1]
                else:
                    dir = b'.'
                cu_filename = '%s/%s' % (bytes2str(dir), cu_filename)

            self._emitline('CU: %s:' % cu_filename)
            self._emitline('File name                            Line number    Starting address')

            # Print each state's file, line and address information. For some
            # instructions other output is needed to be compatible with
            # readelf.
            for entry in lineprogram.get_entries():
                state = entry.state
                if state is None:
                    # Special handling for commands that don't set a new state
                    if entry.command == DW_LNS_set_file:
                        file_entry = lineprogram['file_entry'][entry.args[0] - 1]
                        if file_entry.dir_index == 0:
                            # current directory
                            self._emitline('\n./%s:[++]' % (
                                bytes2str(file_entry.name)))
                        else:
                            self._emitline('\n%s/%s:' % (
                                bytes2str(lineprogram['include_directory'][file_entry.dir_index - 1]),
                                bytes2str(file_entry.name)))
                    elif entry.command == DW_LNE_define_file:
                        self._emitline('%s:' % (
                            bytes2str(lineprogram['include_directory'][entry.args[0].dir_index])))
                elif not state.end_sequence:
                    # readelf doesn't print the state after end_sequence
                    # instructions. I think it's a bug but to be compatible
                    # I don't print them too.
                    self._emitline('%-35s  %11d  %18s' % (
                        bytes2str(lineprogram['file_entry'][state.file - 1].name),
                        state.line,
                        '0' if state.address == 0 else
                               self._format_hex(state.address)))
                if entry.command == DW_LNS_copy:
                    # Another readelf oddity...
                    self._emitline()

    def _dump_debug_frames(self):
        """ Dump the raw frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return
        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline('\n%08x %08x %08x CIE' % (
                    entry.offset, entry['length'], entry['CIE_id']))
                self._emitline('  Version:               %d' % entry['version'])
                self._emitline('  Augmentation:          "%s"' % bytes2str(entry['augmentation']))
                self._emitline('  Code alignment factor: %u' % entry['code_alignment_factor'])
                self._emitline('  Data alignment factor: %d' % entry['data_alignment_factor'])
                self._emitline('  Return address column: %d' % entry['return_address_register'])
                self._emitline()
            else: # FDE
                self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_pointer'],
                    entry.cie.offset,
                    entry['initial_location'],
                    entry['initial_location'] + entry['address_range']))

            self._emit(describe_CFI_instructions(entry))
        self._emitline()

    def _dump_debug_frames_interp(self):
        """ Dump the interpreted (decoded) frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return

        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_id'],
                    bytes2str(entry['augmentation']),
                    entry['code_alignment_factor'],
                    entry['data_alignment_factor'],
                    entry['return_address_register']))
                ra_regnum = entry['return_address_register']
            else: # FDE
                self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_pointer'],
                    entry.cie.offset,
                    entry['initial_location'],
                    entry['initial_location'] + entry['address_range']))
                ra_regnum = entry.cie['return_address_register']

            # Print the heading row for the decoded table
            self._emit('   LOC')
            self._emit('  ' if entry.structs.address_size == 4 else '          ')
            self._emit(' CFA      ')

            # Decode the table nad look at the registers it describes.
            # We build reg_order here to match readelf's order. In particular,
            # registers are sorted by their number, and the register matching
            # ra_regnum is always listed last with a special heading.
            decoded_table = entry.get_decoded()
            reg_order = sorted(ifilter(
                lambda r: r != ra_regnum,
                decoded_table.reg_order))

            # Headings for the registers
            for regnum in reg_order:
                self._emit('%-6s' % describe_reg_name(regnum))
            self._emitline('ra      ')

            # Now include ra_regnum in reg_order to print its values similarly
            # to the other registers.
            reg_order.append(ra_regnum)
            for line in decoded_table.table:
                self._emit(self._format_hex(
                    line['pc'], fullhex=True, lead0x=False))
                self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa']))

                for regnum in reg_order:
                    if regnum in line:
                        s = describe_CFI_register_rule(line[regnum])
                    else:
                        s = 'u'
                    self._emit('%-6s' % s)
                self._emitline()
        self._emitline()

    def _emit(self, s=''):
        """ Emit an object to output
        """
        self.output.write(str(s))

    def _emitline(self, s=''):
        """ Emit an object to output, followed by a newline
        """
        self.output.write(str(s) + '\n')
Ejemplo n.º 49
0
                        sms.add(sm_name)
                    else:
                        existing_sms.add(sm_name)
                    continue

                match = re.match(r'.rela.sm.(\w+).table', name)
                if match:
                    sm_name = match.group(1)
                    if not sm_name in sms_table_order:
                        sms_table_order[sm_name] = []
                        sms_entries[sm_name] = []

                    sms_table_order[sm_name].append(file_name)

                    #find entry points of the SM in this file
                    symtab = elf_file.get_section(section['sh_link'])
                    entries = [(rel['r_offset'],
                                symtab.get_symbol(rel['r_info_sym']).name)
                                    for rel in section.iter_relocations()]
                    entries.sort()
                    sms_entries[sm_name] += \
                        [entry.decode('ascii') for _, entry in entries]
                    continue

                match = re.match(r'.rela.sm.(\w+).text', name)
                if match:
                    sm_name = match.group(1)

                    #find call from this SM to others
                    symtab = elf_file.get_section(section['sh_link'])
                    for rel in section.iter_relocations():
Ejemplo n.º 50
0
        data = elf.get_section_by_name(section_name).data()
        print "[II] Section %s is %d bytes offset %d"%(section_name,len(data),offsets[section_name])
    except:
        data = ''
        print '[WW] No %s section'%section_name
    shellcode.write(data)

print "[II] Total packed data size %d" % shellcode.len

#FIX RELOCS!
relocs = []
for section_name in selected_sections:
    reloc_section = find_relocations_for_section(elf, section_name)
    if reloc_section is None:
        continue
    symtab = elf.get_section(reloc_section['sh_link'])
    for reloc in reloc_section.iter_relocations():
        assert elf.get_machine_arch() == 'x86' and not reloc.is_RELA()
        reloc_base = offsets[section_name]
        reloc_offset = reloc['r_offset']
        reloc_type = reloc['r_info_type']
        target_symbol = symtab.get_symbol(reloc['r_info_sym'])
        target_name = elf.get_section(target_symbol['st_shndx']).name
        target_base = offsets[target_name]
        target_offset = target_symbol['st_value']

        shellcode.seek(reloc_base+reloc_offset)
        value = struct.unpack("<l",shellcode.read(4))[0]
        print "RELOC:",section_name, reloc_base, reloc_offset, "=>", target_name, target_base,target_offset, value
        if reloc_type == ENUM_RELOC_TYPE_i386['R_386_32']:
            value = target_base + target_offset + value
Ejemplo n.º 51
0
class ReadElf(object):
    """ display_* methods are used to emit output into the output stream
    """
    def __init__(self, file, output):
        """ file:
                stream object with the ELF file to read

            output:
                output stream to write to
        """
        self.elffile = ELFFile(file)
        self.output = output

        # Lazily initialized if a debug dump is requested
        self._dwarfinfo = None

    def display_file_header(self):
        """ Display the ELF file header
        """
        self._emitline('ELF Header:')
        self._emit('  Magic:   ')
        self._emitline(' '.join('%2.2x' % byte2int(b)
                                    for b in self.elffile.e_ident_raw))
        header = self.elffile.header
        e_ident = header['e_ident']
        self._emitline('  Class:                             %s' %
                describe_ei_class(e_ident['EI_CLASS']))
        self._emitline('  Data:                              %s' %
                describe_ei_data(e_ident['EI_DATA']))
        self._emitline('  Version:                           %s' %
                describe_ei_version(e_ident['EI_VERSION']))
        self._emitline('  OS/ABI:                            %s' %
                describe_ei_osabi(e_ident['EI_OSABI']))
        self._emitline('  ABI Version:                       %d' %
                e_ident['EI_ABIVERSION'])
        self._emitline('  Type:                              %s' %
                describe_e_type(header['e_type']))
        self._emitline('  Machine:                           %s' %
                describe_e_machine(header['e_machine']))
        self._emitline('  Version:                           %s' %
                describe_e_version_numeric(header['e_version']))
        self._emitline('  Entry point address:               %s' %
                self._format_hex(header['e_entry']))
        self._emit('  Start of program headers:          %s' %
                header['e_phoff'])
        self._emitline(' (bytes into file)')
        self._emit('  Start of section headers:          %s' %
                header['e_shoff'])
        self._emitline(' (bytes into file)')
        self._emitline('  Flags:                             %s' %
                self._format_hex(header['e_flags']))
        self._emitline('  Size of this header:               %s (bytes)' %
                header['e_ehsize'])
        self._emitline('  Size of program headers:           %s (bytes)' %
                header['e_phentsize'])
        self._emitline('  Number of program headers:         %s' %
                header['e_phnum'])
        self._emitline('  Size of section headers:           %s (bytes)' %
                header['e_shentsize'])
        self._emitline('  Number of section headers:         %s' %
                header['e_shnum'])
        self._emitline('  Section header string table index: %s' %
                header['e_shstrndx'])

    def display_program_headers(self, show_heading=True):
        """ Display the ELF program headers.
            If show_heading is True, displays the heading for this information
            (Elf file type is...)
        """
        self._emitline()
        if self.elffile.num_segments() == 0:
            self._emitline('There are no program headers in this file.')
            return

        elfheader = self.elffile.header
        if show_heading:
            self._emitline('Elf file type is %s' %
                describe_e_type(elfheader['e_type']))
            self._emitline('Entry point is %s' %
                self._format_hex(elfheader['e_entry']))
            # readelf weirness - why isn't e_phoff printed as hex? (for section
            # headers, it is...)
            self._emitline('There are %s program headers, starting at offset %s' % (
                elfheader['e_phnum'], elfheader['e_phoff']))
            self._emitline()

        self._emitline('Program Headers:')

        # Now comes the table of program headers with their attributes. Note
        # that due to different formatting constraints of 32-bit and 64-bit
        # addresses, there are some conditions on elfclass here.
        #
        # First comes the table heading
        #
        if self.elffile.elfclass == 32:
            self._emitline('  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align')
        else:
            self._emitline('  Type           Offset             VirtAddr           PhysAddr')
            self._emitline('                 FileSiz            MemSiz              Flags  Align')

        # Now the entries
        #
        for segment in self.elffile.iter_segments():
            self._emit('  %-14s ' % describe_p_type(segment['p_type']))

            if self.elffile.elfclass == 32:
                self._emitline('%s %s %s %s %s %-3s %s' % (
                    self._format_hex(segment['p_offset'], fieldsize=6),
                    self._format_hex(segment['p_vaddr'], fullhex=True),
                    self._format_hex(segment['p_paddr'], fullhex=True),
                    self._format_hex(segment['p_filesz'], fieldsize=5),
                    self._format_hex(segment['p_memsz'], fieldsize=5),
                    describe_p_flags(segment['p_flags']),
                    self._format_hex(segment['p_align'])))
            else: # 64
                self._emitline('%s %s %s' % (
                    self._format_hex(segment['p_offset'], fullhex=True),
                    self._format_hex(segment['p_vaddr'], fullhex=True),
                    self._format_hex(segment['p_paddr'], fullhex=True)))
                self._emitline('                 %s %s  %-3s    %s' % (
                    self._format_hex(segment['p_filesz'], fullhex=True),
                    self._format_hex(segment['p_memsz'], fullhex=True),
                    describe_p_flags(segment['p_flags']),
                    # lead0x set to False for p_align, to mimic readelf.
                    # No idea why the difference from 32-bit mode :-|
                    self._format_hex(segment['p_align'], lead0x=False)))

            if isinstance(segment, InterpSegment):
                self._emitline('      [Requesting program interpreter: %s]' %
                    bytes2str(segment.get_interp_name()))

        # Sections to segments mapping
        #
        if self.elffile.num_sections() == 0:
            # No sections? We're done
            return

        self._emitline('\n Section to Segment mapping:')
        self._emitline('  Segment Sections...')

        for nseg, segment in enumerate(self.elffile.iter_segments()):
            self._emit('   %2.2d     ' % nseg)

            for section in self.elffile.iter_sections():
                if (    not section.is_null() and
                        segment.section_in_segment(section)):
                    self._emit('%s ' % bytes2str(section.name))

            self._emitline('')

    def display_section_headers(self, show_heading=True):
        """ Display the ELF section headers
        """
        elfheader = self.elffile.header
        if show_heading:
            self._emitline('There are %s section headers, starting at offset %s' % (
                elfheader['e_shnum'], self._format_hex(elfheader['e_shoff'])))

        self._emitline('\nSection Header%s:' % (
            's' if elfheader['e_shnum'] > 1 else ''))

        # Different formatting constraints of 32-bit and 64-bit addresses
        #
        if self.elffile.elfclass == 32:
            self._emitline('  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al')
        else:
            self._emitline('  [Nr] Name              Type             Address           Offset')
            self._emitline('       Size              EntSize          Flags  Link  Info  Align')

        # Now the entries
        #
        for nsec, section in enumerate(self.elffile.iter_sections()):
            self._emit('  [%2u] %-17.17s %-15.15s ' % (
                nsec, bytes2str(section.name), describe_sh_type(section['sh_type'])))

            if self.elffile.elfclass == 32:
                self._emitline('%s %s %s %s %3s %2s %3s %2s' % (
                    self._format_hex(section['sh_addr'], fieldsize=8, lead0x=False),
                    self._format_hex(section['sh_offset'], fieldsize=6, lead0x=False),
                    self._format_hex(section['sh_size'], fieldsize=6, lead0x=False),
                    self._format_hex(section['sh_entsize'], fieldsize=2, lead0x=False),
                    describe_sh_flags(section['sh_flags']),
                    section['sh_link'], section['sh_info'],
                    section['sh_addralign']))
            else: # 64
                self._emitline(' %s  %s' % (
                    self._format_hex(section['sh_addr'], fullhex=True, lead0x=False),
                    self._format_hex(section['sh_offset'],
                        fieldsize=16 if section['sh_offset'] > 0xffffffff else 8,
                        lead0x=False)))
                self._emitline('       %s  %s %3s      %2s   %3s     %s' % (
                    self._format_hex(section['sh_size'], fullhex=True, lead0x=False),
                    self._format_hex(section['sh_entsize'], fullhex=True, lead0x=False),
                    describe_sh_flags(section['sh_flags']),
                    section['sh_link'], section['sh_info'],
                    section['sh_addralign']))

        self._emitline('Key to Flags:')
        self._emit('  W (write), A (alloc), X (execute), M (merge), S (strings)')
        if self.elffile['e_machine'] in ('EM_X86_64', 'EM_L10M'):
            self._emitline(', l (large)')
        else:
            self._emitline()
        self._emitline('  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
        self._emitline('  O (extra OS processing required) o (OS specific), p (processor specific)')

    def display_symbol_tables(self):
        """ Display the symbol tables contained in the file
        """
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue

            if section['sh_entsize'] == 0:
                self._emitline("\nSymbol table '%s' has a sh_entsize of zero!" % (
                    bytes2str(section.name)))
                continue

            self._emitline("\nSymbol table '%s' contains %s entries:" % (
                bytes2str(section.name), section.num_symbols()))

            if self.elffile.elfclass == 32:
                self._emitline('   Num:    Value  Size Type    Bind   Vis      Ndx Name')
            else: # 64
                self._emitline('   Num:    Value          Size Type    Bind   Vis      Ndx Name')

            for nsym, symbol in enumerate(section.iter_symbols()):
                # symbol names are truncated to 25 chars, similarly to readelf
                self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % (
                    nsym,
                    self._format_hex(symbol['st_value'], fullhex=True, lead0x=False),
                    symbol['st_size'],
                    describe_symbol_type(symbol['st_info']['type']),
                    describe_symbol_bind(symbol['st_info']['bind']),
                    describe_symbol_visibility(symbol['st_other']['visibility']),
                    describe_symbol_shndx(symbol['st_shndx']),
                    bytes2str(symbol.name)))

    def display_relocations(self):
        """ Display the relocations contained in the file
        """
        has_relocation_sections = False
        for section in self.elffile.iter_sections():
            if not isinstance(section, RelocationSection):
                continue

            has_relocation_sections = True
            self._emitline("\nRelocation section '%s' at offset %s contains %s entries:" % (
                bytes2str(section.name),
                self._format_hex(section['sh_offset']),
                section.num_relocations()))
            if section.is_RELA():
                self._emitline("  Offset          Info           Type           Sym. Value    Sym. Name + Addend")
            else:
                self._emitline(" Offset     Info    Type            Sym.Value  Sym. Name")

            # The symbol table section pointed to in sh_link
            symtable = self.elffile.get_section(section['sh_link'])

            for rel in section.iter_relocations():
                hexwidth = 8 if self.elffile.elfclass == 32 else 12
                self._emit('%s  %s %-17.17s' % (
                    self._format_hex(rel['r_offset'],
                        fieldsize=hexwidth, lead0x=False),
                    self._format_hex(rel['r_info'],
                        fieldsize=hexwidth, lead0x=False),
                    describe_reloc_type(
                        rel['r_info_type'], self.elffile)))

                if rel['r_info_sym'] == 0:
                    self._emitline()
                    continue

                symbol = symtable.get_symbol(rel['r_info_sym'])
                # Some symbols have zero 'st_name', so instead what's used is
                # the name of the section they point at
                if symbol['st_name'] == 0:
                    symsec = self.elffile.get_section(symbol['st_shndx'])
                    symbol_name = symsec.name
                else:
                    symbol_name = symbol.name
                self._emit(' %s %s%22.22s' % (
                    self._format_hex(
                        symbol['st_value'],
                        fullhex=True, lead0x=False),
                    '  ' if self.elffile.elfclass == 32 else '',
                    bytes2str(symbol_name)))
                if section.is_RELA():
                    self._emit(' %s %x' % (
                        '+' if rel['r_addend'] >= 0 else '-',
                        abs(rel['r_addend'])))
                self._emitline()

        if not has_relocation_sections:
            self._emitline('\nThere are no relocations in this file.')

    def display_hex_dump(self, section_spec):
        """ Display a hex dump of a section. section_spec is either a section
            number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" % (
                section_spec))
            return

        self._emitline("\nHex dump of section '%s':" % bytes2str(section.name))
        self._note_relocs_for_section(section)
        addr = section['sh_addr']
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            bytesleft = len(data) - dataptr
            # chunks of 16 bytes per line
            linebytes = 16 if bytesleft > 16 else bytesleft

            self._emit('  %s ' % self._format_hex(addr, fieldsize=8))
            for i in range(16):
                if i < linebytes:
                    self._emit('%2.2x' % byte2int(data[dataptr + i]))
                else:
                    self._emit('  ')
                if i % 4 == 3:
                    self._emit(' ')

            for i in range(linebytes):
                c = data[dataptr + i : dataptr + i + 1]
                if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f:
                    self._emit(bytes2str(c))
                else:
                    self._emit(bytes2str(b'.'))

            self._emitline()
            addr += linebytes
            dataptr += linebytes

        self._emitline()

    def display_string_dump(self, section_spec):
        """ Display a strings dump of a section. section_spec is either a
            section number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" % (
                section_spec))
            return

        self._emitline("\nString dump of section '%s':" % bytes2str(section.name))

        found = False
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            while ( dataptr < len(data) and
                    not (32 <= byte2int(data[dataptr]) <= 127)):
                dataptr += 1

            if dataptr >= len(data):
                break

            endptr = dataptr
            while endptr < len(data) and byte2int(data[endptr]) != 0:
                endptr += 1

            found = True
            self._emitline('  [%6x]  %s' % (
                dataptr, bytes2str(data[dataptr:endptr])))

            dataptr = endptr

        if not found:
            self._emitline('  No strings found in this section.')
        else:
            self._emitline()

    def display_debug_dump(self, dump_what):
        """ Dump a DWARF section
        """
        self._init_dwarfinfo()
        if self._dwarfinfo is None:
            return

        set_global_machine_arch(self.elffile.get_machine_arch())

        if dump_what == 'info':
            self._dump_debug_info()
        elif dump_what == 'decodedline':
            self._dump_debug_line_programs()
        elif dump_what == 'frames':
            self._dump_debug_frames()
        elif dump_what == 'frames-interp':
            self._dump_debug_frames_interp()
        else:
            self._emitline('debug dump not yet supported for "%s"' % dump_what)

    def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True):
        """ Format an address into a hexadecimal string.

            fieldsize:
                Size of the hexadecimal field (with leading zeros to fit the
                address into. For example with fieldsize=8, the format will
                be %08x
                If None, the minimal required field size will be used.

            fullhex:
                If True, override fieldsize to set it to the maximal size
                needed for the elfclass

            lead0x:
                If True, leading 0x is added
        """
        s = '0x' if lead0x else ''
        if fullhex:
            fieldsize = 8 if self.elffile.elfclass == 32 else 16
        if fieldsize is None:
            field = '%x'
        else:
            field = '%' + '0%sx' % fieldsize
        return s + field % addr

    def _section_from_spec(self, spec):
        """ Retrieve a section given a "spec" (either number or name).
            Return None if no such section exists in the file.
        """
        try:
            num = int(spec)
            if num < self.elffile.num_sections():
                return self.elffile.get_section(num)
            else:
                return None
        except ValueError:
            # Not a number. Must be a name then
            return self.elffile.get_section_by_name(str2bytes(spec))

    def _note_relocs_for_section(self, section):
        """ If there are relocation sections pointing to the givne section,
            emit a note about it.
        """
        for relsec in self.elffile.iter_sections():
            if isinstance(relsec, RelocationSection):
                info_idx = relsec['sh_info']
                if self.elffile.get_section(info_idx) == section:
                    self._emitline('  Note: This section has relocations against it, but these have NOT been applied to this dump.')
                    return

    def _init_dwarfinfo(self):
        """ Initialize the DWARF info contained in the file and assign it to
            self._dwarfinfo.
            Leave self._dwarfinfo at None if no DWARF info was found in the file
        """
        if self._dwarfinfo is not None:
            return

        if self.elffile.has_dwarf_info():
            self._dwarfinfo = self.elffile.get_dwarf_info()
        else:
            self._dwarfinfo = None

    def _dump_debug_info(self):
        """ Dump the debugging info section.
        """
        self._emitline('Contents of the .debug_info section:\n')

        # Offset of the .debug_info section in the stream
        section_offset = self._dwarfinfo.debug_info_sec.global_offset

        for cu in self._dwarfinfo.iter_CUs():
            self._emitline('  Compilation Unit @ offset %s:' %
                self._format_hex(cu.cu_offset))
            self._emitline('   Length:        %s (%s)' % (
                self._format_hex(cu['unit_length']),
                '%s-bit' % cu.dwarf_format()))
            self._emitline('   Version:       %s' % cu['version']),
            self._emitline('   Abbrev Offset: %s' % cu['debug_abbrev_offset']),
            self._emitline('   Pointer Size:  %s' % cu['address_size'])

            # The nesting depth of each DIE within the tree of DIEs must be
            # displayed. To implement this, a counter is incremented each time
            # the current DIE has children, and decremented when a null die is
            # encountered. Due to the way the DIE tree is serialized, this will
            # correctly reflect the nesting depth
            #
            die_depth = 0
            for die in cu.iter_DIEs():
                if die.is_null():
                    die_depth -= 1
                    continue
                self._emitline(' <%s><%x>: Abbrev Number: %s (%s)' % (
                    die_depth,
                    die.offset,
                    die.abbrev_code,
                    die.tag))

                for attr in itervalues(die.attributes):
                    name = attr.name
                    # Unknown attribute values are passed-through as integers
                    if isinstance(name, int):
                        name = 'Unknown AT value: %x' % name
                    self._emitline('    <%2x>   %-18s: %s' % (
                        attr.offset,
                        name,
                        describe_attr_value(
                            attr, die, section_offset)))

                if die.has_children:
                    die_depth += 1

        self._emitline()

    def _dump_debug_line_programs(self):
        """ Dump the (decoded) line programs from .debug_line
            The programs are dumped in the order of the CUs they belong to.
        """
        self._emitline('Decoded dump of debug contents of section .debug_line:\n')

        for cu in self._dwarfinfo.iter_CUs():
            lineprogram = self._dwarfinfo.line_program_for_CU(cu)

            cu_filename = ''
            if len(lineprogram['include_directory']) > 0:
                cu_filename = '%s/%s' % (
                    bytes2str(lineprogram['include_directory'][0]),
                    bytes2str(lineprogram['file_entry'][0].name))
            else:
                cu_filename = bytes2str(lineprogram['file_entry'][0].name)

            self._emitline('CU: %s:' % cu_filename)
            self._emitline('File name                            Line number    Starting address')

            # Print each state's file, line and address information. For some
            # instructions other output is needed to be compatible with
            # readelf.
            for entry in lineprogram.get_entries():
                state = entry.state
                if state is None:
                    # Special handling for commands that don't set a new state
                    if entry.command == DW_LNS_set_file:
                        file_entry = lineprogram['file_entry'][entry.args[0] - 1]
                        if file_entry.dir_index == 0:
                            # current directory
                            self._emitline('\n./%s:[++]' % (
                                bytes2str(file_entry.name)))
                        else:
                            self._emitline('\n%s/%s:' % (
                                bytes2str(lineprogram['include_directory'][file_entry.dir_index - 1]),
                                bytes2str(file_entry.name)))
                    elif entry.command == DW_LNE_define_file:
                        self._emitline('%s:' % (
                            bytes2str(lineprogram['include_directory'][entry.args[0].dir_index])))
                elif not state.end_sequence:
                    # readelf doesn't print the state after end_sequence
                    # instructions. I think it's a bug but to be compatible
                    # I don't print them too.
                    self._emitline('%-35s  %11d  %18s' % (
                        bytes2str(lineprogram['file_entry'][state.file - 1].name),
                        state.line,
                        '0' if state.address == 0 else
                               self._format_hex(state.address)))
                if entry.command == DW_LNS_copy:
                    # Another readelf oddity...
                    self._emitline()

    def _dump_debug_frames(self):
        """ Dump the raw frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return
        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline('\n%08x %08x %08x CIE' % (
                    entry.offset, entry['length'], entry['CIE_id']))
                self._emitline('  Version:               %d' % entry['version'])
                self._emitline('  Augmentation:          "%s"' % bytes2str(entry['augmentation']))
                self._emitline('  Code alignment factor: %u' % entry['code_alignment_factor'])
                self._emitline('  Data alignment factor: %d' % entry['data_alignment_factor'])
                self._emitline('  Return address column: %d' % entry['return_address_register'])
                self._emitline()
            else: # FDE
                self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_pointer'],
                    entry.cie.offset,
                    entry['initial_location'],
                    entry['initial_location'] + entry['address_range']))

            self._emit(describe_CFI_instructions(entry))
        self._emitline()

    def _dump_debug_frames_interp(self):
        """ Dump the interpreted (decoded) frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return

        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_id'],
                    bytes2str(entry['augmentation']),
                    entry['code_alignment_factor'],
                    entry['data_alignment_factor'],
                    entry['return_address_register']))
                ra_regnum = entry['return_address_register']
            else: # FDE
                self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_pointer'],
                    entry.cie.offset,
                    entry['initial_location'],
                    entry['initial_location'] + entry['address_range']))
                ra_regnum = entry.cie['return_address_register']

            # Print the heading row for the decoded table
            self._emit('   LOC')
            self._emit('  ' if entry.structs.address_size == 4 else '          ')
            self._emit(' CFA      ')

            # Decode the table nad look at the registers it describes.
            # We build reg_order here to match readelf's order. In particular,
            # registers are sorted by their number, and the register matching
            # ra_regnum is always listed last with a special heading.
            decoded_table = entry.get_decoded()
            reg_order = sorted(ifilter(
                lambda r: r != ra_regnum,
                decoded_table.reg_order))

            # Headings for the registers
            for regnum in reg_order:
                self._emit('%-6s' % describe_reg_name(regnum))
            self._emitline('ra      ')

            # Now include ra_regnum in reg_order to print its values similarly
            # to the other registers.
            reg_order.append(ra_regnum)
            for line in decoded_table.table:
                self._emit(self._format_hex(
                    line['pc'], fullhex=True, lead0x=False))
                self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa']))

                for regnum in reg_order:
                    if regnum in line:
                        s = describe_CFI_register_rule(line[regnum])
                    else:
                        s = 'u'
                    self._emit('%-6s' % s)
                self._emitline()
        self._emitline()

    def _emit(self, s=''):
        """ Emit an object to output
        """
        self.output.write(str(s))

    def _emitline(self, s=''):
        """ Emit an object to output, followed by a newline
        """
        self.output.write(str(s) + '\n')