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
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
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
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
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
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
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)
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
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']
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
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
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
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
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"]
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
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
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}')
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
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()
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
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
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
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
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
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')
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
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}')
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
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')
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')
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')
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")
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
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))
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
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]
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()
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']
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]
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)
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]
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('''} ''')
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
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
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)
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')
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():
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
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')