def __nextlayer(self): if len(self) == 0: return self.protocol last = self.value[-1].l try: t, sz = last.nextlayer() if t is None and self.leftover is not None: return dyn.block(self.leftover) if self.leftover is not None: self.leftover -= last.size() return t if t is None and self.leftover is None: self.leftover = 0 return dyn.block(0) self.leftover = sz return t except (AttributeError, NotImplementedError): pass self.leftover -= last.size() return dyn.block(self.leftover)
def __nextlayer(self): if len(self) == 0: return self.protocol last = self.value[-1].l try: t,sz=last.nextlayer() if t is None and self.leftover is not None: return dyn.block(self.leftover) if self.leftover is not None: self.leftover -= last.size() return t if t is None and self.leftover is None: self.leftover = 0 return dyn.block(0) self.leftover = sz return t except (AttributeError,NotImplementedError): pass self.leftover -= last.size() return dyn.block(self.leftover)
class IMAGE_EPILOGUE_DYNAMIC_RELOCATION_HEADER(pstruct.type): _fields_ = [ (uint32, 'EpilogueCount'), (uint8, 'EpilogueByteCount'), (uint8, 'BranchDescriptorElementSize'), (uint16, 'BranchDescriptorCount'), (lambda s: dyn.block(s['BranchDescriptorCount'].li.int()), 'BranchDescriptors'), (lambda s: dyn.block( ((s['BranchDescriptorCount'].li.int() + 7) & -8) // 8), 'BranchDescriptorBitmap'), ]
def __Info(self): res = self['Size'].li.int() if res < 4: return dyn.block(res - self['Magic'].li.size()) return ResourceFileType.get( self['Magic'].int(), blocksize=lambda s, cb=res - self['Magic'].li.size(): cb)
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_RESOURCE_DATA_ENTRY(pstruct.type): _fields_ = [ (virtualaddress(lambda s: dyn.block(s.parent['Size'].li.int()), type=dword), 'Data'), (dword, 'Size'), (dword, 'Codepage'), (dword, 'Reserved'), ]
class WeakExternalAuxiliaryRecord(pstruct.type): type = 105 _fields_ = [ (dword, 'TagIndex'), (dword, 'Characteristics'), (dyn.block(10), 'Unused') ]
class StringTable(pstruct.type): _fields_ = [(uint32, 'Size'), (lambda s: dyn.block(s['Size'].li.int() - 4), 'Data')] def extract(self, offset): '''return the string associated with a particular offset''' string = self.serialize()[offset:] return ptypes.utils.strdup(string, terminator=b'\0') def add(self, string): '''appends a string to string table, returns offset''' res = string.encode(sys.getdefaultencoding()) + b'\0' ofs, data = self.size(), self['Data'] data.length, data.value = data.length + len( res), data.serialize() + res self['Size'].set(data.size() + self['Size'].size()) return ofs def find(self, string): '''returns the offset of the specified string within the string table''' res, data = string.encode( sys.getdefaultencoding()) + b'\0', self['Data'].serialize() index = data.find(res) if index == -1: raise LookupError( "{:s} : Unable to find null-terminated string ({!r}) within string table" .format(self.instance(), string)) return index + self['Size'].size()
class CLRAuxiliaryRecord(pstruct.type): type = 106 _fields_ = [ (byte, 'bAuxType'), (byte, 'bReserved'), (dword, 'SymbolTableIndex'), (dyn.block(12), 'Reserved') ]
class e_ident(pstruct.type): EI_NIDENT = 16 class EI_CLASS(pint.enum, uchar): _values_ = [ ('ELFCLASSNONE', 0), ('ELFCLASS32', 1), ('ELFCLASS64', 2), ] class EI_DATA(pint.enum, uchar): # FIXME: switch the byteorder of everything based on this value _values_ = [ ('ELFDATANONE', 0), ('ELFDATA2LSB', 1), ('ELFDATA2MSB', 2), ] def order(self): res = self.int() if res == 1: return ptypes.config.byteorder.littleendian elif res == 2: return ptypes.config.byteorder.bigendian return ptypes.config.defaults.integer.order class EI_OSABI(pint.enum, uchar): _values_ = [ ('ELFOSABI_SYSV', 0), ('ELFOSABI_HPUX', 1), ('ELFOSABI_ARM_EABI', 64), ('ELFOSABI_STANDALONE', 255), ] _fields_ = [ (dyn.block(4), 'EI_MAG'), (EI_CLASS, 'EI_CLASS'), (EI_DATA, 'EI_DATA'), (uchar, 'EI_VERSION'), (EI_OSABI, 'EI_OSABI'), (uchar, 'EI_ABIVERSION'), (dyn.block(EI_NIDENT - 9), 'EI_PAD'), ] def valid(self): return self.initialized and self['EI_MAG'].serialize() == '\x7fELF'
class FunctionBoundaryAuxiliaryRecord(pstruct.type): type = 101 _fields_ = [ (dword, 'Unused[0]'), (word, 'Linenumber'), (dyn.block(6), 'Unused[1]'), (off_t, 'PointerToNextFunction'), (word, 'Unused[2]') ]
class SerBlock(pstruct.type): _fields_ = [ (CInt, 'length'), (lambda s: dyn.block(s['length'].li.Get()), 'data'), ] def summary(self): return '{:d} : {:s}'.format(self['length'].Get(), self['data'].summary())
def __Extra(self): fields = [ 'NumberOfSymbols', 'LvaToFirstSymbol', 'NumberOfLinenumbers', 'LvaToFirstLinenumber', 'RvaToFirstByteOfCode', 'RvaToLastByteOfCode', 'RvaToFirstByteOfData', 'RvaToLastByteOfData' ] bs, res = self.blocksize(), sum(self[fld].li.size() for fld in fields) return dyn.block(max(0, bs - res))
class IMAGE_DYNAMIC_RELOCATION64(pstruct.type): _fields_ = [ (uint32, 'HeaderSize'), (uint32, 'FixupInfoSize'), (uint64, 'Symbol'), (uint32, 'SymbolGroup'), (uint32, 'Flags'), (lambda s: dyn.block(s['FixupInfoSize'].li.int()), 'FixupInfo'), ]
class LTCG_ENTRY(pstruct.type): _fields_ = [ (virtualaddress(lambda self: dyn.block( self.getparent(LTCG_ENTRY).li['size'].int()), type=DWORD), 'rva'), (DWORD, 'size'), (pstr.szstring, 'section'), (dyn.padding(4), 'align(section)'), ]
class IMAGE_DEBUG_DATA_MISC(pstruct.type): type = IMAGE_DEBUG_TYPE_.byname('MISC') _fields_ = [ (IMAGE_DEBUG_MISC_, 'DataType'), (uint32, 'Length'), (uint8, 'Unicode'), (dyn.block(3), 'align(Unicode)'), (lambda s: dyn.clone(pstr.wstring if s['Unicode'].int() else pstr.string, length=s['Length'].li.int()), 'Data'), ]
class _object_(pstruct.type): def __data(self): cb = self['length'].li.Get() return dyn.clone(pstr.wstring, length=cb // 2) _fields_ = [ (CInt, 'length'), (__data, 'data'), (lambda self: dyn.block(self['length'].li.Get() & 1), r'\0'), ]
class SectionAuxiliaryRecord(pstruct.type): type = 3 _fields_ = [ (uint32, 'Length'), (uint16, 'NumberOfRelocations'), (uint16, 'NumberOfLinenumbers'), (dword, 'CheckSum'), (word, 'Number'), (IMAGE_COMDAT_SELECT, 'Selection'), (dyn.block(3), 'Unused') ]
class ResourceWString(pstruct.type): _fields_ = [ (EncodedInteger, 'Length'), (lambda s: dyn.clone(pstr.wstring, length=s['Length'].li.int() // 2), 'Name'), (lambda s: dyn.block(s['Length'].li.int() % 2), 'Padding'), ] def str(self): return self['Name'].str() def summary(self): return "{:d} {!r}".format(self['Length'].int(), self['Name'].str())
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)
class IMAGE_DEBUG_DATA_MISC(pstruct.type): type = IMAGE_DEBUG_TYPE_.byname('MISC') def __Extra(self): bs, res = self.blocksize(), sum( self[fld].li.size() for fld in ['DataType', 'Length', 'Unicode', 'align(Unicode)', 'Data']) return dyn.block(max(0, bs - res)) _fields_ = [ (IMAGE_DEBUG_MISC_, 'DataType'), (DWORD, 'Length'), (BYTE, 'Unicode'), (dyn.block(3), 'align(Unicode)'), (lambda self: dyn.clone(pstr.wstring if self['Unicode'].int() else pstr.string, length=self['Length'].li.int()), 'Data'), (__Extra, 'Extra'), ]
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 Certificate(pstruct.type): class wRevision(pint.enum, uint16): _values_ = [ ('WIN_CERT_REVISION_1_0', 0x0100), ('WIN_CERT_REVISION_2_0', 0x0200), ] class wCertificateType(pint.enum, uint16): _values_ = [ ('WIN_CERT_TYPE_X509', 0x0001), ('WIN_CERT_TYPE_PKCS7_SIGNED_DATA', 0x0002), ('WIN_CERT_TYPE_RESERVED_1', 0x0003), ('WIN_CERT_TYPE_TS_STACK_SIGNED', 0x0004), ] _fields_ = [ (uint32, 'dwLength'), (wRevision, 'wRevision'), (wCertificateType, 'wCertificateType'), (lambda s: dyn.block(s['dwLength'].li.int() - 8), 'bCertificate'), ]
class Certificate(pstruct.type): class wRevision(pint.enum, uint16): _values_ = [ ('WIN_CERT_REVISION_1_0', 0x0100), ('WIN_CERT_REVISION_2_0', 0x0200), ] class wCertificateType(pint.enum, uint16): _values_ = [ ('WIN_CERT_TYPE_X509', 0x0001), ('WIN_CERT_TYPE_PKCS7_SIGNED_DATA', 0x0002), ('WIN_CERT_TYPE_RESERVED_1', 0x0003), ('WIN_CERT_TYPE_TS_STACK_SIGNED', 0x0004), ] # XXX: The bCertificate field is padded to a qword-boundary. Keep # this in mind if trying to DER decode it. _fields_ = [ (uint32, 'dwLength'), (wRevision, 'wRevision'), (wCertificateType, 'wCertificateType'), (lambda s: dyn.block(s['dwLength'].li.int() - 8), 'bCertificate'), ]
def __Data(self): res = self.blocksize() - sum(self[n].li.size() for n in ('Manager', 'Reader')) return dyn.clone(self.ResourceData, _value_=dyn.block(res), _object_=self._ResourceData)
class _object_(resources.IMAGE_RESOURCE_DIRECTORY): _fields_ = resources.IMAGE_RESOURCE_DIRECTORY._fields_[:] _fields_.append( (lambda s: dyn.block(s.blocksize() - (s.value[-1].getoffset( ) + s.value[-1].blocksize() - s.value[0].getoffset())), 'ResourceData'))
def __ExportData(self): res = sum(self[n].li.size() for _,n in self._fields_[:-1]) return dyn.block(self.blocksize() - res)
def __Padding(self): cb = sum(self[fld].li.size() for _, fld in self._fields_[:-1]) return dyn.block(self.blocksize() - cb)
def _object_(self): # called by 'Address' sz = self["Size"].num() return dyn.block(sz)
def __Padding(self): cb = self['Size'].li.size() + self['Size'].li.int() res = cb & 7 return dyn.block((8 - res) if res > 0 else 0)
def nextlayer(self): return dyn.block(self.payload)
def __Magic(self): res = self['Size'].li.int() return dyn.block(0) if res < 4 else self._Magic
class IMAGE_DEBUG_DATA_REPRO(pstruct.type): type = IMAGE_DEBUG_TYPE_.byname('REPRO') _fields_ = [ (DWORD, 'Size'), (lambda self: dyn.block(self['Size'].li.int()), 'Unknown'), ]
def __Info(self): res = self['Size'].li.int() if res < 4: return dyn.block(res - self['Magic'].li.size()) return ResourceFileType.get(self['Magic'].int(), blocksize=lambda s, cb=res - self['Magic'].li.size(): cb)
def __Extra(self): bs, res = self.blocksize(), sum(self[fld].li.size() for fld in ['Signature']) return dyn.block(max(0, bs - res))
def Data(self): res = self['Size'].li.int() if self['Magic'].Valid(): return self['Info'] t = dyn.block(res) return self.new(t, offset=4, source=ptypes.prov.proxy(self)).li
def _object_(self): # called by 'Address' res = self['Size'].int() return dyn.block(res)
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()
def __padding_Tables(self): hdr = self.getparent(StreamHdr) cb = hdr['Size'].li.int() total = sum(self[n].blocksize() for _, n in self._fields_[:-1]) return dyn.block(max((0, cb-total)))
class IMAGE_PROLOGUE_DYNAMIC_RELOCATION_HEADER(pstruct.type): _fields_ = [ (uint8, 'PrologueByteCount'), (lambda s: dyn.block(s['PrologueByteCount'].li.int()), 'PrologueBytes'), ]
def __StreamData(self): cb = self.getparent(IMAGE_DATA_DIRECTORY)['Size'].int() total = sum(self[n].li.size() for _, n in self._fields_[:-1]) res = max((0, cb-total)) return dyn.block(res)