import os
import struct
from cStringIO import StringIO
import vstruct
import vstruct.defs.pe as vs_pe
import ordlookup
IMAGE_DLLCHARACTERISTICS_RESERVED_1 = 1
IMAGE_DLLCHARACTERISTICS_RESERVED_2 = 2
IMAGE_DLLCHARACTERISTICS_RESERVED_4 = 4
IMAGE_DLLCHARACTERISTICS_RESERVED_8 = 8
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 # The DLL can be relocated at load time.
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080 # Code integrity checks are forced. If you set this flag and a section contains only uninitialized data, set the PointerToRawData member of IMAGE_SECTION_HEADER for that section to zero; otherwise, the image will fail to load because the digital signature cannot be verified.
IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100 # The image is compatible with data execution prevention (DEP).
IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200 # The image is isolation aware, but should not be isolated.
IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400 # The image does not use structured exception handling (SEH). No handlers can be called in this image.
IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800 # Do not bind the image.
IMAGE_DLLCHARACTERISTICS_RESERVED_1000 = 0x1000 # Reserved
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000 # A WDM driver.
IMAGE_DLLCHARACTERISTICS_RESERVED_4000 = 0x4000 # Reserved
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
IMAGE_FILE_MACHINE_I386 = 0x014c
IMAGE_FILE_MACHINE_IA64 = 0x0200
IMAGE_FILE_MACHINE_AMD64 = 0x8664
machine_names = {
IMAGE_FILE_MACHINE_I386: 'i386',
IMAGE_FILE_MACHINE_IA64: 'ia64',
IMAGE_FILE_MACHINE_AMD64: 'amd64',
}
IMAGE_REL_BASED_ABSOLUTE = 0
IMAGE_REL_BASED_HIGH = 1
IMAGE_REL_BASED_LOW = 2
IMAGE_REL_BASED_HIGHLOW = 3
IMAGE_REL_BASED_HIGHADJ = 4
IMAGE_REL_BASED_MIPS_JMPADDR = 5
IMAGE_REL_BASED_IA64_IMM64 = 9
IMAGE_REL_BASED_DIR64 = 10
IMAGE_DIRECTORY_ENTRY_EXPORT =0 # Export Directory
IMAGE_DIRECTORY_ENTRY_IMPORT =1 # Import Directory
IMAGE_DIRECTORY_ENTRY_RESOURCE =2 # Resource Directory
IMAGE_DIRECTORY_ENTRY_EXCEPTION =3 # Exception Directory
IMAGE_DIRECTORY_ENTRY_SECURITY =4 # Security Directory
IMAGE_DIRECTORY_ENTRY_BASERELOC =5 # Base Relocation Table
IMAGE_DIRECTORY_ENTRY_DEBUG =6 # Debug Directory
IMAGE_DIRECTORY_ENTRY_COPYRIGHT =7 # (X86 usage)
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE =7 # Architecture Specific Data
IMAGE_DIRECTORY_ENTRY_GLOBALPTR =8 # RVA of GP
IMAGE_DIRECTORY_ENTRY_TLS =9 # TLS Directory
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG =10 # Load Configuration Directory
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT =11 # Bound Import Directory in headers
IMAGE_DIRECTORY_ENTRY_IAT =12 # Import Address Table
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT =13 # Delay Load Import Descriptors
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR =14 # COM Runtime descriptor
IMAGE_DEBUG_TYPE_UNKNOWN =0
IMAGE_DEBUG_TYPE_COFF =1
IMAGE_DEBUG_TYPE_CODEVIEW =2
IMAGE_DEBUG_TYPE_FPO =3
IMAGE_DEBUG_TYPE_MISC =4
IMAGE_DEBUG_TYPE_EXCEPTION =5
IMAGE_DEBUG_TYPE_FIXUP =6
IMAGE_DEBUG_TYPE_OMAP_TO_SRC =7
IMAGE_DEBUG_TYPE_OMAP_FROM_SRC =8
IMAGE_DEBUG_TYPE_BORLAND =9
IMAGE_DEBUG_TYPE_RESERVED10 =10
IMAGE_DEBUG_TYPE_CLSID =11
IMAGE_SCN_CNT_CODE = 0x00000020
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
IMAGE_SCN_LNK_OTHER = 0x00000100
IMAGE_SCN_LNK_INFO = 0x00000200
IMAGE_SCN_LNK_REMOVE = 0x00000800
IMAGE_SCN_LNK_COMDAT = 0x00001000
IMAGE_SCN_MEM_FARDATA = 0x00008000
IMAGE_SCN_MEM_PURGEABLE = 0x00020000
IMAGE_SCN_MEM_16BIT = 0x00020000
IMAGE_SCN_MEM_LOCKED = 0x00040000
IMAGE_SCN_MEM_PRELOAD = 0x00080000
IMAGE_SCN_ALIGN_1BYTES = 0x00100000
IMAGE_SCN_ALIGN_2BYTES = 0x00200000
IMAGE_SCN_ALIGN_4BYTES = 0x00300000
IMAGE_SCN_ALIGN_8BYTES = 0x00400000
IMAGE_SCN_ALIGN_16BYTES = 0x00500000
IMAGE_SCN_ALIGN_32BYTES = 0x00600000
IMAGE_SCN_ALIGN_64BYTES = 0x00700000
IMAGE_SCN_ALIGN_128BYTES = 0x00800000
IMAGE_SCN_ALIGN_256BYTES = 0x00900000
IMAGE_SCN_ALIGN_512BYTES = 0x00A00000
IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000
IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000
IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000
IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000
IMAGE_SCN_ALIGN_MASK = 0x00F00000
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000
IMAGE_SCN_MEM_NOT_PAGED = 0x08000000
IMAGE_SCN_MEM_SHARED = 0x10000000
IMAGE_SCN_MEM_EXECUTE = 0x20000000
IMAGE_SCN_MEM_READ = 0x40000000
IMAGE_SCN_MEM_WRITE = 0x80000000
# Flags for the UNWIND_INFO flags field from
# RUNTIME_FUNCTION defs
UNW_FLAG_NHANDLER = 0x0
UNW_FLAG_EHANDLER = 0x1
UNW_FLAG_UHANDLER = 0x2
UNW_FLAG_CHAININFO = 0x4
# Resource Types
RT_CURSOR = 1
RT_BITMAP = 2
RT_ICON = 3
RT_MENU = 4
RT_DIALOG = 5
RT_STRING = 6
RT_FONTDIR = 7
RT_FONT = 8
RT_ACCELERATOR = 9
RT_RCDATA = 10
RT_MESSAGETABLE = 11
RT_GROUP_CURSOR = 12
RT_GROUP_ICON = 14
RT_VERSION = 16
RT_DLGINCLUDE = 17
RT_PLUGPLAY = 19
RT_VXD = 20
RT_ANICURSOR = 21
RT_ANIICON = 22
RT_HTML = 23
RT_MANIFEST = 24
[docs]class VS_VERSIONINFO:
'''
A simple (read-only) VS_VERSIONINFO parser
'''
def __init__(self, bytes):
self._version_info = {}
self._parseBytes(bytes)
[docs] def getVersionValue(self, key, default=None):
'''
Retrieve a key from the VS_VERSIONINFO data.
Example: vs.getVersionValue('FileVersion')
'''
return self._version_info.get(key, default)
[docs] def getVersionKeys(self):
'''
Return a list of the keys in this VS_VERSIONINFO struct.
Example: for keyname in vs.getVersionKeys(): print keyname
'''
return self._version_info.keys()
[docs] def getVersionItems(self):
'''
Return dictionary style key,val tuples for the version keys
in this VS_VERSIONINFO structure.
Example: for vskey,vsdata in vs.getVersionItems(): print vskey,vsdata
'''
return self._version_info.items()
def _parseBytes(self, bytes):
offset = 0
mysize, valsize, vstype = struct.unpack('<HHH', bytes[:6])
offset += 6
offset, vinfosig = self._eatStringAndAlign(bytes, offset)
if vinfosig != 'VS_VERSION_INFO':
Exception('Invalid VS_VERSION_INFO signature!: %s' % repr(vinfosig))
if valsize:
ffinfo = vs_pe.VS_FIXEDFILEINFO()
ffinfo.vsParse(bytes[offset:offset+valsize])
offset += valsize
offmod = offset % 4
if offmod:
offset += (4 - offmod)
xmax = min(mysize, len(bytes))
i = 0
while offset < xmax and i < 2:
offset = self._stringFileInfo(bytes, offset)
i += 1
def _eatStringAndAlign(self, bytes, offset):
ret = ''
blen = len(bytes)
while bytes[offset:offset+2] != '\x00\x00':
ret += bytes[offset:offset+2]
offset += 2
if offset >= blen:
break
# Add 2 for the null terminator
offset += 2
offmod = offset % 4
if offmod:
offset += (4 - offmod)
return offset, ret.decode('utf-16le')
def _stringFileInfo(self, bytes, offset):
xoffset = offset
mysize, valsize, valtype = struct.unpack('<HHH', bytes[xoffset:xoffset+6])
xoffset += 6
xoffset, sigstr = self._eatStringAndAlign(bytes, xoffset)
if sigstr not in ('VarFileInfo','StringFileInfo'):
raise Exception('Invalid StringFileInfo Key!: %s' % repr(sigstr))
xmax = offset + mysize
if sigstr == 'StringFileInfo':
while xoffset < xmax:
xoffset = self._stringTable(bytes, xoffset, mysize - (xoffset-offset))
elif sigstr == 'VarFileInfo':
while xoffset < xmax:
xoffset = self._varTable(bytes, xoffset, mysize - (xoffset-offset))
xmod = xoffset % 4
if xmod:
xoffset += (4 - xmod)
return xoffset
def _varTable(self, bytes, offset, size):
xmax = offset + size
xoffset = offset
mysize, valsize, valtype = struct.unpack('<HHH', bytes[xoffset:xoffset+6])
xoffset += 6
xoffset, varname = self._eatStringAndAlign(bytes, xoffset)
varval = struct.unpack('<I', bytes[xoffset:xoffset+4])[0]
xoffset += 4
self._version_info[varname] = varval
return offset + size
def _stringTable(self, bytes, offset, size):
xmax = offset + size
xoffset = offset
mysize, valsize, valtype = struct.unpack('<HHH', bytes[offset:offset+6])
xoffset += 6
xoffset, hexcpage = self._eatStringAndAlign(bytes, xoffset)
while xoffset < xmax:
xoffset = self._stringData(bytes, xoffset)
xmod = xoffset % 4
if xmod:
xoffset += (4 - xmod)
return offset + size
def _stringData(self, bytes, offset):
'''
Parse out a "String" structure...
'''
xoffset = offset
mysize, valsize, stype = struct.unpack('<HHH', bytes[offset:offset+6])
if mysize == 0: raise Exception()
xoffset += 6
xoffset, strkey = self._eatStringAndAlign(bytes, xoffset)
# valsize is in words...
valsize *= 2
value = bytes[xoffset : xoffset + valsize ]
# Do utf16le decode if we're "textual data"
if stype == 1:
value = value.decode('utf-16le','ignore')
value = value.strip('\x00')
#print 'VALSIZE',valsize,'MYSIZE',mysize
#print 'Key: ->%s<-, ->%s<-' % (strkey,repr(value))
self._version_info[strkey] = value
# No matter what we parse, believe the headers...
return offset + mysize
[docs]class ResourceDirectory:
'''
Resources are sorted into a hierarchy which begins with
"type" and then "name/id" which still points to another
directory entry which has 1 child (id 1033) with data.
'''
def __init__(self):
self._rsrc_subdirs = {}
self._rsrc_data = []
[docs] def addRsrcDirectory(self, name_id):
r = ResourceDirectory()
self._rsrc_subdirs[name_id] = r
return r
[docs] def addRsrcData(self, rva, size, codepage):
self._rsrc_data.append( (rva, size, codepage) )
[docs] def getDirById(self, name_id):
return self._rsrc_subdirs.get(name_id)
#todo = [ self, ]
#while len(todo):
#curdir = todo.pop()
#for rname_id, resdir in curdir._rsrc_subdirs.items():
#if rname_id == name_id:
##return resdir
#yield resdir
#todo.append(resdir)
[docs] def getResourceDef(self, restype, name_id):
'''
This should *only* be called on the root node!
'''
typedir = self._rsrc_subdirs.get(restype)
if typedir == None:
return None
datadir = typedir._rsrc_subdirs.get(name_id)
if datadir == None:
return None
# The first entry in the datadir's data is the one
return datadir._rsrc_data[0]
[docs] def getDataEntries(self):
return self._rsrc_data
[docs]class PE(object):
def __init__(self, fd, inmem=False):
"""
Construct a PE object. use inmem=True if you are
using a MemObjFile or other "memory like" image.
"""
object.__init__(self)
self.inmem = inmem
fd.seek(0)
self.fd = fd
self.pe32p = False
self.psize = 4
self.high_bit_mask = 0x80000000
self.IMAGE_DOS_HEADER = vstruct.getStructure("pe.IMAGE_DOS_HEADER")
dosbytes = self.readAtOffset(0, len(self.IMAGE_DOS_HEADER))
self.IMAGE_DOS_HEADER.vsParse(dosbytes)
nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew,
"pe.IMAGE_NT_HEADERS")
# Parse in a default 32 bit, and then check for 64...
if nt.FileHeader.Machine in [ IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 ]:
nt = self.readStructAtOffset(self.IMAGE_DOS_HEADER.e_lfanew,
"pe.IMAGE_NT_HEADERS64")
self.pe32p = True
self.psize = 8
self.high_bit_mask = 0x8000000000000000
self.IMAGE_NT_HEADERS = nt
[docs] def getPdataEntries(self):
sec = self.getSectionByName('.pdata')
if sec == None:
return ()
ret = []
rbytes = self.readAtRva(sec.VirtualAddress, sec.VirtualSize)
while len(rbytes):
f = vs_pe.IMAGE_RUNTIME_FUNCTION_ENTRY()
f.vsParse(rbytes)
rbytes = rbytes[len(f):]
ret.append(f)
return ret
[docs] def getDllName(self):
'''
Return the "dll name" from the Name field of the IMAGE_EXPORT_DIRECTORY
if one is present. If not, return None.
'''
if self.IMAGE_EXPORT_DIRECTORY != None:
rawname = self.readAtRva(self.IMAGE_EXPORT_DIRECTORY.Name, 32)
return rawname.split('\x00')[0]
return None
[docs] def getImports(self):
"""
Return the list of import tuples for this PE. The tuples
are in the format (rva, libname, funcname).
"""
return self.imports
[docs] def getExports(self):
"""
Return the list of exports in this PE. The list contains
tuples in the format; (rva, ord, name).
"""
return self.exports
[docs] def getForwarders(self):
"""
[ (rva, name, forwardname), ... ]
"""
return self.forwarders
[docs] def getSections(self):
return self.sections
[docs] def rvaToOffset(self, rva):
if self.inmem:
return rva
for s in self.sections:
sbase = s.VirtualAddress
ssize = s.VirtualSize
if rva >= sbase and rva < (sbase + ssize):
return s.PointerToRawData + (rva - sbase)
return 0
[docs] def getSectionByName(self, name):
for s in self.getSections():
if s.Name.split("\x00", 1)[0] == name:
return s
return None
[docs] def readStructAtRva(self, rva, structname, check=False):
s = vstruct.getStructure(structname)
slen = len(s)
if check and not self.checkRva(rva, size=slen):
return None
bytes = self.readAtRva(rva, len(s))
s.vsParse(bytes)
return s
[docs] def readStructAtOffset(self, offset, structname):
s = vstruct.getStructure(structname)
sbytes = self.readAtOffset(offset, len(s))
#print "%s [%s]: %s" % (structname, offset, sbytes.encode('hex'))
s.vsParse(sbytes)
return s
[docs] def getDataDirectory(self, idx):
return self.IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[idx]
[docs] def getResourceDef(self, rtype, name_id):
'''
Get the (rva, size, codepage) tuple for the specified
resource type/id combination. Returns None if not found.
'''
return self.ResourceRoot.getResourceDef(rtype, name_id)
[docs] def getResources(self):
'''
Get the (rtype, nameid, (rva, size, codepage)) tuples for each
resource in the PE.
'''
ret = []
for rtype,subdir in self.ResourceRoot._rsrc_subdirs.items():
for nameid, subsubdir in subdir._rsrc_subdirs.items():
ret.append( (rtype, nameid, subsubdir._rsrc_data[0]) )
return ret
[docs] def readResource(self, rtype, name_id):
'''
Return the bytes which define the specified resource. Returns
None if not found.
'''
rsdef = self.getResourceDef(rtype, name_id)
if rsdef == None:
return None
rsrva, rssize, rscpage = rsdef
return self.readAtRva(rsrva, rssize)
[docs] def getVS_VERSIONINFO(self):
'''
Get a VS_VERSIONINFO object for this PE.
(returns None if version resource is not found)
'''
vbytes = self.readResource(RT_VERSION, 1)
if vbytes == None:
return None
return VS_VERSIONINFO(vbytes)
[docs] def parseResources(self):
self.ResourceRoot = ResourceDirectory()
# RP BUG FIX - Binaries can have a .rsrc section it doesn't mean that the .rsrc section contains the resource data we think it does
# validate .rsrc == RESOURCE Section by checking data directory entries...
dresc = self.getDataDirectory(IMAGE_DIRECTORY_ENTRY_RESOURCE)
if not dresc.VirtualAddress:
return
rsrc_todo = [ (dresc.VirtualAddress, self.ResourceRoot), ]
while len(rsrc_todo):
rsrva, rsdirobj = rsrc_todo.pop()
rsdir = self.readStructAtRva( rsrva, 'pe.IMAGE_RESOURCE_DIRECTORY', check=True )
if rsdir == None:
continue
totcount = rsdir.NumberOfIdEntries + rsdir.NumberOfNamedEntries
offset = len(rsdir)
for i in xrange(totcount):
dentrva = rsrva + offset
dirent = self.readStructAtRva( dentrva, 'pe.IMAGE_RESOURCE_DIRECTORY_ENTRY', check=True )
if dirent == None:
break
# We use name/id interchangably in the python dict...
name_id = None
if dirent.Name & 0x80000000: # If high bit is set, it's a string!
namerva = dresc.VirtualAddress + (dirent.Name & 0x7fffffff)
namelen_bytes = self.readAtRva(namerva, 2)
namelen = struct.unpack('<H', namelen_bytes)[0]
name_id = self.readAtRva(namerva + 2, namelen * 2).decode('utf-16le', 'ignore')
else:
name_id = dirent.Name
# if OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY then we have another directory
if dirent.OffsetToData & 0x80000000:
# This points to a subdirectory
subdir = rsdirobj.addRsrcDirectory(name_id)
rsrc_todo.append( (dresc.VirtualAddress + (dirent.OffsetToData & 0x7fffffff), subdir) )
else:
subdata = self.readStructAtRva( dresc.VirtualAddress + dirent.OffsetToData, 'pe.IMAGE_RESOURCE_DATA_ENTRY')
# RP BUG FIX - sanity check the subdata
if self.checkRva(subdata.OffsetToData, size=subdata.Size):
rsdirobj.addRsrcData(subdata.OffsetToData, subdata.Size, subdata.CodePage)
#print 'Data %s : 0x%.8x (%d)' % (name_id, sec.VirtualAddress + subdata.OffsetToData, subdata.Size)
#print repr(self.readAtRva(subdata.OffsetToData, min(subdata.Size, 40) ))
offset += len(dirent)
#print dirent.tree()
[docs] def parseSections(self):
self.sections = []
off = self.IMAGE_DOS_HEADER.e_lfanew + len(self.IMAGE_NT_HEADERS)
secsize = len(vstruct.getStructure("pe.IMAGE_SECTION_HEADER"))
sbytes = self.readAtOffset(off, secsize * self.IMAGE_NT_HEADERS.FileHeader.NumberOfSections)
while sbytes:
s = vstruct.getStructure("pe.IMAGE_SECTION_HEADER")
s.vsParse(sbytes[:secsize])
self.sections.append(s)
sbytes = sbytes[secsize:]
[docs] def readAtRva(self, rva, size):
offset = self.rvaToOffset(rva)
return self.readAtOffset(offset, size)
[docs] def readAtOffset(self, offset, size):
ret = ""
self.fd.seek(offset)
while len(ret) != size:
rlen = size - len(ret)
x = self.fd.read(rlen)
if x == "":
raise Exception("EOF In readAtOffset()")
ret += x
return ret
[docs] def parseLoadConfig(self):
self.IMAGE_LOAD_CONFIG = None
cdir = self.getDataDirectory(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG)
rva = cdir.VirtualAddress
# RP BUG FIX - validate config directory
if self.checkRva(rva, size=cdir.Size):
self.IMAGE_LOAD_CONFIG = self.readStructAtRva(rva, "pe.IMAGE_LOAD_CONFIG_DIRECTORY")
[docs] def readPointerAtOffset(self, off):
fmt = "<L"
if self.psize == 8:
fmt = "<Q"
return struct.unpack(fmt, self.readAtOffset(off, self.psize))[0]
[docs] def readPointerAtRva(self, rva):
off = self.rvaToOffset(rva)
return self.readPointerAtOffset(off)
[docs] def getMaxRva(self):
return self.IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage
[docs] def checkRva(self, rva, size=None):
'''
Make sure an RVA falls inside the valid mapped range
for the file. (also make sure it's not 0...)
'''
if rva == 0:
return False
isize = self.getMaxRva()
if rva > isize:
#raise Exception('too high! %d > %d' % (rva, isize))
return False
if size != None and (rva + size) > isize:
#raise Exception('too big! %d > %d' % (rva+size, isize))
return False
return True
[docs] def readStringAtRva(self, rva, maxsize=None):
ret = ''
while True:
if maxsize and maxsize <= len(ret):
break
x = self.readAtRva(rva, 1)
if x == '\x00':
break
ret += x
rva += 1
return ret
[docs] def parseImports(self):
self.imports = []
idir = self.getDataDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT)
# RP BUG FIX - invalid IAT entry will point of range of file
irva = idir.VirtualAddress
x = self.readStructAtRva(irva, 'pe.IMAGE_IMPORT_DIRECTORY', check=True)
if x == None:
return
isize = len(x)
while self.checkRva(x.Name):
# RP BUG FIX - we can't assume that we have 256 bytes to read
libname = self.readStringAtRva(x.Name, maxsize=256)
idx = 0
imp_by_name = x.OriginalFirstThunk
if imp_by_name == 0:
imp_by_name = x.FirstThunk
if not self.checkRva(imp_by_name):
break
while True:
arrayoff = self.psize * idx
ibn_rva = self.readPointerAtRva(imp_by_name+arrayoff)
if ibn_rva == 0:
break
if ibn_rva & self.high_bit_mask:
funcname = ordlookup.ordLookup(libname, ibn_rva & 0x7fffffff)
else:
# RP BUG FIX - we can't use this API on this call because we can have binaries that put their import table
# right at the end of the file, statically saying the imported function name is 128 will cause use to potentially
# over run our read and traceback...
diff = self.getMaxRva() - ibn_rva - 2
ibn = vstruct.getStructure("pe.IMAGE_IMPORT_BY_NAME")
ibn.vsGetField('Name').vsSetLength( min(diff, 128) )
bytes = self.readAtRva(ibn_rva, len(ibn))
ibn.vsParse(bytes)
funcname = ibn.Name
self.imports.append((x.FirstThunk+arrayoff,libname,funcname))
idx += 1
irva += isize
# RP BUG FIX - if the import table is at the end of the file we can't count on the ending to be null
if not self.checkRva(irva, size=isize):
break
x.vsParse(self.readAtRva(irva, isize))
[docs] def getRelocations(self):
"""
Return the list of RVA base-relocations in this PE.
"""
return self.relocations
[docs] def parseRelocations(self):
self.relocations = []
edir = self.getDataDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC)
rva = edir.VirtualAddress
rsize = edir.Size
# RP BUG FIX - don't watn to read past the end of the file
if not self.checkRva(rva):
return
reloff = self.rvaToOffset(rva)
relbytes = self.readAtOffset(reloff, rsize)
while relbytes:
pageva, chunksize = struct.unpack("<LL", relbytes[:8])
relcnt = (chunksize - 8) / 2
# RP BUG FIX - when the reloc section has no fixups but the directory exists..
if not chunksize or not pageva:
return
# RP BUG FIX - sometimes the chunksize is invalid we do a quick check to make sure we dont overrun the buffer
if chunksize > len(relbytes):
return
rels = struct.unpack("<%dH" % relcnt, relbytes[8:chunksize])
for r in rels:
rtype = r >> 12
roff = r & 0xfff
self.relocations.append((pageva+roff, rtype))
relbytes = relbytes[chunksize:]
[docs] def getExportName(self):
'''
Return the name of this file acording to it's export entry.
(if there are no exports, return None)
'''
e = self.IMAGE_EXPORT_DIRECTORY
if e == None:
return None
return self.readAtRva(e.Name, 128).split('\x00')[0]
[docs] def parseExports(self):
# Initialize our required locals.
self.exports = []
self.forwarders = []
self.IMAGE_EXPORT_DIRECTORY = None
edir = self.getDataDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
poff = self.rvaToOffset(edir.VirtualAddress)
if poff == 0: # No exports...
return
self.IMAGE_EXPORT_DIRECTORY = self.readStructAtOffset(poff, "pe.IMAGE_EXPORT_DIRECTORY")
funcoff = self.rvaToOffset(self.IMAGE_EXPORT_DIRECTORY.AddressOfFunctions)
funcsize = 4 * self.IMAGE_EXPORT_DIRECTORY.NumberOfFunctions
nameoff = self.rvaToOffset(self.IMAGE_EXPORT_DIRECTORY.AddressOfNames)
namesize = 4 * self.IMAGE_EXPORT_DIRECTORY.NumberOfNames
ordoff = self.rvaToOffset(self.IMAGE_EXPORT_DIRECTORY.AddressOfOrdinals)
ordsize = 2 * self.IMAGE_EXPORT_DIRECTORY.NumberOfNames
# RP BUG FIX - sanity check the exports before reading
if not funcoff or not ordoff and not nameoff or funcsize > 0x7FFF:
return
funcbytes = self.readAtOffset(funcoff, funcsize)
namebytes = self.readAtOffset(nameoff, namesize)
ordbytes = self.readAtOffset(ordoff, ordsize)
funclist = struct.unpack("%dI" % (len(funcbytes) / 4), funcbytes)
namelist = struct.unpack("%dI" % (len(namebytes) / 4), namebytes)
ordlist = struct.unpack("%dH" % (len(ordbytes) / 2), ordbytes)
#for i in range(len(funclist)):
for i in range(len(namelist)):
ord = ordlist[i]
nameoff = self.rvaToOffset(namelist[i])
funcoff = funclist[ord]
ffoff = self.rvaToOffset(funcoff)
name = None
if nameoff != 0:
name = self.readAtOffset(nameoff, 256).split("\x00", 1)[0]
else:
name = "ord_%.4x" % ord
if ffoff >= poff and ffoff < poff + edir.Size:
fwdname = self.readAtOffset(ffoff, 260).split("\x00", 1)[0]
self.forwarders.append((funclist[ord],name,fwdname))
else:
self.exports.append((funclist[ord], ord, name))
def __getattr__(self, name):
"""
Use a getattr over-ride to allow "on demand" parsing of particular sections.
"""
if name == "exports":
self.parseExports()
return self.exports
elif name == "IMAGE_IMPORT_DIRECTORY":
self.parseImports()
return self.IMAGE_IMPORT_DIRECTORY
elif name == "imports":
self.parseImports()
return self.imports
elif name == "IMAGE_EXPORT_DIRECTORY":
self.parseExports()
return self.IMAGE_EXPORT_DIRECTORY
elif name == "forwarders":
self.parseExports()
return self.forwarders
elif name == "sections":
self.parseSections()
return self.sections
elif name == "ResourceRoot":
self.parseResources()
return self.ResourceRoot
elif name == "relocations":
self.parseRelocations()
return self.relocations
elif name == "IMAGE_LOAD_CONFIG":
self.parseLoadConfig()
return self.IMAGE_LOAD_CONFIG
else:
raise AttributeError
[docs]class MemObjFile:
"""
A file like object that wraps a MemoryObject (envi) compatable
object with a file-like object where seek == VA.
"""
def __init__(self, memobj, baseaddr):
self.baseaddr = baseaddr
self.offset = baseaddr
self.memobj = memobj
[docs] def seek(self, offset):
self.offset = self.baseaddr + offset
[docs] def read(self, size):
ret = self.memobj.readMemory(self.offset, size)
self.offset += size
return ret
[docs] def write(self, bytes):
self.memobj.writeMemory(self.offset, bytes)
self.offset += len(bytes)
[docs]def peFromMemoryObject(memobj, baseaddr):
fd = MemObjFile(memobj, baseaddr)
return PE(fd, inmem=True)
[docs]def peFromFileName(fname):
"""
Utility helper that assures that the file is opened in
binary mode which is required for proper functioning.
"""
f = file(fname, "rb")
return PE(f)
[docs]def peFromBytes(fbytes):
fd = StringIO(fbytes)
return PE(fd)