def DecodeElf(data, location): """Decode an ELF file and return information about it Args: data: Data from ELF file location: Start address of data to return Returns: ElfInfo object containing information about the decoded ELF file """ file_size = len(data) with io.BytesIO(data) as fd: elf = ELFFile(fd) data_start = 0xffffffff data_end = 0 mem_end = 0 virt_to_phys = 0 for i in range(elf.num_segments()): segment = elf.get_segment(i) if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']: skipped = 1 # To make code-coverage see this line continue start = segment['p_paddr'] mend = start + segment['p_memsz'] rend = start + segment['p_filesz'] data_start = min(data_start, start) data_end = max(data_end, rend) mem_end = max(mem_end, mend) if not virt_to_phys: virt_to_phys = segment['p_paddr'] - segment['p_vaddr'] output = bytearray(data_end - data_start) for i in range(elf.num_segments()): segment = elf.get_segment(i) if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']: skipped = 1 # To make code-coverage see this line continue start = segment['p_paddr'] offset = 0 if start < location: offset = location - start start = location # A legal ELF file can have a program header with non-zero length # but zero-length file size and a non-zero offset which, added # together, are greater than input->size (i.e. the total file size). # So we need to not even test in the case that p_filesz is zero. # Note: All of this code is commented out since we don't have a test # case for it. size = segment['p_filesz'] #if not size: #continue #end = segment['p_offset'] + segment['p_filesz'] #if end > file_size: #raise ValueError('Underflow copying out the segment. File has %#x bytes left, segment end is %#x\n', #file_size, end) output[start - data_start:start - data_start + size] = (segment.data()[offset:]) return ElfInfo(output, data_start, elf.header['e_entry'] + virt_to_phys, mem_end - data_start)
def openCoreDump(filename): f = open(filename, "rb") elf = ELFFile(f) try: # assert that we can process the file assert elf.elfclass == 64, "elfclass == {}; only elfclass 64 is supported".format( elf.elfclass) assert elf.header[ "e_machine"] == "EM_X86_64", "e_machine == {}; only e_machine EM_X86_64 is supported".format( elf.header["e_machine"]) assert elf.header[ "e_type"] == "ET_CORE", "e_type == {}; only e_type ET_CORE is supported".format( elf.header['e_type']) assert elf.header["e_ident"][ "EI_OSABI"] == "ELFOSABI_FREEBSD", "ei_osabi == {}; only ei_osabi ELFOSABI_FREEBSD is supported".format( elf.header["e_ident"]["EI_OSABI"]) except Exception as e: # in case of exception, skip to the next file print("Exception for {}".format(elf.stream.name)) print(e) f.close() return None else: # get elf segments # check that we have a note segment elf.notes = None if isinstance(elf.get_segment(0), NoteSegment): elf.notes = elf.get_segment(0) return elf
def read_segments(filename): elffile = ELFFile(open(filename, 'rb')) segments = list() for segment_idx in range(elffile.num_segments()): segments.insert(segment_idx, dict()) segments[segment_idx]['segment'] = elffile.get_segment(segment_idx) return segments
def read_segments(data): """Read segments from an ELF file Args: data (bytes): Contents of file Returns: tuple: list of segments, each: int: Segment number (0 = first) int: Start address of segment in memory bytes: Contents of segment int: entry address for image Raises: ValueError: elftools is not available """ if not ELF_TOOLS: raise ValueError('Python elftools package is not available') with io.BytesIO(data) as inf: try: elf = ELFFile(inf) except ELFError as err: raise ValueError(err) entry = elf.header['e_entry'] segments = [] for i in range(elf.num_segments()): segment = elf.get_segment(i) if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']: skipped = 1 # To make code-coverage see this line continue start = segment['p_offset'] rend = start + segment['p_filesz'] segments.append((i, segment['p_paddr'], data[start:rend])) return segments, entry
def elfheader(): """ Prints the section mappings contained in the ELF header. """ local_path = pwndbg.file.get_file(pwndbg.proc.exe) if not local_path: print('No file is selected') return with open(local_path, 'rb') as f: elffile = ELFFile(f) load_segment = elffile.get_segment(3) segment_base = load_segment['p_vaddr'] sections = [] for section in elffile.iter_sections(): start = section['sh_addr'] # Don't print sections that aren't mapped into memory if start == 0: continue size = section['sh_size'] sections.append((start, start + size, section.name)) sections.sort() for start, end, name in sections: print('%#x - %#x ' % (start, end), name)
def dump_segments(self): with open(self.filename, "rb") as f: elf = ELFFile(f) self.structs = elf.structs segments = [] for i in range(elf.num_segments()): seg = elf.get_segment(i) segments.append(seg.header) return segments
def generate_atf_binary(bl31_file_name): with open(bl31_file_name) as bl31_file: bl31 = ELFFile(bl31_file) num = bl31.num_segments() for i in range(num): seg = bl31.get_segment(i) if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)): paddr = seg.__getitem__(ELF_SEG_P_PADDR) file_name = 'bl31_0x%08x.bin' % paddr with open(file_name, "wb") as atf: atf.write(seg.data())
def get_elf_entry_point_offset(path): physical_ep = None try: with open(path, "rb") as f: elf = ELFFile(f) physical_ep = elf._parse_elf_header().e_entry physical_ep -= elf.get_segment(0).header.p_vaddr except Exception as e: logger.error(e.__str__()) physical_ep = None return physical_ep
def generate_atf_fit_dts(fit_file_name, bl31_file_name, uboot_file_name, dtbs_file_name): """ Generate FIT script for ATF image. """ if fit_file_name != sys.stdout: fit_file = open(fit_file_name, "wb") else: fit_file = sys.stdout num_load_seg = 0 p_paddr = 0xFFFFFFFF with open(uboot_file_name, 'rb') as uboot_file: uboot = ELFFile(uboot_file) for i in range(uboot.num_segments()): seg = uboot.get_segment(i) if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)): p_paddr = seg.__getitem__(ELF_SEG_P_PADDR) num_load_seg = num_load_seg + 1 assert (p_paddr != 0xFFFFFFFF and num_load_seg == 1) fit_file.write(DT_HEADER % p_paddr) with open(bl31_file_name, 'rb') as bl31_file: bl31 = ELFFile(bl31_file) elf_entry = bl31.header['e_entry'] for i in range(bl31.num_segments()): seg = bl31.get_segment(i) if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)): paddr = seg.__getitem__(ELF_SEG_P_PADDR) p = seg.__getitem__(ELF_SEG_P_PADDR) append_atf_node(fit_file, i + 1, paddr, elf_entry) atf_cnt = i + 1 append_fdt_node(fit_file, dtbs_file_name) fit_file.write('%s\n' % DT_IMAGES_NODE_END) append_conf_node(fit_file, dtbs_file_name, atf_cnt) fit_file.write('%s\n' % DT_END) if fit_file_name != sys.stdout: fit_file.close()
def generate_atf_binary(bl31_file_name): with open(bl31_file_name) as bl31_file: bl31 = ELFFile(bl31_file) num = bl31.num_segments() for i in range(num): seg = bl31.get_segment(i) if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)): paddr = seg.__getitem__(ELF_SEG_P_PADDR) file_name = 'bl31_0x%08x.bin' % paddr with open(file_name, "wb") as atf: atf.write(seg.data());
def generate_atf_fit_dts(fit_file_name, bl31_file_name, uboot_file_name, dtbs_file_name): """ Generate FIT script for ATF image. """ if fit_file_name != sys.stdout: fit_file = open(fit_file_name, "wb") else: fit_file = sys.stdout num_load_seg = 0 p_paddr = 0xFFFFFFFF with open(uboot_file_name, 'rb') as uboot_file: uboot = ELFFile(uboot_file) for i in range(uboot.num_segments()): seg = uboot.get_segment(i) if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)): p_paddr = seg.__getitem__(ELF_SEG_P_PADDR) num_load_seg = num_load_seg + 1 assert (p_paddr != 0xFFFFFFFF and num_load_seg == 1) fit_file.write(DT_HEADER % p_paddr) with open(bl31_file_name, 'rb') as bl31_file: bl31 = ELFFile(bl31_file) elf_entry = bl31.header['e_entry'] for i in range(bl31.num_segments()): seg = bl31.get_segment(i) if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)): paddr = seg.__getitem__(ELF_SEG_P_PADDR) p= seg.__getitem__(ELF_SEG_P_PADDR) append_atf_node(fit_file, i+1, paddr, elf_entry) atf_cnt = i+1 append_fdt_node(fit_file, dtbs_file_name) fit_file.write('%s\n' % DT_IMAGES_NODE_END) append_conf_node(fit_file, dtbs_file_name, atf_cnt) fit_file.write('%s\n' % DT_END) if fit_file_name != sys.stdout: fit_file.close()
def generate_atf_fit_dts_bl31(fit_file, bl31_file_name, dtbs_file_name): with open(bl31_file_name, 'rb') as bl31_file: bl31 = ELFFile(bl31_file) elf_entry = bl31.header['e_entry'] segments = bl31.num_segments() for i in range(segments): seg = bl31.get_segment(i) if seg.__getitem__(ELF_SEG_P_TYPE) == 'PT_LOAD': paddr = seg.__getitem__(ELF_SEG_P_PADDR) append_bl31_node(fit_file, i + 1, paddr, elf_entry) append_fdt_node(fit_file, dtbs_file_name) fit_file.write(DT_IMAGES_NODE_END) append_conf_node(fit_file, dtbs_file_name, segments)
def generate_atf_fit_dts_bl31(fit_file, bl31_file_name, dtbs_file_name): with open(bl31_file_name, 'rb') as bl31_file: bl31 = ELFFile(bl31_file) elf_entry = bl31.header['e_entry'] segments = bl31.num_segments() for i in range(segments): seg = bl31.get_segment(i) if seg.__getitem__(ELF_SEG_P_TYPE) == 'PT_LOAD': paddr = seg.__getitem__(ELF_SEG_P_PADDR) append_bl31_node(fit_file, i + 1, paddr, elf_entry) append_fdt_node(fit_file, dtbs_file_name) fit_file.write(DT_IMAGES_NODE_END) append_conf_node(fit_file, dtbs_file_name, segments)
def elf_to_dol(elf_path, dol_path): from elftools.elf.elffile import ELFFile with open(elf_path, 'rb') as elf_file, open(dol_path, 'wb') as dol_file: elf = ELFFile(elf_file) num_segments = elf.num_segments() dol_file.write(bytes([0x00] * 0x100)) idx = 0 for i in range(num_segments): segment = elf.get_segment(i) if not segment_is_text(segment): continue write_segment_to_dol(idx, segment, dol_file) idx += 1 idx = 7 for i in range(num_segments): segment = elf.get_segment(i) if not segment_is_data(segment): continue write_segment_to_dol(idx, segment, dol_file) idx += 1 bss_start = 0 bss_end = 0 for i in range(num_segments): segment = elf.get_segment(i) if not segment_is_bss(segment): continue if bss_start == 0: bss_start = segment["p_vaddr"] bss_end = segment["p_vaddr"] + segment["p_memsz"] write_to_dol_header(dol_file, 0xd8, bss_start) bss_size = bss_end - bss_start write_to_dol_header(dol_file, 0xdc, bss_size) write_to_dol_header(dol_file, 0xe0, elf["e_entry"])
def generate_atf_fit_dts_uboot(fit_file, uboot_file_name): num_load_seg = 0 p_paddr = 0xFFFFFFFF with open(uboot_file_name, 'rb') as uboot_file: uboot = ELFFile(uboot_file) for i in range(uboot.num_segments()): seg = uboot.get_segment(i) if seg.__getitem__(ELF_SEG_P_TYPE) == 'PT_LOAD': p_paddr = seg.__getitem__(ELF_SEG_P_PADDR) num_load_seg = num_load_seg + 1 assert (p_paddr != 0xFFFFFFFF and num_load_seg == 1) fit_file.write(DT_UBOOT % p_paddr)
def generate_atf_fit_dts_uboot(fit_file, uboot_file_name): num_load_seg = 0 p_paddr = 0xFFFFFFFF with open(uboot_file_name, 'rb') as uboot_file: uboot = ELFFile(uboot_file) for i in range(uboot.num_segments()): seg = uboot.get_segment(i) if seg.__getitem__(ELF_SEG_P_TYPE) == 'PT_LOAD': p_paddr = seg.__getitem__(ELF_SEG_P_PADDR) num_load_seg = num_load_seg + 1 assert (p_paddr != 0xFFFFFFFF and num_load_seg == 1) fit_file.write(DT_UBOOT % p_paddr)
def get_bl31_segments_info(bl31_file_name): """ Get load offset, physical offset, file size from bl31 elf file program headers. """ with open(bl31_file_name) as bl31_file: bl31 = ELFFile(bl31_file) num = bl31.num_segments() print 'Number of Segments : %d' % bl31.num_segments() for i in range(num): print 'Segment %d' % i seg = bl31.get_segment(i) ptype = seg[ELF_SEG_P_TYPE] poffset = seg[ELF_SEG_P_OFFSET] pmemsz = seg[ELF_SEG_P_MEMSZ] pfilesz = seg[ELF_SEG_P_FILESZ] print 'type: %s\nfilesz: %08x\nmemsz: %08x\noffset: %08x' % (ptype, pfilesz, pmemsz, poffset) paddr = seg[ELF_SEG_P_PADDR] print 'paddr: %08x' % paddr
def get_bl31_segments_info(bl31_file_name): """ Get load offset, physical offset, file size from bl31 elf file program headers. """ with open(bl31_file_name) as bl31_file: bl31 = ELFFile(bl31_file) num = bl31.num_segments() print('Number of Segments : %d' % bl31.num_segments()) for i in range(num): print('Segment %d' % i) seg = bl31.get_segment(i) ptype = seg[ELF_SEG_P_TYPE] poffset = seg[ELF_SEG_P_OFFSET] pmemsz = seg[ELF_SEG_P_MEMSZ] pfilesz = seg[ELF_SEG_P_FILESZ] print('type: %s\nfilesz: %08x\nmemsz: %08x\noffset: %08x' % (ptype, pfilesz, pmemsz, poffset)) paddr = seg[ELF_SEG_P_PADDR] print('paddr: %08x' % paddr)
def elfheader(): """ Prints the section mappings contained in the ELF header. """ local_path = pwndbg.file.get_file(pwndbg.proc.exe) with open(local_path, 'rb') as f: elffile = ELFFile(f) load_segment = elffile.get_segment(3) segment_base = load_segment['p_vaddr'] sections = [] for section in elffile.iter_sections(): start = section['sh_addr'] # Don't print sections that aren't mapped into memory if start == 0: continue size = section['sh_size'] sections.append((start, start + size, section.name.decode('ascii'))) sections.sort() for start, end, name in sections: print('%#x - %#x %s' % (start, end, name))
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
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
if __name__ == '__main__': if len(sys.argv) < 2: print( "You must provide this script with an elf binary file you want to examine" ) exit(1) print(f"Mapping between segments and sections in the file {sys.argv[1]}") elffile = ELFFile(open(sys.argv[1], 'rb')) segments = list() for segment_idx in range(elffile.num_segments()): segments.insert(segment_idx, dict()) segments[segment_idx]['segment'] = elffile.get_segment(segment_idx) segments[segment_idx]['sections'] = list() for section_idx in range(elffile.num_sections()): section = elffile.get_section(section_idx) for segment in segments: if segment['segment'].section_in_segment(section): segment['sections'].append(section) for segment in segments: seg_head = segment['segment'].header print("Segment:") print( f"Type: {seg_head.p_type}\nOffset: {hex(seg_head.p_offset)}\nVirtual address: {hex(seg_head.p_vaddr)}\nPhysical address: {(seg_head.p_paddr)}\nSize in file: {hex(seg_head.p_filesz)}\nSize in memory: {hex(seg_head.p_memsz)}\n" )
#!/usr/bin/env python3 import sys from elftools.elf.elffile import ELFFile from hexdump import hexdump import struct import binascii e = ELFFile(open("devcfg.mbn" if len(sys.argv) == 1 else sys.argv[1], "rb")) # segment 0 = ELF header # segment 1 = cert # segment 2 = cert ss = e.get_segment(2) print(dir(ss)) la = ss.header['p_vaddr'] print(hex(la)) d = ss.data() #hexdump(d) #for i in range(0, len(d), 4): for i in range(0, 0x100, 4): aa = struct.unpack("I", d[i:i + 4])[0] - la if aa >= 0 and aa < len(d): ss = d[aa:aa + 0x100].split(b"\x00")[0] print(hex(i), hex(aa), ss) def g32(i):
def process_file(filename): print('Processing file:', filename) with open(filename, 'rb') as f: elffile = ELFFile(f) # The provided linkerscript sorts the sections in the appropriate order assert (elffile.num_segments() == 1) segment = elffile.get_segment(0) symtab = elffile.get_section_by_name('.symtab') # A table of exported callbacks where each entry is made of two u32 # values, the first is the event ID and the second is the address of the # callback function exports_sym = symtab.get_symbol_by_name('Exports')[0] exports_addr = exports_sym['st_value'] exports_size = exports_sym['st_size'] assert ((exports_addr & 3) == 0) assert ((exports_size & 7) == 0) # The text and data sections are merged together, the bss is allocated # and zeroed by the loader. The 4 takes into account the extra pointer # to the application class that's inserted by the loader. code_size = segment['p_filesz'] - 4 bss_size = segment['p_memsz'] - code_size # Address of the funalizer function finish_sym = symtab.get_symbol_by_name('_finish')[0] finish_addr = finish_sym['st_value'] asset0 = struct.pack( '<16I', 0x1000, # Fixed? 1, # Version code_size, # Code + Data size 0, # Same but for libs 4, # Space for the Klass pointer bss_size, # Size of the zeroed area code_size, # Offset for the Klass pointer exports_addr, # Offset for the exports array 0xffffffff, # Same but for libs 0xffffffff, # Init array start 0xffffffff, # Fini array start 0, # Init array len (in words) 0, # Fini array len (in words) elffile['e_entry'], # Entry point finish_addr, # Exit point 0x2B0B3ED5) # Used to check if the .sig file is valid # Make it writable, we may perform some modification later asset1 = bytearray(segment.data()) reloc_offsets = [] reloc_sections = [ sect for sect in elffile.iter_sections() if isinstance(sect, RelocationSection) ] # The only relocation performed by the loader is adding the address # where the app is loaded to a whole u32 for rel_section in reloc_sections: for reloc in rel_section.iter_relocations(): ty = reloc['r_info_type'] # Make sure it's not STN_UNDEF assert (reloc['r_info_sym'] != 0) if ty == ENUM_RELOC_TYPE_ARM['R_ARM_ABS32']: reloc_offsets.append(reloc['r_offset']) elif ty == ENUM_RELOC_TYPE_ARM['R_ARM_REL32'] or \ ty == ENUM_RELOC_TYPE_ARM['R_ARM_CALL']: # Already PC-independent pass else: raise RuntimeError('Unknown relocation type!') # Terminates the list reloc_offsets.append(0xffffffff) asset2 = struct.pack('<IIII', 1, len(reloc_offsets) - 1, 16 + len(reloc_offsets) * 4, 0xf0123456) for off in reloc_offsets: asset2 += struct.pack('<I', off) # The key file is simply chopped, shuffled and hashed with SHA1 together # with the "magic" word at 0x3c in the asset0. with open(filename + '.sig', 'w+b') as out: header = struct.pack( '<II', 0x1000, # Fixed ? 8) # Key slot out.write(header) out.write( b'\x63\x9a\x83\xa1\x99\x4b\xfb\x42\x56\xd4\x5e\x44\x2a\x90\x2a\x35' ) out.write( b'\xf5\xd5\xdc\xef\xb9\x80\x90\x3d\x80\x33\x94\x5f\x54\x6c\xcc\x9a' ) out.write( b'\xe8\xa7\xad\xf7\xe0\x22\x3d\x1e\x59\x30\x38\x3a\x4d\x30\x39\x3a' ) out.write( b'\x44\x30\x31\x3a\x54\x30\x39\x3a\x30\x30\xe3\xd1\xa5\xca\xbc\x94' ) out.write( b'\x75\xd1\x0c\x79\x70\x03\x1b\x10\x33\x50\x31\xad\x79\xbc') with open(filename + '.aab', 'w+b') as out: out.write(b'ABHS') header = struct.pack( '<3I', 0x1000, # Fixed ? 0x10, # Application type 3) # Number of assets out.write(header) off = 16 + 3 * 12 for i, asset in enumerate([asset0, asset1, asset2]): entry = struct.pack('<3I', i, len(asset), off) off += len(asset) out.write(entry) out.write(asset0) out.write(asset1) out.write(asset2)
def load(cls, fo): ef = ELFFile(fo) if ef.header['e_type'] != 'ET_EXEC': raise RuntimeError( 'not an ELF executable file (type {})'.format(ef.header['e_type'])) if ef.header['e_machine'] != 'EM_68K': raise RuntimeError('not an M68K ELF file') if ef.num_segments() != 2: raise RuntimeError('wrong number of segments in ELF file') # Look at segments for text and data; note that we # expect to see exactly two segments, one RX, one RW, # for text and data respectively, with data immediately # following text in memory. textSegment = ef.get_segment(0) textAddress = textSegment['p_vaddr'] textSize = textSegment['p_filesz'] dataSegment = ef.get_segment(1) dataAddress = dataSegment['p_vaddr'] dataSize = dataSegment['p_filesz'] # Look for BSS sections bssAddress = None bssLimit = None for section in ef.iter_sections(): if (section['sh_type'] == 'SHT_NOBITS') and (section['sh_flags'] & SH_FLAGS.SHF_ALLOC): secStart = section['sh_addr'] secLimit = section['sh_addr'] + section['sh_size'] # track low BSS address if bssAddress is None: bssAddress = secStart elif secStart < bssAddress: bssAddress = secStart # track BSS limit if bssLimit is None: bssLimit = secLimit elif secLimit > bssLimit: bssLimit = secLimit if bssAddress is None: bssAddress = dataAddress + dataSize bssSize = 0 else: bssSize = bssLimit - bssAddress # extend text to cover the gap created by data segment alignment dataGap = dataAddress - (textAddress + textSize) if dataGap < 0: raise RuntimeError('data segment before text') textSize += dataGap # extend data to cover the gap created by BSS alignment bssGap = bssAddress - (dataAddress + dataSize) if bssGap < 0: raise RuntimeError('BSS before data segment (0x{:x} inside/before 0x{:x}/0x{:x}'.format( bssAddress, dataAddress, dataSize)) dataSize += bssGap # sanity-check the text and data segments if (textSegment['p_type'] != 'PT_LOAD') or (dataSegment['p_type'] != 'PT_LOAD'): raise RuntimeError('expected two PT_LOAD segments') if (textSegment['p_flags'] & cls.P_FLAGS_MASK) != cls.P_FLAGS_RX: raise RuntimeError('text segment is not RX') if textAddress != 0: raise RuntimeError('text segment is not at 0') if (dataSegment['p_flags'] & cls.P_FLAGS_MASK) != cls.P_FLAGS_RW: raise RuntimeError('data segment is not RW') if dataAddress != textAddress + textSize: raise RuntimeError('data segment @ 0x{:x} does not follow text 0x{:x}/0x{:x}'.format( dataAddress, textAddress, textSize)) text = textSegment.data().ljust(textSize, '\0') data = dataSegment.data().ljust(dataSize, '\0') if len(text) != textSize: raise RuntimeError('text size mismatch') if len(data) != dataSize: raise RuntimeError('data size mismatch') print('text 0x{:x} data 0x{:x} bss 0x{:x}'.format(textSize, dataSize, bssSize)) # look for relocations relocs = dict() for section in ef.iter_sections(): if isinstance(section, RelocationSection): # what section do these relocations affect? relocSection = ef.get_section(section['sh_info']) if not (relocSection['sh_flags'] & SH_FLAGS.SHF_ALLOC): #print('Not relocating {}'.format(relocSection.name)) continue #print('Relocate: {} using {}'.format(relocSection.name, section.name)) symtab = ef.get_section(section['sh_link']) for reloc in section.iter_relocations(): relAddress = reloc['r_offset'] if relAddress >= (len(text) + len(data)): raise RuntimeError('relocation outside known space') if not reloc.is_RELA(): raise RuntimeError('unexpected REL reloc') relType = reloc['r_info_type'] # get the symbol table entry the reloc refers to if reloc['r_info_sym'] >= symtab.num_symbols(): raise RuntimeError( 'symbol reference in relocation out of bounds') relTarget = symtab.get_symbol(reloc['r_info_sym'])['st_value'] # It looks like we can ignore the addend, as it's already # present in the object file... relAddend = reloc['r_addend'] # Sort out what we're going to do with this relocation... if relType == R_68K_32: pass elif relType == R_68K_NONE: #print('ignoring none-reloc @ 0x{:x}'.format(relAddress)) continue elif relType == R_68K_PC32: #print('ignoring PC32 reloc @ 0x{:x} -> 0x{:x}+{:x}'.format(relAddress, relTarget, relAddend)) continue elif relType == R_68K_PC16: #print('ignoring PC16 reloc @ 0x{:x} -> 0x{:x}+{:x}'.format(relAddress, relTarget, relAddend)) continue elif relType == R_68K_PC8: #print('ignoring PC8 reloc @ 0x{:x} -> 0x{:x}+{:x}'.format(relAddress, relTarget, relAddend)) continue else: raise RuntimeError('unexpected relocation type {} @ 0x{:x} -> 0x{:x}+x'.format(relType, relAddress, relTarget, relAddend)) #print('RELA address 0x{:08x} target 0x{:x} type {} addend 0x{:x}'.format(relAddress, relTarget, relType, relAddend)) if relTarget < len(text): relType |= R_TEXT if relAddend > len(text): raise RuntimeError('addend outside of text section') elif relTarget < (len(text) + len(data)): relType |= R_DATA if relAddend > len(data): raise RuntimeError('addend outside of data section') elif relTarget <= (len(text) + len(data) + bssSize): # note, <= to allow pointer to _end, which is immediately *after* the BSS relType |= R_BSS if relAddend > bssSize: raise RuntimeError('addend outside of bss section') else: raise RuntimeError( 'relocation target not in known space') #print(' -> type 0x{:03x}'.format(relType)) if relAddress < len(text): inSeg = text segOffset = relAddress elif relAddress < (len(text) + len(data)): inSeg = data segOffset = relAddress - len(text) else: raise RuntimeError('relocation not in known space') unRelocated = struct.unpack('>L', inSeg[segOffset:segOffset+4])[0] #print(' unrelocated: 0x{:x}'.format(unRelocated)) if unRelocated != (relTarget + relAddend): raise RuntimeError("unrelocated field 0x{:x} != target 0x{:x} + addend 0x{:x}".format(unRelocated, relTarget, relAddend)) relocs[relAddress] = relType return cls(text=text, data=data, bssSize=bssSize, relocs=relocs)