Commit f9086ed4302e52d642bad61a3d95bff8fb558616

Authored by Philippe Lagadec
Committed by GitHub
2 parents 1b3768ad e860d4fe

Merge pull request #314 from Feandil/issue_313

WIP: oleform: add support for other types of controls
oletools/oleform.py
@@ -24,6 +24,11 @@ class Mask(object): @@ -24,6 +24,11 @@ class Mask(object):
24 def __getitem__(self, key): 24 def __getitem__(self, key):
25 return self._val[self._names.index(key)] 25 return self._val[self._names.index(key)]
26 26
  27 + def consume(self, stream, props):
  28 + for (name, size) in props:
  29 + if self[name]:
  30 + stream.read(size)
  31 +
27 class FormPropMask(Mask): 32 class FormPropMask(Mask):
28 """FormPropMask: [MS-OFORMS] 2.2.10.2""" 33 """FormPropMask: [MS-OFORMS] 2.2.10.2"""
29 _size = 28 34 _size = 28
@@ -52,12 +57,60 @@ class MorphDataPropMask(Mask): @@ -52,12 +57,60 @@ class MorphDataPropMask(Mask):
52 'fBorderColor', 'fSpecialEffect', 'fMouseIcon', 'fPicture', 'fAccelerator', 57 'fBorderColor', 'fSpecialEffect', 'fMouseIcon', 'fPicture', 'fAccelerator',
53 'UnusedBits2', 'Reserved', 'fGroupName'] 58 'UnusedBits2', 'Reserved', 'fGroupName']
54 59
  60 +class ImagePropMask(Mask):
  61 + """ImagePropMask: [MS-OFORMS] 2.2.3.2"""
  62 + _size = 15
  63 + _names = ['UnusedBits1_1', 'UnusedBits1_2', 'fAutoSize', 'fBorderColor', 'fBackColor',
  64 + 'fBorderStyle', 'fMousePointer', 'fPictureSizeMode', 'fSpecialEffect', 'fSize',
  65 + 'fPicture', 'fPictureAlignment', 'fPictureTiling', 'fVariousPropertyBits',
  66 + 'fMouseIcon']
  67 +
  68 +class CommandButtonPropMask(Mask):
  69 + """CommandButtonPropMask: [MS-OFORMS] 2.2.1.2"""
  70 + _size = 11
  71 + _names = ['fForeColor', 'fBackColor', 'fVariousPropertyBits', 'fCaption', 'fPicturePosition',
  72 + 'fSize', 'fMousePointer', 'fPicture', 'fAccelerator', 'fTakeFocusOnClick',
  73 + 'fMouseIcon']
  74 +
  75 +class SpinButtonPropMask(Mask):
  76 + """SpinButtonPropMask: [MS-OFORMS] 2.2.8.2"""
  77 + _size = 15
  78 + _names = ['fForeColor', 'fBackColor', 'fVariousPropertyBits', 'fSize', 'UnusedBits1',
  79 + 'fMin', 'fMax', 'fPosition', 'fPrevEnabled', 'fNextEnabled', 'fSmallChange',
  80 + 'fOrientation', 'fDelay', 'fMouseIcon', 'fMousePointer']
  81 +
  82 +class TabStripPropMask(Mask):
  83 + """TabStripPropMask: [MS-OFORMS] 2.2.9.2"""
  84 + _size = 25
  85 + _names = ['fListIndex', 'fBackColor', 'fForeColor', 'Unused1', 'fSize', 'fItems',
  86 + 'fMousePointer', 'Unused2', 'fTabOrientation', 'fTabStyle', 'fMultiRow',
  87 + 'fTabFixedWidth', 'fTabFixedHeight', 'fTooltips', 'Unused3', 'fTipStrings',
  88 + 'Unused4', 'fNames', 'fVariousPropertyBits', 'fNewVersion', 'fTabsAllocated',
  89 + 'fTags', 'fTabData', 'fAccelerator', 'fMouseIcon']
  90 +
  91 +class LabelPropMask(Mask):
  92 + """LabelPropMask: [MS-OFORMS] 2.2.4.2"""
  93 + _size = 13
  94 + _names = ['fForeColor', 'fBackColor', 'fVariousPropertyBits', 'fCaption',
  95 + 'fPicturePosition', 'fSize', 'fMousePointer', 'fBorderColor', 'fBorderStyle',
  96 + 'fSpecialEffect', 'fPicture', 'fAccelerator', 'fMouseIcon']
  97 +
  98 +class ScrollBarPropMask(Mask):
  99 + """ScrollBarPropMask: [MS-OFORMS] 2.2.7.2"""
  100 + _size = 17
  101 + _names = ['fForeColor', 'fBackColor', 'fVariousPropertyBits', 'fSize', 'fMousePointer',
  102 + 'fMin', 'fMax', 'fPosition', 'UnusedBits1', 'fPrevEnabled', 'fNextEnabled',
  103 + 'fSmallChange', 'fLargeChange', 'fOrientation', 'fProportionalThumb',
  104 + 'fDelay', 'fMouseIcon']
  105 +
55 class ExtendedStream(object): 106 class ExtendedStream(object):
56 def __init__(self, stream, path): 107 def __init__(self, stream, path):
57 self._pos = 0 108 self._pos = 0
58 self._jumps = [] 109 self._jumps = []
59 self._stream = stream 110 self._stream = stream
60 self._path = path 111 self._path = path
  112 + self._padding = False
  113 + self._pad_start = 0
61 114
62 @classmethod 115 @classmethod
63 def open(cls, ole_file, path): 116 def open(cls, ole_file, path):
@@ -68,31 +121,55 @@ class ExtendedStream(object): @@ -68,31 +121,55 @@ class ExtendedStream(object):
68 # print('declared size: %d' % ole_file.get_size(path)) 121 # print('declared size: %d' % ole_file.get_size(path))
69 return cls(stream, path) 122 return cls(stream, path)
70 123
71 - def read(self, size): 124 + def _read(self, size):
72 self._pos += size 125 self._pos += size
73 return self._stream.read(size) 126 return self._stream.read(size)
74 127
  128 + def _pad(self, start, size=4):
  129 + offset = (self._pos - start) % size
  130 + if offset:
  131 + self._read(size - offset)
  132 +
  133 + def read(self, size):
  134 + if self._padding:
  135 + self._pad(self._pad_start, size)
  136 + return self._read(size)
  137 +
75 def will_jump_to(self, size): 138 def will_jump_to(self, size):
76 - self._next_jump = (True, size) 139 + self._next_jump = ('jump', (self._pos, size))
  140 + return self
  141 +
  142 + def will_pad(self):
  143 + self._next_jump = ('pad', self._pos)
77 return self 144 return self
78 145
79 - def will_pad(self, pad=4):  
80 - self._next_jump = (False, pad) 146 + def padded_struct(self):
  147 + self._next_jump = ('padded', (self._padding, self._pad_start))
  148 + self._padding = True
  149 + self._pad_start = self._pos
81 return self 150 return self
82 151
83 def __enter__(self): 152 def __enter__(self):
84 - (jump_type, size) = self._next_jump  
85 - self._jumps.append((self._pos, jump_type, size)) 153 + assert(self._next_jump)
  154 + self._jumps.append(self._next_jump)
  155 + self._next_jump = None
86 156
87 def __exit__(self, exc_type, exc_value, traceback): 157 def __exit__(self, exc_type, exc_value, traceback):
88 if exc_type is None: 158 if exc_type is None:
89 - (start, jump_type, size) = self._jumps.pop()  
90 - if jump_type:  
91 - self.read(size - (self._pos - start))  
92 - else:  
93 - align = (self._pos - start) % size  
94 - if align:  
95 - self.read(size - align) 159 + (jump_type, data) = self._jumps.pop()
  160 + if jump_type == 'jump':
  161 + (start, size) = data
  162 + consummed = self._pos - start
  163 + if consummed > size:
  164 + self.raise_error('Bad jump: too much read ({0} > {1})'.format(consummed, size))
  165 + self.read(size - consummed)
  166 + elif jump_type == 'pad':
  167 + self._pad(data)
  168 + elif jump_type == 'padded':
  169 + (prev_padding, prev_pad_start) = data
  170 + self._pad(self._pad_start)
  171 + self._padding = prev_padding
  172 + self._pad_start = prev_pad_start
96 173
97 def unpacks(self, format, size): 174 def unpacks(self, format, size):
98 return struct.unpack(format, self.read(size)) 175 return struct.unpack(format, self.read(size))
@@ -148,8 +225,6 @@ def consume_GuidAndPicture(stream): @@ -148,8 +225,6 @@ def consume_GuidAndPicture(stream):
148 def consume_CountOfBytesWithCompressionFlag(stream): 225 def consume_CountOfBytesWithCompressionFlag(stream):
149 # CountOfBytesWithCompressionFlag or CountOfCharsWithCompressionFlag: [MS-OFORMS] 2.4.14.2 or 2.4.14.3 226 # CountOfBytesWithCompressionFlag or CountOfCharsWithCompressionFlag: [MS-OFORMS] 2.4.14.2 or 2.4.14.3
150 count = stream.unpack('<L', 4) 227 count = stream.unpack('<L', 4)
151 - if not count & 0x80000000 and count != 0:  
152 - stream.aise_error('Uncompress string length', 4)  
153 return count & 0x7FFFFFFF 228 return count & 0x7FFFFFFF
154 229
155 def consume_SiteClassInfo(stream): 230 def consume_SiteClassInfo(stream):
@@ -175,33 +250,27 @@ def consume_OleSiteConcreteControl(stream): @@ -175,33 +250,27 @@ def consume_OleSiteConcreteControl(stream):
175 with stream.will_jump_to(cbSite): 250 with stream.will_jump_to(cbSite):
176 propmask = SitePropMask(stream.unpack('<L', 4)) 251 propmask = SitePropMask(stream.unpack('<L', 4))
177 # SiteDataBlock: [MS-OFORMS] 2.2.10.12.3 252 # SiteDataBlock: [MS-OFORMS] 2.2.10.12.3
178 - name_len = tag_len = id = 0  
179 - if propmask.fName:  
180 - name_len = consume_CountOfBytesWithCompressionFlag(stream)  
181 - if propmask.fTag:  
182 - tag_len = consume_CountOfBytesWithCompressionFlag(stream)  
183 - if propmask.fID:  
184 - id = stream.unpack('<L', 4)  
185 - for prop in ['fHelpContextID', 'fBitFlags', 'fObjectStreamSize']:  
186 - if propmask[prop]:  
187 - stream.read(4)  
188 - tabindex = ClsidCacheIndex = 0  
189 - with stream.will_pad(): 253 + with stream.padded_struct():
  254 + name_len = tag_len = id = 0
  255 + if propmask.fName:
  256 + name_len = consume_CountOfBytesWithCompressionFlag(stream)
  257 + if propmask.fTag:
  258 + tag_len = consume_CountOfBytesWithCompressionFlag(stream)
  259 + if propmask.fID:
  260 + id = stream.unpack('<L', 4)
  261 + propmask.consume(stream, [('fHelpContextID', 4), ('fBitFlags', 4), ('fObjectStreamSize', 4)])
  262 + tabindex = ClsidCacheIndex = 0
190 if propmask.fTabIndex: 263 if propmask.fTabIndex:
191 tabindex = stream.unpack('<H', 2) 264 tabindex = stream.unpack('<H', 2)
192 if propmask.fClsidCacheIndex: 265 if propmask.fClsidCacheIndex:
193 ClsidCacheIndex = stream.unpack('<H', 2) 266 ClsidCacheIndex = stream.unpack('<H', 2)
194 - if propmask.fGroupID:  
195 - stream.read(2)  
196 - # For the next 4 entries, the documentation adds padding, but it should already be aligned??  
197 - for prop in ['fControlTipText', 'fRuntimeLicKey', 'fControlSource', 'fRowSource']:  
198 - if propmask[prop]:  
199 - stream.read(4) 267 + propmask.consume(stream, [('fGroupID', 2), ('fControlTipText', 4), ('fRuntimeLicKey', 4),
  268 + ('fControlSource', 4), ('fRowSource', 4)])
200 # SiteExtraDataBlock: [MS-OFORMS] 2.2.10.12.4 269 # SiteExtraDataBlock: [MS-OFORMS] 2.2.10.12.4
201 name = stream.read(name_len) 270 name = stream.read(name_len)
202 tag = stream.read(tag_len) 271 tag = stream.read(tag_len)
203 return {'name': name, 'tag': tag, 'id': id, 'tabindex': tabindex, 272 return {'name': name, 'tag': tag, 'id': id, 'tabindex': tabindex,
204 - 'ClsidCacheIndex': ClsidCacheIndex} 273 + 'ClsidCacheIndex': ClsidCacheIndex, 'value': None}
205 274
206 def consume_FormControl(stream): 275 def consume_FormControl(stream):
207 # FormControl: [MS-OFORMS] 2.2.10.1 276 # FormControl: [MS-OFORMS] 2.2.10.1
@@ -210,9 +279,7 @@ def consume_FormControl(stream): @@ -210,9 +279,7 @@ def consume_FormControl(stream):
210 with stream.will_jump_to(cbform): 279 with stream.will_jump_to(cbform):
211 propmask = FormPropMask(stream.unpack('<L', 4)) 280 propmask = FormPropMask(stream.unpack('<L', 4))
212 # FormDataBlock: [MS-OFORMS] 2.2.10.3 281 # FormDataBlock: [MS-OFORMS] 2.2.10.3
213 - for prop in ['fBackColor', 'fForeColor', 'fNextAvailableID']:  
214 - if propmask[prop]:  
215 - stream.read(4) 282 + propmask.consume(stream, [('fBackColor', 4), ('fForeColor', 4), ('fNextAvailableID', 4)])
216 if propmask.fBooleanProperties: 283 if propmask.fBooleanProperties:
217 BooleanProperties = stream.unpack('<L', 4) 284 BooleanProperties = stream.unpack('<L', 4)
218 FORM_FLAG_DONTSAVECLASSTABLE = (BooleanProperties & (1<<15)) >> 15 285 FORM_FLAG_DONTSAVECLASSTABLE = (BooleanProperties & (1<<15)) >> 15
@@ -233,11 +300,12 @@ def consume_FormControl(stream): @@ -233,11 +300,12 @@ def consume_FormControl(stream):
233 consume_SiteClassInfo(stream) 300 consume_SiteClassInfo(stream)
234 (CountOfSites, CountOfBytes) = stream.unpacks('<LL', 8) 301 (CountOfSites, CountOfBytes) = stream.unpacks('<LL', 8)
235 remaining_SiteDepthsAndTypes = CountOfSites 302 remaining_SiteDepthsAndTypes = CountOfSites
236 - with stream.will_pad():  
237 - while remaining_SiteDepthsAndTypes > 0:  
238 - remaining_SiteDepthsAndTypes -= consume_FormObjectDepthTypeCount(stream)  
239 - for i in range(CountOfSites):  
240 - yield consume_OleSiteConcreteControl(stream) 303 + with stream.will_jump_to(CountOfBytes):
  304 + with stream.will_pad():
  305 + while remaining_SiteDepthsAndTypes > 0:
  306 + remaining_SiteDepthsAndTypes -= consume_FormObjectDepthTypeCount(stream)
  307 + for i in range(CountOfSites):
  308 + yield consume_OleSiteConcreteControl(stream)
241 309
242 def consume_MorphDataControl(stream): 310 def consume_MorphDataControl(stream):
243 # MorphDataControl: [MS-OFORMS] 2.2.5.1 311 # MorphDataControl: [MS-OFORMS] 2.2.5.1
@@ -246,36 +314,25 @@ def consume_MorphDataControl(stream): @@ -246,36 +314,25 @@ def consume_MorphDataControl(stream):
246 with stream.will_jump_to(cbMorphData): 314 with stream.will_jump_to(cbMorphData):
247 propmask = MorphDataPropMask(stream.unpack('<Q', 8)) 315 propmask = MorphDataPropMask(stream.unpack('<Q', 8))
248 # MorphDataDataBlock: [MS-OFORMS] 2.2.5.3 316 # MorphDataDataBlock: [MS-OFORMS] 2.2.5.3
249 - for prop in ['fVariousPropertyBits', 'fBackColor', 'fForeColor', 'fMaxLength']:  
250 - if propmask[prop]:  
251 - stream.read(4)  
252 - with stream.will_pad():  
253 - for prop in ['fBorderStyle', 'fScrollBars', 'fDisplayStyle', 'fMousePointer']:  
254 - if propmask[prop]:  
255 - stream.read(1)  
256 - # PasswordChar, BoundColumn, TextColumn, ColumnCount, and ListRows are 2B + pad = 4B  
257 - # ListWidth is 4B + pad = 4B  
258 - for prop in ['fPasswordChar', 'fListWidth', 'fBoundColumn', 'fTextColumn', 'fColumnCount',  
259 - 'fListRows']:  
260 - if propmask[prop]:  
261 - stream.read(4)  
262 - with stream.will_pad():  
263 - if propmask.fcColumnInfo:  
264 - stream.read(2)  
265 - for prop in ['fMatchEntry', 'fListStyle', 'fShowDropButtonWhen', 'fDropButtonStyle',  
266 - 'fMultiSelect']:  
267 - if propmask[prop]:  
268 - stream.read(1)  
269 - if propmask.fValue:  
270 - value_size = consume_CountOfBytesWithCompressionFlag(stream)  
271 - else:  
272 - value_size = 0  
273 - # Caption, PicturePosition, BorderColor, SpecialEffect, GroupName are 4B + pad = 4B  
274 - # MouseIcon, Picture, Accelerator are 2B + pad = 4B  
275 - for prop in ['fCaption', 'fPicturePosition', 'fBorderColor', 'fSpecialEffect',  
276 - 'fMouseIcon', 'fPicture', 'fAccelerator', 'fGroupName']:  
277 - if propmask[prop]:  
278 - stream.read(4) 317 + with stream.padded_struct():
  318 + propmask.consume(stream, [('fVariousPropertyBits', 4), ('fBackColor', 4),
  319 + ('fForeColor', 4), ('fMaxLength', 4),
  320 + ('fBorderStyle', 1), ('fScrollBars', 1),
  321 + ('fDisplayStyle', 1), ('fMousePointer', 1),
  322 + ('fPasswordChar', 2), ('fListWidth', 4),
  323 + ('fBoundColumn', 2), ('fTextColumn', 2),
  324 + ('fColumnCount', 2), ('fListRows', 2),
  325 + ('fcColumnInfo', 2), ('fMatchEntry', 1),
  326 + ('fListStyle', 1), ('fShowDropButtonWhen', 1),
  327 + ('fDropButtonStyle', 1), ('fMultiSelect', 1)])
  328 + if propmask.fValue:
  329 + value_size = consume_CountOfBytesWithCompressionFlag(stream)
  330 + else:
  331 + value_size = 0
  332 + propmask.consume(stream, [('fCaption', 4), ('fPicturePosition', 4),
  333 + ('fBorderColor', 4), ('fSpecialEffect', 4),
  334 + ('fMouseIcon', 2), ('fPicture', 2),
  335 + ('fAccelerator', 2), ('fGroupName', 4)])
279 # MorphDataExtraDataBlock: [MS-OFORMS] 2.2.5.4 336 # MorphDataExtraDataBlock: [MS-OFORMS] 2.2.5.4
280 stream.read(8) 337 stream.read(8)
281 value = stream.read(value_size) 338 value = stream.read(value_size)
@@ -287,17 +344,141 @@ def consume_MorphDataControl(stream): @@ -287,17 +344,141 @@ def consume_MorphDataControl(stream):
287 consume_TextProps(stream) 344 consume_TextProps(stream)
288 return value 345 return value
289 346
  347 +def consume_ImageControl(stream):
  348 + # ImageControl: [MS-OFORMS] 2.2.3.1
  349 + stream.check_values('ImageControl (versions)', '<BB', 2, (0, 2))
  350 + cbImage = stream.unpack('<H', 2)
  351 + with stream.will_jump_to(cbImage):
  352 + propmask = ImagePropMask(stream.unpack('<L', 4))
  353 + # Skip the DataBlock and the ExtraDataBlock
  354 + # ImageStreamData: [MS-OFORMS] 2.2.3.5
  355 + if propmask.fPicture:
  356 + consume_GuidAndPicture(stream)
  357 + if propmask.fMouseIcon:
  358 + consume_GuidAndPicture(stream)
  359 +
  360 +def consume_CommandButtonControl(stream):
  361 + # CommandButtonControl: [MS-OFORMS] 2.2.1.1
  362 + stream.check_values('CommandButtonControl (versions)', '<BB', 2, (0, 2))
  363 + cbCommandButton = stream.unpack('<H', 2)
  364 + with stream.will_jump_to(cbCommandButton):
  365 + propmask = CommandButtonPropMask(stream.unpack('<L', 4))
  366 + # Skip the DataBlock and the ExtraDataBlock
  367 + # ImageStreamData: [MS-OFORMS] 2.2.1.5
  368 + if propmask.fPicture:
  369 + consume_GuidAndPicture(stream)
  370 + if propmask.fMouseIcon:
  371 + consume_GuidAndPicture(stream)
  372 + consume_TextProps(stream)
  373 +
  374 +def consume_SpinButtonControl(stream):
  375 + # SpinButtonControl: [MS-OFORMS] 2.2.8.1
  376 + stream.check_values('SpinButtonControl (versions)', '<BB', 2, (0, 2))
  377 + cbSpinButton = stream.unpack('<H', 2)
  378 + with stream.will_jump_to(cbSpinButton):
  379 + propmask = SpinButtonPropMask(stream.unpack('<L', 4))
  380 + # Skip the DataBlock and the ExtraDataBlock
  381 + # SpinButtonStreamData: [MS-OFORMS] 2.2.8.5
  382 + if propmask.fMouseIcon:
  383 + consume_GuidAndPicture(stream)
  384 +
  385 +def consume_TabStripControl(stream):
  386 + # TabStripControl: [MS-OFORMS] 2.2.9.1
  387 + stream.check_values('TabStripControl (versions)', '<BB', 2, (0, 2))
  388 + cbTabStrip = stream.unpack('<H', 2)
  389 + with stream.will_jump_to(cbTabStrip):
  390 + propmask = TabStripPropMask(stream.unpack('<L', 4))
  391 + # TabStripDataBlock: [MS-OFORMS] 2.2.9.3
  392 + propmask.consume(stream, [('fListIndex', 4), ('fBackColor', 4),
  393 + ('fForeColor', 4), ('fSize', 4),
  394 + ('fMousePointer', 1), ('fTabOrientation', 4),
  395 + ('fTabStyle', 4), ('fTabFixedWidth', 4),
  396 + ('fTabFixedHeight', 4), ('fTipStrings', 4),
  397 + ('fNames', 4), ('fVariousPropertyBits', 4),
  398 + ('fTabsAllocated', 4), ('fTags', 4)])
  399 + tabData = 0
  400 + if propmask['fTabData']:
  401 + tabData = stream.unpack('<L', 4)
  402 + # Skip the ExtraDataBlock
  403 + # TabStripStreamData: [MS-OFORMS] 2.2.9.5
  404 + if propmask.fMouseIcon:
  405 + consume_GuidAndPicture(stream)
  406 + # TextProps
  407 + consume_TextProps(stream)
  408 + # TabStripTabFlagData: [MS-OFORMS] 2.2.9.6
  409 + for i in range(tabData):
  410 + stream.read(4)
  411 +
  412 +def consume_LabelControl(stream):
  413 + # LabelControl: [MS-OFORMS] 2.2.4.1
  414 + stream.check_values('LabelControl (versions)', '<BB', 2, (0, 2))
  415 + cbLabel = stream.unpack('<H', 2)
  416 + with stream.will_jump_to(cbLabel):
  417 + propmask = LabelPropMask(stream.unpack('<L', 4))
  418 + # LabelDataBlock: [MS-OFORMS] 2.2.4.3
  419 + with stream.padded_struct():
  420 + propmask.consume(stream, [('fForeColor', 4), ('fBackColor', 4),
  421 + ('fVariousPropertyBits', 4)])
  422 + if propmask.fCaption:
  423 + caption_size = consume_CountOfBytesWithCompressionFlag(stream)
  424 + else:
  425 + caption_size = 0
  426 + propmask.consume(stream, [('fPicturePosition', 4), ('fMousePointer', 1),
  427 + ('fBorderColor', 4), ('fBorderStyle', 2),
  428 + ('fSpecialEffect', 2), ('fPicture', 2),
  429 + ('fAccelerator', 2), ('fMouseIcon', 2)])
  430 + # LabelExtraDataBlock: [MS-OFORMS] 2.2.4.4
  431 + caption = stream.read(caption_size)
  432 + stream.read(8)
  433 + # LabelStreamData: [MS-OFORMS] 2.2.4.5
  434 + if propmask.fPicture:
  435 + consume_GuidAndPicture(stream)
  436 + if propmask.fMouseIcon:
  437 + consume_GuidAndPicture(stream)
  438 + # TextProps
  439 + consume_TextProps(stream)
  440 + return caption
  441 +
  442 +def consume_ScrollBarControl(stream):
  443 + # ScrollBarControl: [MS-OFORMS] 2.2.7.1
  444 + stream.check_values('LabelControl (versions)', '<BB', 2, (0, 2))
  445 + cbScrollBar = stream.unpack('<H', 2)
  446 + with stream.will_jump_to(cbScrollBar):
  447 + propmask = ScrollBarPropMask(stream.unpack('<L', 4))
  448 + # Skip the DataBlock and the ExtraDataBlock
  449 + # ScrollBarStreamData: [MS-OFORMS] 2.2.7.5
  450 + if propmask.fMouseIcon:
  451 + consume_GuidAndPicture(stream)
  452 +
290 def extract_OleFormVariables(ole_file, stream_dir): 453 def extract_OleFormVariables(ole_file, stream_dir):
291 control = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['f'])) 454 control = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['f']))
292 variables = list(consume_FormControl(control)) 455 variables = list(consume_FormControl(control))
293 # print('/'.join(stream_dir + ['o'])) 456 # print('/'.join(stream_dir + ['o']))
294 data = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['o'])) 457 data = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['o']))
295 for var in variables: 458 for var in variables:
296 - if var['ClsidCacheIndex'] != 23:  
297 - #raise OleFormParsingError('Unsupported stored type: {0}'.format(str(var['ClsidCacheIndex']))) 459 + # See FormEmbeddedActiveXControlCached for type definition: [MS-OFORMS] 2.4.5
  460 + if var['ClsidCacheIndex'] == 7:
  461 + consume_FormControl(data)
  462 + elif var['ClsidCacheIndex'] == 12:
  463 + consume_ImageControl(data)
  464 + elif var['ClsidCacheIndex'] == 14:
  465 + consume_FormControl(data)
  466 + elif var['ClsidCacheIndex'] in [15, 23, 24, 25, 26, 27, 28]:
  467 + var['value'] = consume_MorphDataControl(data)
  468 + elif var['ClsidCacheIndex'] == 16:
  469 + consume_SpinButtonControl(data)
  470 + elif var['ClsidCacheIndex'] == 17:
  471 + consume_CommandButtonControl(data)
  472 + elif var['ClsidCacheIndex'] == 18:
  473 + consume_TabStripControl(data)
  474 + elif var['ClsidCacheIndex'] == 21:
  475 + consume_LabelControl(data)
  476 + elif var['ClsidCacheIndex'] == 47:
  477 + consume_ScrollBarControl(data)
  478 + elif var['ClsidCacheIndex'] == 57:
  479 + consume_FormControl(data)
  480 + else:
298 # TODO: use logging instead of print 481 # TODO: use logging instead of print
299 print('ERROR: Unsupported stored type in user form: {0}'.format(str(var['ClsidCacheIndex']))) 482 print('ERROR: Unsupported stored type in user form: {0}'.format(str(var['ClsidCacheIndex'])))
300 - var['value'] = None  
301 - else:  
302 - var['value'] = consume_MorphDataControl(data) 483 + break
303 return variables 484 return variables
tests/oleform/__init__.py 0 โ†’ 100644
tests/oleform/test_basic.py 0 โ†’ 100644
  1 +""" Test oleform basic functionality """
  2 +
  3 +import unittest
  4 +from os.path import join
  5 +import sys
  6 +
  7 +# Directory with test data, independent of current working directory
  8 +from tests.test_utils import DATA_BASE_DIR
  9 +
  10 +if sys.version_info[0] <= 2:
  11 + from oletools.olevba import VBA_Parser
  12 +else:
  13 + from oletools.olevba3 import VBA_Parser
  14 +
  15 +SAMPLES = [('oleform-PR314.docm', [('word/vbaProject.bin', u'UserFormTEST1', {'name': 'Label1', 'value': None, 'tag': 'l\x18sdf', 'ClsidCacheIndex': 21, 'id': 1, 'tabindex': 0}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'TextBox1', 'value': 'heyhey', 'tag': '', 'ClsidCacheIndex': 23, 'id': 2, 'tabindex': 1}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'ComboBox1', 'value': 'none dd', 'tag': '', 'ClsidCacheIndex': 25, 'id': 3, 'tabindex': 2}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'CheckBox1', 'value': '1', 'tag': '', 'ClsidCacheIndex': 26, 'id': 5, 'tabindex': 4}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'OptionButton1', 'value': '0', 'tag': '', 'ClsidCacheIndex': 27, 'id': 6, 'tabindex': 5}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'ToggleButton1', 'value': '0', 'tag': '', 'ClsidCacheIndex': 28, 'id': 7, 'tabindex': 6}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'Frame1', 'value': None, 'tag': '', 'ClsidCacheIndex': 14, 'id': 8, 'tabindex': 7}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'TabStrip1', 'value': None, 'tag': '', 'ClsidCacheIndex': 18, 'id': 10, 'tabindex': 8}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'CommandButton1', 'value': None, 'tag': '', 'ClsidCacheIndex': 17, 'id': 9, 'tabindex': 9}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'MultiPage1', 'value': None, 'tag': '', 'ClsidCacheIndex': 57, 'id': 12, 'tabindex': 10}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'ScrollBar1', 'value': None, 'tag': '', 'ClsidCacheIndex': 47, 'id': 16, 'tabindex': 11}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'SpinButton1', 'value': None, 'tag': '', 'ClsidCacheIndex': 16, 'id': 17, 'tabindex': 12}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'Image1', 'value': None, 'tag': '', 'ClsidCacheIndex': 12, 'id': 18, 'tabindex': 13}), ('word/vbaProject.bin', u'UserFormTEST1', {'name': 'ListBox1', 'value': '', 'tag': '', 'ClsidCacheIndex': 24, 'id': 4, 'tabindex': 3}), ('word/vbaProject.bin', u'UserFormTEST1/i08', {'name': 'TextBox2', 'value': 'abcd', 'tag': '', 'ClsidCacheIndex': 23, 'id': 20, 'tabindex': 0}), ('word/vbaProject.bin', u'UserFormTEST1/i12', {'name': '', 'value': None, 'tag': '', 'ClsidCacheIndex': 18, 'id': 13, 'tabindex': 2}), ('word/vbaProject.bin', u'UserFormTEST1/i12', {'name': 'Page1', 'value': None, 'tag': '', 'ClsidCacheIndex': 7, 'id': 14, 'tabindex': 0}), ('word/vbaProject.bin', u'UserFormTEST1/i12', {'name': 'Page2', 'value': None, 'tag': '', 'ClsidCacheIndex': 7, 'id': 15, 'tabindex': 1}), ('word/vbaProject.bin', u'UserFormTEST1/i12/i14', {'name': 'TextBox3', 'value': 'last one', 'tag': '', 'ClsidCacheIndex': 23, 'id': 24, 'tabindex': 0}), ('word/vbaProject.bin', u'UserFormTest2', {'name': 'Label1', 'value': None, 'tag': '', 'ClsidCacheIndex': 21, 'id': 1, 'tabindex': 0}), ('word/vbaProject.bin', u'UserFormTest2', {'name': 'Label2', 'value': None, 'tag': '', 'ClsidCacheIndex': 21, 'id': 2, 'tabindex': 1}), ('word/vbaProject.bin', u'UserFormTest2', {'name': 'TextBox1', 'value': '&\xe9"\'', 'tag': '', 'ClsidCacheIndex': 23, 'id': 3, 'tabindex': 2})])]
  16 +
  17 +class TestOleForm(unittest.TestCase):
  18 +
  19 + def test_samples(self):
  20 + if sys.version_info[0] > 2:
  21 + # Unfortunately, olevba3 doesn't have extract_form_strings_extended
  22 + return
  23 + for sample, expected_result in SAMPLES:
  24 + full_name = join(DATA_BASE_DIR, 'oleform', sample)
  25 + parser = VBA_Parser(full_name)
  26 + variables = list(parser.extract_form_strings_extended())
  27 + self.assertEqual(variables, expected_result)
  28 +
  29 +
  30 +# just in case somebody calls this file as a script
  31 +if __name__ == '__main__':
  32 + unittest.main()
  33 +
tests/test-data/oleform/oleform-PR314.docm 0 โ†’ 100755
No preview for this file type