('st_name', 'st_value', 'st_size',
'st_info', 'st_other', 'st_shndx'))
+ def parse_rela(this):
+ return this.read_struct('NNn', ('r_offset', 'r_info', 'r_addend'))
+
+ def parse_rel(this):
+ return this.read_struct('NN', ('r_offset', 'r_info'))
+
+ def fixup_reloc(this, reloc):
+ if not 'r_addend' in reloc:
+ reloc['r_addend'] = 0
+ if this.elf_wordsize == 64:
+ reloc['r_sym'] = reloc['r_info'] >> 32
+ reloc['r_type'] = reloc['r_info'] & 0xffffffff
+ else:
+ reloc['r_sym'] = reloc['r_info'] >> 8
+ reloc['r_type'] = reloc['r_info'] & 0xff
+ return reloc
+
def parse_symbols(this, symsecname, strsecname):
try:
symsechdr = this.find_section(symsecname)
return {}
strsec = this.read_section(strsechdr)
this.file.seek(symsechdr['sh_offset'])
- syms = [this.parse_symbol() for i in
+ syms = [dict(this.parse_symbol(),number=i) for i in
range(0, symsechdr['sh_size'] // symsechdr['sh_entsize'])]
return {this.get_name(sym['st_name'], strsec): sym for sym in syms}
+ def parse_relocs(this, section):
+ this.file.seek(section['sh_offset'])
+ if section['sh_type'] == 4:
+ relocs = [this.fixup_reloc(this.parse_rela()) for i in
+ range(0, section['sh_size'] // section['sh_entsize'])]
+ else:
+ relocs = [this.fixup_reloc(this.parse_rel()) for i in
+ range(0, section['sh_size'] // section['sh_entsize'])]
+ return relocs
+
def address_to_offset(this, addr):
for section in this.shdrs:
if (section['sh_addr'] <= addr and
this.symtab = this.parse_symbols('.symtab', '.strtab')
this.dynsym = this.parse_symbols('.dynsym', '.dynstr')
+ this.relocs = {}
+ for section in this.shdrs:
+ if section['sh_type'] == 4 or section['sh_type'] == 9:
+ rels = {}
+ symsec = this.shdrs[section['sh_link']]
+ if this.get_name(symsec['sh_name']) == '.symtab':
+ rels['symbols'] = this.symtab
+ elif this.get_name(symsec['sh_name']) == '.dynsym':
+ rels['symbols'] = this.dynsym
+ rels['relocs'] = this.parse_relocs(section)
+ this.relocs[this.get_name(section['sh_name'])] = rels
+
def __del__(this):
try:
this.file.close()
##
import sys
+import struct
import parseelf
+class searcher:
+
+ def reset(this, offs = 0):
+ if offs < 0 or offs > this.length:
+ raise Exception('Reset past end of section')
+ this.address = this.baseaddr + offs
+ this.offset = offs
+
+ def skip(this, cnt):
+ if this.offset + cnt > this.length:
+ raise Exception('Skip past end of section')
+ this.address += cnt
+ this.offset += cnt
+
+ def peek(this, cnt, offs=0):
+ if this.offset + offs + cnt > this.length:
+ raise Exception('Peek past end of section')
+ return this.data[this.offset + offs : this.offset + offs + cnt]
+
+ def look_for(this, needle):
+ pos = this.data.find(needle, this.offset)
+ if pos < 0:
+ raise Exception('Needle not found in haystack')
+ this.skip(pos - this.offset)
+
+ def __init__(this, data, addr):
+ this.data = data
+ this.baseaddr = addr
+ this.length = len(data)
+ this.reset()
+
+def search_plt_32(plt, addr):
+ plt.reset()
+ plt.look_for(struct.pack('<BBI', 0xff, 0x25, addr)) # jmp *addr32
+ return plt.address
+
+def search_plt_64(plt, addr):
+ plt.reset()
+ while True:
+ plt.look_for(b'\xff\x25') # jmpq *offs32(%rip)
+ offs = struct.unpack('<i', plt.peek(4, 2))[0]
+ if plt.address + offs + 6 == addr:
+ return plt.address
+ plt.skip(2)
+
+def find_hex_file_lines_constructor_32(text, hex_file_lines_got, got_plt):
+ while True:
+ text.look_for(b'\x8b\xbb') # mov offs32(%ebx),%edi
+ offs = struct.unpack('<i', text.peek(4, 2))[0]
+ if got_plt + offs == hex_file_lines_got:
+ text.skip(6)
+ return
+ text.skip(2)
+
+def find_hex_file_lines_constructor_64(text, hex_file_lines_got):
+ while True:
+ text.look_for(b'\x48\x8b\x2d') # mov offs32(%rip),%rbp
+ offs = struct.unpack('<i', text.peek(4, 3))[0]
+ if text.address + offs + 7 == hex_file_lines_got:
+ text.skip(7)
+ return
+ text.skip(3)
+
+def parse_hex_file_lines_constructor_32(text, basic_string_plt, got_plt, lines):
+ cnt = len(lines)
+ while cnt > 0:
+ if text.peek(2) == b'\x8d\x45': # lea offs8(%ebp),%eax
+ text.skip(3)
+ elif text.peek(2) == b'\x8d\x85': # lea offs32(%ebp),%eax
+ text.skip(6)
+ if text.peek(1) == b'\xbe': # mov $imm32,%esi
+ text.skip(5)
+ elif text.peek(2) == b'\x31\xf6': # xor %esi,%esi
+ text.skip(2)
+ if text.peek(4) == b'\x89\x44\x24\x08': # mov %eax,0x8(%esp)
+ text.skip(4)
+ if text.peek(2) == b'\x8d\x83': # lea offs32(%ebx),%eax
+ straddr = struct.unpack('<i', text.peek(4, 2))[0]
+ text.skip(6)
+ straddr += got_plt
+ else:
+ raise Exception('Expected lea offs32(%ebx),%eax @ '+
+ ('0x%x' % text.address))
+ if text.peek(4) == b'\x89\x44\x24\x04': # mov %eax,0x4(%esp)
+ text.skip(4)
+ if text.peek(3) == b'\x89\x3c\x24': # mov %edi,(%esp)
+ offs = 0
+ text.skip(3)
+ elif text.peek(2) == b'\x8d\x47': # lea offs8(%edi),%eax
+ offs = struct.unpack('<b', text.peek(1, 2))[0]
+ text.skip(3)
+ elif text.peek(2) == b'\x8d\x87': # lea offs32(%edi),%eax
+ offs = struct.unpack('<i', text.peek(4, 2))[0]
+ text.skip(6)
+ else:
+ raise Exception('Expected lea offs(%edi),%eax @ '+
+ ('0x%x' % text.address))
+ if offs < 0 or offs > (len(lines) << 2) or (offs & 3) != 0:
+ raise Exception('Invalid offset %d' % offs)
+ index = offs >> 2
+ if lines[index] != 0:
+ raise Exception('Line %d filled multiple times' % index)
+ if text.peek(3) == b'\x89\x04\x24': # mov %eax,(%esp)
+ text.skip(3)
+ if text.peek(1) == b'\xe8': # call offs32
+ offs = struct.unpack('<i', text.peek(4, 1))[0]
+ text.skip(5)
+ if text.address + offs != basic_string_plt:
+ raise Exception('Expected call ZNSsC1EPKcRKSaIcE@plt @ '+
+ ('0x%x' % text.address))
+ else:
+ raise Exception('Expected call ZNSsC1EPKcRKSaIcE@plt @ '+
+ ('0x%x' % text.address))
+ if straddr == 0:
+ raise Exception('NULL pointer stored to index %d' % index)
+ lines[index] = straddr
+ cnt -= 1
+
+def parse_hex_file_lines_constructor_64(text, basic_string_plt, lines):
+ cnt = len(lines)
+ while cnt > 0:
+ if text.peek(1) == b'\xbb': # mov $imm32,%ebx
+ text.skip(5)
+ elif text.peek(2) == b'\x31\xdb': # xor %ebx,%ebx
+ text.skip(2)
+ if text.peek(4) == b'\x48\x8d\x54\x24': # lea offs8(%rsp),%rdx
+ text.skip(5)
+ elif text.peek(4) == b'\x48\x8d\x94\x24': # lea offs32(%rsp),%rdx
+ text.skip(8)
+ if text.peek(3) == b'\x48\x8d\x35': # lea offs32(%rip),%rsi
+ straddr = struct.unpack('<i', text.peek(4, 3))[0]
+ text.skip(7)
+ straddr += text.address
+ else:
+ raise Exception('Expected lea offs(%rip),%rsi @ '+
+ ('0x%x' % text.address))
+ if text.peek(3) == b'\x48\x89\xef': # mov %rbp,%rdi
+ offs = 0
+ text.skip(3)
+ elif text.peek(3) == b'\x48\x8d\x7d': # lea offs8(%rbp),%rdi
+ offs = struct.unpack('<b', text.peek(1, 3))[0]
+ text.skip(4)
+ elif text.peek(3) == b'\x48\x8d\xbd': # lea offs32(%rbp),%rdi
+ offs = struct.unpack('<i', text.peek(4, 3))[0]
+ text.skip(7)
+ else:
+ raise Exception('Expected lea offs(%rbp),%rdi @ '+
+ ('0x%x' % text.address))
+ if offs < 0 or offs > (len(lines) << 3) or (offs & 7) != 0:
+ raise Exception('Invalid offset %d' % offs)
+ index = offs >> 3
+ if lines[index] != 0:
+ raise Exception('Line %d filled multiple times' % index)
+ if text.peek(1) == b'\xe8': # callq offs32
+ offs = struct.unpack('<i', text.peek(4, 1))[0]
+ text.skip(5)
+ if text.address + offs != basic_string_plt:
+ raise Exception('Expected callq ZNSsC1EPKcRKSaIcE@plt @ '+
+ ('0x%x' % text.address))
+ else:
+ raise Exception('Expected callq ZNSsC1EPKcRKSaIcE@plt @ '+
+ ('0x%x' % text.address))
+ if straddr == 0:
+ raise Exception('NULL pointer stored to index %d' % index)
+ lines[index] = straddr
+ cnt -= 1
+
+def find_reloc(elf, symname):
+ for section, relocs in elf.relocs.items():
+ if 'symbols' in relocs and symname in relocs['symbols']:
+ symnum = relocs['symbols'][symname]['number']
+ for reloc in relocs['relocs']:
+ if reloc['r_sym'] == symnum:
+ return reloc
+ raise Exception('Unable to find a relocation against '+symname)
+
+def ihex_to_binary(lines):
+ chunks = {}
+ for line in lines:
+ if line[0] != ':':
+ raise Exception('ihex line does not start with ":"')
+ line = bytes.fromhex(line[1:])
+ if (sum(bytearray(line)) & 0xff) != 0:
+ raise Exception('Invalid checksum in ihex')
+ (byte_count, address, rectype) = struct.unpack('>BHB', line[:4])
+ (data, checksum) = struct.unpack('>%dsB' % (byte_count), line[4:])
+ if rectype == 1 and byte_count == 0:
+ pass
+ elif rectype != 0 or byte_count == 0:
+ raise Exception('Unexpected rectype %d with bytecount %d' %
+ (rectype, byte_count))
+ elif address in chunks:
+ raise Exception('Multiple ihex lines with address 0x%x' % address)
+ else:
+ chunks[address] = data
+ blob = b''
+ for address in sorted(iter(chunks)):
+ if address < len(blob):
+ raise Exception('Overlapping ihex chunks')
+ elif address > len(blob):
+ blob += b'\x00' * (address - len(blob))
+ blob += chunks[address]
+ return blob
+
+def extract_fx2_firmware(elf, symname, filename):
+ blob = elf.load_symbol(elf.dynsym[symname+'Count'])
+ count = struct.unpack('<I', blob)[0]
+ got_plt = elf.find_section('.got.plt')['sh_addr']
+ hex_file_lines_got = find_reloc(elf, symname)['r_offset'];
+ basic_string_got = find_reloc(elf, '_ZNSsC1EPKcRKSaIcE')['r_offset']
+ pltsec = elf.find_section('.plt')
+ plt = searcher(elf.read_section(pltsec), pltsec['sh_addr'])
+ try:
+ if elf.elf_wordsize == 64:
+ basic_string_plt = search_plt_64(plt, basic_string_got)
+ else:
+ basic_string_plt = search_plt_32(plt, basic_string_got)
+ except:
+ raise Exception('Unable to find a PLT entry for _ZNSsC1EPKcRKSaIcE')
+ textsec = elf.find_section('.text')
+ text = searcher(elf.read_section(textsec), textsec['sh_addr'])
+ while True:
+ try:
+ if elf.elf_wordsize == 64:
+ find_hex_file_lines_constructor_64(text, hex_file_lines_got)
+ else:
+ find_hex_file_lines_constructor_32(text, hex_file_lines_got,
+ got_plt)
+ except:
+ raise Exception('Unable to find constructor for '+symname)
+ oldoffs = text.offset
+ l = [0]*count
+ try:
+ if elf.elf_wordsize == 64:
+ parse_hex_file_lines_constructor_64(text, basic_string_plt, l)
+ else:
+ parse_hex_file_lines_constructor_32(text, basic_string_plt,
+ got_plt, l)
+ break
+ except KeyError:
+ text.reset(oldoffs)
+ rodatasec = elf.find_section('.rodata')
+ rodata = elf.read_section(rodatasec)
+ lo = rodatasec['sh_addr']
+ hi = lo + rodatasec['sh_size']
+ for i in range(count):
+ addr = l[i]
+ if addr < lo or addr >= hi:
+ raise Exception('Address 0x%x outside of .rodata section' % addr)
+ l[i] = elf.get_name(addr - lo, rodata)
+ blob = ihex_to_binary(l)
+ f = open(filename, 'wb')
+ f.write(blob)
+ f.close()
+ print("saved %d bytes to %s" % (len(blob), filename))
+
def extract_symbol(elf, symname, filename):
blob = elf.load_symbol(elf.dynsym[symname])
f = open(filename, 'wb')
try:
filename = sys.argv[1]
elf = parseelf.elf(filename)
+ if elf.ehdr['e_machine'] != 3 and elf.ehdr['e_machine'] != 62:
+ raise Exception('Unsupported e_machine')
+ extract_fx2_firmware(elf, 'gLogic16HexFileLines', 'saleae-logic16-fx2.fw')
extract_bitstream(elf, '18')
extract_bitstream(elf, '33')
except Exception as e: