Commit 9d2795aa1a3b3af0d7255da9b63e7171e65af843
1 parent
425a038a
WIP: oleform: implement other types of stored controls
Showing
1 changed file
with
158 additions
and
3 deletions
oletools/oleform.py
| @@ -52,6 +52,52 @@ class MorphDataPropMask(Mask): | @@ -52,6 +52,52 @@ class MorphDataPropMask(Mask): | ||
| 52 | 'fBorderColor', 'fSpecialEffect', 'fMouseIcon', 'fPicture', 'fAccelerator', | 52 | 'fBorderColor', 'fSpecialEffect', 'fMouseIcon', 'fPicture', 'fAccelerator', |
| 53 | 'UnusedBits2', 'Reserved', 'fGroupName'] | 53 | 'UnusedBits2', 'Reserved', 'fGroupName'] |
| 54 | 54 | ||
| 55 | +class ImagePropMask(Mask): | ||
| 56 | + """ImagePropMask: [MS-OFORMS] 2.2.3.2""" | ||
| 57 | + _size = 15 | ||
| 58 | + _names = ['UnusedBits1_1', 'UnusedBits1_2', 'fAutoSize', 'fBorderColor', 'fBackColor', | ||
| 59 | + 'fBorderStyle', 'fMousePointer', 'fPictureSizeMode', 'fSpecialEffect', 'fSize', | ||
| 60 | + 'fPicture', 'fPictureAlignment', 'fPictureTiling', 'fVariousPropertyBits', | ||
| 61 | + 'fMouseIcon'] | ||
| 62 | + | ||
| 63 | +class CommandButtonPropMask(Mask): | ||
| 64 | + """CommandButtonPropMask: [MS-OFORMS] 2.2.1.2""" | ||
| 65 | + _size = 11 | ||
| 66 | + _names = ['fForeColor', 'fBackColor', 'fVariousPropertyBits', 'fCaption', 'fPicturePosition', | ||
| 67 | + 'fSize', 'fMousePointer', 'fPicture', 'fAccelerator', 'fTakeFocusOnClick', | ||
| 68 | + 'fMouseIcon'] | ||
| 69 | + | ||
| 70 | +class SpinButtonPropMask(Mask): | ||
| 71 | + """SpinButtonPropMask: [MS-OFORMS] 2.2.8.2""" | ||
| 72 | + _size = 15 | ||
| 73 | + _names = ['fForeColor', 'fBackColor', 'fVariousPropertyBits', 'fSize', 'UnusedBits1', | ||
| 74 | + 'fMin', 'fMax', 'fPosition', 'fPrevEnabled', 'fNextEnabled', 'fSmallChange', | ||
| 75 | + 'fOrientation', 'fDelay', 'fMouseIcon', 'fMousePointer'] | ||
| 76 | + | ||
| 77 | +class TabStripPropMask(Mask): | ||
| 78 | + """TabStripPropMask: [MS-OFORMS] 2.2.9.2""" | ||
| 79 | + _size = 25 | ||
| 80 | + _names = ['fListIndex', 'fBackColor', 'fForeColor', 'Unused1', 'fSize', 'fItems', | ||
| 81 | + 'fMousePointer', 'Unused2', 'fTabOrientation', 'fTabStyle', 'fMultiRow', | ||
| 82 | + 'fTabFixedWidth', 'fTabFixedHeight', 'fTooltips', 'Unused3', 'fTipStrings', | ||
| 83 | + 'Unused4', 'fNames', 'fVariousPropertyBits', 'fNewVersion', 'fTabsAllocated', | ||
| 84 | + 'fTags', 'fTabData', 'fAccelerator', 'fMouseIcon'] | ||
| 85 | + | ||
| 86 | +class LabelPropMask(Mask): | ||
| 87 | + """LabelPropMask: [MS-OFORMS] 2.2.4.2""" | ||
| 88 | + _size = 13 | ||
| 89 | + _names = ['fForeColor', 'fBackColor', 'fVariousPropertyBits', 'fCaption', | ||
| 90 | + 'fPicturePosition', 'fSize', 'fMousePointer', 'fBorderColor', 'fBorderStyle', | ||
| 91 | + 'fSpecialEffect', 'fPicture', 'fAccelerator', 'fMouseIcon'] | ||
| 92 | + | ||
| 93 | +class ScrollBarPropMask(Mask): | ||
| 94 | + """ScrollBarPropMask: [MS-OFORMS] 2.2.7.2""" | ||
| 95 | + _size = 17 | ||
| 96 | + _names = ['fForeColor', 'fBackColor', 'fVariousPropertyBits', 'fSize', 'fMousePointer', | ||
| 97 | + 'fMin', 'fMax', 'fPosition', 'UnusedBits1', 'fPrevEnabled', 'fNextEnabled', | ||
| 98 | + 'fSmallChange', 'fLargeChange', 'fOrientation', 'fProportionalThumb', | ||
| 99 | + 'fDelay', 'fMouseIcon'] | ||
| 100 | + | ||
| 55 | class ExtendedStream(object): | 101 | class ExtendedStream(object): |
| 56 | def __init__(self, stream, path): | 102 | def __init__(self, stream, path): |
| 57 | self._pos = 0 | 103 | self._pos = 0 |
| @@ -287,16 +333,125 @@ def consume_MorphDataControl(stream): | @@ -287,16 +333,125 @@ def consume_MorphDataControl(stream): | ||
| 287 | consume_TextProps(stream) | 333 | consume_TextProps(stream) |
| 288 | return value | 334 | return value |
| 289 | 335 | ||
| 336 | +def consume_ImageControl(stream): | ||
| 337 | + # ImageControl: [MS-OFORMS] 2.2.3.1 | ||
| 338 | + stream.check_values('ImageControl (versions)', '<BB', 2, (0, 2)) | ||
| 339 | + cbImage = stream.unpack('<H', 2) | ||
| 340 | + with stream.will_jump_to(cbImage): | ||
| 341 | + propmask = ImagePropMask(stream.unpack('<L', 4)) | ||
| 342 | + # Skip the DataBlock and the ExtraDataBlock | ||
| 343 | + # ImageStreamData: [MS-OFORMS] 2.2.3.5 | ||
| 344 | + if propmask.fPicture: | ||
| 345 | + consume_GuidAndPicture(stream) | ||
| 346 | + if propmask.fMouseIcon: | ||
| 347 | + consume_GuidAndPicture(stream) | ||
| 348 | + | ||
| 349 | +def consume_CommandButtonControl(stream): | ||
| 350 | + # CommandButtonControl: [MS-OFORMS] 2.2.1.1 | ||
| 351 | + stream.check_values('CommandButtonControl (versions)', '<BB', 2, (0, 2)) | ||
| 352 | + cbCommandButton = stream.unpack('<H', 2) | ||
| 353 | + with stream.will_jump_to(cbCommandButton): | ||
| 354 | + propmask = CommandButtonPropMask(stream.unpack('<L', 4)) | ||
| 355 | + # Skip the DataBlock and the ExtraDataBlock | ||
| 356 | + # ImageStreamData: [MS-OFORMS] 2.2.1.5 | ||
| 357 | + if propmask.fPicture: | ||
| 358 | + consume_GuidAndPicture(stream) | ||
| 359 | + if propmask.fMouseIcon: | ||
| 360 | + consume_GuidAndPicture(stream) | ||
| 361 | + consume_TextProps(stream) | ||
| 362 | + | ||
| 363 | +def consume_SpinButtonControl(stream): | ||
| 364 | + # SpinButtonControl: [MS-OFORMS] 2.2.8.1 | ||
| 365 | + stream.check_values('SpinButtonControl (versions)', '<BB', 2, (0, 2)) | ||
| 366 | + cbSpinButton = stream.unpack('<H', 2) | ||
| 367 | + with stream.will_jump_to(cbSpinButton): | ||
| 368 | + propmask = SpinButtonPropMask(stream.unpack('<L', 4)) | ||
| 369 | + # Skip the DataBlock and the ExtraDataBlock | ||
| 370 | + # SpinButtonStreamData: [MS-OFORMS] 2.2.8.5 | ||
| 371 | + if propmask.fMouseIcon: | ||
| 372 | + consume_GuidAndPicture(stream) | ||
| 373 | + | ||
| 374 | +def consume_TabStripControl(stream): | ||
| 375 | + # TabStripControl: [MS-OFORMS] 2.2.9.1 | ||
| 376 | + stream.check_values('TabStripControl (versions)', '<BB', 2, (0, 2)) | ||
| 377 | + cbTabStrip = stream.unpack('<H', 2) | ||
| 378 | + with stream.will_jump_to(cbTabStrip): | ||
| 379 | + propmask = TabStripPropMask(stream.unpack('<L', 4)) | ||
| 380 | + # TabStripDataBlock: [MS-OFORMS] 2.2.9.3 | ||
| 381 | + # All are 4B (or 4B + pad = 4B), except MousePointer, which is 1B + pad = 4b | ||
| 382 | + for prop in ['fListIndex', 'fBackColor', 'fForeColor', 'fSize', 'fTabOrientation' | ||
| 383 | + 'fTabStyle', 'fTabFixedWidth', 'fTabFixedHeight', 'fTipStrings', | ||
| 384 | + 'fNames', 'fVariousPropertyBits', 'fTabsAllocated', 'fTags']: | ||
| 385 | + if propmask[prop]: | ||
| 386 | + stream.read(4) | ||
| 387 | + tabData = 0 | ||
| 388 | + if propmask['fTabData']: | ||
| 389 | + tabData = stream.unpack('<L', 4) | ||
| 390 | + # Skip the ExtraDataBlock | ||
| 391 | + # TabStripStreamData: [MS-OFORMS] 2.2.9.5 | ||
| 392 | + if propmask.fMouseIcon: | ||
| 393 | + consume_GuidAndPicture(stream) | ||
| 394 | + # TextProps | ||
| 395 | + consume_TextProps(stream) | ||
| 396 | + # TabStripTabFlagData: [MS-OFORMS] 2.2.9.6 | ||
| 397 | + for i in range(tabData): | ||
| 398 | + stream.read(4) | ||
| 399 | + | ||
| 400 | +def consume_LabelControl(stream): | ||
| 401 | + # LabelControl: [MS-OFORMS] 2.2.4.1 | ||
| 402 | + stream.check_values('LabelControl (versions)', '<BB', 2, (0, 2)) | ||
| 403 | + cbLabel = stream.unpack('<H', 2) | ||
| 404 | + with stream.will_jump_to(cbLabel): | ||
| 405 | + propmask = LabelPropMask(stream.unpack('<L', 4)) | ||
| 406 | + # Skip the DataBlock and the ExtraDataBlock | ||
| 407 | + # LabelStreamData: [MS-OFORMS] 2.2.4.5 | ||
| 408 | + if propmask.fPicture: | ||
| 409 | + consume_GuidAndPicture(stream) | ||
| 410 | + if propmask.fMouseIcon: | ||
| 411 | + consume_GuidAndPicture(stream) | ||
| 412 | + # TextProps | ||
| 413 | + consume_TextProps(stream) | ||
| 414 | + | ||
| 415 | +def consume_ScrollBarControl(stream): | ||
| 416 | + # ScrollBarControl: [MS-OFORMS] 2.2.7.1 | ||
| 417 | + stream.check_values('LabelControl (versions)', '<BB', 2, (0, 2)) | ||
| 418 | + cbScrollBar = stream.unpack('<H', 2) | ||
| 419 | + with stream.will_jump_to(cbScrollBar): | ||
| 420 | + propmask = ScrollBarPropMask(stream.unpack('<L', 4)) | ||
| 421 | + # Skip the DataBlock and the ExtraDataBlock | ||
| 422 | + # ScrollBarStreamData: [MS-OFORMS] 2.2.7.5 | ||
| 423 | + if propmask.fMouseIcon: | ||
| 424 | + consume_GuidAndPicture(stream) | ||
| 425 | + | ||
| 290 | def extract_OleFormVariables(ole_file, stream_dir): | 426 | def extract_OleFormVariables(ole_file, stream_dir): |
| 291 | control = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['f'])) | 427 | control = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['f'])) |
| 292 | variables = list(consume_FormControl(control)) | 428 | variables = list(consume_FormControl(control)) |
| 293 | # print('/'.join(stream_dir + ['o'])) | 429 | # print('/'.join(stream_dir + ['o'])) |
| 294 | data = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['o'])) | 430 | data = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['o'])) |
| 295 | for var in variables: | 431 | for var in variables: |
| 296 | - if var['ClsidCacheIndex'] != 23: | 432 | + # See FormEmbeddedActiveXControlCached for type definition: [MS-OFORMS] 2.4.5 |
| 433 | + if var['ClsidCacheIndex'] == 7: | ||
| 434 | + raise OleFormParsingError('Malformed document: Forms should be stored in the f stream') | ||
| 435 | + elif var['ClsidCacheIndex'] == 12: | ||
| 436 | + consume_ImageControl(data) | ||
| 437 | + elif var['ClsidCacheIndex'] == 14: | ||
| 438 | + raise OleFormParsingError('Malformed document: Frames should be stored in the f stream') | ||
| 439 | + elif var['ClsidCacheIndex'] in [15, 23, 24, 25, 26, 27, 28]: | ||
| 440 | + var['value'] = consume_MorphDataControl(data) | ||
| 441 | + elif var['ClsidCacheIndex'] == 16: | ||
| 442 | + consume_SpinButtonControl(data) | ||
| 443 | + elif var['ClsidCacheIndex'] == 17: | ||
| 444 | + consume_CommandButtonControl(data) | ||
| 445 | + elif var['ClsidCacheIndex'] == 18: | ||
| 446 | + consume_TabStripControl(data) | ||
| 447 | + elif var['ClsidCacheIndex'] == 21: | ||
| 448 | + consume_LabelControl(data) | ||
| 449 | + elif var['ClsidCacheIndex'] == 47: | ||
| 450 | + consume_ScrollBarControl(data) | ||
| 451 | + elif var['ClsidCacheIndex'] == 57: | ||
| 452 | + raise OleFormParsingError('Malformed document: MultiPages should be stored in a x stream') | ||
| 453 | + else: | ||
| 297 | # TODO: use logging instead of print | 454 | # TODO: use logging instead of print |
| 298 | print('ERROR: Unsupported stored type in user form: {0}'.format(str(var['ClsidCacheIndex']))) | 455 | print('ERROR: Unsupported stored type in user form: {0}'.format(str(var['ClsidCacheIndex']))) |
| 299 | break | 456 | break |
| 300 | - else: | ||
| 301 | - var['value'] = consume_MorphDataControl(data) | ||
| 302 | return variables | 457 | return variables |