And if you ever feel the need to do it manually or in an environment other than IDA, you might find this useful.
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
class section_header(object):
packstring = '8BIIIIIIHHI'
packcount = 16
packchunk = 17
packinfo = [
[ 'B', 'Name', 8, 'string' ],
[ 'I', 'VirtualSize' ],
[ 'I', 'VirtualAddress' ],
[ 'I', 'SizeOfRawData' ],
[ 'I', 'PointerToRawData' ],
[ 'I', 'PointerToRelocations' ],
[ 'I', 'PointerToLinenumbers' ],
[ 'H', 'NumberOfRelocations' ],
[ 'H', 'NumberOfLinenumbers' ],
[ 'I', 'Characteristics' ]
]
def __str__(self):
return "{:32} {} - {}".format(self.name(),
hex(self.base + self.data.VirtualAddress),
hex(self.base + self.data.VirtualAddress + self.data.VirtualSize))
def __repr__(self):
return "<{} '{}'>".format(str(__class__)[1:-2].split('.', 2)[1], self.name())
def __init__(self, base, data):
self.base = base
self.data = AttrDict()
for i in range(len(self.packinfo)):
count = 1
if len(self.packinfo[i]) > 2:
count = self.packinfo[i][2]
l = []
for unused in range(count):
l.append(data.pop(0))
if len(self.packinfo[i]) > 3:
iteratee = self.packinfo[i][3]
fn = getattr(self, iteratee)
result = (fn(l))
else:
result = (l)
else:
result = (data.pop(0))
self.data[self.packinfo[i][1]] = result
def name(self):
return self.data.Name
def empty(self):
return self.data.VirtualSize == 0 and self.data.VirtualAddress == 0
def string(self, data):
return ''.join([chr(x) for x in data]).rstrip('\0')
class data_directory(section_header):
names = [
"Export Directory", "Import Directory", "Resource Directory",
"Exception Directory", "Security Directory", "Base Relocation Table",
"Debug Directory", "Architecture Specific Data", "RVA of GP",
"TLS Directory", "Load Configuration Directory",
"Bound Import Directory", "Import Address Table",
"Delay Load Import Descriptors", "COM Runtime descriptor"
]
packstring = 'II'
packcount = 16
packchunk = 2
packinfo = [
[ 'I', 'VirtualAddress' ],
[ 'I', 'Size' ],
]
def __init__(self, base, data, number):
super(data_directory, self).__init__(base, data)
if number < len(self.names):
self.data.Name = self.names[number]
else:
self.data.Name = 'Unknown'
def __str__(self):
return "{:32} {} - {}".format(self.name(),
hex(self.base + self.data.VirtualAddress),
hex(self.base + self.data.VirtualAddress + self.data.Size))
def empty(self):
return self.data.Size == 0 and self.data.VirtualAddress == 0
class WinPE(object):
"""
example usage:
w = WinPE(64)
print(w.nt.SizeOfCode)
print(w.dos.e_lfanew)
print(w.get_rva(w.nt.BaseOfCode))
"""
_nt_nam_32 = [ "Signature", "Machine", "NumberOfSections",
"TimeDateStamp", "PointerToSymbolTable", "NumberOfSymbols",
"SizeOfOptionalHeader", "Characteristics", "Magic", "MajorLinkerVersion",
"MinorLinkerVersion", "SizeOfCode", "SizeOfInitializedData",
"SizeOfUninitializedData", "AddressOfEntryPoint", "BaseOfCode",
"BaseOfData", "ImageBase", "SectionAlignment", "FileAlignment",
"MajorOperatingSystemVersion", "MinorOperatingSystemVersion",
"MajorImageVersion", "MinorImageVersion", "MajorSubsystemVersion",
"MinorSubsystemVersion", "Win32VersionValue", "SizeOfImage",
"SizeOfHeaders", "CheckSum", "Subsystem", "DllCharacteristics",
"SizeOfStackReserve", "SizeOfStackCommit", "SizeOfHeapReserve",
"SizeOfHeapCommit", "LoaderFlags", "NumberOfRvaAndSizes" ]
_nt_nam_64 = _nt_nam_32.copy()
_nt_nam_64.remove('BaseOfData')
_dos_nam = ['e_magic', 'e_cblp', 'e_cp', 'e_crlc', 'e_cparhdr',
'e_minalloc', 'e_maxalloc', 'e_ss', 'e_sp', 'e_csum', 'e_ip', 'e_cs',
'e_lfarlc', 'e_ovno', 'e_res_0', 'e_res_1', 'e_res_2', 'e_res_3',
'e_oemid', 'e_oeminfo', 'e_res2_0', 'e_res2_1', 'e_res2_2', 'e_res2_3',
'e_res2_4', 'e_res2_5', 'e_res2_6', 'e_res2_7', 'e_res2_8', 'e_res2_9',
'e_lfanew' ]
_dos_fmt = 'HHHHHHHHHHHHHH4HHH10Hi'
_sig_fmt = 'I'
_img_fmt = 'HHIIIHH'
_dir_fmt = data_directory.packstring * data_directory.packcount
_sec_fmt = section_header.packstring * section_header.packcount
_nt_fmt_32 = _sig_fmt + _img_fmt + "HBBIIIIIIIIIHHHHHHIIIIHHIIIIII" \
+ _dir_fmt + _sec_fmt
_nt_fmt_64 = _sig_fmt + _img_fmt + "HBBIIIIIQIIHHHHHHIIIIHHQQQQII" \
+ _dir_fmt + _sec_fmt
def __init__(self, bits=64, base=None):
if bits not in (32, 64):
raise RuntimeError("bits must be 32 or 64")
if base is None:
import idaapi
base = idaapi.cvar.inf.min_ea
self.bits = bits
self.base = base
self.dos = self.unpack(self.get_rva(0), self._dos_nam, self._dos_fmt)
self.nt = self.unpack(
self.get_rva(self.dos.e_lfanew),
getattr(self, "_nt_nam_%i" % bits),
getattr(self, "_nt_fmt_%i" % bits))
t2s = data_directory.packcount * data_directory.packchunk
t4s = section_header.packcount * section_header.packchunk
self.dirs = [y for y in [data_directory(base, x[1], x[0]) \
for x in enumerate(chunk_list(self._overspill[0:t2s], \
data_directory.packchunk))] \
if not y.empty()]
self.sections = [y for y in [section_header(base, x) \
for x in chunk_list(self._overspill[t2s:t2s+t4s], \
section_header.packchunk)] \
if not y.empty()]
self.end = self.base + self.nt.SizeOfCode;
self.size = self.nt.SizeOfImage;
print("-- DOS Header --")
for k, s in self.dos.items(): print("{:32} {}".format(k, hex(s)))
print("-- NT Header --")
for k, s in self.nt.items(): print("{:32} {}".format(k, hex(s)))
print("-- Directories --")
for s in self.dirs: print(s)
print("-- Segments --")
for s in self.sections: print(s)
def get_rva(self, offset):
return self.base + offset
def zipObject(self, keys, values):
result = {}
for x in zip(keys, values):
result[x[0]] = x[1]
return result
def unpack(self, ea, names, fmt):
d = struct.unpack(fmt, idc.get_bytes(ea, struct.calcsize(fmt)))
o = self.zipObject(names, d)
self._overspill = list(d[len(names):])
return AttrDict(o)
def chunk_list(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i:i + n]