Commit 5c03a4af1ac7a7b7a568ee92245042d60c9d215f
Merge remote-tracking branch 'kijeong/add/projectcompatversion_record'
# Conflicts: # tests/olevba/test_basic.py
Showing
4 changed files
with
45 additions
and
3 deletions
oletools/olevba.py
| ... | ... | @@ -234,6 +234,7 @@ from __future__ import print_function |
| 234 | 234 | # 2020-09-28 PL: - added VBA_Parser.get_vba_code_all_modules (partial fix |
| 235 | 235 | # for issue #619) |
| 236 | 236 | # 2021-04-14 PL: - added detection of Workbook_BeforeClose (issue #518) |
| 237 | +# 2021-11-09 KJ: - added PROJECTCOMPATVERSION Record on dir Stream | |
| 237 | 238 | |
| 238 | 239 | __version__ = '0.60.2dev1' |
| 239 | 240 | |
| ... | ... | @@ -1701,9 +1702,25 @@ class VBA_Project(object): |
| 1701 | 1702 | if self.syskind not in SYSKIND_NAME: |
| 1702 | 1703 | log.error("invalid PROJECTSYSKIND_SysKind {0:04X}".format(self.syskind)) |
| 1703 | 1704 | |
| 1704 | - # PROJECTLCID Record | |
| 1705 | + # PROJECTLCID Record or PROJECTCOMPATVERSION Record | |
| 1706 | + project_id = struct.unpack("<H", dir_stream.read(2))[0] | |
| 1707 | + if project_id == 0x004A: | |
| 1708 | + # PROJECTCOMPATVERSION Record | |
| 1709 | + # Specifies the VBA project's compat version. | |
| 1710 | + projectcompatversion_id = project_id | |
| 1711 | + self.check_value('PROJETCOMPATVERSION_Id', 0x004A, projectcompatversion_id) | |
| 1712 | + projectcompatversion_size = struct.unpack("<L", dir_stream.read(4))[0] | |
| 1713 | + self.check_value('PROJECTCOMPATVERSION_Size', 0x0004, projectcompatversion_size) | |
| 1714 | + projectcompatversion_compatversion = struct.unpack("<L", dir_stream.read(4))[0] | |
| 1715 | + # compat version: A 32-bit number that identifies the Office Model version used by a VBA project. | |
| 1716 | + log.debug("compat version: {compat_version}".format(compat_version=projectcompatversion_compatversion)) | |
| 1717 | + | |
| 1718 | + # PROJECTLCID Record | |
| 1719 | + project_id = struct.unpack("<H", dir_stream.read(2))[0] | |
| 1720 | + | |
| 1721 | + projectlcid_id = project_id | |
| 1722 | + | |
| 1705 | 1723 | # Specifies the VBA project's LCID. |
| 1706 | - projectlcid_id = struct.unpack("<H", dir_stream.read(2))[0] | |
| 1707 | 1724 | self.check_value('PROJECTLCID_Id', 0x0002, projectlcid_id) |
| 1708 | 1725 | projectlcid_size = struct.unpack("<L", dir_stream.read(4))[0] |
| 1709 | 1726 | self.check_value('PROJECTLCID_Size', 0x0004, projectlcid_size) | ... | ... |
tests/oleid/test_basic.py
| ... | ... | @@ -67,6 +67,11 @@ class TestOleIDBasic(unittest.TestCase): |
| 67 | 67 | '949: ANSI/OEM Korean (Unified Hangul Code)') |
| 68 | 68 | self.assertEqual(value_dict['author'], |
| 69 | 69 | b'\xb1\xe8\xb1\xe2\xc1\xa4;kijeong') |
| 70 | + elif 'olevba/sample_with_vba.ppt' in filename: | |
| 71 | + self.assertEqual(value_dict['codepage'], | |
| 72 | + '949: ANSI/OEM Korean (Unified Hangul Code)') | |
| 73 | + self.assertEqual(value_dict['author'], | |
| 74 | + b'\xb1\xe8 \xb1\xe2\xc1\xa4') | |
| 70 | 75 | else: |
| 71 | 76 | self.assertEqual(value_dict['codepage'], |
| 72 | 77 | '1252: ANSI Latin 1; Western European (Windows)') |
| ... | ... | @@ -120,7 +125,9 @@ class TestOleIDBasic(unittest.TestCase): |
| 120 | 125 | 'msodde/dde-in-excel2003.xml', # same as above |
| 121 | 126 | 'oleform/oleform-PR314.docm', |
| 122 | 127 | 'basic/empty', # WTF? |
| 123 | - 'basic/text'): # no macros! | |
| 128 | + 'basic/text', # no macros! | |
| 129 | + 'olevba/sample_with_vba.ppt', | |
| 130 | + ): | |
| 124 | 131 | self.assertEqual(value_dict['vba'], 'Yes') |
| 125 | 132 | else: |
| 126 | 133 | self.assertEqual(value_dict['vba'], 'No') | ... | ... |
tests/olevba/test_basic.py
| ... | ... | @@ -147,6 +147,24 @@ class TestOlevbaBasic(unittest.TestCase): |
| 147 | 147 | self.assertIn('AutoExec', types) |
| 148 | 148 | self.assertIn('Suspicious', types) |
| 149 | 149 | |
| 150 | + def test_dir_stream_record_project_compat_version(self): | |
| 151 | + """Test PROJECTCOMPATVERSION record on dir stream with a ppt file.""" | |
| 152 | + input_file = join(DATA_BASE_DIR, 'olevba', 'sample_with_vba.ppt') | |
| 153 | + output, ret_code = call_and_capture('olevba', args=(input_file, "--loglevel", "debug")) | |
| 154 | + | |
| 155 | + # check return code | |
| 156 | + self.assertEqual(ret_code, 0) | |
| 157 | + | |
| 158 | + # not expected string: | |
| 159 | + self.assertNotIn('invalid value for PROJECTLCID_Id expected 0002 got', output) | |
| 160 | + self.assertNotIn('Error in _extract_vba', output) | |
| 161 | + | |
| 162 | + # compat version in debug mode: | |
| 163 | + self.assertIn('compat version: 2', output) | |
| 164 | + | |
| 165 | + # vba contents: | |
| 166 | + self.assertIn('Sub Action_Click()\n MsgBox "The action button clicked!"\nEnd Sub', output) | |
| 167 | + | |
| 150 | 168 | |
| 151 | 169 | # just in case somebody calls this file as a script |
| 152 | 170 | if __name__ == '__main__': | ... | ... |
tests/test-data/olevba/sample_with_vba.ppt
0 → 100644
No preview for this file type