--- /dev/null
+#!/usr/bin/python3
+##
+## This file is part of the sigrok project.
+##
+## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
+##
+
+import sys
+import os
+from getopt import getopt
+import struct
+
+
+def parse(filename):
+ f = open(filename, 'rb')
+ if f.read(2) != b'MZ':
+ raise Exception("MZ signature not found.")
+
+ sections = []
+ # long e_lfanew
+ f.seek(0x3c)
+ pe_ptr = struct.unpack("<L", f.read(4))[0]
+ f.seek(pe_ptr)
+ if f.read(4) != b'\x50\x45\x00\x00':
+ raise Exception("PE signature not found.")
+ # skip Machine
+ f.seek(f.tell() + 2)
+ sections.append(['header', 324, 0])
+ num_sections = struct.unpack("<H", f.read(2))[0]
+ # skip TimeDateStamp
+ f.seek(f.tell() + 4)
+ symboltable_address = struct.unpack("<L", f.read(4))[0]
+ num_symbols = struct.unpack("<L", f.read(4))[0]
+ optheader_size = struct.unpack("<H", f.read(2))[0]
+ # skip past PE header and PE optional header
+ f.seek(f.tell() + 2 + optheader_size)
+
+ for i in range(num_sections):
+ name = f.read(8).decode('ascii', errors='ignore').strip('\x00')
+ # skip past Misc and VirtualAddress
+ f.seek(f.tell() + 8)
+ # SizeOfRawData
+ size = struct.unpack("<L", f.read(4))[0]
+ # PointerToRawData
+ ptr = struct.unpack("<L", f.read(4))[0]
+ # skip to next section header
+ f.seek(f.tell() + 16)
+ sections.append([name, size, ptr])
+
+ symbols = []
+ addr = symboltable_address
+ stringtable_address = symboltable_address + num_symbols * 18
+ for i in range(num_symbols):
+ f.seek(addr)
+ tmp = f.read(8)
+ symaddr = struct.unpack("<L", f.read(4))[0]
+ # skip SectionNumber and Type
+ symtype = struct.unpack("B", f.read(1))[0]
+ f.seek(f.tell() + 4)
+ if tmp[:4] == b'\x00\x00\x00\x00':
+ # symbol name is in the string table
+ straddr = stringtable_address + struct.unpack("<l", tmp[4:])[0]
+ f.seek(straddr)
+ tmpname = f.read(64)
+ name = tmpname[:tmpname.find(b'\x00')]
+ else:
+ name = tmp
+ name = name.decode('ascii', errors='ignore').strip('\x00')
+ # need IMAGE_SYM_CLASS_EXTERNAL
+ if symtype == 0x02:
+ size = 0
+ else:
+ size = None
+ if i != 0 and symbols[-1][2] is not None and symaddr > symbols[-1][1]:
+ symbols[-1][2] = symaddr - symbols[-1][1]
+ symbols.append([name, symaddr, size])
+ addr += 18
+
+ f.close()
+
+ return sections, symbols
+
+
+def list_all(filename):
+ sections, symbols = parse(filename)
+ if sections:
+ print("Sections:\n Name Size\t Position")
+ cnt = 0
+ for name, size, address in sections:
+ print("%-3d %-8s %5d\t 0x%.8x" % (cnt, name, size, address))
+ cnt += 1
+ if symbols:
+ print("\nSymbol table:\n Address Size Symbol")
+ for symbol, address, size in symbols:
+ if size is not None:
+ sizestr = "%5d" % size
+ else:
+ sizestr = " "
+ print("0x%.8x %s %-8s" % (address, sizestr, symbol))
+ print()
+
+
+def extract_symbol(filename, symbol):
+ sections, symbols = parse(filename)
+ if not symbols:
+ return None
+ data = None
+ for symbolname, address, size in symbols:
+ if symbolname == symbol:
+ if size is None:
+ raise Exception("symbol %s found, but has unknown size")
+ f = open(filename, 'rb')
+ f.seek(address)
+ data = f.read(size)
+ f.close()
+ if len(data) != size:
+ raise Exception("short file")
+ break
+
+ if data is None:
+ raise Exception("symbol %s not found" % symbol)
+
+ return data
+
+
+
+def usage():
+ print("usage: parsepe.py [-s <symbol>] <-l|-x> <filename>")
+ print(" -l list all sections and symbols in file")
+ print(" -x extract symbol from file (specify symbol name with -s)")
+ sys.exit()
+
+
+#
+# main
+#
+
+if __name__ == '__main__':
+ filename = symbol = mode = None
+ opts, args = getopt(sys.argv[1:], 's:lx')
+ for opt, arg in opts:
+ if opt == '-s':
+ symbol = arg
+ elif opt == '-l':
+ mode = 'list'
+ elif opt == '-x':
+ mode = 'extract'
+
+ if len(args) != 1:
+ usage()
+ if mode is None and symbol is None:
+ usage()
+
+ try:
+ filename = args[0]
+ if mode == 'list':
+ list_all(filename)
+ elif mode == 'extract':
+ if symbol is None:
+ raise Exception("specify a symbol to extract")
+ data = extract_symbol(filename, symbol)
+ outfile = os.path.splitext(filename)[0] + symbol
+ open(outfile, 'wb').write(data)
+ print("saved %d bytes to %s" % (len(data), outfile))
+ else:
+ raise Exception("specify -l or -x")
+ except Exception as e:
+ print("Error: %s" % str(e))
+
+