def test_files(fns, quiet=False, profile=False, runtime=False): for fn in fns: short_fn = fn.split("/")[-1] if "/" in fn else fn if os.path.isdir(fn): if not quiet: print "{} {}: skipping directory".format(notice, short_fn) continue try: elf = ELFFile(open(fn)) except ELFError: if not quiet: print "{} {}: skipping non-ELF file".format(notice, short_fn) continue engine_functions = {} for engine in ENGINES: try: this_engine = Static(fn, debug=0, static_engine=engine) #no debug output if args.profile: #needs pycallgraph from pycallgraph import PyCallGraph from pycallgraph.output import GraphvizOutput graphviz = GraphvizOutput() graphviz.output_file = 'prof.png' with PyCallGraph(output=graphviz): this_engine.process() else: this_engine.process() engine_functions[engine] = { x.start for x in this_engine['functions'] } except KeyboardInterrupt: print "{} User stopped processing test cases.".format(notice) sys.exit() except MemoryError: #print "{} {}: bap encountered a memory error.".format(fail, short_fn, engine) continue except Exception as e: print "{} {}: {} engine failed to process file with `{}'".format( fail, short_fn, engine, e) continue if runtime: if not quiet: print "{} {}: {} ran without exceptions".format( ok_green, short_fn, engine) continue if elf.has_dwarf_info(): dwarfinfo = elf.get_dwarf_info() dwarf_functions = get_functions(dwarfinfo) for engine, functions in engine_functions.iteritems(): missed = dwarf_functions - functions total_fxns = len(dwarf_functions) if len(missed) == 0: print "{} {}: {} engine found all {} function(s)".format( ok_green, short_fn, engine, total_fxns) else: status = fail if len(missed) == total_fxns else warn if args.verbose: fmt = "{} {}: {} engine missed {}/{} function(s): {}" missed_s = ", ".join(hex(fxn) for fxn in missed) print fmt.format(status, short_fn, engine, len(missed), total_fxns, missed_s) else: fmt = "{} {}: {} engine missed {}/{} function(s)" print fmt.format(status, short_fn, engine, len(missed), total_fxns) else: for engine, functions in engine_functions.iteritems(): status = fail if len(functions) == 0 else ok_blue print "{} {}: {} engine found {} function(s). (dwarf info unavailable)".format( status, short_fn, engine, len(functions))
def load_module(self, filename, do_init=True): m = self.find_module_by_name(filename) if (m != None): return m # logger.debug("Loading module '%s'." % filename) #do sth like linker with open(filename, 'rb') as fstream: #TODO: load elf without Section Header,pyelftools do not support. elf = ELFFile(fstream) dynamic = elf.header.e_type == 'ET_DYN' if not dynamic: raise NotImplementedError( "Only ET_DYN is supported at the moment.") # Parse program header (Execution view). # - LOAD (determinate what parts of the ELF file get mapped into memory) load_segments = [ x for x in elf.iter_segments() if x.header.p_type == 'PT_LOAD' ] # Find bounds of the load segments. bound_low = 0 bound_high = 0 for segment in load_segments: if segment.header.p_memsz == 0: continue if bound_low > segment.header.p_vaddr: bound_low = segment.header.p_vaddr high = segment.header.p_vaddr + segment.header.p_memsz if bound_high < high: bound_high = high ''' // Segment addresses in memory. Elf32_Addr seg_start = phdr->p_vaddr + load_bias_; Elf32_Addr seg_end = seg_start + phdr->p_memsz; Elf32_Addr seg_page_start = PAGE_START(seg_start); Elf32_Addr seg_page_end = PAGE_END(seg_end); // File offsets. Elf32_Addr file_start = phdr->p_offset; Elf32_Addr file_end = file_start + phdr->p_filesz; Elf32_Addr seg_file_end = seg_start + phdr->p_filesz; Elf32_Addr file_page_start = PAGE_START(file_start); Elf32_Addr file_length = file_end - file_page_start; if (file_length != 0) { void* seg_addr = mmap((void*)seg_page_start, file_length, PFLAGS_TO_PROT(phdr->p_flags), MAP_FIXED|MAP_PRIVATE, fd_, file_page_start); ''' # Retrieve a base address for this module. load_base = self.mem_reserve(bound_low, bound_high) vf = VirtualFile( misc_utils.system_path_to_vfs_path(self.__vfs_root, filename), misc_utils.my_open(filename, os.O_RDONLY), filename) for segment in load_segments: prot = get_segment_protection(segment.header.p_flags) prot = prot if prot is not 0 else UC_PROT_ALL #self.emu.memory.map(load_base + segment.header.p_vaddr, segment.header.p_memsz, prot) #self.emu.mu.mem_write(load_base + segment.header.p_vaddr, segment.data()) seg_start = load_base + segment.header.p_vaddr seg_page_start = page_start(seg_start) file_start = segment.header.p_offset file_end = file_start + segment.header.p_filesz file_page_start = page_start(file_start) file_length = file_end - file_page_start assert (file_length > 0) if (file_length > 0): self.emu.memory.map(seg_page_start, file_length, prot, vf, file_page_start) # seg_end = seg_start + segment.header.p_memsz seg_page_end = page_end(seg_end) seg_file_end = seg_start + segment.header.p_filesz seg_file_end = page_end(seg_file_end) ''' void* zeromap = mmap((void*)seg_file_end, seg_page_end - seg_file_end, PFLAGS_TO_PROT(phdr->p_flags), MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); ''' self.emu.memory.map(seg_file_end, seg_page_end - seg_file_end, prot) # # Find init array. init_array_size = 0 init_array_offset = 0 init_array = [] init_addr = 0 dynstr_addr = 0 dt_needed = [] for x in elf.iter_segments(): if x.header.p_type == "PT_DYNAMIC": for tag in x.iter_tags(): if tag.entry.d_tag == "DT_INIT_ARRAYSZ": init_array_size = tag.entry.d_val elif tag.entry.d_tag == "DT_INIT_ARRAY": init_array_offset = tag.entry.d_val elif tag.entry.d_tag == "DT_INIT": init_addr = tag.entry.d_val + load_base elif tag.entry.d_tag == "DT_STRTAB": dynstr_addr = tag.entry.d_val + load_base elif tag.entry.d_tag == "DT_NEEDED": dt_needed.append(tag.entry.d_val) # # break # # so_needed = [] for str_off in dt_needed: str_addr = dynstr_addr + str_off so_name = memory_helpers.read_utf8(self.emu.mu, str_addr) so_needed.append(so_name) # for so_name in so_needed: path = misc_utils.vfs_path_to_system_path( self.__vfs_root, so_name) if (not os.path.exists(path)): logger.warn("%s needed by %s do not exist in vfs %s" % (so_name, filename, self.__vfs_root)) continue # libmod = self.load_module(path) # rel_section = None for section in elf.iter_sections(): if not isinstance(section, RelocationSection): continue rel_section = section break # # Parse section header (Linking view). dynsym = elf.get_section_by_name(".dynsym") for _ in range(int(init_array_size / 4)): b = self.emu.mu.mem_read(load_base + init_array_offset, 4) fun_ptr = int.from_bytes(b, byteorder='little', signed=False) if fun_ptr != 0: # fun_ptr += load_base init_array.append(fun_ptr + load_base) # print ("find init array for :%s %x" % (filename, fun_ptr)) else: # search in reloc for rel in rel_section.iter_relocations(): rel_info_type = rel['r_info_type'] rel_addr = rel['r_offset'] if rel_info_type == arm.R_ARM_ABS32 and rel_addr == init_array_offset: sym = dynsym.get_symbol(rel['r_info_sym']) sym_value = sym['st_value'] init_array.append(load_base + sym_value) # print ("find init array for :%s %x" % (filename, sym_value)) break # # init_array_offset += 4 # Resolve all symbols. symbols_resolved = dict() for section in elf.iter_sections(): if not isinstance(section, SymbolTableSection): continue itersymbols = section.iter_symbols() next(itersymbols) # Skip first symbol which is always NULL. for symbol in itersymbols: symbol_address = self._elf_get_symval( elf, load_base, symbol) if symbol_address is not None: symbols_resolved[symbol.name] = SymbolResolved( symbol_address, symbol) # Relocate. for section in elf.iter_sections(): if not isinstance(section, RelocationSection): continue for rel in section.iter_relocations(): sym = dynsym.get_symbol(rel['r_info_sym']) sym_value = sym['st_value'] rel_addr = load_base + rel[ 'r_offset'] # Location where relocation should happen rel_info_type = rel['r_info_type'] #print(filename) #print("%x"%rel_addr) # Relocation table for ARM if rel_info_type == arm.R_ARM_ABS32: if sym.name in symbols_resolved: sym_addr = symbols_resolved[sym.name].address value_orig_bytes = self.emu.mu.mem_read( rel_addr, 4) value_orig = int.from_bytes(value_orig_bytes, byteorder='little') #R_ARM_ABS32 重定位方式参见 android linker源码 #*reinterpret_cast<Elf32_Addr*>(reloc) += sym_addr; value = sym_addr + value_orig # Write the new value #print(value) self.emu.mu.mem_write( rel_addr, value.to_bytes(4, byteorder='little')) # # elif rel_info_type in (arm.R_ARM_GLOB_DAT, arm.R_ARM_JUMP_SLOT, arm.R_AARCH64_GLOB_DAT, arm.R_AARCH64_JUMP_SLOT): # Resolve the symbol. #R_ARM_GLOB_DAT,R_ARM_JUMP_SLOT修复方式见linker源码 #*reinterpret_cast<Elf32_Addr*>(reloc) = sym_addr; if sym.name in symbols_resolved: value = symbols_resolved[sym.name].address # Write the new value #print(value) self.emu.mu.mem_write( rel_addr, value.to_bytes(4, byteorder='little')) # # elif rel_info_type in (arm.R_ARM_RELATIVE, arm.R_AARCH64_RELATIVE): if sym_value == 0: # Load address at which it was linked originally. value_orig_bytes = self.emu.mu.mem_read( rel_addr, 4) value_orig = int.from_bytes(value_orig_bytes, byteorder='little') # Create the new value value = load_base + value_orig #print(value) # Write the new value self.emu.mu.mem_write( rel_addr, value.to_bytes(4, byteorder='little')) else: raise NotImplementedError() else: logger.error("Unhandled relocation type %i." % rel_info_type) # # Store information about loaded module. module = Module(filename, load_base, bound_high - bound_low, symbols_resolved, init_addr, init_array) self.modules.append(module) #TODO init tls like linker ''' void __libc_init_tls(KernelArgumentBlock& args) { __libc_auxv = args.auxv; unsigned stack_top = (__get_sp() & ~(PAGE_SIZE - 1)) + PAGE_SIZE; unsigned stack_size = 128 * 1024; unsigned stack_bottom = stack_top - stack_size; static void* tls[BIONIC_TLS_SLOTS]; static pthread_internal_t thread; thread.tid = gettid(); thread.tls = tls; pthread_attr_init(&thread.attr); pthread_attr_setstack(&thread.attr, (void*) stack_bottom, stack_size); _init_thread(&thread, false); __init_tls(&thread); tls[TLS_SLOT_BIONIC_PREINIT] = &args; } ''' if do_init: ''' for r in self.emu.mu.mem_regions(): print("region begin :0x%08X end:0x%08X, prot:%d"%(r[0], r[1], r[2])) # ''' module.call_init(self.emu) # logger.info("finish load lib %s" % filename) return module
def return_parsed_section(filename, secname): with open(filename, 'rb') as f: elffile = ELFFile(f) #ll=elffile.iter_sections() #for x in ll: # print x.name section = elffile.get_section_by_name(str2bytes(secname)) if section is None: print 'ERROR! Section ' + secname + ' does not exist in the file!' #print 'Name of section is: '+bytes2str(section.name) ##assuming no relocation section for now #self._note_relocs_for_section(section) addr = section['sh_addr'] data = section.data() dataptr = 0 setStartAddress(' %s ' % _format_hex(addr, elffile, fieldsize=8)) toreturn = [] while dataptr < len(data): bytesleft = len(data) - dataptr # chunks of 16 bytes per line linebytes = 16 if bytesleft > 16 else bytesleft #not adding addresses in beginning of 4 * 4 bytes #toreturn+=' %s ' % _format_hex(addr, elffile, fieldsize=8 ) for i in range(16): if i < linebytes: toreturn.append('%2.2x' % byte2int(data[dataptr + i])) else: pass #not doing anything #toreturn+=' ' if i % 4 == 3: pass #not doing anything #toreturn+=' ' for i in range(linebytes): c = data[dataptr + i:dataptr + i + 1] if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f: pass #removing not used info #toreturn+=bytes2str(c) else: pass #removing not used info #toreturn+=(bytes2str(b'.')) #again not adding string or newline #toreturn+='\n' addr += linebytes dataptr += linebytes for x in range(len(toreturn) % 4): toreturn.append('') if toreturn == []: print 'No data to display in ' + secname + ' section' return global little little = isLittleEndian(elffile) finalreturn = arrangeData(toreturn, isLittleEndian(elffile)) setNumOfInst(len(finalreturn)) return finalreturn
def do_elf(path_to_elf, out_dir='/tmp'): """This function reads information from elf file and translates it to appropiate config structure. """ ret = {} elf = ELFFile(open(path_to_elf, 'rb')) if 'ARM' != elf.get_machine_arch(): raise Exception("Architecture not supported") ret['architecture'] = 'arm' ret['cpu_model'] = 'arm926' ret['endianness'] = 'little' if elf.little_endian else 'big' ret['entry_address'] = [elf.header.e_entry] thumb_targets = [] targets = [] for sec in elf.iter_sections(): if sec.name.startswith(b'.symtab'): log.info("[Translator] binary contains symbols! Using those instead of the single entry") ret['entry_address'] = [] # nm can run on any type of elf binary p = subprocess.Popen(['nm', path_to_elf], stdout=subprocess.PIPE) out, _ = p.communicate() for l in out.split('\n'): try: addr, t, name = l.split(' ') except: continue if (t == 't' or t == 'T') and not name.startswith('$'): targets.append(int(addr, 16)) # call readelf -s for getting the thumb bit # somehow, the $a and $t are not always generated? p = subprocess.Popen(['readelf', '-s', path_to_elf], \ stdout=subprocess.PIPE) out, _ = p.communicate() for l in out.split('\n'): try: _, addr, _, t, _, _, _, name = l.split() except: #print("QQ: %s: %d" % (l, len(l.split(' ')))) #print(str(l.split(None))) continue if t != 'FUNC': continue jumpPC = int(addr, 16) if jumpPC & 1 == 0x1: thumb_targets.append(jumpPC & -2) segments = [] cnt = 0 mapped_targets = [] mapped_thumb_targets = [] for i in range(elf.num_segments()): seg = elf.get_segment(i) if seg.header.p_type != 'PT_LOAD': continue #print(dir(seg.header)) assert(seg.header.p_paddr == seg.header.p_vaddr) padding = seg.header.p_paddr % 4096 #assert(seg.header.p_paddr % 4096 == 0) new_section = {} s = max(seg.header.p_memsz, seg.header.p_filesz) # round up to 4k if s % 4096 != 0: s = 4096*int((s+4096)/4096) s += padding # round up to 4k if s % 4096 != 0: s = 4096*int((s+4096)/4096) # build segment info segm_name = 'seg-'+str(cnt)+'.bin' segm_file = os.path.join(out_dir, segm_name) offset = seg.header.p_offset-padding assert(offset >= 0) segm_desc = {} segm_desc['file'] = segm_file segm_desc['size'] = s segm_desc['address'] = seg.header.p_paddr - padding segm_desc['name'] = segm_name # save chunk save_chunk(segm_file, path_to_elf, offset, s) cnt += 1 segments.append(segm_desc) log.debug("[Translator] loaded %s%08x@%08x" % \ (seg.header.p_type, seg.header.p_paddr, \ seg.header.p_offset)) def inside_segment(e): return e >= segm_desc['address'] and \ e < (segm_desc['address'] + \ segm_desc['size']) # filter data map(mapped_targets.append, filter(inside_segment, \ targets)) map(mapped_thumb_targets.append, filter(inside_segment, \ thumb_targets)) ret['segments'] = segments # unique mapped_thumb_targets = sorted(list(set(mapped_thumb_targets))) mapped_targets = sorted(list(set(mapped_targets))) log.debug("[Translator] elf: %d entries and %d thumb bits" % \ (len(mapped_targets), len(mapped_thumb_targets))) if len(mapped_thumb_targets) > 0: fout = os.path.join(out_dir, "is-thumb-initial.json") with open(fout, 'wt') as f: f.write(json.dumps(mapped_thumb_targets)) else: fout = None map(ret['entry_address'].append, mapped_targets) return ret, fout
def process_file(filename, modulename, language, outputname): functions = [] with open(filename, "rb") as f: elffile = ELFFile(f) if not elffile.has_dwarf_info(): print("File has no DWARF info. Compile with -g.") sys.exit(1) dwarfinfo = elffile.get_dwarf_info() funcs_addr = 0 funcs_size = 0 vars_addr = 0 vars_size = 0 for section in elffile.iter_sections(): if section.name == ".lf.funcs": funcs_addr = section["sh_addr"] funcs_size = section["sh_size"] if section.name == ".lf.vars": vars_addr = section["sh_addr"] vars_size = section["sh_size"] if funcs_addr == 0: outc = open(outputname, "w") sys.exit(0) # This iterates through all CUs, even the ones without .lf.funcs section i = 0 for cu in dwarfinfo.iter_CUs(): top = cu.get_top_DIE() for child in top.iter_children(): if child.tag == "DW_TAG_subprogram" and "DW_AT_low_pc" in child.attributes: address = child.attributes["DW_AT_low_pc"].value if address in range(funcs_addr, funcs_addr + funcs_size): name = child.attributes["DW_AT_name"].value # print "n: " + name if "DW_AT_type" in child.attributes.keys(): tdie = get_die_at_offset( cu, child.attributes["DW_AT_type"].value) if tdie == None: print( "Failed to get die at offset: " + hex(child.attributes["DW_AT_type"].value)) return 1 while tdie.tag == "DW_TAG_typedef": tdie = get_die_at_offset( cu, tdie.attributes["DW_AT_type"].value) if "DW_AT_name" in tdie.attributes: type = tdie.attributes["DW_AT_name"].value ret = tdie.attributes["DW_AT_byte_size"].value elif "DW_AT_byte_size" in tdie.attributes: type = "void *" ret = tdie.attributes["DW_AT_byte_size"].value else: type = "void" ret = 0x2 params = get_parameters_from_die(cu, child) functions.append(Function(type, name, ret, params)) if language.lower() == "c": generate_c(modulename, outputname, functions) elif language.lower() == "python": generate_py(modulename, outputname, functions) else: print("Invalid language: " + language) sys.exit(1)
def __main(): # Get the LD parameters if len(sys.argv) < 7: raise ValueError( 'Command line:\n\tappname hexpath stacklen ldexe ldscript ldoself ldmap ldobjelf ldobj*\nGiven:\n\t' + str(sys.argv)) appname = sys.argv[1].strip()[:MAX_NAME_LENGTH] hexpath = sys.argv[2] stacklen = int(sys.argv[3][3:], 16) if sys.argv[3].strip().lower()[0:2] == '0x' else int( sys.argv[3]) ldexe = sys.argv[4] ldscript = sys.argv[5] ldoself = sys.argv[6] ldmap = sys.argv[7] ldobjelf = sys.argv[8] ldobjs = sys.argv[9:] # Get the length of each section pgmlen, bsslen, datalen = 0, 0, 0 with open(ldobjelf, 'rb') as f: elffile = ELFFile(f) for section in elffile.iter_sections(): if section.name == '.text': pgmlen = int(section['sh_size']) elif section.name == '.bss': bsslen = int(section['sh_size']) elif section.name == '.data': datalen = int(section['sh_size']) # Open the serial port ser = serial.Serial(SERIAL_PORT, SERIAL_BAUD) sio = io.TextIOWrapper(buffer=io.BufferedRWPair(ser, ser, 1), newline='\r\n', line_buffering=True, encoding='ascii') try: sio.write(unicode('\n\n')) time.sleep(0.1) while ser.inWaiting(): ser.flushInput() time.sleep(0.1) sio.write(unicode('app_install\n')) print '"' + sio.readline() + '"' # Send the section sizes and app name sizestr = '%0.8X,%0.8X,%0.8X,%0.8X,%0.2X%s' % ( pgmlen, bsslen, datalen, stacklen, len(appname), appname) print sizestr sio.write(unicode(sizestr + '\n')) # Receive the allocated addresses addrs = sio.readline().split(',') pgmadr = int(addrs[0].strip(), 16) bssadr = int(addrs[1].strip(), 16) dataadr = int(addrs[2].strip(), 16) datapgmadr = int(addrs[3].strip(), 16) # Link to the OS symbols sectopt = [ '--section-start', '.text=0x%0.8X' % pgmadr, '--section-start', '.bss=0x%0.8X' % bssadr, '--section-start', '.data=0x%0.8X' % dataadr ] args = [ ldexe, '--script', ldscript, '--just-symbols', ldoself, '-Map', ldmap, '-o', ldobjelf ] + sectopt + ldobjs print args subprocess.call(args) subprocess.call(['make']) with open(ldobjelf, 'rb') as f: elffile = ELFFile(f) threadadr = __find_address(elffile, ENTRY_THREAD_NAME) print 'app_thread = 0x%0.8X' % threadadr # Read the generated IHEX file and remove unused records with open(hexpath, 'r') as f: hexdata = f.readlines() hexdata = [ line.strip() for line in hexdata if not line[7:9] in ['05', '03'] and len(line) >= 11 ] hexdata = [line for line in hexdata if len(line) > 0] if len([None for line in hexdata if line[7:9] == '01' ]) != 1 and hexdata[-1][7:9] != '01': raise RuntimeError( 'The IHEX must contain a single EOF record, as last record') # Insert the entry point thread record chks = threadadr & 0xFFFFFFFF chks = (chks >> 24) + (chks >> 16) + (chks >> 8) + (chks & 0xFF) chks = 0x100 - (0x04 + 0x00 + 0x00 + 0x05 + chks) & 0xFF hexdata[0:0] = [':04000005%0.8X%0.2X' % (threadadr, chks)] # Send IHEX records for i in range(len(hexdata)): line = sio.readline().strip() print line if line != ',': raise RuntimeError( 'Error while loading line %d ("%s", received "%s")' % (i, hexdata[i], line)) sio.write(unicode(hexdata[i] + '\n')) print hexdata[i] line = sio.readline().strip() print line if line != '$': raise RuntimeError( 'Error while terminating programming (received "%s")' % line) ser.close() except: ser.close() raise
if __name__ == "__main__": # Validate command line args try: libcoldstart_path = sys.argv[1] except IndexError: libcoldstart_path = os.path.join(os.getcwd(), "libcoldstart.so") try: new_path = sys.argv[2] except IndexError: new_path = os.path.join(os.path.dirname(libcoldstart_path), "libcoldstart-patched.so") f = open(libcoldstart_path, "rb") # Validate input file elf = ELFFile(f) arch = elf.get_machine_arch() if arch != "ARM" and arch != "x86": print( "[!] ERROR: Unknown architecture in libcoldstart.so, this script only supports ARM and x86!" ) shutil.copyfile(libcoldstart_path, new_path) patched = False patcher13 = TLS13Patcher(f, elf, arch, new_path) if patcher13.find_error_strings(): print("[+] Patching TLS1.3 stack!") patcher13.patch() patched = True else:
def ParseELF(path, root='/', prefix='', ldpaths={ 'conf': [], 'env': [], 'interp': [] }, display=None, debug=False, _first=True, _all_libs={}): """Parse the ELF dependency tree of the specified file Args: path: The ELF to scan root: The root tree to prepend to paths; this applies to interp and rpaths only as |path| and |ldpaths| are expected to be prefixed already prefix: The path under |root| to search ldpaths: dict containing library paths to search; should have the keys: conf, env, interp display: The path to show rather than |path| debug: Enable debug output _first: Recursive use only; is this the first ELF ? _all_libs: Recursive use only; dict of all libs we've seen Returns: a dict containing information about all the ELFs; e.g. { 'interp': '/lib64/ld-linux.so.2', 'needed': ['libc.so.6', 'libcurl.so.4',], 'libs': { 'libc.so.6': { 'path': '/lib64/libc.so.6', 'needed': [], }, 'libcurl.so.4': { 'path': '/usr/lib64/libcurl.so.4', 'needed': ['libc.so.6', 'librt.so.1',], }, }, } """ if _first: _all_libs = {} ldpaths = ldpaths.copy() ret = { 'interp': None, 'path': path if display is None else display, 'realpath': path, 'needed': [], 'rpath': [], 'runpath': [], 'libs': _all_libs, } dbg(debug, 'ParseELF(%s)' % path) with open(path, 'rb') as f: elf = ELFFile(f) # If this is the first ELF, extract the interpreter. if _first: for segment in elf.iter_segments(): if segment.header.p_type != 'PT_INTERP': continue interp = bstr(segment.get_interp_name()) dbg(debug, ' interp =', interp) ret['interp'] = normpath(root + interp) real_interp = readlink(ret['interp'], root, prefixed=True) ret['libs'][os.path.basename(interp)] = { 'path': ret['interp'], 'realpath': real_interp, 'needed': [], } # XXX: Could read it and scan for /lib paths. # If the interp is a symlink, lets follow it on the assumption that it # is in this path purely for ABI reasons, and the distro is using a # different (probably more correct) path. This can come up in some # multilib situations like s390x where /lib64/ contains all the native # libraries, but /lib/ld64.so.1 is the interp hardcoded in gcc, so the # ld64.so.1 is really a symlink to ../lib64/ld64.so.1. In the multiarch # setup, it'll be /lib/ld64.so.1 -> /lib/s390x-linux-gnu/ld64.so.1. # That is why we use |real_interp| here instead of |interp|. ldpaths['interp'] = [ os.path.dirname(real_interp), normpath(root + prefix + '/usr/' + os.path.dirname(real_interp)[len(root) + len(prefix):]), ] dbg(debug, ' ldpaths[interp] =', ldpaths['interp']) break # Parse the ELF's dynamic tags. libs = [] rpaths = [] runpaths = [] for segment in elf.iter_segments(): if segment.header.p_type != 'PT_DYNAMIC': continue for t in segment.iter_tags(): if t.entry.d_tag == 'DT_RPATH': rpaths = ParseLdPaths(bstr(t.rpath), root=root, path=path) elif t.entry.d_tag == 'DT_RUNPATH': runpaths = ParseLdPaths(bstr(t.runpath), root=root, path=path) elif t.entry.d_tag == 'DT_NEEDED': libs.append(bstr(t.needed)) if runpaths: # If both RPATH and RUNPATH are set, only the latter is used. rpaths = [] # XXX: We assume there is only one PT_DYNAMIC. This is # probably fine since the runtime ldso does the same. break if _first: # Propagate the rpaths used by the main ELF since those will be # used at runtime to locate things. ldpaths['rpath'] = rpaths ldpaths['runpath'] = runpaths dbg(debug, ' ldpaths[rpath] =', rpaths) dbg(debug, ' ldpaths[runpath] =', runpaths) ret['rpath'] = rpaths ret['runpath'] = runpaths ret['needed'] = libs # Search for the libs this ELF uses. all_ldpaths = None for lib in libs: if lib in _all_libs: continue if all_ldpaths is None: all_ldpaths = rpaths + ldpaths['rpath'] + ldpaths[ 'env'] + runpaths + ldpaths['runpath'] + ldpaths[ 'conf'] + ldpaths['interp'] realpath, fullpath = FindLib(elf, lib, all_ldpaths, root, debug=debug) _all_libs[lib] = { 'realpath': realpath, 'path': fullpath, 'needed': [], } if fullpath: try: lret = ParseELF(realpath, root, prefix, ldpaths, display=fullpath, debug=debug, _first=False, _all_libs=_all_libs) except exceptions.ELFError as e: warn('%s: %s' % (realpath, e)) _all_libs[lib]['needed'] = lret['needed'] del elf return ret
def main(): parse_args() with open(args.kernel, "rb") as fp: kernel = ELFFile(fp) syms = get_symbols(kernel) if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: max_irq_per = syms["CONFIG_MAX_IRQ_PER_AGGREGATOR"] if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms: num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"] irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"] list_2nd_lvl_offsets = [ syms['CONFIG_2ND_LVL_INTR_{}_OFFSET'.format(str(i).zfill(2))] for i in range(num_aggregators) ] debug('2nd level offsets: {}'.format(list_2nd_lvl_offsets)) if "CONFIG_3RD_LEVEL_INTERRUPTS" in syms: num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"] irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"] list_3rd_lvl_offsets = [ syms['CONFIG_3RD_LVL_INTR_{}_OFFSET'.format( str(i).zfill(2))] for i in range(num_aggregators) ] debug('3rd level offsets: {}'.format(list_3rd_lvl_offsets)) intlist = read_intlist(args.intlist, syms) nvec = intlist["num_vectors"] offset = intlist["offset"] if nvec > pow(2, 15): raise ValueError('nvec is too large, check endianness.') swt_spurious_handler = "((uintptr_t)&z_irq_spurious)" vt_spurious_handler = "z_irq_spurious" vt_irq_handler = "_isr_wrapper" debug('offset is ' + str(offset)) debug('num_vectors is ' + str(nvec)) # Set default entries in both tables if args.sw_isr_table: # All vectors just jump to the common vt_irq_handler. If some entries # are used for direct interrupts, they will be replaced later. if args.vector_table: vt = [vt_irq_handler for i in range(nvec)] else: vt = None # Default to spurious interrupt handler. Configured interrupts # will replace these entries. swt = [(0, swt_spurious_handler) for i in range(nvec)] else: if args.vector_table: vt = [vt_spurious_handler for i in range(nvec)] else: error( "one or both of -s or -V needs to be specified on command line" ) swt = None for irq, flags, func, param in intlist["interrupts"]: if flags & ISR_FLAG_DIRECT: if param != 0: error("Direct irq %d declared, but has non-NULL parameter" % irq) if not 0 <= irq - offset < len(vt): error("IRQ %d (offset=%d) exceeds the maximum of %d" % (irq - offset, offset, len(vt) - 1)) vt[irq - offset] = func else: # Regular interrupt if not swt: error("Regular Interrupt %d declared with parameter 0x%x " "but no SW ISR_TABLE in use" % (irq, param)) if not "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: table_index = irq - offset else: # Figure out third level interrupt position debug('IRQ = ' + hex(irq)) irq3 = (irq & THIRD_LVL_INTERRUPTS) >> 16 irq2 = (irq & SECND_LVL_INTERRUPTS) >> 8 irq1 = (irq & FIRST_LVL_INTERRUPTS) if irq3: irq_parent = irq2 list_index = getindex(irq_parent, list_3rd_lvl_offsets) irq3_pos = irq3_baseoffset + max_irq_per * list_index + irq3 - 1 debug('IRQ_level = 3') debug('IRQ_Indx = ' + str(irq3)) debug('IRQ_Pos = ' + str(irq3_pos)) table_index = irq3_pos - offset # Figure out second level interrupt position elif irq2: irq_parent = irq1 list_index = getindex(irq_parent, list_2nd_lvl_offsets) irq2_pos = irq2_baseoffset + max_irq_per * list_index + irq2 - 1 debug('IRQ_level = 2') debug('IRQ_Indx = ' + str(irq2)) debug('IRQ_Pos = ' + str(irq2_pos)) table_index = irq2_pos - offset # Figure out first level interrupt position else: debug('IRQ_level = 1') debug('IRQ_Indx = ' + str(irq1)) debug('IRQ_Pos = ' + str(irq1)) table_index = irq1 - offset if not 0 <= table_index < len(swt): error("IRQ %d (offset=%d) exceeds the maximum of %d" % (table_index, offset, len(swt) - 1)) if swt[table_index] != (0, swt_spurious_handler): error( f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})" + f"\nExisting handler 0x{swt[table_index][1]:x}, new handler 0x{func:x}" + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?" ) swt[table_index] = (param, func) with open(args.output_source, "w") as fp: write_source_file(fp, vt, swt, intlist, syms)
def main(): """Main program""" global syms parse_args() with open(args.kernel, "rb") as elf_fp: kernel = ELFFile(elf_fp) syms = get_symbols(kernel) if isdef("CONFIG_X86_64"): pclass = PtablesIA32e elif isdef("CONFIG_X86_PAE"): pclass = PtablesPAE else: pclass = Ptables32bit debug("building %s" % pclass.__name__) vm_base = syms["CONFIG_KERNEL_VM_BASE"] vm_size = syms["CONFIG_KERNEL_VM_SIZE"] vm_offset = syms["CONFIG_KERNEL_VM_OFFSET"] sram_base = syms["CONFIG_SRAM_BASE_ADDRESS"] sram_size = syms["CONFIG_SRAM_SIZE"] * 1024 mapped_kernel_base = syms["z_mapped_start"] mapped_kernel_size = syms["z_mapped_size"] if isdef("CONFIG_SRAM_OFFSET"): sram_offset = syms["CONFIG_SRAM_OFFSET"] else: sram_offset = 0 # Figure out if there is any need to do virtual-to-physical # address translation virt_to_phys_offset = (sram_base + sram_offset) - (vm_base + vm_offset) if isdef("CONFIG_ARCH_MAPS_ALL_RAM"): image_base = sram_base image_size = sram_size else: image_base = mapped_kernel_base image_size = mapped_kernel_size image_base_phys = image_base + virt_to_phys_offset ptables_phys = syms["z_x86_pagetables_start"] + virt_to_phys_offset debug("Address space: 0x%x - 0x%x size 0x%x" % (vm_base, vm_base + vm_size - 1, vm_size)) debug("Zephyr image: 0x%x - 0x%x size 0x%x" % (image_base, image_base + image_size - 1, image_size)) is_perm_regions = isdef("CONFIG_SRAM_REGION_PERMISSIONS") if image_size >= vm_size: error("VM size is too small (have 0x%x need more than 0x%x)" % (vm_size, image_size)) if is_perm_regions: # Don't allow execution by default for any pages. We'll adjust this # in later calls to pt.set_region_perms() map_flags = FLAG_P | ENTRY_XD else: map_flags = FLAG_P pt = pclass(ptables_phys) # Instantiate all the paging structures for the address space pt.reserve(vm_base, vm_size) # Map the zephyr image pt.map(image_base_phys, image_base, image_size, map_flags | ENTRY_RW) if isdef("CONFIG_X86_64"): # 64-bit has a special region in the first 64K to bootstrap other CPUs # from real mode locore_base = syms["_locore_start"] locore_size = syms["_lodata_end"] - locore_base debug("Base addresses: physical 0x%x size 0x%x" % (locore_base, locore_size)) pt.map(locore_base, None, locore_size, map_flags | ENTRY_RW) if isdef("CONFIG_XIP"): # Additionally identity-map all ROM as read-only pt.map(syms["CONFIG_FLASH_BASE_ADDRESS"], None, syms["CONFIG_FLASH_SIZE"] * 1024, map_flags) # Adjust mapped region permissions if configured if is_perm_regions: # Need to accomplish the following things: # - Text regions need the XD flag cleared and RW flag removed # if not built with gdbstub support # - Rodata regions need the RW flag cleared # - User mode needs access as we currently do not separate application # text/rodata from kernel text/rodata if isdef("CONFIG_GDBSTUB"): flags = FLAG_P | ENTRY_US | ENTRY_RW pt.set_region_perms("_image_text", flags) else: flags = FLAG_P | ENTRY_US pt.set_region_perms("_image_text", flags) flags = FLAG_P | ENTRY_US | ENTRY_XD pt.set_region_perms("_image_rodata", flags) if isdef("CONFIG_COVERAGE_GCOV") and isdef("CONFIG_USERSPACE"): # If GCOV is enabled, user mode must be able to write to its # common data area pt.set_region_perms("__gcov_bss", FLAG_P | ENTRY_RW | ENTRY_US | ENTRY_XD) if isdef("CONFIG_X86_64"): # Set appropriate permissions for locore areas much like we did # with the main text/rodata regions if isdef("CONFIG_X86_KPTI"): # Set the User bit for the read-only locore/lorodata areas. # This ensures they get mapped into the User page tables if # KPTI is turned on. There is no sensitive data in them, and # they contain text/data needed to take an exception or # interrupt. flag_user = ENTRY_US else: flag_user = 0 pt.set_region_perms("_locore", FLAG_P | flag_user) pt.set_region_perms("_lorodata", FLAG_P | ENTRY_XD | flag_user) pt.write_output(args.output)
def main(input, output, format='nro'): format = format.lower() assert format in ('nro', 'nso') with open(input, 'rb') as f: elffile = ELFFile(f) elffile.iter_sections_by_type = lambda type: (x for x in elffile.iter_sections() if isinstance(x, type)) symbols = {} symbolList = [] for x in elffile.iter_sections_by_type(SymbolTableSection): for i, sym in enumerate(x.iter_symbols()): sectaddr = elffile.get_section(sym['st_shndx'])['sh_addr'] if isinstance(sym['st_shndx'], int) else 0 symbols[sym.name] = sectaddr + sym['st_value'] symbolList.append(sym.name) textCont, rodataCont, relaDynCont, dataCont, dynamicCont, dynstrCont, dynsymCont = [elffile.get_section_by_name(x).data() for x in ( '.text', '.rodata', '.rela.dyn', '.data', '.dynamic', '.dynstr', '.dynsym')] csec = dict(text=textCont, rodata=rodataCont, relaDyn=relaDynCont, data=dataCont, dynamic=dynamicCont, dynstr=dynstrCont, dynsym=dynsymCont) def replace(tgt, offset, data): orig = csec[tgt] csec[tgt] = orig[:offset] + data + orig[offset + len(data):] for x in elffile.iter_sections_by_type(RelocationSection): tgtsect = elffile.get_section(x['sh_info']) tgt = tgtsect.name[1:] if tgt not in csec: continue for iter in x.iter_relocations(): symname = symbolList[iter['r_info_sym']] if not symname.startswith('NORELOC_'): continue reloc_type = iter['r_info_type'] if reloc_type == R_AARCH64_PREL32: replace(tgt, iter['r_offset'], struct.pack('<i', symbols[symname] + iter['r_addend'] - ( tgtsect['sh_addr'] + iter['r_offset']))) elif reloc_type == R_AARCH64_ABS32: replace(tgt, iter['r_offset'], struct.pack('<I', symbols[symname] + iter['r_addend'])) else: print('Unknown relocation type!', reloc_type) assert False text, rodata, data = csec['text'], csec['rodata'], csec['data'] if len(rodata) & 0x7: rodata += '\0'.encode() * (0x8 - (len(rodata) & 0x7)) rodata += csec['relaDyn'] if len(data) & 0x7: data += '\0'.encode() * (0x8 - (len(data) & 0x7)) if len(text) & 0xFFF: text += '\0'.encode() * (0x1000 - (len(text) & 0xFFF)) if len(rodata) & 0xFFF: rodata += '\0'.encode() * (0x1000 - (len(rodata) & 0xFFF)) data += csec['dynamic'] if len(data) & 0xFFF: data += '\0'.encode() * (0x1000 - (len(data) & 0xFFF)) data += csec['dynsym'] if len(data) & 0x7: data += '\0'.encode() * (0x8 - (len(data) & 0x7)) data += csec['dynstr'] if len(data) & 0xFFF: data += '\0'.encode() * (0x1000 - (len(data) & 0xFFF)) bssSize = elffile.get_section_by_name('.bss')['sh_size'] if bssSize & 0xFFF: bssSize += 0x1000 - (bssSize & 0xFFF) if format == 'nro': # text = text[0x80:] with open(output, 'wb') as fp: fp.write(text[:0x4]) # first branch instruction fp.write(struct.pack('<III', len(text) + len(rodata) + 8, 0, 0)) fp.write('NRO0'.encode()) fp.write(struct.pack('<III', 0, len(text) + len(rodata) + len(data), 0)) fp.write(struct.pack('<II', 0, len(text))) # exec segment fp.write(struct.pack('<II', len(text), len(rodata))) # read only segment fp.write(struct.pack('<II', len(text) + len(rodata), len(data))) # rw segment fp.write(struct.pack('<II', bssSize, 0)) fp.write('\0'.encode() * 0x40) fp.write(text[0x80:]) fp.write(rodata) fp.write(data) else: with open(output, 'wb') as fp: ctext, crodata, cdata = [lz4.block.compress(x, store_size=False) for x in (text, rodata, data)] fp.write('NSO0'.encode()) fp.write('\0'.encode() * 0xC) off = 0x101 fp.write(struct.pack('<IIII', off, 0, len(text), 0)) off += len(ctext) fp.write(struct.pack('<IIII', off, len(text), len(rodata), 0)) off += len(crodata) fp.write(struct.pack('<IIII', off, len(text) + len(rodata), len(data), symbols['NORELOC_BSS_END_'] - symbols['NORELOC_BSS_START_'])) fp.write('\0'.encode() * 0x20) fp.write(struct.pack('<IIII', len(ctext), len(crodata), len(cdata), 0)) fp.write('\0'.encode() * 0x91) fp.write(ctext) fp.write(crodata) fp.write(cdata)
def parse_elf(file, elf, starting_function, start_addr, size, section_name, ret, caller, call_dic): i = 0 current_func = Function(starting_function, ret, file) ret.add_function(current_func) section = elf.get_section_by_name(section_name) sh_addr = section['sh_addr'] sh_size = section['sh_size'] if (size == 0): # sometime the size is 0 size = sh_size offset = start_addr - sh_addr stop_offset = offset + size code = section.data()[offset:stop_offset] opcode = "" for b in code: opcode += "%02x" % b address = start_addr callers = set() caller = list() while (i < len(opcode)): op_list = opcode.split() abs_instr = AbstractInstruction(current_func) instr_string, length, arguments, clock, register, index, immediate, indirect, dst_register, dst_index = abs_instr.parse( op_list[0][i:]) if (instr_string == "" ): # --------- this will be changed later (sancus instruction) return ret instr = InstructionFactory.get_instruction(instr_string, function=current_func) instr.get_info(length, address, arguments, clock, op_list[0][i:], register, index, immediate, indirect, dst_register, dst_index, file) current_func.add_instruction(instr) if (instr_string == 'call' and op_list[0][i + 1] == '0'): hex_addr = op_list[0][i + 6] + op_list[0][i + 7] + op_list[0][ i + 4] + op_list[0][i + 5] call_target = int(hex_addr, 16) elf = ELFFile(open(file, 'rb')) sym_table_name, sym_table_size, section_name = SyntaxConverter.find_symbol_by_addr( elf, call_target) callee_function_name = sym_table_name callee_function_size = sym_table_size # Check for recursionn ------------------------- callers.clear() if caller is not None: caller.clear() if (call_target == start_addr): return ret else: count = 0 call_dic.setdefault(call_target, []).append(start_addr) callers = {start_addr} tmp = call_dic.get(start_addr) if tmp is not None: caller.extend(tmp) while len(caller) > 0: first_caller = caller.pop(0) if (len(callers) == count): return ret if (call_target in callers): return ret count = len(callers) callers.add(first_caller) temp = call_dic.get(first_caller) if temp is not None: caller.extend(temp) SyntaxConverter.parse_elf(file, elf, callee_function_name, call_target, callee_function_size, section_name, ret, current_func, call_dic) i = i + (length * 4) address = address + (length * 2) return ret
def process_file(filename): # print('Processing file:', filename) debug_paths = [] with open(filename, 'rb') as f: elffile = ELFFile(f) if not elffile.has_dwarf_info(): for section in elffile.iter_sections(): name = bytes2str(section.name) # print(name) # first try to find ".note.gnu.build-id" in ELF itself # uint32 name_size; /* size of the name */ # uint32 hash_size; /* size of the hash */ # uint32 identifier; /* NT_GNU_BUILD_ID == 0x3 */ # char name[name_size]; /* the name "GNU" */ # char hash[hash_size]; /* the hash */ # # objdump -s -j .note.gnu.build-id /usr/bin/openssl if name == ".note.gnu.build-id": data = section.data() hash = data[16:] value = binascii.hexlify(hash).decode("ascii") # print(value) # a value of "0834ce567a2d57deed6706e28fa29225cf043e16" # implies that we will have a path which looks like, # /usr/lib/debug/.build-id/08/34ce5...25cf043e16.debug path = os.path.join(value[0:2], value[2:] + ".debug") # print(path) debug_paths.append(path) # A filename, with any leading directory components removed, # followed by a zero byte, zero to three bytes of padding, as # needed to reach the next four-byte boundary within the # section, and a four-byte CRC checksum, stored in the same # endianness used for the executable file itself. # # objdump -s -j .gnu_debuglink /usr/bin/openssl if name == ".gnu_debuglink": data = section.data() fdata = data[0:data.find(b"\x00")] debug_paths.append(fdata.decode("utf-8")) else: # this file itself has the DWARF information, must be my lucky day! get_producer(filename) # get_dwarf_info returns a DWARFInfo context object, which is the # starting point for all DWARF-based processing in pyelftools. for path in debug_paths: # So, for example, suppose you ask gdb to debug /usr/bin/ls, which # has a debug link that specifies the file ls.debug, and a build ID # whose value in hex is abcdef1234. If the list of the global debug # directories includes /usr/lib/debug, then gdb will look for the # following debug information files, in the indicated order: # # /usr/lib/debug/.build-id/ab/cdef1234.debug # /usr/bin/ls.debug # /usr/bin/.debug/ls.debug # /usr/lib/debug/usr/bin/ls.debug. rpath = os.path.join("/usr/lib/debug/.build-id", path) if os.path.isfile(rpath): producer = get_producer(rpath) # got producer, is one enough? if producer: print(producer) continue # `cwd` + "/usr/lib/debug/.build-id" is our hack ;) debug_prefixes = ["/usr/lib/debug/.build-id/", "/usr/bin/", "/usr/lib/debug/usr/bin/"] for prefix in debug_prefixes: rpath = os.path.join(prefix, path) if os.path.isfile(rpath): get_producer(rpath)
import sys from elftools.elf.elffile import ELFFile from elftools.elf.relocation import RelocationSection with open('./chall.elf', '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}')
#!/usr/bin/env python3 import capstone as cs from elftools.elf.elffile import ELFFile import diff_settings from pathlib import Path from typing import Any, Dict, Set import utils config: Dict[str, Any] = {} diff_settings.apply(config, {}) base_elf = ELFFile((Path(__file__).parent.parent / config["baseimg"]).open("rb")) my_elf = ELFFile((Path(__file__).parent.parent / config["myimg"]).open("rb")) my_symtab = my_elf.get_section_by_name(".symtab") if not my_symtab: utils.fail(f'{config["myimg"]} has no symbol table') def get_file_offset(elf, addr: int) -> int: for seg in elf.iter_segments(): if seg.header["p_type"] != "PT_LOAD": continue if seg["p_vaddr"] <= addr < seg["p_vaddr"] + seg["p_filesz"]: return addr - seg["p_vaddr"] + seg["p_offset"] assert False def get_symbol_file_offset(elf, table, name: str) -> int: syms = table.get_symbol_by_name(name) if not syms or len(syms) != 1:
def process_file(dwarf_name, typedes_name): try: dwarf_file = open(dwarf_name, 'rb') if typedes_name: typedes_file = open(typedes_name, 'w') else: typedes_file = sys.stdout except IOError: print("Error: can't open file.") elffile = ELFFile(dwarf_file) if not elffile.has_dwarf_info(): print('Error: File has no DWARF info') return 1 # get_dwarf_info returns a DWARFInfo context object, which is the # starting point for all DWARF-based processing in pyelftools. dwarfinfo = elffile.get_dwarf_info() for CU in dwarfinfo.iter_CUs(): # DWARFInfo allows to iterate over the compile units contained in # the .debug_info section. CU is a CompileUnit object, with some # computed attributes (such as its offset in the section) and # a header which conforms to the DWARF standard. The access to # header elements is, as usual, via item-lookup. # print('// Found a compile unit at offset %s, length %s' % ( # CU.cu_offset, CU['unit_length'])) # Start with the top DIE, the root for this CU's DIE tree top_DIE = CU.get_top_DIE() # print('// Top DIE with tag=%s' % top_DIE.tag) # Each DIE holds an OrderedDict of attributes, mapping names to # values. Values are represented by AttributeValue objects in # elftools/dwarf/die.py # We're interested in the DW_AT_name attribute. Note that its value # is usually a string taken from the .debug_string section. This # is done transparently by the library, and such a value will be # simply given as a string. name_attr = top_DIE.attributes['DW_AT_name'] # Only C supported! C++ is more complex and currently not supported if die_get_attr_val(top_DIE, 'DW_AT_language') not in [DW_LANG_C, DW_LANG_C89]: continue typedes_file.write('file %s\n' % bytes2str(name_attr.value)) # if name_attr != 'sbo/com_so/elf.c': # continue die_build_refs(top_DIE, {}) # die_order_children(top_DIE) # Display DIEs recursively starting with top_DIE die_info_rec(dwarfinfo, typedes_file, top_DIE, False, print_pointers=False) die_del_dict_rec(top_DIE) return 0
def __init__(self, filename): self.filename = filename with open(self.filename, 'rb') as f: self.elffile = ELFFile(f)
def config_from_elf(self, path): """Load all the necessary information about the program parsing the ELF headers. Furthermore, check some pre-requisites for the exploit to be successful.""" executable_file = open(path, "r") elf = ELFFile(executable_file) get_section = lambda name: first_or_none( filter(lambda section: section.name == name, elf.iter_sections())) get_section_address = lambda section: None if (get_section( section) is None) else get_section(section).header.sh_addr # Checks if elf.header.e_type == ENUM_E_TYPE["ET_EXEC"]: raise Exception("Only non-PIE executables are supported") # Binary type self.arch = elf.header.e_machine self.little = elf.little_endian self.pointer_size = elf.elfclass / 8 self.pointer_format = ("0x%." + str(self.pointer_size * 2) + "x") self.structs = elftools.elf.structs.ELFStructs(self.little, self.pointer_size * 8) # Useful sections self.sections = { section.name: (section.header.sh_addr, section.header.sh_addr + section.header.sh_size) for section in elf.iter_sections() } self.plt = get_section_address(".plt") self.got = get_section_address(".got") self.gotplt = get_section_address(".got.plt") # Dynamic section dynamic_section = get_section(".dynamic") self.writable_dynamic = dynamic_section.header.sh_flags & SH_FLAGS.SHF_WRITE self.dynamic = dynamic_section.header.sh_addr dynamic_entries = [ self.structs.Elf_Dyn.parse(dynamic_entry) for dynamic_entry in chunks(dynamic_section.data(), self.structs.Elf_Dyn.sizeof()) ] # Dynamic symbols # TODO: we're relying on section names here symbol_table = elf.get_section_by_name(".dynsym") has_name = lambda name: lambda symbol: symbol.name == name attribute_or_default = lambda default, attribute, x: getattr( x, attribute) if x is not None else default memcpy_symbol = first_or_none( filter(has_name("memcpy"), symbol_table.iter_symbols())) self.memcpy_plt = 0 if memcpy_symbol is None else memcpy_symbol.entry.st_value # We try not to rely on section names get_dynamic = lambda name: first_or_none( map(lambda entry: entry.d_val, filter(lambda entry: entry.d_tag == name, dynamic_entries))) get_dynamic_index = lambda name: filter( lambda entry: entry[1].d_tag == name, enumerate(dynamic_entries))[ 0][0] self.dynstr = get_dynamic("DT_STRTAB") self.dynsym = get_dynamic("DT_SYMTAB") self.versym = get_dynamic("DT_VERSYM") self.verneed = get_dynamic("DT_VERNEED") self.relplt = get_dynamic("DT_JMPREL") self.addend = get_dynamic("DT_RELA") is not None self.dt_debug = self.dynamic + get_dynamic_index( "DT_DEBUG") * self.structs.Elf_Dyn.sizeof() + self.pointer_size self.full_relro = (get_dynamic("DT_FLAGS") is not None) and \ ((get_dynamic("DT_FLAGS") & DF_BIND_NOW) != 0) self.full_relro = self.full_relro or ((get_dynamic("DT_FLAGS_1") is not None) and \ ((get_dynamic("DT_FLAGS_1") & DF_1_NOW) != 0)) # Choose between Elf_Rel and Elf_Rela depending on the architecture self.rel_struct = self.structs.Elf_Rela if self.addend else self.structs.Elf_Rel # Looks like 64-bit and 32-bit have different alignment for the call to _dl_fixup self.reloc_alignment = 1 if self.pointer_size == 4 else self.rel_struct.sizeof( ) self.reloc_index_multiplier = self.rel_struct.sizeof( ) if self.pointer_size == 4 else 1 # # Find candidate writeable areas # # Collect PT_LOAD segments (what gets mapped) loaded_segments = filter( lambda segment: segment.header.p_type == "PT_LOAD", elf.iter_segments()) # Collect the segments which are writeable writeable_segments = filter( lambda segment: segment.header.p_flags & P_FLAGS.PF_W, loaded_segments) # Get their memory ranges (start, end) writeable_ranges = RangeSet.mutual_union(*map( lambda segment: (segment.header.p_vaddr, segment.header.p_vaddr + segment.header.p_memsz), writeable_segments)) # List of sections we don't want to write to dont_overwrite_sections = filter_none([ self.dynstr, self.dynsym, self.versym, self.relplt, self.dynamic, self.got, self.gotplt ]) # Memory ranges of the sections we don't want to write to dont_overwrite_ranges = RangeSet.mutual_union(*[ self.sections[self.section_from_address(start)] for start in dont_overwrite_sections ]) # Handle RELRO segment, we don't want to write there relro_segment = first_or_none( filter(lambda segment: segment.header.p_type == "PT_GNU_RELRO", elf.iter_segments())) if relro_segment is not None: dont_overwrite_ranges = dont_overwrite_ranges | RangeSet( relro_segment.header.p_vaddr, relro_segment.header.p_vaddr + relro_segment.header.p_memsz) # Compute the set of candidate memory ranges self.writeable_ranges = writeable_ranges - dont_overwrite_ranges # Save the index of the DT_FINI entry fini = filter(lambda (i, entry): entry.d_tag == "DT_FINI", enumerate(dynamic_entries)) if len(fini) > 0: self.fini = self.dynamic + self.structs.Elf_Dyn.sizeof( ) * fini[0][0] # Gadgets if self.gadgets.has_key(self.arch): executable_segments = filter( lambda segment: segment.header.p_flags & P_FLAGS.PF_X, elf.iter_segments()) for name, (info, gadget) in self.gadgets[self.arch].iteritems(): locations = find_all_strings(executable_segments, hex_bytes(gadget)) locations = map(self.ptr2str, locations) location = first_or_none( filter( lambda address: not reduce( lambda accumulate, badchar: badchar in address or accumulate, self.badchars, False), locations)) if location is None: self.gadgets[self.arch][name] = None else: self.gadgets[self.arch][name] = (info, gadget, location) # Find all '\x00\x00' in non-writeable segments self.non_writeable_segments = filter( lambda segment: not (segment.header.p_flags & P_FLAGS.PF_W), loaded_segments) self.zero_or_one_addresses = find_all_strings(self.non_writeable_segments, "\x00\x00") + \ find_all_strings(self.non_writeable_segments, "\x01\x00" if self.little else "\x00\x01") self.filler = self.ptr2str( reduce(lambda x, y: (x << 32) | 0xdeadb00b, xrange(1 + (self.pointer_size % 4)), 0)) self.relocation_type = relocation_types[self.arch] # # Find the reloc pointing to the symbol whose name is the earliest in .dynstr # relplt_section = elf.get_section_by_name( self.section_from_address(self.relplt)) dynsym_section = elf.get_section_by_name( self.section_from_address(self.dynsym)) if not (isinstance(relplt_section, RelocationSection) and \ isinstance(dynsym_section, SymbolTableSection)): raise Exception("Unexpect type for dynamic sections: " + str(relplt_section) + " " + str(dynsym_section)) # Grab .got.plt relocs symbol indexes symbol_indexes = [ reloc.entry.r_info_sym if reloc.entry.r_info_type == self.relocation_type else None for reloc in relplt_section.iter_relocations() ] # Get offsets in .dynstr names_offsets = [ dynsym_section.get_symbol(index).entry.st_name if index is not None else None for index in symbol_indexes ] # Filter out unamed offsets names_offsets = [ offset if offset > 0 else None for offset in names_offsets ] # Get the minimum value self.min_reloc_index, self.min_string_offset = min( enumerate(names_offsets), key=operator.itemgetter(1)) self.min_symbol_index = symbol_indexes[self.min_reloc_index] log(self.dump())
def __init__(self, file, verbose=False): self.elffile = ELFFile(file) self._versioninfo = None self.data = {} self.data["arch"] = self.elffile.elfclass self._verbose = verbose
def open(self): self.fd = open(self.elffile, "rb") self.elf = ELFFile(self.fd)
def loadElf(self, elfStream, parameters="", stackSize=100000, maxResponseSize=4096, continuation=None, signature=None): if parameters == None: parameters = "" elffile = ELFFile(elfStream) # Locate signature if not passed if signature == None: for section in elffile.iter_sections(): if section.name == '.ledger': signature = section.data()[0:ord(section.data()[1]) + 2] break if signature == None: raise Exception("Missing code signature") # Allocate session allocateSize = stackSize for segment in elffile.iter_segments(): if segment['p_type'] == 'PT_LOAD': allocateSize = allocateSize + segment['p_memsz'] cmd = struct.pack(">H", self.CMD_CODE_INIT) cmd = cmd + struct.pack(">I", allocateSize) response = self.link.exchange(cmd) if response[0] != self.STATUS_CODE_EXEC_OK: raise Exception("Unexpected status on CODE_INIT %.2x" % response[0]) # Load each component for segment in elffile.iter_segments(): if segment['p_type'] == 'PT_LOAD': flags = 0 if ((segment['p_flags'] & P_FLAGS.PF_W) == 0): flags = flags | self.MSG_LOAD_SECTION_FLAG_READ_ONLY cmd = struct.pack(">H", self.CMD_CODE_LOAD_SECTION) cmd = cmd + chr(flags) cmd = cmd + struct.pack(">I", segment['p_vaddr']) cmd = cmd + struct.pack( ">I", segment['p_vaddr'] + segment['p_memsz']) cmd = cmd + struct.pack(">I", segment['p_filesz']) cmd = cmd + segment.data() response = self.link.exchange(cmd) if response[0] != self.STATUS_CODE_EXEC_OK: raise Exception( "Unexpected status on CODE_LOAD_SECTION %.2x" % response[0]) # Run or resume if continuation == None: cmd = struct.pack(">H", self.CMD_CODE_RUN) cmd = cmd + struct.pack(">I", elffile.header['e_entry']) cmd = cmd + struct.pack(">I", stackSize) cmd = cmd + struct.pack(">I", 0) cmd = cmd + struct.pack(">I", len(parameters)) cmd = cmd + struct.pack(">I", len(signature)) cmd = cmd + str(parameters) cmd = cmd + signature else: cmd = struct.pack(">H", self.CMD_CODE_RESUME) cmd = cmd + struct.pack(">I", continuation['slot']) cmd = cmd + struct.pack(">I", len(continuation['blob'])) cmd = cmd + struct.pack(">I", 0) cmd = cmd + struct.pack(">I", len(parameters)) cmd = cmd + str(continuation['blob']) cmd = cmd + str(parameters) response = self.link.exchange(cmd, maxResponseSize) result = {} if response[0] == self.STATUS_CODE_EXEC_OK: result['suspended'] = False result['response'] = response[1:] elif response[0] == self.STATUS_CODE_EXEC_SUSPENDED: result['suspended'] = True slot, blobSize, appDataSize = struct.unpack( ">III", str(response[1:1 + 12])) result['slot'] = slot result['blob'] = response[1 + 12:1 + 12 + blobSize] result['response'] = response[1 + 12 + blobSize:1 + 12 + blobSize + appDataSize] else: raise Exception("Application error reported %.2x" % response[0]) return result
def ParseElfFile(elfFileArg): FileName = elfFileArg with open(FileName, 'rb') as f: e = ELFFile(f) TextPhysicalAddress = 0 APPSize = 0 EntryPoint = e['e_entry'] DataPhysicalAddress = 0 DataSize = 0 print('Entry point = ' + hex(e['e_entry'])) for i in range(int(e.num_segments())): segment = e.get_segment(i) physicalAddress = hex(segment.header['p_paddr']) offest = hex(segment.header['p_offset']) size = hex(segment.header['p_filesz']) APPSize += segment.header['p_filesz'] name = '' if i == 0: name = '.text' TextPhysicalAddress = segment.header['p_paddr'] elif i == 1: name = '.data' DataPhysicalAddress = segment.header['p_paddr'] DataSize = segment.header['p_filesz'] elif i == 2: name = '.bss' print('Section name = ' + name) print('Section physical address = ' + physicalAddress) print('Section offest = ' + offest) print('Section size = ' + size + '\n') # the text section #textSec = e.get_section_by_name('.text') textSec = e.get_segment(0) textval = textSec.data() #data section datasec = e.get_segment(1) dataval = datasec.data() file = open('TEXT_FILE.txt', 'wb') file.write(textval) file.close file = open('DATA_FILE.txt', 'wb') file.write(dataval) file.close print("EntryPoint = " + hex(EntryPoint)) print("APPSize = " + hex(APPSize)) print("TextPhysicalAddress = " + hex(TextPhysicalAddress)) print("DataPhysicalAddress = " + hex(DataPhysicalAddress)) print("DataSize = " + hex(DataSize)) file = open('INFO_FILE.txt', 'wb') file.write(int_to_bytes(TextPhysicalAddress)) file.write(int_to_bytes(APPSize)) file.write(int_to_bytes(EntryPoint)) file.write(int_to_bytes(DataPhysicalAddress)) file.write(int_to_bytes(DataSize)) file.close
from elftools.elf.sections import SymbolTableSection from pandare import Panda, blocking, ffi # Single arg of arch, defaults to i386 arch = "i386" if len(argv) <= 1 else argv[1] panda = Panda(generic=arch) bin_dir = "taint" bin_name = "taint" out = [] mappings = {} # Read symbols from bin into mappings with open(path.join(bin_dir, bin_name), 'rb') as f: our_elf = ELFFile(f) for section in our_elf.iter_sections(): if not isinstance(section, SymbolTableSection): continue for symbol in section.iter_symbols(): if len(symbol.name): # Sometimes empty mappings[symbol['st_value']] = symbol.name tainted = False g_phys_addrs = [] @panda.cb_before_block_exec_invalidate_opt(procname=bin_name) def taint_it(cpu, tb): if tb.pc in mappings and mappings[tb.pc] == "apply_taint": global tainted if not tainted:
def set_elf(self, filename): self.elf_file = ELFFile(open(filename))
from elftools.elf.constants import P_FLAGS from elftools.elf.elffile import ELFFile from elftools.elf.sections import SymbolTableSection from unicorn import Uc, UC_ARCH_ARM, UC_MODE_LITTLE_ENDIAN, UC_PROT_WRITE, UC_PROT_READ, UC_PROT_EXEC, UC_HOOK_CODE, \ UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE, UC_MEM_WRITE, UC_MEM_READ from unicorn.arm_const import * from capstone import Cs, CS_ARCH_ARM, CS_MODE_THUMB, CsInsn from keystone import Ks, KS_MODE_THUMB, KS_ARCH_ARM, KS_MODE_ARM import struct import json # 加载so文件 filename = "./libnative-lib.so" fd = open(filename, 'r+b') elf = ELFFile(fd) def align(addr, size, align): fr_addr = addr // align * align to_addr = (addr + size + align - 1) // align * align return fr_addr, to_addr - fr_addr def pflags2prot(p_flags): ret = 0 if p_flags & P_FLAGS.PF_R != 0: ret |= UC_PROT_READ if p_flags & P_FLAGS.PF_W != 0: ret |= UC_PROT_WRITE if p_flags & P_FLAGS.PF_X != 0: ret |= UC_PROT_EXEC
def parseImage(self): # basic stats self._SizeBytes = os.path.getsize(self._Path) # open self._Handle = open(self._Path, 'rb') self._Elf = ELFFile(self._Handle) # executable if self._Elf['e_type'] == 'ET_EXEC': self._IsExecutable = True # header string table hdstrtbl = None cnt = 0 for sec in self._Elf.iter_sections(): if (sec['sh_type'] == 'SHT_STRTAB') and (self._Elf['e_shstrndx'] == cnt): hdstrtbl = sec break cnt += 1 if hdstrtbl is None: raise Exception("[ERROR] Could not find header string table!") # register segments for seg in self._Elf.iter_segments(): self._Segments.append(seg) # register sections for sec in self._Elf.iter_sections(): curname = hdstrtbl.get_string(sec['sh_name']) if (sec['sh_addr'] != 0): cursec = SectionInfo() cursec._Name = curname cursec._Addr = sec['sh_addr'] cursec._Size = sec['sh_size'] cursec._Obj = sec self._Sections[cursec._Addr] = cursec # special sections secnames = [] for sec in self._Elf.iter_sections(): if sec['sh_size'] > 0: secnames.append(hdstrtbl.get_string(sec['sh_name'])) if ('.text' not in secnames): raise Exception("[ERROR] No text section found!") if (('.symtab' not in secnames) and ('.dynsym' not in secnames)): raise Exception("[ERROR] No symbol table found!") if (('.strtab' not in secnames) and ('.dynstr' not in secnames)): raise Exception("[ERROR] No string table found!") usedebugtables = (('.symtab' in secnames) and ('.strtab' in secnames)) # register special sections for sec in self._Elf.iter_sections(): if sec['sh_size'] > 0: cursec = SectionInfo() cursec._Name = hdstrtbl.get_string(sec['sh_name']) cursec._Addr = sec['sh_addr'] cursec._Size = sec['sh_size'] cursec._Obj = sec if cursec._Name == '.text': self._TextSection = cursec elif cursec._Name == '.plt': self._PLTSection = cursec elif (cursec._Name == '.symtab') and usedebugtables: self._SymbolTable = cursec elif (cursec._Name == '.strtab') and usedebugtables: self._StringTable = cursec elif (cursec._Name == '.dynsym') and not usedebugtables: self._SymbolTable = cursec elif (cursec._Name == '.dynstr') and not usedebugtables: self._StringTable = cursec # sanity check if (self._TextSection is None): raise Exception("[ERROR] Could not assign text section!") if (self._PLTSection is None): raise Exception("[ERROR] Could not assign plt section!") if (self._SymbolTable is None): raise Exception("[ERROR] Could not assign symbol table!") if (self._StringTable is None): raise Exception("[ERROR] Could not assign string table!") # parse strings binstr = self._StringTable._Obj.data() binstrdec = binstr.decode() curstart = 0 for cmatch in re.finditer('\x00', binstrdec): curstr = binstr[curstart:cmatch.start()].decode("utf-8") if curstr != "": self._Strings[curstart] = curstr curstart = cmatch.start() + 1 self._Strings[0] = '' # register symbols for symb in self._SymbolTable._Obj.iter_symbols(): if (symb['st_value'] != 0) and \ (symb['st_info']['type'] != 'STT_SECTION') and \ (symb['st_info']['type'] != 'STT_FILE') and \ (symb['st_info']['type'] != 'STT_NOTYPE') and \ (symb['st_info']['bind'] != 'STB_LOCAL'): # new symbol cursymb = SymbolInfo() cursymb._Name = symb.name cursymb._Addr = symb['st_value'] cursymb._Size = symb['st_size'] cursymb._Type = symb['st_info']['type'] cursymb._Obj = symb # fix name if cursymb._Name == '': cursymb._Name = '0x%08x' % cursymb._Addr # safe add if cursymb._Addr in self._Symbols.keys(): if sys.stdout.isatty(): print ("[INFO] Symbols with same start addr: new=%s and old=%s" \ % (cursymb._Name, self._Symbols[cursymb._Addr]._Name)) if cursymb._Size == self._Symbols[cursymb._Addr]._Size: self._Symbols[cursymb._Addr]._Name += ("+%s" % cursymb._Name) elif cursymb._Size > self._Symbols[cursymb._Addr]._Size: cursymb._Name += ("+%s(len=%d)" % \ (self._Symbols[cursymb._Addr]._Name, \ self._Symbols[cursymb._Addr]._Size)) self._Symbols[cursymb._Addr] = cursymb elif cursymb._Size < self._Symbols[cursymb._Addr]._Size: self._Symbols[cursymb._Addr]._Name += ("+%s(len=%d)" % \ (cursymb._Name, \ cursymb._Size)) else: self._Symbols[cursymb._Addr] = cursymb # prune overlay functions ksort = sorted(self._Symbols.keys()) krem = [] for i in range(0, len(ksort) - 1): if ((self._Symbols[ksort[i]]._Addr + self._Symbols[ksort[i]]._Size) > \ self._Symbols[ksort[i+1]]._Addr) and \ ((self._Symbols[ksort[i]]._Addr + self._Symbols[ksort[i]]._Size) == \ (self._Symbols[ksort[i+1]]._Addr + self._Symbols[ksort[i+1]]._Size)): krem.append((ksort[i], ksort[i + 1])) for k in krem: if sys.stdout.isatty(): print("[INFO] Pruning overlay function %s." % self._Symbols[k[1]]._Name) self._Symbols[k[0]]._Name += ("+%s(%d)" % \ (self._Symbols[k[1]]._Name, k[1]-k[0])) self._Symbols.pop(k[1]) # fast access self._SectionsFast = numpy.zeros(len(self._Sections), \ dtype=numpy.dtype([('Start', numpy.uintp, 1), \ ('Size', numpy.uintp, 1)])) ksort = sorted(self._Sections.keys()) for i in range(0, len(self._Sections)): self._SectionsFast[i]['Start'] = self._Sections[ksort[i]]._Addr self._SectionsFast[i]['Size'] = self._Sections[ksort[i]]._Size self._SymbolsFast = numpy.zeros(len(self._Symbols), \ dtype=numpy.dtype([('Start', numpy.uintp, 1), \ ('Size', numpy.uintp, 1)])) ksort = sorted(self._Symbols.keys()) for i in range(0, len(self._Symbols)): self._SymbolsFast[i]['Start'] = self._Symbols[ksort[i]]._Addr self._SymbolsFast[i]['Size'] = self._Symbols[ksort[i]]._Size # consistency check for i in range(0, len(self._SectionsFast) - 1): if self._SectionsFast[i]['Start'] + self._SectionsFast[i]['Size'] > \ self._SectionsFast[i+1]['Start']: raise Exception('[ERROR] Inconsistent section placement!') for i in range(0, len(self._SymbolsFast) - 1): if self._SymbolsFast[i]['Start'] + self._SymbolsFast[i]['Size'] > \ self._SymbolsFast[i+1]['Start']: raise Exception('[ERROR] Inconsistent symbol placement: %s -> %s!' % \ (self._Symbols[self._SymbolsFast[i]['Start']]._Name, \ self._Symbols[self._SymbolsFast[i+1]['Start']]._Name)) # set up disassembler if 'x64' in self._Elf.get_machine_arch().lower(): md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64) elif 'x86' in self._Elf.get_machine_arch().lower(): md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32) elif 'arm' in self._Elf.get_machine_arch().lower(): md = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_ARM) elif 'aarch64' in self._Elf.get_machine_arch().lower(): md = capstone.Cs(capstone.CS_ARCH_ARM64, capstone.CS_MODE_ARM + \ capstone.CS_MODE_V8) else: raise Exception( "[ERROR] Image architecture currently not supported!") md.skipdata = True # parse .text section instructions = md.disasm_lite(self._TextSection._Obj.data(), \ self._TextSection._Addr) for (address, size, mnemonic, op_str) in instructions: self._TextInstructions[address] = (size, "%s\t%s" % (mnemonic, op_str)) # parse .plt instructions instructions = md.disasm_lite(self._PLTSection._Obj.data(), \ self._PLTSection._Addr) for (address, size, mnemonic, op_str) in instructions: self._PLTInstructions[address] = (size, "%s\t%s" % (mnemonic, op_str))
def __init__(self, ql: Qiling, elf_stream): self.ql = ql self.elffile = ELFFile(elf_stream) self._versioninfo = None
def main(): """Main function of database generator""" args = parse_args() # Setup logging logging.basicConfig(format=LOGGER_FORMAT, level=logging.WARNING) if args.debug: logger.setLevel(logging.DEBUG) elif args.verbose: logger.setLevel(logging.INFO) elffile = open(args.elffile, "rb") if not elffile: logger.error("ERROR: Cannot open ELF file: %s, exiting...", args.elffile) sys.exit(1) logger.info("ELF file %s", args.elffile) if args.json: logger.info("JSON Database file %s", args.json) section_extraction = True if args.syst: logger.info("MIPI Sys-T Collateral file %s", args.syst) section_extraction = False elf = ELFFile(elffile) database = LogDatabase() if args.build_header: with open(args.build_header) as f: for l in f: match = re.match(r'\s*#define\s+BUILD_VERSION\s+(.*)', l) if match: database.set_build_id(match.group(1)) break if args.build: database.set_build_id(args.build) logger.info("Build ID: %s", args.build) extract_elf_information(elf, database) process_kconfigs(elf, database) logger.info("Target: %s, %d-bit", database.get_arch(), database.get_tgt_bits()) if database.is_tgt_little_endian(): logger.info("Endianness: Little") else: logger.info("Endianness: Big") if database.is_tgt_64bit(): global PTR_FMT PTR_FMT = '0x%016x' # Extract strings from ELF files string_mappings = extract_static_strings(elf, database, section_extraction) if len(string_mappings) > 0: database.set_string_mappings(string_mappings) logger.info("Found %d strings", len(string_mappings)) # Extract information related to logging subsystem if not section_extraction: # The logging subsys information (e.g. log module names) # may require additional strings outside of those extracted # via ELF DWARF variables. So generate a new string mappings # with strings in various ELF sections. string_mappings = extract_static_strings(elf, database, section_extraction=True) extract_logging_subsys_information(elf, database, string_mappings) # Write database file if args.json: if not LogDatabase.write_json_database(args.json, database): logger.error( "ERROR: Cannot open database file for write: %s, exiting...", args.json) sys.exit(1) if args.syst: if not LogDatabase.write_syst_database(args.syst, database): logger.error( "ERROR: Cannot open database file for write: %s, exiting...", args.syst) sys.exit(1) elffile.close()
def main(): """Main function""" global args parse_args() if not os.path.exists(args.kernel): error("{0} does not exist.".format(args.kernel)) elf_fd = open(args.kernel, "rb") # Create a modifiable byte stream raw_elf = elf_fd.read() output = create_string_buffer(raw_elf) elf = ELFFile(elf_fd) if not elf.has_dwarf_info(): error("ELF file has no DWARF information") if elf.num_segments() == 0: error("ELF file has no program header table") syms = extract_all_symbols_from_elf(elf) vm_base = syms["CONFIG_KERNEL_VM_BASE"] vm_size = syms["CONFIG_KERNEL_VM_SIZE"] sram_base = syms["CONFIG_SRAM_BASE_ADDRESS"] sram_size = syms["CONFIG_SRAM_SIZE"] * 1024 vm_offset = syms["CONFIG_KERNEL_VM_OFFSET"] sram_offset = syms.get("CONFIG_SRAM_OFFSET", 0) # # Calculate virtual-to-physical address translation # virt_to_phys_offset = (sram_base + sram_offset) - (vm_base + vm_offset) log("Virtual address space: 0x%x - 0x%x size 0x%x (offset 0x%x)" % (vm_base, vm_base + vm_size, vm_size, vm_offset)) log("Physical address space: 0x%x - 0x%x size 0x%x (offset 0x%x)" % (sram_base, sram_base + sram_size, sram_size, sram_offset)) # # Update the entry address in header # if elf.elfclass == 32: load_entry_type = "I" else: load_entry_type = "Q" entry_virt = struct.unpack_from(load_entry_type, output, ELF_HDR_ENTRY_OFFSET)[0] entry_phys = entry_virt + virt_to_phys_offset struct.pack_into(load_entry_type, output, ELF_HDR_ENTRY_OFFSET, entry_phys) log("Entry Address: 0x%x -> 0x%x" % (entry_virt, entry_phys)) # # Update load address in program header segments # # Program header segment offset from beginning of file ph_off = elf.header['e_phoff'] # ph_seg_type: segment type and other fields before virtual address # ph_seg_addr: virtual and phyiscal addresses # ph_seg_whole: whole segment if elf.elfclass == 32: ph_seg_type = "II" ph_seg_addr = "II" ph_seg_whole = "IIIIIIII" else: ph_seg_type = "IIQ" ph_seg_addr = "QQ" ph_seg_whole = "IIQQQQQQ" # Go through all segments for ph_idx in range(elf.num_segments()): seg_off = ph_off + struct.calcsize(ph_seg_whole) * ph_idx seg_type, _ = struct.unpack_from(ph_seg_type, output, seg_off) # Only process LOAD segments if seg_type != 0x01: continue # Add offset to get to the addresses addr_off = seg_off + struct.calcsize(ph_seg_type) # Grab virtual and physical addresses seg_vaddr, seg_paddr = struct.unpack_from(ph_seg_addr, output, addr_off) # Apply virt-to-phys offset so it will load into # physical address seg_paddr_new = seg_vaddr + virt_to_phys_offset log("Segment %d: physical address 0x%x -> 0x%x" % (ph_idx, seg_paddr, seg_paddr_new)) # Put the addresses back struct.pack_into(ph_seg_addr, output, addr_off, seg_vaddr, seg_paddr_new) out_fd = open(args.output, "wb") out_fd.write(output) out_fd.close() elf_fd.close()
#!/usr/bin/env python import usb.core import usb.util import time, struct from hidapi import hidapi import ctypes from elftools.elf.elffile import ELFFile pid_runtime = 0x6080 pid_bootloader = 0x6084 e = ELFFile(open('arcin.elf')) firmware = '' for segment in sorted(e.iter_segments(), key=lambda x: x.header.p_paddr): if segment.header.p_type != 'PT_LOAD': continue data = segment.data() lma = segment.header.p_paddr # Workaround for LD aligning segments to a larger boundary than 8k. if lma == 0x8000000: lma += 0x2000 data = data[0x2000:] # Add padding if necessary.