class ResourceReaderHeader(pstruct.type): class _Types(pstruct.type): _fields_ = [ (pint.uint32_t, 'Count'), (lambda s: dyn.array(ResourceString, s['Count'].li.int()), 'Name'), ] def __Padding(self): cb = 0 ri = self.getparent(ResourceInfo) cb += ri['Magic'].li.size() rfi = self.getparent(ResourceFileInfo) cb += rfi['Manager'].li.size() cb += sum(self[n].li.size() for n in ('Version', 'Count', 'Types')) top = ri.getoffset() + ri['Size'].li.size() bottom = top + cb res = (bottom - top) & 7 return dyn.block((8 - res) if res > 0 else 0) _fields_ = [ (pint.uint32_t, 'Version'), (pint.uint32_t, 'Count'), (_Types, 'Types'), (__Padding, 'Padding'), (lambda s: dyn.array(pint.uint32_t, s['Count'].li.int()), 'Hash'), (lambda s: dyn.array(pint.uint32_t, s['Count'].li.int()), 'NameOffset'), (pint.uint32_t, 'DataOffset'), ]
class IMAGE_ENCLAVE_CONFIG64(pstruct.type): def blocksize(self): # If we're already loaded, then we can just read our size field. If we're not # loaded yet, then to get the size we need to cheat by duplicating the instance, # allocating with the original blocksize, and then taking its loaded size. Fblocksize = super(IMAGE_ENCLAVE_CONFIG64, self).blocksize return self['Size'].li.int() if self.value else self.copy( blocksize=Fblocksize).a.size() def __ImportList(self): count, size = (self[fld].li for fld in ['NumberOfImports', 'ImportEntrySize']) # FIXME: set the size of IMAGE_ENCLAVE_IMPORT to the ImportEntrySize t = dyn.array(IMAGE_ENCLAVE_IMPORT, count.int()) return virtualaddress(t, type=DWORD) _fields_ = [ (DWORD, 'Size'), (DWORD, 'MinimumRequiredConfigSize'), (IMAGE_ENCLAVE_POLICY_, 'PolicyFlags'), (DWORD, 'NumberOfImports'), (__ImportList, 'ImportList'), (DWORD, 'ImportEntrySize'), (dyn.array(BYTE, IMAGE_ENCLAVE_SHORT_ID_LENGTH), 'FamilyID'), (dyn.array(BYTE, IMAGE_ENCLAVE_SHORT_ID_LENGTH), 'ImageID'), (DWORD, 'ImageVersion'), (DWORD, 'SecurityVersion'), (ULONGLONG, 'EnclaveSize'), (DWORD, 'NumberOfThreads'), (IMAGE_ENCLAVE_FLAG_, 'EnclaveFlags'), ]
class IMAGE_ENCLAVE_IMPORT(pstruct.type): _fields_ = [ (IMAGE_ENCLAVE_IMPORT_MATCH_, 'MatchType'), (DWORD, 'MinimumSecurityVersion'), (dyn.array(BYTE, IMAGE_ENCLAVE_SHORT_ID_LENGTH), 'UniqueOrAuthorID'), (dyn.array(BYTE, IMAGE_ENCLAVE_SHORT_ID_LENGTH), 'FamilyID'), (dyn.array(BYTE, IMAGE_ENCLAVE_SHORT_ID_LENGTH), 'ImageID'), (virtualaddress(pstr.szstring, type=DWORD), 'ImportName'), (DWORD, 'Reserved'), ]
class IMAGE_LOADCONFIG_DIRECTORY64(pstruct.type): _fields_ = [ (uint32, 'Size'), (TimeDateStamp, 'TimeDateStamp'), (uint16, 'MajorVersion'), (uint16, 'MinorVersion'), (uint32, 'GlobalFlagsClear'), (uint32, 'GlobalFlagsSet'), (uint32, 'CriticalSectionDefaultTimeout'), (uint64, 'DeCommitFreeBlockThreshold'), (uint64, 'DeCommitTotalFreeThreshold'), (realaddress(ptype.undefined, type=uint64), 'LockPrefixTable'), (uint64, 'MaximumAllocationSize'), (uint64, 'VirtualMemoryThreshold'), (uint64, 'ProcessAffinityMask'), (uint32, 'ProcessHeapFlags'), (uint16, 'CSDVersion'), (uint16, 'Reserved1'), (realaddress(ptype.undefined, type=uint64), 'EditList'), (realaddress(uint64, type=uint64), 'SecurityCookie'), (realaddress( lambda s: dyn.array(uint64, s.parent['SEHandlerCount'].li.int()), type=uint64), 'SEHandlerTable'), (uint64, 'SEHandlerCount'), (realaddress(uint64, type=uint64), 'GuardCFCheckFunctionPointer'), (realaddress(uint64, type=uint64), 'GuardCFDispatchFunctionPointer'), (realaddress(lambda s: dyn.array( uint64, s.parent['GuardCFFunctionCount'].li.int()), type=uint64), 'GuardCFFunctionTable'), (uint64, 'GuardCFFunctionCount'), (pbinary.littleendian(IMAGE_GUARD_), 'GuardFlags'), (IMAGE_LOAD_CONFIG_CODE_INTEGRITY, 'CodeIntegrity'), (realaddress(lambda s: dyn.array( uint64, s.parent['GuardAddressTakenIatEntryCount'].li.int()), type=uint64), 'GuardAddressTakenIatEntryTable'), (uint64, 'GuardAddressTakenIatEntryCount'), (realaddress(lambda s: dyn.array( uint64, s.parent['GuardLongJumpTargetCount'].li.int()), type=uint64), 'GuardLongJumpTargetTable'), (uint64, 'GuardLongJumpTargetCount'), (realaddress(ptype.undefined, type=uint64), 'DynamicValueRelocTable'), (realaddress(ptype.undefined, type=uint64), 'CHPEMetadataPointer'), (realaddress(uint64, type=uint64), 'GuardRFFailureRoutine'), (realaddress(uint64, type=uint64), 'GuardRFFailureRoutineFunctionPointer'), (uint32, 'DynamicValueRelocTableOffset'), (uint16, 'DynamicValueRelocTableSection'), (uint16, 'Reserved2'), (realaddress(uint64, type=uint64), 'GuardRFVerifyStackPointerFunctionPointer'), (uint32, 'HotPatchTableOffset'), (uint32, 'Reserved3'), (realaddress(pstr.szwstring, type=uint64), 'AddressOfSomeUnicodeString'), ]
def _object_(self): res = self.getparent(HTables) index, lengths = len(self.value), res['Rows'].li count = lengths[index].int() if count: cls = self.__class__ logging.debug( "{:s} : {:s} : Loading {:s}({:d}) table with {:d} rows. : {:d} of {:d}" .format('.'.join((res.typename(), cls.__name__)), self.instance(), TableType.byvalue(index, 'undefined'), index, count, 1 + len(self.value), self.length)) rowtype = Table.withdefault(index, type=index) rowsize = rowtype.PreCalculateSize(res) if Table.has(index) else 0 tr, tn = TableRow.__name__, TableType.byvalue(index, None) t = dyn.clone( TableRow, _object_=rowtype, _value_=dyn.block(rowsize), typename=classmethod(lambda cls: "{:s}({:s})".format(tr, tn) if tn else tr)) def Get(self, index): if index > 0: return self[index - 1] raise IndexError(index) return dyn.array(t, count, Get=Get, blocksize=(lambda s, cb=rowsize * count: cb))
def __ImportList(self): count, size = (self[fld].li for fld in ['NumberOfImports', 'ImportEntrySize']) # FIXME: set the size of IMAGE_ENCLAVE_IMPORT to the ImportEntrySize t = dyn.array(IMAGE_ENCLAVE_IMPORT, count.int()) return virtualaddress(t, type=DWORD)
class ESTypeList(pstruct.type): _fields_ = [ (int32, 'nCount'), (lambda self: virtualaddress( dyn.array(HandlerType, self['nCount'].li.int()), type=dword), 'pHandlerArray'), ]
class UNWIND_INFO(pstruct.type): class Header(pbinary.struct): class UNW_FLAG_(pbinary.enum): width = 5 _values_ = [ ('NHANDLER', 0), ('EHANDLER', 1), ('UHANDLER', 2), ('FHANDLER', 3), ('CHAININFO', 4), ] _fields_ = [ (UNW_FLAG_, 'Flags'), (3, 'Version'), ] class Frame(pbinary.struct): _fields_ = [ (4, 'Register'), (4, 'Offset'), ] class ExceptionHandler(pstruct.type): _fields_ = [(virtualaddress(ptype.undefined, type=dword), 'Address'), (ptype.undefined, 'Data')] def __ExceptionHandler(self): h = self['Header'].l n = h.__field__('Flags') if (n.int() & (n.byname('EHANDLER') | n.byname('UHANDLER')) > 0) and (n.int() & n.byname('CHAININFO') == 0): return self.ExceptionHandler return ptype.undefined def __ChainedUnwindInfo(self): h = self['Header'].l n = h.__field__('Flags') if n.int() == n.byname('CHAININFO') > 0: return RUNTIME_FUNCTION return ptype.undefined _fields_ = [ (Header, 'Header'), (byte, 'SizeOfProlog'), (byte, 'CountOfCodes'), (Frame, 'Frame'), (lambda s: dyn.array(UNWIND_CODE, s['CountOfCodes'].li.int()), 'UnwindCode'), (dyn.align(4), 'align(ExceptionHandler)'), # FIXME: this was copied from IDA (__ExceptionHandler, 'ExceptionHandler'), (__ChainedUnwindInfo, 'ChainedUnwindInfo'), ]
class PermissionSet(pstruct.type): class Attribute(pstruct.type): _fields_ = [ (SerString, 'String'), (SerBlock, 'Properties'), ] _fields_ = [ (pint.uint8_t, 'period'), (CInt, 'count'), (lambda s: dyn.array(s.Attribute, s['count'].li.Get()), 'attributes'), ]
def __Entry(self): self = self.getparent(VtableFixup) cls = self.__class__ res = self['Type'].li if res['32BIT'] and res['64BIT']: logging.warn("{:s} : {:s} : Both 32-bit and 64-bit flag is set. Assuming 0-bit. : {!r}".format('.'.join((__name__,cls.__name__)), self.instance(), res.summary())) t = pint.uint_t elif not res['32BIT'] and not res['64BIT']: logging.warn("{:s} : {:s} : Neither 32-bit and 64-bit flag is set. Assuming 0-bit. : {!r}".format('.'.join((__name__,cls.__name__)), self.instance(), res.summary())) t = pint.uint_t else: t = pint.uint32_t if res['32BIT'] else pint.uint64_t if res['64BIT'] else pint.uint_t count = self['Size'].li.int() return dyn.array(t, count)
class IMAGE_DYNAMIC_RELOCATION64_V2(pstruct.type): class _Symbol(pint.enum, ULONGLONG): _values_ = [ ('PROLOGUE', 1), ('EPILOGUE', 2), ] _fields_ = [ (DWORD, 'HeaderSize'), (DWORD, 'FixupInfoSize'), (_Symbol, 'Symbol'), (DWORD, 'SymbolGroup'), (DWORD, 'Flags'), (lambda self: dyn.array(BYTE, self['FixupInfoSize'].li.int()), 'FixupInfo'), ]
def __Entry(self): self = self.getparent(VtableFixup) res = self['Type'].li if res['32BIT'] and res['64BIT']: logging.warn( "{:s} : {:s} : Both 32-bit and 64-bit flag is set. Assuming 0-bit. : {!r}" .format('.'.join((__name__, self.__class__.__name__)), self.instance(), res.summary())) t = pint.uint_t elif not res['32BIT'] and not res['64BIT']: logging.warn( "{:s} : {:s} : Neither 32-bit and 64-bit flag is set. Assuming 0-bit. : {!r}" .format('.'.join((__name__, self.__class__.__name__)), self.instance(), res.summary())) t = pint.uint_t else: t = pint.uint32_t if res[ '32BIT'] else pint.uint64_t if res['64BIT'] else pint.uint_t count = self['Size'].li.int() return dyn.array(t, count)
def _object_(self): res = self.getparent(HTables) index, lengths = len(self.value), res['Rows'].li count = lengths[index].int() if count: cls = self.__class__ logging.debug("{:s} : {:s} : Loading {:s}({:d}) table with {:d} rows. : {:d} of {:d}".format('.'.join((res.typename(),cls.__name__)), self.instance(), TableType.byvalue(index, 'undefined'), index, count, 1+len(self.value), self.length)) rowtype = Table.withdefault(index, type=index) rowsize = rowtype.PreCalculateSize(res) if Table.has(index) else 0 tr, tn = TableRow.__name__, TableType.byvalue(index, None) t = dyn.clone(TableRow, _object_=rowtype, _value_=dyn.block(rowsize), typename=classmethod(lambda cls: "{:s}({:s})".format(tr, tn) if tn else tr)) def Get(self, index): if index > 0: return self[index - 1] raise IndexError(index) return dyn.array(t, count, Get=Get, blocksize=(lambda s, cb=rowsize*count: cb))
class IMAGE_SECTION_HEADER(pstruct.type): """PE Executable Section Table Entry""" class IMAGE_SCN(pbinary.flags): _fields_ = [ (1, 'MEM_WRITE'), # 0x80000000 (1, 'MEM_READ'), # 0x40000000 (1, 'MEM_EXECUTE'), # 0x20000000 (1, 'MEM_SHARED'), # 0x10000000 (1, 'MEM_NOT_PAGED'), # 0x08000000 (1, 'MEM_NOT_CACHED'), # 0x04000000 (1, 'MEM_DISCARDABLE'), # 0x02000000 (1, 'LNK_NRELOC_OVFL'), # 0x01000000 # (1, 'ALIGN_8192BYTES'), # 0x00e00000 # (1, 'ALIGN_4096BYTES'), # 0x00d00000 # (1, 'ALIGN_2048BYTES'), # 0x00c00000 # (1, 'ALIGN_1024BYTES'), # 0x00b00000 # (1, 'ALIGN_512BYTES'), # 0x00a00000 # (1, 'ALIGN_256BYTES'), # 0x00900000 # (1, 'ALIGN_128BYTES'), # 0x00800000 # (1, 'ALIGN_64BYTES'), # 0x00700000 # (1, 'ALIGN_32BYTES'), # 0x00600000 # (1, 'ALIGN_16BYTES'), # 0x00500000 # (1, 'ALIGN_8BYTES'), # 0x00400000 # (1, 'ALIGN_4BYTES'), # 0x00300000 # (1, 'ALIGN_2BYTES'), # 0x00200000 # (1, 'ALIGN_1BYTES'), # 0x00100000 (4, 'ALIGN'), # 0x00?00000 (1, 'MEM_PRELOAD'), # 0x00080000 (1, 'MEM_LOCKED'), # 0x00040000 # (1, 'MEM_16BIT'), # 0x00020000 # ARM (1, 'MEM_PURGEABLE'), # 0x00020000 (1, 'reserved_16'), (1, 'GPREL'), # 0x00008000 (2, 'reserved_14'), (1, 'LNK_COMDAT'), # 0x00001000 (1, 'LNK_REMOVE'), # 0x00000800 (1, 'reserved_11'), (1, 'LNK_INFO'), # 0x00000200 (1, 'LNK_OTHER'), # 0x00000100 (1, 'CNT_UNINITIALIZED_DATA'), # 0x00000080 (1, 'CNT_INITIALIZED_DATA'), # 0x00000040 (1, 'CNT_CODE'), # 0x00000020 (1, 'reserved_4'), (1, 'TYPE_NO_PAD'), # 0x00000008 (3, 'reserved_0'), ] # FIXME: we can store a longer than 8 byte Name if we want to implement code that navigates to the string table # apparently executables don't care though... _fields_ = [ (dyn.clone(pstr.string, length=8), 'Name'), (uint32, 'VirtualSize'), (virtualaddress(lambda s:dyn.block(s.parent.getloadedsize()), type=uint32), 'VirtualAddress'), (uint32, 'SizeOfRawData'), (fileoffset(lambda s:dyn.block(s.parent.getreadsize()), type=uint32), 'PointerToRawData'), (fileoffset(lambda s:dyn.array(relocations.MachineRelocation.lookup(s.getparent(Header).Machine().int()), s.parent['NumberOfRelocations'].li.int()), type=uint32), 'PointerToRelocations'), (fileoffset(lambda s:dyn.array(linenumbers.LineNumber, s.parent['NumberOfLinenumbers'].li.int()), type=uint32), 'PointerToLinenumbers'), (uint16, 'NumberOfRelocations'), (uint16, 'NumberOfLinenumbers'), (pbinary.littleendian(IMAGE_SCN), 'Characteristics'), ] def getreadsize(self): portable = self.getparent(SectionTableArray) # if it's a portable executable, then apply the alignment try: nt = portable.p alignment = nt['OptionalHeader']['FileAlignment'].int() mask = alignment - 1 # otherwise, there's no alignment necessary except KeyError: mask = 0 res = (self['SizeOfRawData'].int() + mask) & ~mask return min((self.source.size() - self['PointerToRawData'].int(), res)) if hasattr(self.source, 'size') else res def getloadedsize(self): # XXX: even though the loadedsize is aligned to SectionAlignment, # the loader doesn't actually map data there and thus the # actual mapped size is rounded to pagesize # nt = self.getparent(Header) # alignment = nt['OptionalHeader']['SectionAlignment'].int() alignment = 0x1000 # pagesize mask = alignment - 1 return (self['VirtualSize'].int() + mask) & ~mask def containsaddress(self, address): start = self['VirtualAddress'].int() return True if (address >= start) and (address < start + self.getloadedsize()) else False def containsoffset(self, offset): start = self['PointerToRawData'].int() return True if (offset >= start) and (offset < start + self.getreadsize()) else False def data(self): return self['PointerToRawData'].d getrelocations = lambda self: self['PointerToRelocations'].d getlinenumbers = lambda self: self['NumberOfLinenumbers'].d ## offset means file offset def getoffsetbyaddress(self, address): return address - self['VirtualAddress'].int() + self['PointerToRawData'].int() def getaddressbyoffset(self, offset): return offset - self['PointerToRawData'].int() + self['VirtualAddress'].int()
class Fields(pstruct.type): _fields_ = [ (clr.CInt, 'Count'), (lambda s: dyn.array(CustomField, s['Count'].li.Get()), 'Fields'), ]
class IMAGE_EXPORT_DIRECTORY(pstruct.type): _p_AddressOfFunctions = lambda self: virtualaddress(dyn.array(FuncPointer, self['NumberOfFunctions'].li.int()), type=dword) _p_AddressOfNames = lambda self: virtualaddress(dyn.array(NamePointer, self['NumberOfNames'].li.int()), type=dword) _p_AddressOfNameOrdinals = lambda self: virtualaddress(dyn.array(Ordinal, self['NumberOfNames'].li.int()), type=dword) def __ExportData(self): res = sum(self[n].li.size() for _,n in self._fields_[:-1]) return dyn.block(self.blocksize() - res) _fields_ = [ ( dword, 'Flags' ), ( TimeDateStamp, 'TimeDateStamp' ), ( word, 'MajorVersion' ), ( word, 'MinorVersion' ), ( virtualaddress(pstr.szstring, type=dword), 'Name' ), ( dword, 'Base' ), ( dword, 'NumberOfFunctions' ), ( dword, 'NumberOfNames' ), ( _p_AddressOfFunctions, 'AddressOfFunctions' ), ( _p_AddressOfNames, 'AddressOfNames' ), ( _p_AddressOfNameOrdinals, 'AddressOfNameOrdinals' ), ( __ExportData, 'ExportData'), ] def GetNames(self): """Returns a list of all the export names""" Header = headers.locateHeader(self) cache, sections = {}, Header['Sections'] res = [] for va in self['AddressOfNames'].d.l: section = sections.getsectionbyaddress(va.int()) sectionva, data = cache[section.getoffset()] if section.getoffset() in cache else cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) nameofs = va.int() - sectionva res.append(utils.strdup(data[nameofs:].tostring())) return res def GetNameOrdinals(self): """Returns a list of all the Ordinals for each export""" Header = headers.locateHeader(self) address = self['AddressOfNameOrdinals'].int() section = Header['Sections'].getsectionbyaddress(address) sectionva = section['VirtualAddress'].int() base, offset = self['Base'].int(), address - sectionva data = section.data().load().serialize() block = data[offset: offset + 2*self['NumberOfNames'].int()] return [base+ordinal for ordinal in array.array('H', block)] def GetExportAddressTable(self): """Returns (export address table offset,[virtualaddress of each export]) from the export address table""" Header = headers.locateHeader(self) ExportDirectory = self.getparent(headers.IMAGE_DATA_DIRECTORY) address = self['AddressOfFunctions'].int() section = Header['Sections'].getsectionbyaddress(address) sectionva = section['VirtualAddress'].int() offset = address - sectionva data = section.data().l.serialize() block = data[offset: offset + 4*self['NumberOfFunctions'].int()] return address, array.array('L', block) def Hint(self, index): '''Returns the hint/ordinal of the specified export.''' aono = self['AddressOfNameOrdinals'] if aono.int() == 0: raise ValueError("{:s} : No ordinals found in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aono.summary())) # validate the index against the ordinals table ordinals = aono.d.li if not (0 <= index < len(ordinals)): raise IndexError("{:s} : Specified index {:d} is out of bounds for IMAGE_EXPORT_DIRECTORY. ({:d}{:+d})".format('.'.join((cls.__module__, cls.__name__)), index, 0, len(ordinals))) # got it res = ordinals[index] return res.int() def Offset(self, index): '''Returns the va for the address of the export.''' aof = self['AddressOfFunctions'] if aof.int() == 0: raise ValueError("{:s} : No functions found in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aof.summary())) # validate the ordinal hint = self.Hint(index) if not (0 <= hint < len(aof.d)): raise IndexError("{:s} : Specified ordinal {:d} is out of bounds for IMAGE_EXPORT_DIRECTORY. ({:d}{:+d})".format('.'.join((cls.__module__, cls.__name__)), hint, 0, len(aof.d))) # now we can figure the index into the function table res = headers.calculateRelativeOffset(self, aof.int()) return res + hint * 4 def Name(self, index): '''Returns the name of the specified export.''' aon = self['AddressOfNames'] if aon.int() == 0: raise ValueError("{:s} : No names found in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aon.summary())) # validate the index names = aon.d.li if not (0 <= index < len(names)): raise IndexError("{:s} : Specified index {:d} is out of bounds for IMAGE_EXPORT_DIRECTORY. ({:d}{:+d})".format('.'.join((cls.__module__, cls.__name__)), index, 0, len(names))) # got it res = names[index] return res.d.li.str() def Ordinal(self, index): hint = self.Hint(index) return hint + self['Base'].int() def OrdinalName(self, index): '''Returns the ordinal name for the specified export.''' return "Ordinal{:d}".format(self.Ordinal(index)) def ForwardQ(self, index): '''Returns True if the specified index is a forwarded export.''' ExportDirectory = self.getparent(headers.IMAGE_DATA_DIRECTORY) aof = self['AddressOfFunctions'] if aof.int() == 0: raise ValueError("{:s} : No functions found in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aof.summary())) # validate the ordinal hint = self.Hint(index) if not (0 <= hint < len(aof.d)): raise IndexError("{:s} : Specified ordinal {:d} is out of bounds for IMAGE_EXPORT_DIRECTORY. ({:d}{:+d})".format('.'.join((cls.__module__, cls.__name__)), hint, 0, len(aof.d))) # now check if it's a forwarded function or not va = aof.d.li[hint] return ExportDirectory.containsaddress(va.int()) def Entrypoint(self, index): '''Returns the va of the entrypoint for the specified export.''' ExportDirectory = self.getparent(headers.IMAGE_DATA_DIRECTORY) aof = self['AddressOfFunctions'] if aof.int() == 0: raise ValueError("{:s} : No functions found in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aof.summary())) # validate the ordinal hint = self.Hint(index) if not (0 <= hint < len(aof.d)): raise IndexError("{:s} : Specified ordinal {:d} is out of bounds for IMAGE_EXPORT_DIRECTORY. ({:d}{:+d})".format('.'.join((cls.__module__, cls.__name__)), hint, 0, len(aof.d))) # grab the address out of the function table va = aof.d.li[hint] return None if ExportDirectory.containsaddress(va.int()) else va.int() def ForwardTarget(self, index): '''Returns the string that the specified export is forwarded to.''' ExportDirectory = self.getparent(headers.IMAGE_DATA_DIRECTORY) aof = self['AddressOfFunctions'] if aof.int() == 0: raise ValueError("{:s} : No functions found in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aof.summary())) # validate the ordinal hint = self.Hint(index) if not (0 <= hint < len(aof.d)): raise IndexError("{:s} : Specified ordinal {:d} is out of bounds for IMAGE_EXPORT_DIRECTORY. ({:d}{:+d})".format('.'.join((cls.__module__, cls.__name__)), hint, 0, len(aof.d))) # grab the address out of the function table va = aof.d.li[hint] return va.d.li.str() if ExportDirectory.containsaddress(va.int()) else None def iterate(self): """For each export, yields (rva offset, hint, name, ordinalname, entrypoint, forwardedrva)""" cls = self.__class__ ExportDirectory = self.getparent(headers.IMAGE_DATA_DIRECTORY) Header, Base = headers.locateHeader(self), self['Base'].int() # our section data cache cache, sections = {}, Header['Sections'] # grab everything that's important aof, aon, aono = self['AddressOfFunctions'], self['AddressOfNames'], self['AddressOfNameOrdinals'] ## export address table if aof.int() > 0: # cache the section the eat is contained in since we need it anyways section = sections.getsectionbyaddress(aof.int()) cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) # convert the aof into an array that's wikiwiki data = aof.d.l.cast(dyn.array(dword, len(aof.d))) eat = array.array('L', data.l.serialize()) # check that the aof is within the bounds of the section, warn the user despite supporting it anyways if any(not section.containsaddress(ea) for ea in (aof.int(), aof.int() + 4*self['NumberOfFunctions'].int())): logging.warn("{:s} : Export Address Table goes outside bounds of designated section. ({:#x} <= {:#x}{:+#x} < {:#x})".format('.'.join((cls.__module__, cls.__name__)), section['VirtualAddress'].int(), aof.int(), aof.int() + 4*self['NumberOfFunctions'].int(), section['VirtualAddress'].int() + section['VirtualSize'].int())) else: logging.warn("{:s} : No export addresses found in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aof.summary())) eat = array.array('L', []) ## name ordinal table if aono.int() > 0: # cache the section the aono is contained in since we need it anyways section = sections.getsectionbyaddress(aono.int()) cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) # convert the aono into an array that's also quick data = aono.d.l.cast(dyn.array(word, len(aono.d))) no = array.array('H', data.l.serialize()) # check that the aono is within the bounds of the section, warn the user despite supporting it anyways if any(not section.containsaddress(ea) for ea in (aono.int(), aono.int() + 2*self['NumberOfNames'].int())): logging.warn("{:s} : Export Name Ordinal Table goes outside bounds of designated section. ({:#x} <= {:#x}{:+#x} < {:#x})".format('.'.join((cls.__module__, cls.__name__)), section['VirtualAddress'].int(), aono.int(), aono.int() + 2*self['NumberOfNames'].int(), section['VirtualAddress'].int() + section['VirtualSize'].int())) else: logging.warn("{:s} : No Export Name Ordinal Table in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aono.summary())) no = array.array('H', []) # check the name table if aon.int() == 0: logging.warn("{:s} : No Export Name Table in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aon.summary())) nt = [] else: nt = aon.d.l # now we can start returning things to the user va = headers.calculateRelativeOffset(self, aof.int()) for nameva, ordinal in map(None, nt, no): # grab the name if we can if nameva is None: name = None else: section = sections.getsectionbyaddress(nameva.int()) sectionva, data = cache[section.getoffset()] if section.getoffset() in cache else cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) nameofs = nameva.int() - sectionva name = utils.strdup(data[nameofs:].tostring()) # grab the ordinal if we can if ordinal is None: forwarded, value = None, None elif 0 <= ordinal <= len(eat): # this is inside the export directory, so it's a forwardedrva if ExportDirectory.containsaddress(eat[ordinal]): section = sections.getsectionbyaddress(eat[ordinal]) sectionva, data = cache[section.getoffset()] if section.getoffset() in cache else cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) forwarded, value = utils.strdup(data[eat[ordinal] - sectionva:].tostring()), None # otherwise, it's a valid address else: forwarded, value = None, eat[ordinal] # this ordinal is outside the export address table, although # we can read from the file to deal with these sort of fuxed # things... this is currently unsupported by pecoff else: logging.warning("{:s} : Error resolving export address for {:s} : !({:d} <= {:d} < {:d})".format('.'.join((cls.__module__, cls.__name__)), name, 0, ordinal, len(eat))) forwarded, value = None, None ordinalstring = None if ordinal is None else "Ordinal{:d}".format(ordinal + Base) yield va, ordinal, name, ordinalstring, value, forwarded va += 4 return def search(self, key): '''Search the export list for an export that matches key. Return its index/hint. ''' for offset, ordinal, name, ordinalstring, value, forwardedrva in self.iterate(): if key == ordinal or key == name or key == ordinalstring or key == forwardedrva: return ordinal continue raise KeyError(key)
def iterate(self): """For each export, yields (rva offset, hint, name, ordinalname, entrypoint, forwardedrva)""" cls = self.__class__ ExportDirectory = self.getparent(headers.IMAGE_DATA_DIRECTORY) Header, Base = headers.locateHeader(self), self['Base'].int() # our section data cache cache, sections = {}, Header['Sections'] # grab everything that's important aof, aon, aono = self['AddressOfFunctions'], self['AddressOfNames'], self['AddressOfNameOrdinals'] ## export address table if aof.int() > 0: # cache the section the eat is contained in since we need it anyways section = sections.getsectionbyaddress(aof.int()) cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) # convert the aof into an array that's wikiwiki data = aof.d.l.cast(dyn.array(dword, len(aof.d))) eat = array.array('L', data.l.serialize()) # check that the aof is within the bounds of the section, warn the user despite supporting it anyways if any(not section.containsaddress(ea) for ea in (aof.int(), aof.int() + 4*self['NumberOfFunctions'].int())): logging.warn("{:s} : Export Address Table goes outside bounds of designated section. ({:#x} <= {:#x}{:+#x} < {:#x})".format('.'.join((cls.__module__, cls.__name__)), section['VirtualAddress'].int(), aof.int(), aof.int() + 4*self['NumberOfFunctions'].int(), section['VirtualAddress'].int() + section['VirtualSize'].int())) else: logging.warn("{:s} : No export addresses found in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aof.summary())) eat = array.array('L', []) ## name ordinal table if aono.int() > 0: # cache the section the aono is contained in since we need it anyways section = sections.getsectionbyaddress(aono.int()) cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) # convert the aono into an array that's also quick data = aono.d.l.cast(dyn.array(word, len(aono.d))) no = array.array('H', data.l.serialize()) # check that the aono is within the bounds of the section, warn the user despite supporting it anyways if any(not section.containsaddress(ea) for ea in (aono.int(), aono.int() + 2*self['NumberOfNames'].int())): logging.warn("{:s} : Export Name Ordinal Table goes outside bounds of designated section. ({:#x} <= {:#x}{:+#x} < {:#x})".format('.'.join((cls.__module__, cls.__name__)), section['VirtualAddress'].int(), aono.int(), aono.int() + 2*self['NumberOfNames'].int(), section['VirtualAddress'].int() + section['VirtualSize'].int())) else: logging.warn("{:s} : No Export Name Ordinal Table in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aono.summary())) no = array.array('H', []) # check the name table if aon.int() == 0: logging.warn("{:s} : No Export Name Table in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aon.summary())) nt = [] else: nt = aon.d.l # now we can start returning things to the user va = headers.calculateRelativeOffset(self, aof.int()) for nameva, ordinal in map(None, nt, no): # grab the name if we can if nameva is None: name = None else: section = sections.getsectionbyaddress(nameva.int()) sectionva, data = cache[section.getoffset()] if section.getoffset() in cache else cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) nameofs = nameva.int() - sectionva name = utils.strdup(data[nameofs:].tostring()) # grab the ordinal if we can if ordinal is None: forwarded, value = None, None elif 0 <= ordinal <= len(eat): # this is inside the export directory, so it's a forwardedrva if ExportDirectory.containsaddress(eat[ordinal]): section = sections.getsectionbyaddress(eat[ordinal]) sectionva, data = cache[section.getoffset()] if section.getoffset() in cache else cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) forwarded, value = utils.strdup(data[eat[ordinal] - sectionva:].tostring()), None # otherwise, it's a valid address else: forwarded, value = None, eat[ordinal] # this ordinal is outside the export address table, although # we can read from the file to deal with these sort of fuxed # things... this is currently unsupported by pecoff else: logging.warning("{:s} : Error resolving export address for {:s} : !({:d} <= {:d} < {:d})".format('.'.join((cls.__module__, cls.__name__)), name, 0, ordinal, len(eat))) forwarded, value = None, None ordinalstring = None if ordinal is None else "Ordinal{:d}".format(ordinal + Base) yield va, ordinal, name, ordinalstring, value, forwarded va += 4 return
def __ModuleForwarderRefs(self): res = self['NumberOfModuleForwarderRefs'].li return dyn.array(IMAGE_BOUND_FORWARDER_REF, res.int())
class IMAGE_LOADCONFIG_DIRECTORY(pstruct.type): # FIXME: The size field in the DataDirectory is used to determine which # IMAGE_LOADCONFIG_DIRECTORY to use. Instead we're cheating and # using the size specified in the data-directory entry and a # feature of pstruct.type when defining a custom .blocksize(). A # proper implementation should check the 'Size' field and then # use this to determine which IMAGE_LOADCONFIG_DIRECTORY # should be used. Once that's done, then we can define a # sub-object that chooses the correct IMAGE_LOADCONFIG_DIRECTORY # to use. _fields_ = [ (uint32, 'Size'), (TimeDateStamp, 'TimeDateStamp'), (uint16, 'MajorVersion'), (uint16, 'MinorVersion'), (uint32, 'GlobalFlagsClear'), # FIXME (uint32, 'GlobalFlagsSet'), # FIXME (uint32, 'CriticalSectionDefaultTimeout'), (uint32, 'DeCommitFreeBlockThreshold'), (uint32, 'DeCommitTotalFreeThreshold'), (realaddress(ptype.undefined, type=uint32), 'LockPrefixTable'), # XXX: NULL-terminated list of VAs (uint32, 'MaximumAllocationSize'), (uint32, 'VirtualMemoryThreshold'), (uint32, 'ProcessAffinityMask'), (uint32, 'ProcessHeapFlags'), # FIXME: where are these flags at? (uint16, 'CSDVersion'), (uint16, 'Reserved'), (realaddress(ptype.undefined, type=uint32), 'EditList'), # XXX: also probably a NULL-terminated list of VAs (realaddress(uint32, type=uint32), 'SecurityCookie'), (realaddress( lambda s: dyn.array(uint32, s.parent['SEHandlerCount'].li.int()), type=uint32), 'SEHandlerTable'), (uint32, 'SEHandlerCount'), (realaddress(uint32, type=uint32), 'GuardCFCheckFunctionPointer'), (realaddress(uint32, type=uint32), 'GuardCFDispatchFunctionPointer'), (realaddress(lambda s: dyn.array( uint32, s.parent['GuardCFFunctionCount'].li.int()), type=uint32), 'GuardCFFunctionTable'), (uint32, 'GuardCFFunctionCount'), (pbinary.littleendian(IMAGE_GUARD_), 'GuardFlags'), (IMAGE_LOAD_CONFIG_CODE_INTEGRITY, 'CodeIntegrity'), (realaddress(lambda s: dyn.array( uint32, s.parent['GuardAddressTakenIatEntryCount'].li.int()), type=uint32), 'GuardAddressTakenIatEntryTable'), (uint32, 'GuardAddressTakenIatEntryCount'), (realaddress(lambda s: dyn.array( uint32, s.parent['GuardLongJumpTargetCount'].li.int()), type=uint32), 'GuardLongJumpTargetTable'), (uint32, 'GuardLongJumpTargetCount'), (realaddress(ptype.undefined, type=uint32), 'DynamicValueRelocTable' ), # XXX: Probably another NULL-terminated list of VAs (realaddress(ptype.undefined, type=uint32), 'CHPEMetadataPointer'), # FIXME (realaddress(uint32, type=uint32), 'GuardRFFailureRoutine'), (realaddress(uint32, type=uint32), 'GuardRFFailureRoutineFunctionPointer'), (uint32, 'DynamicValueRelocTableOffset' ), # XXX: depends on DynamicValueRelocTableSection (uint16, 'DynamicValueRelocTableSection'), (uint16, 'Reserved2'), (realaddress(uint32, type=uint32), 'GuardRFVerifyStackPointerFunctionPointer'), (uint32, 'HotPatchTableOffset'), (realaddress(pstr.wstring, type=uint32), 'AddressOfSomeUnicodeString'), (uint32, 'Reserved3'), ]
class IMAGE_LOAD_CONFIG_DIRECTORY64(IMAGE_LOAD_CONFIG_DIRECTORY): _fields_ = [ (DWORD, 'Size'), (TimeDateStamp, 'TimeDateStamp'), (WORD, 'MajorVersion'), (WORD, 'MinorVersion'), (DWORD, 'GlobalFlagsClear'), (DWORD, 'GlobalFlagsSet'), (DWORD, 'CriticalSectionDefaultTimeout'), (ULONGLONG, 'DeCommitFreeBlockThreshold'), (ULONGLONG, 'DeCommitTotalFreeThreshold'), (realaddress(VOID, type=ULONGLONG), 'LockPrefixTable'), (ULONGLONG, 'MaximumAllocationSize'), (ULONGLONG, 'VirtualMemoryThreshold'), (ULONGLONG, 'ProcessAffinityMask'), (DWORD, 'ProcessHeapFlags'), (WORD, 'CSDVersion'), (LOAD_LIBRARY_SEARCH_, 'DependentLoadFlags'), (realaddress(VOID, type=ULONGLONG), 'EditList'), (realaddress(ULONGLONG, type=ULONGLONG), 'SecurityCookie'), (realaddress(lambda self: dyn.array( ULONGLONG, self.parent['SEHandlerCount'].li.int()), type=ULONGLONG), 'SEHandlerTable'), (ULONGLONG, 'SEHandlerCount'), (realaddress(realaddress(VOID, type=ULONGLONG), type=ULONGLONG), 'GuardCFCheckFunctionPointer'), (realaddress(realaddress(VOID, type=ULONGLONG), type=ULONGLONG), 'GuardCFDispatchFunctionPointer'), (realaddress(lambda self: dyn.array( virtualaddress(VOID, type=DWORD), self.parent[ 'GuardCFFunctionCount'].li.int()), type=ULONGLONG), 'GuardCFFunctionTable'), (ULONGLONG, 'GuardCFFunctionCount'), (pbinary.littleendian(IMAGE_GUARD_), 'GuardFlags'), (IMAGE_LOAD_CONFIG_CODE_INTEGRITY, 'CodeIntegrity'), (realaddress(lambda self: dyn.array( ULONGLONG, self.parent['GuardAddressTakenIatEntryCount'].li.int()), type=ULONGLONG), 'GuardAddressTakenIatEntryTable'), (ULONGLONG, 'GuardAddressTakenIatEntryCount'), (realaddress(lambda self: dyn.array( ULONGLONG, self.parent['GuardLongJumpTargetCount'].li.int()), type=ULONGLONG), 'GuardLongJumpTargetTable'), (ULONGLONG, 'GuardLongJumpTargetCount'), (realaddress(IMAGE_DYNAMIC_RELOCATION_TABLE, type=ULONGLONG), 'DynamicValueRelocTable'), (realaddress(VOID, type=ULONGLONG), 'CHPEMetadataPointer'), (realaddress(VOID, type=ULONGLONG), 'GuardRFFailureRoutine'), (realaddress(VOID, type=ULONGLONG), 'GuardRFFailureRoutineFunctionPointer'), (DWORD, 'DynamicValueRelocTableOffset'), (WORD, 'DynamicValueRelocTableSection'), (WORD, 'Reserved2'), (realaddress(ULONGLONG, type=ULONGLONG), 'GuardRFVerifyStackPointerFunctionPointer'), (DWORD, 'HotPatchTableOffset'), (DWORD, 'Reserved3'), (realaddress(IMAGE_ENCLAVE_CONFIG64, type=ULONGLONG), 'EnclaveConfigurationPointer'), (realaddress(VOID, type=ULONGLONG), 'VolatileMetadataPointer'), (realaddress(lambda self: dyn.array( ULONGLONG, self.parent['GuardEHContinuationCount'].li.int()), type=ULONGLONG), 'GuardEHContinuationTable'), (ULONGLONG, 'GuardEHContinuationCount'), (realaddress(VOID, type=ULONGLONG), 'GuardXFGCheckFunctionPointer'), (realaddress(VOID, type=ULONGLONG), 'GuardXFGDispatchFunctionPointer'), (realaddress(VOID, type=ULONGLONG), 'GuardXFGTableDispatchFunctionPointer'), (ULONGLONG, 'CastGuardOsDeterminedFailureMode'), ]
def iterate(self): """For each export, yields (rva offset, hint, name, ordinalname, entrypoint, forwardedrva)""" cls = self.__class__ ExportDirectory = self.getparent(headers.IMAGE_DATA_DIRECTORY) Header, Base = LocateHeader(self), self['Base'].int() # our section data cache cache, sections = {}, Header['Sections'] # grab everything that's important aof, aon, aono = self['AddressOfFunctions'], self['AddressOfNames'], self['AddressOfNameOrdinals'] ## export address table if aof.int() > 0: # cache the section the eat is contained in since we need it anyways section = sections.getsectionbyaddress(aof.int()) cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) # convert the aof into an array that's wikiwiki data = aof.d.l.cast(dyn.array(dword, len(aof.d))) eat = array.array('I', data.serialize()) # check that the aof is within the bounds of the section, warn the user despite supporting it anyways if any(not section.containsaddress(ea) for ea in (aof.int(), aof.int() + 4*self['NumberOfFunctions'].int())): logging.warn("{:s} : Export Address Table goes outside bounds of designated section. ({:#x} <= {:#x}{:+#x} < {:#x})".format('.'.join((cls.__module__, cls.__name__)), section['VirtualAddress'].int(), aof.int(), aof.int() + 4*self['NumberOfFunctions'].int(), section['VirtualAddress'].int() + section['VirtualSize'].int())) else: logging.warn("{:s} : No export addresses found in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aof.summary())) eat = array.array('I', []) ## name ordinal table if aono.int() > 0: # cache the section the aono is contained in since we need it anyways section = sections.getsectionbyaddress(aono.int()) cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) # convert the aono into an array that's also quick data = aono.d.l.cast(dyn.array(word, len(aono.d))) no = array.array('H', data.l.serialize()) # check that the aono is within the bounds of the section, warn the user despite supporting it anyways if any(not section.containsaddress(ea) for ea in (aono.int(), aono.int() + 2*self['NumberOfNames'].int())): logging.warn("{:s} : Export Name Ordinal Table goes outside bounds of designated section. ({:#x} <= {:#x}{:+#x} < {:#x})".format('.'.join((cls.__module__, cls.__name__)), section['VirtualAddress'].int(), aono.int(), aono.int() + 2*self['NumberOfNames'].int(), section['VirtualAddress'].int() + section['VirtualSize'].int())) else: logging.warn("{:s} : No Export Name Ordinal Table in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aono.summary())) no = array.array('H', []) # check the name table if aon.int() == 0: logging.warn("{:s} : No Export Name Table in IMAGE_EXPORT_DIRECTORY. ({:s})".format('.'.join((cls.__module__, cls.__name__)), aon.summary())) nt = [] else: nt = aon.d.l # now we can start returning things to the user va = CalculateRelativeOffset(self, aof.int()) for nameva, ordinal in map(None, nt, no): # grab the name if we can if nameva is None: name = None else: section = sections.getsectionbyaddress(nameva.int()) sectionva, data = cache[section.getoffset()] if section.getoffset() in cache else cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) nameofs = nameva.int() - sectionva name = utils.strdup(data[nameofs:].tostring()) # grab the ordinal if we can if ordinal is None: forwarded, value = None, None elif 0 <= ordinal <= len(eat): # this is inside the export directory, so it's a forwardedrva if ExportDirectory.containsaddress(eat[ordinal]): section = sections.getsectionbyaddress(eat[ordinal]) sectionva, data = cache[section.getoffset()] if section.getoffset() in cache else cache.setdefault(section.getoffset(), (section['VirtualAddress'].int(), array.array('B', section.data().l.serialize()))) forwarded, value = utils.strdup(data[eat[ordinal] - sectionva:].tostring()), None # otherwise, it's a valid address else: forwarded, value = None, eat[ordinal] # this ordinal is outside the export address table, although # we can read from the file to deal with these sort of fuxed # things... this is currently unsupported by pecoff else: logging.warning("{:s} : Error resolving export address for {:s} : !({:d} <= {:d} < {:d})".format('.'.join((cls.__module__, cls.__name__)), name, 0, ordinal, len(eat))) forwarded, value = None, None ordinalstring = None if ordinal is None else "Ordinal{:d}".format(ordinal + Base) yield va, ordinal, name, ordinalstring, value, forwarded va += 4 return
class IMAGE_PROLOGUE_DYNAMIC_RELOCATION_HEADER(pstruct.type): _fields_ = [ (BYTE, 'PrologueByteCount'), (lambda self: dyn.array(BYTE, self['PrologueByteCount'].li.int()), 'PrologueBytes'), ]
def result(self, type=type): res = self.getparent(ResourceFileInfo) header = res['Reader'].li return dyn.array(type, header['Count'].int())
class _Types(pstruct.type): _fields_ = [ (pint.uint32_t, 'Count'), (lambda s: dyn.array(ResourceString, s['Count'].li.int()), 'Name'), ]