diff --git a/oletools/oleform.py b/oletools/oleform.py new file mode 100644 index 0000000..0b9be2e --- /dev/null +++ b/oletools/oleform.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python + +import struct + +class OleFormParsingError(Exception): + pass + +class Mask(object): + def __init__(self, val): + self._val = [(val & (1<>i for i in range(self._size)] + + def __str__(self): + return ', '.join(self._names[i] for i in range(self._size) if self._val[i]) + + def __getattr__(self, name): + return self._val[self._names.index(name)] + + def __len__(self): + return self.size + + def __getitem__(self, key): + return self._val[self._names.index(key)] + +class FormPropMask(Mask): + """FormPropMask: [MS-OFORMS] 2.2.10.2""" + _size = 28 + _names = ['Unused1', 'fBackColor', 'fForeColor', 'fNextAvailableID', 'Unused2_0', 'Unused2_1', + 'fBooleanProperties', 'fBooleanProperties', 'fMousePointer', 'fScrollBars', + 'fDisplayedSize', 'fLogicalSize', 'fScrollPosition', 'fGroupCnt', 'Reserved', + 'fMouseIcon', 'fCycle', 'fSpecialEffect', 'fBorderColor', 'fCaption', 'fFont', + 'fPicture', 'fZoom', 'fPictureAlignment', 'fPictureTiling', 'fPictureSizeMode', + 'fShapeCookie', 'fDrawBuffer'] + +class SitePropMask(Mask): + """SitePropMask: [MS-OFORMS] 2.2.10.12.2""" + _size = 15 + _names = ['fName', 'fTag', 'fID', 'fHelpContextID', 'fBitFlags', 'fObjectStreamSize', + 'fTabIndex', 'fClsidCacheIndex', 'fPosition', 'fGroupID', 'Unused1', + 'fControlTipText', 'fRuntimeLicKey', 'fControlSource', 'fRowSource'] + +class MorphDataPropMask(Mask): + """MorphDataPropMask: [MS-OFORMS] 2.2.5.2""" + _size = 33 + _names = ['fVariousPropertyBits', 'fBackColor', 'fForeColor', 'fMaxLength', 'fBorderStyle', + 'fScrollBars', 'fDisplayStyle', 'fMousePointer', 'fSize', 'fPasswordChar', + 'fListWidth', 'fBoundColumn', 'fTextColumn', 'fColumnCount', 'fListRows', + 'fcColumnInfo', 'fMatchEntry', 'fListStyle', 'fShowDropButtonWhen', 'UnusedBits1', + 'fDropButtonStyle', 'fMultiSelect', 'fValue', 'fCaption', 'fPicturePosition', + 'fBorderColor', 'fSpecialEffect', 'fMouseIcon', 'fPicture', 'fAccelerator', + 'UnusedBits2', 'Reserved', 'fGroupName'] + +class ExtendedStream(object): + def __init__(self, stream, path): + self._pos = 0 + self._jumps = [] + self._stream = stream + self._path = path + + @classmethod + def open(cls, ole_file, path): + stream = ole_file.openstream(path) + return cls(stream, path) + + def read(self, size): + self._pos += size + return self._stream.read(size) + + def will_jump_to(self, size): + self._next_jump = (True, size) + return self + + def will_pad(self, pad=4): + self._next_jump = (False, pad) + return self + + def __enter__(self): + (jump_type, size) = self._next_jump + self._jumps.append((self._pos, jump_type, size)) + + def __exit__(self, exc_type, exc_value, traceback): + if exc_type is None: + (start, jump_type, size) = self._jumps.pop() + if jump_type: + self.read(size - (self._pos - start)) + else: + align = (self._pos - start) % size + if align: + self.read(size - align) + + def unpacks(self, format, size): + return struct.unpack(format, self.read(size)) + + def unpack(self, format, size): + return self.unpacks(format, size)[0] + + def raise_error(self, reason, back=0): + raise OleFormParsingError('{0}:{1}: {2}'.format(self.path, self._pos - back)) + + def check_values(self, name, format, size, expected): + value = self.unpacks(format, size) + if value != expected: + self.raise_error('Invalid {0}: expected {1} got {2}'.format(name, str(expected), str(value))) + + def check_value(self, name, format, size, expected): + self.check_values(name, format, size, (expected,)) + + +def consume_TextProps(stream): + # TextProps: [MS-OFORMS] 2.3.1 + stream.check_values('TextProps (versions)', 'Q', 8) + if UUIDS == (199447043, 36753, 4558, 11376937813817407569L): + # UUID == {0BE35203-8F91-11CE-9DE300AA004BB851} + # StdFont: [MS-OFORMS] 2.4.12 + stream.check_value('StdFont (version)', 'Q', 8, 11376937813817407569L) + # StdPicture: [MS-OFORMS] 2.4.13 + stream.check_value('StdPicture (Preamble)', '> 15 + else: + FORM_FLAG_DONTSAVECLASSTABLE = 0 + # Skip the rest of DataBlock and ExtraDataBlock + # FormStreamData: [MS-OFORMS] 2.2.10.5 + if propmask.fMouseIcon: + consume_GuidAndPicture(stream) + if propmask.fFont: + consume_GuidAndFont(stream) + if propmask.fPicture: + consume_GuidAndPicture(stream) + # FormSiteData: [MS-OFORMS] 2.2.10.6 + if not FORM_FLAG_DONTSAVECLASSTABLE: + CountOfSiteClassInfo = stream.unpack(' 0: + remaining_SiteDepthsAndTypes -= consume_FormObjectDepthTypeCount(stream) + for i in range(CountOfSites): + yield consume_OleSiteConcreteControl(stream) + +def consume_MorphDataControl(stream): + # MorphDataControl: [MS-OFORMS] 2.2.5.1 + stream.check_values('MorphDataControl (versions)', '