Commit f2f6134ab04faf6c19fb7396467c9e4c50fc755a

Authored by Jürgen Löhel
1 parent be57af2f

plugin_biff: updated to v0.0.17

New version of the BIFF plugin from Didier Stevens. Changelog:

- 2020/05/26: 0.0.16 added logic for reserved bits in BOUNDSHEET
- 2020/07/17: 0.0.17 added option --statistics

Signed-off-by: Jürgen Löhel <juergen.loehel@inlyse.com>
oletools/thirdparty/oledump/plugin_biff.py
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 2
3 __description__ = 'BIFF plugin for oledump.py' 3 __description__ = 'BIFF plugin for oledump.py'
4 __author__ = 'Didier Stevens' 4 __author__ = 'Didier Stevens'
5 -__version__ = '0.0.15'  
6 -__date__ = '2020/05/22' 5 +__version__ = '0.0.17'
  6 +__date__ = '2020/07/17'
7 7
8 # Slightly modified version by Philippe Lagadec to be imported into olevba 8 # Slightly modified version by Philippe Lagadec to be imported into olevba
9 9
@@ -41,7 +41,9 @@ History: @@ -41,7 +41,9 @@ History:
41 2020/05/18: continue 41 2020/05/18: continue
42 2020/05/20: 0.0.13 option -j 42 2020/05/20: 0.0.13 option -j
43 2020/05/21: 0.0.14 improved parsing for a83890bbc081b9ec839c9a32ec06eae6f549a0f85fe0a30751ef229a58e440af, bc39d3bb128f329d95393bf0a4f6ec813356e847a00794c18258bfa48df6937f, 002a8371570487bc81eec4aeea9fdfb7 43 2020/05/21: 0.0.14 improved parsing for a83890bbc081b9ec839c9a32ec06eae6f549a0f85fe0a30751ef229a58e440af, bc39d3bb128f329d95393bf0a4f6ec813356e847a00794c18258bfa48df6937f, 002a8371570487bc81eec4aeea9fdfb7
44 - 2020/05/22: Python 3 fix STRING record 0x207 44 + 2020/05/22: 0.0.15 Python 3 fix STRING record 0x207
  45 + 2020/05/26: 0.0.16 added logic for reserved bits in BOUNDSHEET
  46 + 2020/07/17: 0.0.17 added option --statistics
45 47
46 48
47 Todo: 49 Todo:
@@ -55,7 +57,14 @@ import json @@ -55,7 +57,14 @@ import json
55 # Modifications for olevba: 57 # Modifications for olevba:
56 import sys 58 import sys
57 import binascii 59 import binascii
58 -from .oledump_extract import * 60 +from .oledump_extract import(
  61 + cPluginParent,
  62 + AddPlugin,
  63 + CIC,
  64 + IFF,
  65 + P23Ord,
  66 + P23Chr
  67 +)
59 # end modifications 68 # end modifications
60 69
61 DEFAULT_SEPARATOR = ',' 70 DEFAULT_SEPARATOR = ','
@@ -1301,8 +1310,6 @@ def DecodeRKValue(data): @@ -1301,8 +1310,6 @@ def DecodeRKValue(data):
1301 if number & 0x01: 1310 if number & 0x01:
1302 divider = 100.0 1311 divider = 100.0
1303 if number & 0x02: 1312 if number & 0x02:
1304 - print(repr(data))  
1305 - raise Exception('DecodeRKValue')  
1306 return (struct.unpack('<i', data)[0] >> 2) / divider 1313 return (struct.unpack('<i', data)[0] >> 2) / divider
1307 else: 1314 else:
1308 return struct.unpack('<d', b'\x00\x00\x00\x00' + data)[0] / divider 1315 return struct.unpack('<d', b'\x00\x00\x00\x00' + data)[0] / divider
@@ -1607,6 +1614,7 @@ class cBIFF(cPluginParent): @@ -1607,6 +1614,7 @@ class cBIFF(cPluginParent):
1607 oParser.add_option('-c', '--csv', action='store_true', default=False, help='Produce CSV') 1614 oParser.add_option('-c', '--csv', action='store_true', default=False, help='Produce CSV')
1608 oParser.add_option('-j', '--json', action='store_true', default=False, help='Produce JSON') 1615 oParser.add_option('-j', '--json', action='store_true', default=False, help='Produce JSON')
1609 oParser.add_option('-r', '--cellrefformat', type=str, default='rc', help='Cell reference format (RC, LN)') 1616 oParser.add_option('-r', '--cellrefformat', type=str, default='rc', help='Cell reference format (RC, LN)')
  1617 + oParser.add_option('-S', '--statistics', action='store_true', default=False, help='Produce BIFF record statistics')
1610 (options, args) = oParser.parse_args(self.options.split(' ')) 1618 (options, args) = oParser.parse_args(self.options.split(' '))
1611 1619
1612 if options.find.startswith('0x'): 1620 if options.find.startswith('0x'):
@@ -1619,12 +1627,14 @@ class cBIFF(cPluginParent): @@ -1619,12 +1627,14 @@ class cBIFF(cPluginParent):
1619 sheetNames = [] 1627 sheetNames = []
1620 definesNames = [] 1628 definesNames = []
1621 currentSheetname = '' 1629 currentSheetname = ''
  1630 + dOpcodeStatistics = {}
1622 while position < len(stream): 1631 while position < len(stream):
1623 formatcodes = 'HH' 1632 formatcodes = 'HH'
1624 formatsize = struct.calcsize(formatcodes) 1633 formatsize = struct.calcsize(formatcodes)
1625 if len(stream[position:position + formatsize]) < formatsize: 1634 if len(stream[position:position + formatsize]) < formatsize:
1626 break 1635 break
1627 opcode, length = struct.unpack(formatcodes, stream[position:position + formatsize]) 1636 opcode, length = struct.unpack(formatcodes, stream[position:position + formatsize])
  1637 + dOpcodeStatistics[opcode] = [dOpcodeStatistics.get(opcode, [0, 0])[0] + 1, dOpcodeStatistics.get(opcode, [0, 0])[1] + length]
1628 data = stream[position + formatsize:position + formatsize + length] 1638 data = stream[position + formatsize:position + formatsize + length]
1629 positionBIFFRecord = position 1639 positionBIFFRecord = position
1630 position = position + formatsize + length 1640 position = position + formatsize + length
@@ -1674,7 +1684,10 @@ class cBIFF(cPluginParent): @@ -1674,7 +1684,10 @@ class cBIFF(cPluginParent):
1674 definesNames.append(name) 1684 definesNames.append(name)
1675 if flags & 0x01: 1685 if flags & 0x01:
1676 line += ' hidden' 1686 line += ' hidden'
1677 - parsedExpression, stack = ParseExpression(data[offset+lnName:offset+lnName+szFormula], definesNames, sheetNames, options.cellrefformat) 1687 + try:
  1688 + parsedExpression, stack = ParseExpression(data[offset+lnName:offset+lnName+szFormula], definesNames, sheetNames, options.cellrefformat)
  1689 + except IndexError:
  1690 + parsedExpression = '*PARSING ERROR*'
1678 line += ' len=%d %s' % (szFormula, parsedExpression) 1691 line += ' len=%d %s' % (szFormula, parsedExpression)
1679 1692
1680 # FILEPASS record 1693 # FILEPASS record
@@ -1689,11 +1702,17 @@ class cBIFF(cPluginParent): @@ -1689,11 +1702,17 @@ class cBIFF(cPluginParent):
1689 dSheetType = {0: 'worksheet or dialog sheet', 1: 'Excel 4.0 macro sheet', 2: 'chart', 6: 'Visual Basic module'} 1702 dSheetType = {0: 'worksheet or dialog sheet', 1: 'Excel 4.0 macro sheet', 2: 'chart', 6: 'Visual Basic module'}
1690 if sheetType == 1: 1703 if sheetType == 1:
1691 macros4Found = True 1704 macros4Found = True
1692 - dSheetState = {0: 'visible', 1: 'hidden', 2: 'very hidden'}  
1693 sheetName = ShortXLUnicodeString(data[6:]) 1705 sheetName = ShortXLUnicodeString(data[6:])
1694 dSheetNames[positionBOF] = sheetName 1706 dSheetNames[positionBOF] = sheetName
1695 sheetNames.append(sheetName) 1707 sheetNames.append(sheetName)
1696 - line += ' - %s, %s - %s' % (dSheetType.get(sheetType, '%02x' % sheetType), dSheetState.get(sheetState, '%02x' % sheetState), sheetName) 1708 +
  1709 + dSheetState = {0: 'visible', 1: 'hidden', 2: 'very hidden', 3: 'visibility=3'}
  1710 + visibility = ''
  1711 + if sheetState > 3:
  1712 + visibility = 'reserved bits not zero: 0x%02x ' % (sheetState & 0xFC)
  1713 + visibility += dSheetState.get(sheetState & 3, '0x%02x' % (sheetState & 3))
  1714 +
  1715 + line += ' - %s, %s - %s' % (dSheetType.get(sheetType, '%02x' % sheetType), visibility, sheetName)
1697 1716
1698 # BOF record 1717 # BOF record
1699 if opcode == 0x0809 and len(data) >= 4: 1718 if opcode == 0x0809 and len(data) >= 4:
@@ -1715,7 +1734,6 @@ class cBIFF(cPluginParent): @@ -1715,7 +1734,6 @@ class cBIFF(cPluginParent):
1715 if values[1] != []: 1734 if values[1] != []:
1716 if strings != '': 1735 if strings != '':
1717 strings += ' ' 1736 strings += ' '
1718 - print(values)  
1719 strings += ' '.join(values[1]) 1737 strings += ' '.join(values[1])
1720 line += ' - %s' % strings 1738 line += ' - %s' % strings
1721 1739
@@ -1761,6 +1779,19 @@ class cBIFF(cPluginParent): @@ -1761,6 +1779,19 @@ class cBIFF(cPluginParent):
1761 1779
1762 if options.xlm and filepassFound: 1780 if options.xlm and filepassFound:
1763 result = ['FILEPASS record: file is password protected'] 1781 result = ['FILEPASS record: file is password protected']
  1782 + elif options.statistics:
  1783 + stats = []
  1784 + for opcode in sorted(dOpcodeStatistics.keys()):
  1785 + stats.append((opcode, dOpcodes.get(opcode, ''), dOpcodeStatistics[opcode][0], dOpcodeStatistics[opcode][1]))
  1786 + if options.csv:
  1787 + result = [MakeCSVLine(['opcode', 'description', 'count', 'totalsize'], DEFAULT_SEPARATOR, QUOTE)]
  1788 + else:
  1789 + result = []
  1790 + for item in stats:
  1791 + if options.csv:
  1792 + result.append(MakeCSVLine(item, DEFAULT_SEPARATOR, QUOTE))
  1793 + else:
  1794 + result.append('%d %s: %d %d' % item)
1764 elif options.xlm and not macros4Found: 1795 elif options.xlm and not macros4Found:
1765 result = [] 1796 result = []
1766 elif options.csv: 1797 elif options.csv: