From dbd2a780c8e9d47f7bad0093def80fb365755358 Mon Sep 17 00:00:00 2001 From: Vincent Brillault Date: Mon, 30 Jan 2017 23:12:24 +0100 Subject: [PATCH] WIP: Extract variable name and value from UserForm --- oletools/oleform.py | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ oletools/olevba.py | 25 +++++++++++++++++++++++++ 2 files changed, 268 insertions(+), 0 deletions(-) create mode 100644 oletools/oleform.py diff --git a/oletools/oleform.py b/oletools/oleform.py new file mode 100644 index 0000000..a7be38d --- /dev/null +++ b/oletools/oleform.py @@ -0,0 +1,243 @@ +#!/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(32)] + + def __str__(self): + return ', '.join(self._names[i] for i in range(32) if self._val[i]) + + def __getattr__(self, name): + return self._val[self._names.index(name)] + +class PropMask(Mask): + _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', 'Unused3_0', 'Unused3_1', 'Unused3_2', 'Unused3_3'] + +class SitePropMask(Mask): + _names = ['fName', 'fTag', 'fID', 'fHelpContextID', 'fBitFlags', 'fObjectStreamSize', + 'fTabIndex', 'fClsidCacheIndex', 'fPosition', 'fGroupID', 'Unused1', + 'fControlTipText', 'fRuntimeLicKey', 'fControlSource', 'fRowSource', 'Unused2_0', + 'Unused2_1', 'Unused2_2', 'Unused2_3', 'Unused2_4', 'Unused2_5', 'Unused2_6', + 'Unused2_7', 'Unused2_8', 'Unused2_9', 'Unused2_10', 'Unused2_11', 'Unused2_12', + 'Unused2_13', 'Unused2_14', 'Unused2_15', 'Unused2_16'] + +class OleUserFormParser(object): + def __init__(self, stream): + self.content = [] + self._stream = stream + self._pos = 0 + self._frozen_pos = [] + + def read(self, size): + self._pos += size + return self._stream.read(size) + + def freeze(self): + self._frozen_pos.append(self._pos) + + def unfreeze(self, size): + self.read(self._frozen_pos.pop() - self._pos + size) + + def unfreeze_pad(self): + align_pos = (self._pos - self._frozen_pos.pop()) % 4 + if align_pos: + self.read(4 - align_pos) + + def unpacks(self, format, size): + return struct.unpack(format, self.read(size)) + + def unpack(self, format, size): + return self.unpacks(format, size)[0] + + def check_values(self, name, format, size, expected): + value = self.unpacks(format, size) + if value != expected: + raise OleFormParsingError('Invalid {0} at {1}: expected {2} got {3}'.format(name, self._pos - size, str(expected), str(value))) + + def check_value(self, name, format, size, expected): + self.check_values(name, format, size, (expected,)) + + def consume_GuidAndFont(self): + # GuidAndFont: [MS-OFORMS] 2.4.7 + UUIDS = self.unpacks('Q', 8) + if UUIDS == (199447043, 36753, 4558, 11376937813817407569L): + # UUID == {0BE35203-8F91-11CE-9DE300AA004BB851} + # StdFont: [MS-OFORMS] 2.4.12 + self.check_value('StdFont (version)', 'Q', 8, 11376937813817407569L) + # StdPicture: [MS-OFORMS] 2.4.13 + self.check_value('StdPicture (Preamble)', '> 15 + else: + FORM_FLAG_DONTSAVECLASSTABLE = 0 + # Skip the rest of DataBlock and ExtraDataBlock + self.unfreeze(cbform) + # FormStreamData: [MS-OFORMS] 2.2.10.5 + if propmask.fMouseIcon: + self.consume_GuidAndPicture() + if propmask.fFont: + self.consume_GuidAndFont() + if propmask.fPicture: + self.consume_GuidAndPicture() + # FormSiteData: [MS-OFORMS] 2.2.10.6 + if not FORM_FLAG_DONTSAVECLASSTABLE: + CountOfSiteClassInfo = self.unpack(' 0: + remaining_SiteDepthsAndTypes -= self.consume_FormObjectDepthTypeCount() + self.unfreeze_pad() + for i in range(CountOfSites): + self.consume_OleSiteConcreteControl() + + def consume_stream_o(self): + # Adapted from plugin_stream_o.py from Didier Stevens's oledump.py + while(True): + try: + (code, length) = self.unpacks('