├── tests ├── __init__.py ├── oleid │ ├── __init__.py │ └── test_issue_166.py ├── ooxml │ ├── __init__.py │ ├── test_zip_sub_file.py │ └── test_basic.py ├── common │ ├── __init__.py │ └── log_helper │ │ ├── __init__.py │ │ ├── log_helper_test_imported.py │ │ └── log_helper_test_main.py ├── msodde │ ├── __init__.py │ ├── test_crypto.py │ └── test_csv.py ├── oleform │ ├── __init__.py │ └── test_basic.py ├── oleobj │ ├── __init__.py │ └── test_external_links.py ├── olevba │ ├── __init__.py │ ├── test_crypto.py │ └── test_basic.py ├── ppt_parser │ ├── __init__.py │ └── test_basic.py ├── rtfobj │ ├── __init__.py │ ├── test_issue_251.py │ ├── test_issue_185.py │ └── test_is_rtf.py ├── test-data │ ├── basic │ │ ├── empty │ │ ├── text │ │ └── encrypted.docx │ ├── msodde │ │ ├── dde-in-csv.csv │ │ ├── dde-test.docm │ │ ├── dde-test.docx │ │ ├── dde-test.xlsb │ │ ├── dde-test.xlsm │ │ ├── dde-test.xlsx │ │ ├── harmless-clean.doc │ │ ├── harmless-clean.docm │ │ ├── harmless-clean.docx │ │ ├── dde-in-word2003.xml.zip │ │ ├── dde-in-word2007.xml.zip │ │ ├── dde-test-from-office2003.doc.zip │ │ ├── dde-test-from-office2016.doc.zip │ │ ├── dde-test-from-office2013-utf_16le-korean.doc.zip │ │ └── dde-in-excel2003.xml │ ├── encrypted │ │ ├── encrypted.doc │ │ ├── encrypted.ppt │ │ ├── encrypted.xls │ │ ├── encrypted.docm │ │ ├── encrypted.docx │ │ ├── encrypted.pptm │ │ ├── encrypted.pptx │ │ ├── encrypted.xlsb │ │ ├── encrypted.xlsm │ │ ├── encrypted.xlsx │ │ ├── autostart-encrypt-standardpassword.xls │ │ ├── dde-test-encrypt-standardpassword.xls │ │ ├── dde-test-encrypt-standardpassword.xlsb │ │ ├── dde-test-encrypt-standardpassword.xlsm │ │ ├── dde-test-encrypt-standardpassword.xlsx │ │ ├── autostart-encrypt-standardpassword.xlsb │ │ └── autostart-encrypt-standardpassword.xlsm │ ├── rtfobj │ │ ├── issue_185.rtf.zip │ │ └── issue_251.rtf │ ├── oleform │ │ └── oleform-PR314.docm │ ├── oleobj │ │ ├── embedded-unicode.doc │ │ ├── embedded-simple-2007.doc │ │ ├── embedded-simple-2007.docm │ │ ├── embedded-simple-2007.docx │ │ ├── embedded-simple-2007.dot │ │ ├── embedded-simple-2007.dotm │ │ ├── embedded-simple-2007.dotx │ │ ├── embedded-simple-2007.odp │ │ ├── embedded-simple-2007.ods │ │ ├── embedded-simple-2007.odt │ │ ├── embedded-simple-2007.pot │ │ ├── embedded-simple-2007.potm │ │ ├── embedded-simple-2007.potx │ │ ├── embedded-simple-2007.pps │ │ ├── embedded-simple-2007.ppsm │ │ ├── embedded-simple-2007.ppsx │ │ ├── embedded-simple-2007.ppt │ │ ├── embedded-simple-2007.pptm │ │ ├── embedded-simple-2007.pptx │ │ ├── embedded-simple-2007.xla │ │ ├── embedded-simple-2007.xlam │ │ ├── embedded-simple-2007.xls │ │ ├── embedded-simple-2007.xlsb │ │ ├── embedded-simple-2007.xlsm │ │ ├── embedded-simple-2007.xlsx │ │ ├── embedded-simple-2007.xlt │ │ ├── embedded-simple-2007.xltm │ │ ├── embedded-simple-2007.xltx │ │ ├── embedded-unicode-2007.docx │ │ ├── sample_with_lnk_file.doc │ │ ├── sample_with_lnk_file.pps │ │ ├── sample_with_lnk_file.ppt │ │ ├── sample_with_lnk_to_calc.doc │ │ ├── sample_with_calc_embedded.doc │ │ └── external_link │ │ │ ├── sample_with_external_link_to_doc.docm │ │ │ ├── sample_with_external_link_to_doc.docx │ │ │ ├── sample_with_external_link_to_doc.dotm │ │ │ ├── sample_with_external_link_to_doc.dotx │ │ │ ├── sample_with_external_link_to_doc.potm │ │ │ ├── sample_with_external_link_to_doc.potx │ │ │ ├── sample_with_external_link_to_doc.ppsm │ │ │ ├── sample_with_external_link_to_doc.ppsx │ │ │ ├── sample_with_external_link_to_doc.pptm │ │ │ ├── sample_with_external_link_to_doc.pptx │ │ │ ├── sample_with_external_link_to_doc.xlsb │ │ │ ├── sample_with_external_link_to_doc.xlsm │ │ │ └── sample_with_external_link_to_doc.xlsx │ └── ooxml │ │ ├── dde-in-word2003.xml.zip │ │ └── dde-in-excel2003.xml ├── test_utils │ ├── __init__.py │ ├── testdata_reader.py │ └── utils.py ├── unittest_template.py └── howto_add_unittests.txt ├── oletools ├── __init__.py ├── common │ ├── __init__.py │ ├── log_helper │ │ ├── __init__.py │ │ ├── _root_logger_wrapper.py │ │ ├── _json_formatter.py │ │ └── _logger_adapter.py │ └── errors.py ├── thirdparty │ ├── __init__.py │ ├── xxxswf │ │ └── __init__.py │ ├── oledump │ │ └── __init__.py │ ├── prettytable │ │ ├── __init__.py │ │ └── COPYING │ ├── tablestream │ │ └── __init__.py │ └── xglob │ │ ├── __init__.py │ │ └── LICENSE.txt ├── doc │ ├── oledir.png │ ├── olemap1.png │ ├── olemap2.png │ ├── mraptor1.png │ ├── olemeta1.png │ ├── olebrowse1_menu.png │ ├── olebrowse2_stream.png │ ├── olebrowse3_hexview.png │ ├── olemeta.md │ ├── oleobj.md │ ├── olemap.md │ ├── Contribute.md │ ├── oledir.md │ ├── olebrowse.md │ ├── oleobj.html │ ├── olemeta.html │ ├── olemap.html │ ├── Contribute.html │ ├── oletimes.md │ ├── oledir.html │ ├── olebrowse.html │ ├── Home.md │ ├── License.md │ ├── mraptor.md │ ├── rtfobj.md │ ├── pyxswf.md │ ├── oleid.md │ ├── oletimes.html │ ├── License.html │ ├── mraptor.html │ ├── Install.md │ ├── Home.html │ ├── pyxswf.html │ └── Install.html ├── olevba3.py ├── mraptor3.py ├── LICENSE.txt ├── DocVarDump.vba └── olebrowse.py ├── README.rst ├── doc └── empty_file.txt ├── requirements.txt ├── cheatsheet ├── oletools_cheatsheet.pdf └── oletools_cheatsheet.docx ├── MANIFEST.in ├── .travis.yml ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── .gitignore ├── LICENSE.md ├── install.bat └── INSTALL.txt /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oletools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/oleid/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/ooxml/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oletools/common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/msodde/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/oleform/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/oleobj/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/olevba/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/ppt_parser/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/rtfobj/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test-data/basic/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oletools/thirdparty/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Needed for setup.py 2 | -------------------------------------------------------------------------------- /oletools/thirdparty/xxxswf/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/common/log_helper/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test-data/basic/text: -------------------------------------------------------------------------------- 1 | bla 2 | -------------------------------------------------------------------------------- /doc/empty_file.txt: -------------------------------------------------------------------------------- 1 | Nothing to see here. 2 | -------------------------------------------------------------------------------- /oletools/thirdparty/oledump/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oletools/thirdparty/prettytable/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oletools/thirdparty/tablestream/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .utils import * 2 | -------------------------------------------------------------------------------- /oletools/thirdparty/xglob/__init__.py: -------------------------------------------------------------------------------- 1 | from .xglob import * -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-in-csv.csv: -------------------------------------------------------------------------------- 1 | =cmd|'/k \..\..\..\Windows\System32\calc.exe'!A0 2 | -------------------------------------------------------------------------------- /oletools/doc/oledir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/oletools/doc/oledir.png -------------------------------------------------------------------------------- /oletools/doc/olemap1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/oletools/doc/olemap1.png -------------------------------------------------------------------------------- /oletools/doc/olemap2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/oletools/doc/olemap2.png -------------------------------------------------------------------------------- /oletools/doc/mraptor1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/oletools/doc/mraptor1.png -------------------------------------------------------------------------------- /oletools/doc/olemeta1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/oletools/doc/olemeta1.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyparsing>=2.1.0 2 | olefile>=0.46 3 | easygui 4 | colorclass 5 | msoffcrypto-tool 6 | pcodedmp>=1.2.5 -------------------------------------------------------------------------------- /cheatsheet/oletools_cheatsheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/cheatsheet/oletools_cheatsheet.pdf -------------------------------------------------------------------------------- /oletools/doc/olebrowse1_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/oletools/doc/olebrowse1_menu.png -------------------------------------------------------------------------------- /oletools/doc/olebrowse2_stream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/oletools/doc/olebrowse2_stream.png -------------------------------------------------------------------------------- /cheatsheet/oletools_cheatsheet.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/cheatsheet/oletools_cheatsheet.docx -------------------------------------------------------------------------------- /oletools/doc/olebrowse3_hexview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/oletools/doc/olebrowse3_hexview.png -------------------------------------------------------------------------------- /tests/test-data/basic/encrypted.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/basic/encrypted.docx -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-test.docm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-test.docm -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-test.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-test.docx -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-test.xlsb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-test.xlsb -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-test.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-test.xlsm -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-test.xlsx -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.doc -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.ppt -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.xls -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.docm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.docm -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.docx -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.pptm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.pptm -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.pptx -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.xlsb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.xlsb -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.xlsm -------------------------------------------------------------------------------- /tests/test-data/encrypted/encrypted.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/encrypted.xlsx -------------------------------------------------------------------------------- /tests/test-data/msodde/harmless-clean.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/harmless-clean.doc -------------------------------------------------------------------------------- /tests/test-data/rtfobj/issue_185.rtf.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/rtfobj/issue_185.rtf.zip -------------------------------------------------------------------------------- /tests/test-data/msodde/harmless-clean.docm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/harmless-clean.docm -------------------------------------------------------------------------------- /tests/test-data/msodde/harmless-clean.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/harmless-clean.docx -------------------------------------------------------------------------------- /tests/test-data/oleform/oleform-PR314.docm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleform/oleform-PR314.docm -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-unicode.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-unicode.doc -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-in-word2003.xml.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-in-word2003.xml.zip -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-in-word2007.xml.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-in-word2007.xml.zip -------------------------------------------------------------------------------- /tests/test-data/ooxml/dde-in-word2003.xml.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/ooxml/dde-in-word2003.xml.zip -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.doc -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.docm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.docm -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.docx -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.dot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.dot -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.dotm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.dotm -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.dotx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.dotx -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.odp -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.ods -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.odt -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.pot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.pot -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.potm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.potm -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.potx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.potx -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.pps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.pps -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.ppsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.ppsm -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.ppsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.ppsx -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.ppt -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.pptm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.pptm -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.pptx -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.xla: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.xla -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.xlam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.xlam -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.xls -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.xlsb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.xlsb -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.xlsm -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.xlsx -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.xlt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.xlt -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.xltm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.xltm -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-simple-2007.xltx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-simple-2007.xltx -------------------------------------------------------------------------------- /tests/test-data/oleobj/embedded-unicode-2007.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/embedded-unicode-2007.docx -------------------------------------------------------------------------------- /tests/test-data/oleobj/sample_with_lnk_file.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/sample_with_lnk_file.doc -------------------------------------------------------------------------------- /tests/test-data/oleobj/sample_with_lnk_file.pps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/sample_with_lnk_file.pps -------------------------------------------------------------------------------- /tests/test-data/oleobj/sample_with_lnk_file.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/sample_with_lnk_file.ppt -------------------------------------------------------------------------------- /tests/test-data/oleobj/sample_with_lnk_to_calc.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/sample_with_lnk_to_calc.doc -------------------------------------------------------------------------------- /oletools/common/log_helper/__init__.py: -------------------------------------------------------------------------------- 1 | from . import log_helper as log_helper_ 2 | 3 | log_helper = log_helper_.LogHelper() 4 | 5 | __all__ = ['log_helper'] 6 | -------------------------------------------------------------------------------- /tests/test-data/oleobj/sample_with_calc_embedded.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/sample_with_calc_embedded.doc -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-test-from-office2003.doc.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-test-from-office2003.doc.zip -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-test-from-office2016.doc.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-test-from-office2016.doc.zip -------------------------------------------------------------------------------- /tests/test-data/encrypted/autostart-encrypt-standardpassword.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/autostart-encrypt-standardpassword.xls -------------------------------------------------------------------------------- /tests/test-data/encrypted/dde-test-encrypt-standardpassword.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/dde-test-encrypt-standardpassword.xls -------------------------------------------------------------------------------- /tests/test-data/encrypted/dde-test-encrypt-standardpassword.xlsb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/dde-test-encrypt-standardpassword.xlsb -------------------------------------------------------------------------------- /tests/test-data/encrypted/dde-test-encrypt-standardpassword.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/dde-test-encrypt-standardpassword.xlsm -------------------------------------------------------------------------------- /tests/test-data/encrypted/dde-test-encrypt-standardpassword.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/dde-test-encrypt-standardpassword.xlsx -------------------------------------------------------------------------------- /tests/test-data/encrypted/autostart-encrypt-standardpassword.xlsb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/autostart-encrypt-standardpassword.xlsb -------------------------------------------------------------------------------- /tests/test-data/encrypted/autostart-encrypt-standardpassword.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/encrypted/autostart-encrypt-standardpassword.xlsm -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-test-from-office2013-utf_16le-korean.doc.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/msodde/dde-test-from-office2013-utf_16le-korean.doc.zip -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.docm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.docm -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.docx -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.dotm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.dotm -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.dotx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.dotx -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.potm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.potm -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.potx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.potx -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.ppsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.ppsm -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.ppsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.ppsx -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.pptm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.pptm -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.pptx -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.xlsb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.xlsb -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.xlsm -------------------------------------------------------------------------------- /tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zerospam/oletools/master/tests/test-data/oleobj/external_link/sample_with_external_link_to_doc.xlsx -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include install.bat 2 | include INSTALL.txt 3 | include README.md 4 | include requirements.txt 5 | include oletools/README.rst 6 | include oletools/README.html 7 | include oletools/LICENSE.txt 8 | include oletools/DocVarDump.vba 9 | recursive-include oletools/thirdparty *.* 10 | recursive-include cheatsheet *.* 11 | global-exclude *.pyc 12 | 13 | recursive-include tests *.py 14 | graft tests/test-data 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | cache: pip 3 | sudo: false 4 | 5 | matrix: 6 | include: 7 | - python: 2.7 8 | - python: 3.4 9 | - python: 3.5 10 | - python: 3.6 11 | - python: 3.7 12 | dist: xenial 13 | sudo: yes 14 | - python: nightly 15 | dist: xenial 16 | sudo: yes 17 | - python: pypy 18 | - python: pypy3 19 | 20 | install: 21 | - pip install msoffcrypto-tool 22 | 23 | script: 24 | - python setup.py test 25 | -------------------------------------------------------------------------------- /tests/rtfobj/test_issue_251.py: -------------------------------------------------------------------------------- 1 | import unittest, sys, os 2 | 3 | from tests.test_utils import testdata_reader 4 | from oletools import rtfobj 5 | 6 | class TestRtfObjIssue251(unittest.TestCase): 7 | def test_bin_no_param(self): 8 | data = testdata_reader.read('rtfobj/issue_251.rtf') 9 | rtfp = rtfobj.RtfObjParser(data) 10 | rtfp.parse() 11 | objects = rtfp.objects 12 | 13 | self.assertTrue(len(objects) == 1) 14 | 15 | if __name__ == '__main__': 16 | unittest.main() 17 | -------------------------------------------------------------------------------- /tests/rtfobj/test_issue_185.py: -------------------------------------------------------------------------------- 1 | import unittest, sys, os 2 | 3 | from tests.test_utils import testdata_reader 4 | from oletools import rtfobj 5 | 6 | class TestRtfObjIssue185(unittest.TestCase): 7 | def test_skip_space_after_bin_control_word(self): 8 | data = testdata_reader.read_encrypted('rtfobj/issue_185.rtf.zip') 9 | rtfp = rtfobj.RtfObjParser(data) 10 | rtfp.parse() 11 | objects = rtfp.objects 12 | 13 | self.assertTrue(len(objects) == 1) 14 | 15 | if __name__ == '__main__': 16 | unittest.main() 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /oletools/common/log_helper/_root_logger_wrapper.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | def is_logging_initialized(): 5 | """ 6 | We use the same strategy as the logging module when checking if 7 | the logging was initialized - look for handlers in the root logger 8 | """ 9 | return len(logging.root.handlers) > 0 10 | 11 | 12 | def set_formatter(fmt): 13 | """ 14 | Set the formatter to be used by every handler of the root logger. 15 | """ 16 | if not is_logging_initialized(): 17 | return 18 | 19 | for handler in logging.root.handlers: 20 | handler.setFormatter(fmt) 21 | 22 | 23 | def level(): 24 | return logging.root.level 25 | -------------------------------------------------------------------------------- /tests/oleid/test_issue_166.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test if oleid detects encrypted documents 3 | """ 4 | 5 | import unittest, sys, os 6 | 7 | from tests.test_utils import DATA_BASE_DIR 8 | from os.path import join 9 | 10 | from oletools import oleid 11 | 12 | class TestEncryptedDocumentDetection(unittest.TestCase): 13 | def test_encrypted_document_detection(self): 14 | """ Run oleid and check if the document is flagged as encrypted """ 15 | filename = join(DATA_BASE_DIR, 'basic/encrypted.docx') 16 | 17 | oleid_instance = oleid.OleID(filename) 18 | indicators = oleid_instance.check() 19 | 20 | is_encrypted = next(i.value for i in indicators if i.id == 'encrypted') 21 | 22 | self.assertEqual(is_encrypted, True) 23 | 24 | # just in case somebody calls this file as a script 25 | if __name__ == '__main__': 26 | unittest.main() -------------------------------------------------------------------------------- /tests/common/log_helper/log_helper_test_imported.py: -------------------------------------------------------------------------------- 1 | """ 2 | Dummy file that logs messages, meant to be imported 3 | by the main test file 4 | """ 5 | 6 | from oletools.common.log_helper import log_helper 7 | import logging 8 | 9 | DEBUG_MESSAGE = 'imported: debug log' 10 | INFO_MESSAGE = 'imported: info log' 11 | WARNING_MESSAGE = 'imported: warning log' 12 | ERROR_MESSAGE = 'imported: error log' 13 | CRITICAL_MESSAGE = 'imported: critical log' 14 | RESULT_MESSAGE = 'imported: result log' 15 | RESULT_TYPE = 'imported: result' 16 | 17 | logger = log_helper.get_or_create_silent_logger('test_imported', logging.ERROR) 18 | 19 | 20 | def log(): 21 | logger.debug(DEBUG_MESSAGE) 22 | logger.info(INFO_MESSAGE) 23 | logger.warning(WARNING_MESSAGE) 24 | logger.error(ERROR_MESSAGE) 25 | logger.critical(CRITICAL_MESSAGE) 26 | logger.info(RESULT_MESSAGE, type=RESULT_TYPE) 27 | -------------------------------------------------------------------------------- /oletools/olevba3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # olevba3 is a stub that redirects to olevba.py, for backwards compatibility 4 | 5 | import sys, os, warnings 6 | 7 | warnings.warn('olevba3 is deprecated, olevba should be used instead.', DeprecationWarning) 8 | 9 | # IMPORTANT: it should be possible to run oletools directly as scripts 10 | # in any directory without installing them with pip or setup.py. 11 | # In that case, relative imports are NOT usable. 12 | # And to enable Python 2+3 compatibility, we need to use absolute imports, 13 | # so we add the oletools parent folder to sys.path (absolute+normalized path): 14 | _thismodule_dir = os.path.normpath(os.path.abspath(os.path.dirname(__file__))) 15 | _parent_dir = os.path.normpath(os.path.join(_thismodule_dir, '..')) 16 | if _parent_dir not in sys.path: 17 | sys.path.insert(0, _parent_dir) 18 | 19 | from oletools.olevba import * 20 | from oletools.olevba import __doc__, __version__ 21 | 22 | if __name__ == '__main__': 23 | main() 24 | 25 | -------------------------------------------------------------------------------- /oletools/doc/olemeta.md: -------------------------------------------------------------------------------- 1 | olemeta 2 | ======= 3 | 4 | olemeta is a script to parse OLE files such as MS Office documents (e.g. Word, 5 | Excel), to extract all standard properties present in the OLE file. 6 | 7 | It is part of the [python-oletools](http://www.decalage.info/python/oletools) package. 8 | 9 | ## Usage 10 | 11 | ```text 12 | olemeta 13 | ``` 14 | 15 | ### Example 16 | 17 | ![](olemeta1.png) 18 | 19 | ## How to use olemeta in Python applications 20 | 21 | TODO 22 | 23 | -------------------------------------------------------------------------- 24 | 25 | python-oletools documentation 26 | ----------------------------- 27 | 28 | - [[Home]] 29 | - [[License]] 30 | - [[Install]] 31 | - [[Contribute]], Suggest Improvements or Report Issues 32 | - Tools: 33 | - [[mraptor]] 34 | - [[msodde]] 35 | - [[olebrowse]] 36 | - [[oledir]] 37 | - [[oleid]] 38 | - [[olemap]] 39 | - [[olemeta]] 40 | - [[oleobj]] 41 | - [[oletimes]] 42 | - [[olevba]] 43 | - [[pyxswf]] 44 | - [[rtfobj]] 45 | -------------------------------------------------------------------------------- /oletools/mraptor3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # mraptor3 is a stub that redirects to mraptor.py, for backwards compatibility 4 | 5 | import sys, os, warnings 6 | 7 | warnings.warn('mraptor3 is deprecated, mraptor should be used instead.', DeprecationWarning) 8 | 9 | # IMPORTANT: it should be possible to run oletools directly as scripts 10 | # in any directory without installing them with pip or setup.py. 11 | # In that case, relative imports are NOT usable. 12 | # And to enable Python 2+3 compatibility, we need to use absolute imports, 13 | # so we add the oletools parent folder to sys.path (absolute+normalized path): 14 | _thismodule_dir = os.path.normpath(os.path.abspath(os.path.dirname(__file__))) 15 | _parent_dir = os.path.normpath(os.path.join(_thismodule_dir, '..')) 16 | if _parent_dir not in sys.path: 17 | sys.path.insert(0, _parent_dir) 18 | 19 | from oletools.mraptor import * 20 | from oletools.mraptor import __doc__, __version__ 21 | 22 | if __name__ == '__main__': 23 | main() 24 | -------------------------------------------------------------------------------- /oletools/doc/oleobj.md: -------------------------------------------------------------------------------- 1 | oleobj 2 | ====== 3 | 4 | oleobj is a script to extract embedded objects from OLE files. 5 | 6 | It can be used either as a command-line tool, or as a python module from your own applications. 7 | 8 | It is part of the [python-oletools](http://www.decalage.info/python/oletools) package. 9 | 10 | ## Usage 11 | 12 | ```text 13 | TODO 14 | ``` 15 | 16 | -------------------------------------------------------------------------- 17 | 18 | ## How to use oleobj in Python applications 19 | 20 | See rtfobj.py source code. 21 | 22 | TODO 23 | 24 | -------------------------------------------------------------------------- 25 | 26 | python-oletools documentation 27 | ----------------------------- 28 | 29 | - [[Home]] 30 | - [[License]] 31 | - [[Install]] 32 | - [[Contribute]], Suggest Improvements or Report Issues 33 | - Tools: 34 | - [[mraptor]] 35 | - [[msodde]] 36 | - [[olebrowse]] 37 | - [[oledir]] 38 | - [[oleid]] 39 | - [[olemap]] 40 | - [[olemeta]] 41 | - [[oleobj]] 42 | - [[oletimes]] 43 | - [[olevba]] 44 | - [[pyxswf]] 45 | - [[rtfobj]] 46 | -------------------------------------------------------------------------------- /tests/unittest_template.py: -------------------------------------------------------------------------------- 1 | """ Test my new feature 2 | 3 | Some more info if you want 4 | 5 | Should work with python2 and python3! 6 | """ 7 | 8 | import unittest 9 | 10 | # if you need data from oletools/test-data/DIR/, uncomment these lines: 11 | ## Directory with test data, independent of current working directory 12 | #from tests.test_utils import DATA_BASE_DIR 13 | 14 | 15 | class TestMyFeature(unittest.TestCase): 16 | """ Tests my cool new feature """ 17 | 18 | def test_this(self): 19 | """ check that this works """ 20 | pass # your code here 21 | 22 | def test_that(self): 23 | """ check that that also works """ 24 | pass # your code here 25 | 26 | def helper_function(self, filename): 27 | """ to be called from other test functions to avoid copy-and-paste 28 | 29 | this is not called by unittest directly, only from your functions """ 30 | pass # your code here 31 | # e.g.: msodde.main(join(DATA_DIR, filename)) 32 | 33 | 34 | # just in case somebody calls this file as a script 35 | if __name__ == '__main__': 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /oletools/common/log_helper/_json_formatter.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import json 3 | 4 | 5 | class JsonFormatter(logging.Formatter): 6 | """ 7 | Format every message to be logged as a JSON object 8 | """ 9 | _is_first_line = True 10 | 11 | def format(self, record): 12 | """ 13 | Since we don't buffer messages, we always prepend messages with a comma to make 14 | the output JSON-compatible. The only exception is when printing the first line, 15 | so we need to keep track of it. 16 | 17 | We assume that all input comes from the OletoolsLoggerAdapter which 18 | ensures that there is a `type` field in the record. Otherwise will have 19 | to add a try-except around the access to `record.type`. 20 | """ 21 | json_dict = dict(msg=record.msg.replace('\n', ' '), level=record.levelname) 22 | json_dict['type'] = record.type 23 | formatted_message = ' ' + json.dumps(json_dict) 24 | 25 | if self._is_first_line: 26 | self._is_first_line = False 27 | return formatted_message 28 | 29 | return ', ' + formatted_message 30 | -------------------------------------------------------------------------------- /oletools/doc/olemap.md: -------------------------------------------------------------------------------- 1 | olemap 2 | ====== 3 | 4 | olemap is a script to display a map of all the sectors in an OLE file. 5 | 6 | It can be used either as a command-line tool, or as a python module from your own applications. 7 | 8 | It is part of the [python-oletools](http://www.decalage.info/python/oletools) package. 9 | 10 | ## Usage 11 | 12 | ```text 13 | Usage: olemap 14 | ``` 15 | 16 | ### Examples 17 | 18 | Scan a single file: 19 | 20 | ```text 21 | olemap file.doc 22 | ``` 23 | 24 | ![](olemap1.png) 25 | 26 | ![](olemap2.png) 27 | 28 | -------------------------------------------------------------------------- 29 | 30 | ## How to use olemap in Python applications 31 | 32 | TODO 33 | 34 | -------------------------------------------------------------------------- 35 | 36 | python-oletools documentation 37 | ----------------------------- 38 | 39 | - [[Home]] 40 | - [[License]] 41 | - [[Install]] 42 | - [[Contribute]], Suggest Improvements or Report Issues 43 | - Tools: 44 | - [[mraptor]] 45 | - [[msodde]] 46 | - [[olebrowse]] 47 | - [[oledir]] 48 | - [[oleid]] 49 | - [[olemap]] 50 | - [[olemeta]] 51 | - [[oleobj]] 52 | - [[oletimes]] 53 | - [[olevba]] 54 | - [[pyxswf]] 55 | - [[rtfobj]] 56 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us fix issues 4 | 5 | --- 6 | 7 | **Affected tool:** 8 | olevba, mraptor, rtfobj, oleid, etc 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **File/Malware sample to reproduce the bug** 14 | Please attach the file in a password protected zip archive, or provide a link where it can be downloaded (e.g. Hybrid Analysis, preferably not VirusTotal which requires paid access). If not possible, please provide a hash. 15 | 16 | **How To Reproduce the bug** 17 | Steps to reproduce the behavior, including the full command line or the options you used. 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Console output / Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | Use the option "-l debug" to add debugging information, if possible. 25 | 26 | **Version information:** 27 | - OS: Windows/Linux/Mac/Other 28 | - OS version: x.xx - 32/64 bits 29 | - Python version: 2.7/3.6 - 32/64 bits 30 | - oletools version: 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /tests/test_utils/testdata_reader.py: -------------------------------------------------------------------------------- 1 | import os, sys, zipfile 2 | from os.path import dirname, abspath, normpath, join 3 | from . import DATA_BASE_DIR 4 | 5 | ENCRYPTED_FILES_PASSWORD='infected-test' 6 | 7 | if sys.version_info[0] <= 2: 8 | # Python 2.x 9 | if sys.version_info[1] <= 6: 10 | # Python 2.6 11 | # use is_zipfile backported from Python 2.7: 12 | from thirdparty.zipfile27 import is_zipfile 13 | else: 14 | # Python 2.7 15 | from zipfile import is_zipfile 16 | else: 17 | # Python 3.x+ 18 | from zipfile import is_zipfile 19 | ENCRYPTED_FILES_PASSWORD = ENCRYPTED_FILES_PASSWORD.encode() 20 | 21 | def read(relative_path): 22 | with open(get_path_from_root(relative_path), 'rb') as file_handle: 23 | return file_handle.read() 24 | 25 | def read_encrypted(relative_path, filename=None): 26 | z = zipfile.ZipFile(get_path_from_root(relative_path)) 27 | 28 | if filename == None: 29 | contents = z.read(z.namelist()[0], pwd=ENCRYPTED_FILES_PASSWORD) 30 | else: 31 | contents = z.read(filename, pwd=ENCRYPTED_FILES_PASSWORD) 32 | 33 | z.close() 34 | return contents 35 | 36 | def get_path_from_root(relative_path): 37 | return join(DATA_BASE_DIR, relative_path) -------------------------------------------------------------------------------- /tests/msodde/test_crypto.py: -------------------------------------------------------------------------------- 1 | """Check decryption of files from msodde works.""" 2 | 3 | import sys 4 | import unittest 5 | from os.path import basename, join as pjoin 6 | 7 | from tests.test_utils import DATA_BASE_DIR, call_and_capture 8 | 9 | from oletools import crypto 10 | 11 | 12 | @unittest.skipIf(not crypto.check_msoffcrypto(), 13 | 'Module msoffcrypto not installed for {}' 14 | .format(basename(sys.executable))) 15 | class MsoddeCryptoTest(unittest.TestCase): 16 | """Test integration of decryption in msodde.""" 17 | 18 | def test_standard_password(self): 19 | """Check dde-link is found in xls[mb] sample files.""" 20 | for suffix in 'xls', 'xlsx', 'xlsm', 'xlsb': 21 | example_file = pjoin(DATA_BASE_DIR, 'encrypted', 22 | 'dde-test-encrypt-standardpassword.' + suffix) 23 | output, _ = call_and_capture('msodde', [example_file, ]) 24 | self.assertIn('\nDDE Links:\ncmd /c calc.exe\n', output, 25 | msg='Unexpected output {!r} for {}' 26 | .format(output, suffix)) 27 | 28 | # TODO: add more, in particular a sample with a "proper" password 29 | 30 | 31 | if __name__ == '__main__': 32 | unittest.main() 33 | -------------------------------------------------------------------------------- /oletools/doc/Contribute.md: -------------------------------------------------------------------------------- 1 | How to Suggest Improvements, Report Issues or Contribute 2 | ======================================================== 3 | 4 | This is a personal open-source project, developed on my spare time. 5 | Any contribution, suggestion, feedback or bug report is welcome. 6 | 7 | To **suggest improvements, report a bug or any issue**, 8 | please use the [issue reporting page](https://github.com/decalage2/oletools/issues), 9 | and provide all the information and files to reproduce the problem. 10 | 11 | You may also [contact the author](http://decalage.info/contact) directly 12 | to **send feedback**. 13 | 14 | The code is available in [a repository on GitHub](https://github.com/decalage2/oletools). 15 | You may use it to **submit enhancements** using forks and pull requests. 16 | 17 | -------------------------------------------------------------------------- 18 | 19 | python-oletools documentation 20 | ----------------------------- 21 | 22 | - [[Home]] 23 | - [[License]] 24 | - [[Install]] 25 | - [[Contribute]], Suggest Improvements or Report Issues 26 | - Tools: 27 | - [[mraptor]] 28 | - [[msodde]] 29 | - [[olebrowse]] 30 | - [[oledir]] 31 | - [[oleid]] 32 | - [[olemap]] 33 | - [[olemeta]] 34 | - [[oleobj]] 35 | - [[oletimes]] 36 | - [[olevba]] 37 | - [[pyxswf]] 38 | - [[rtfobj]] 39 | -------------------------------------------------------------------------------- /tests/oleobj/test_external_links.py: -------------------------------------------------------------------------------- 1 | """ Test that oleobj detects external links in relationships files. 2 | """ 3 | 4 | import unittest 5 | import os 6 | from os import path 7 | 8 | # Directory with test data, independent of current working directory 9 | from tests.test_utils import DATA_BASE_DIR, call_and_capture 10 | from oletools import oleobj 11 | 12 | BASE_DIR = path.join(DATA_BASE_DIR, 'oleobj', 'external_link') 13 | 14 | 15 | class TestExternalLinks(unittest.TestCase): 16 | def test_external_links(self): 17 | """ 18 | loop through sample files asserting that external links are found 19 | """ 20 | 21 | for dirpath, _, filenames in os.walk(BASE_DIR): 22 | for filename in filenames: 23 | file_path = path.join(dirpath, filename) 24 | 25 | output, ret_val = call_and_capture('oleobj', [file_path, ], 26 | accept_nonzero_exit=True) 27 | self.assertEqual(ret_val, oleobj.RETURN_DID_DUMP, 28 | msg='Wrong return value {} for {}. Output:\n{}' 29 | .format(ret_val, filename, output)) 30 | 31 | 32 | # just in case somebody calls this file as a script 33 | if __name__ == '__main__': 34 | unittest.main() 35 | -------------------------------------------------------------------------------- /oletools/thirdparty/xglob/LICENSE.txt: -------------------------------------------------------------------------------- 1 | LICENSE: 2 | 3 | xglob is copyright (c) 2013-2015, Philippe Lagadec (http://www.decalage.info) 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /tests/ppt_parser/test_basic.py: -------------------------------------------------------------------------------- 1 | """ Test ppt_parser and ppt_record_parser """ 2 | 3 | import unittest 4 | import os 5 | from os.path import join, splitext 6 | 7 | # Directory with test data, independent of current working directory 8 | from tests.test_utils import DATA_BASE_DIR 9 | 10 | from oletools import ppt_record_parser 11 | # ppt_parser not tested yet 12 | 13 | 14 | class TestBasic(unittest.TestCase): 15 | """ test basic functionality of ppt parsing """ 16 | 17 | def test_is_ppt(self): 18 | """ test ppt_record_parser.is_ppt(filename) """ 19 | exceptions = ['encrypted.ppt', ] # actually is ppt but embedded 20 | for base_dir, _, files in os.walk(DATA_BASE_DIR): 21 | for filename in files: 22 | if filename in exceptions: 23 | continue 24 | full_name = join(base_dir, filename) 25 | extn = splitext(filename)[1] 26 | if extn in ('.ppt', '.pps', '.pot'): 27 | self.assertTrue(ppt_record_parser.is_ppt(full_name), 28 | msg='{0} not recognized as ppt file' 29 | .format(full_name)) 30 | else: 31 | self.assertFalse(ppt_record_parser.is_ppt(full_name), 32 | msg='{0} erroneously recognized as ppt' 33 | .format(full_name)) 34 | 35 | 36 | # just in case somebody calls this file as a script 37 | if __name__ == '__main__': 38 | unittest.main() 39 | -------------------------------------------------------------------------------- /tests/howto_add_unittests.txt: -------------------------------------------------------------------------------- 1 | Howto: Add unittests 2 | -------------------- 3 | 4 | Note: The following are just guidelines to help inexperienced users create unit 5 | tests. The python unittest library (see 6 | https://docs.python.org/2/library/unittest.html) offers much more flexibility 7 | than described here. 8 | 9 | For helping python's unittest to discover your tests, do the following: 10 | 11 | * create a subdirectory within oletools/tests/ 12 | - The directory name must be a valid python package name, 13 | so must not include '-', for example 14 | - e.g. oletools/tests/my_feature 15 | 16 | * Create a __init__.py inside that directory 17 | - can be empty but must be there 18 | 19 | * Copy the unittest_template.py into your test directory 20 | 21 | * Rename your copy of the template to fit its purpose 22 | - file name must start with 'test' and end with '.py' 23 | - e.g. oletools/tests/my_feature/test_bla.py 24 | 25 | * Create python code inside that directory 26 | - classes names must start with Test and must be subclasses 27 | of Unittest.TestCase 28 | - test functions inside your test cases must start with test_ 29 | - see unittest_template.py for examples 30 | 31 | * If your unit test requires test files, put them into a subdir 32 | of oletools/tests/test-data with some name that clarifies what 33 | tests it belongs to 34 | - e.g. oletools/tests/test-data/my_feature/example.doc 35 | - Do not add files with actual evil malware macros! Only harmless 36 | test data! 37 | 38 | * Test that unittests work by running from the oletools base dir: 39 | python -m unittest discover -v 40 | 41 | * Re-test with python2 and python3 (if possible) 42 | -------------------------------------------------------------------------------- /oletools/thirdparty/prettytable/COPYING: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009-2013 Luke Maurits 2 | # All rights reserved. 3 | # With contributions from: 4 | # * Chris Clark 5 | # * Christoph Robbert 6 | # * Klein Stephane 7 | # * "maartendb" 8 | # 9 | # Redistribution and use in source and binary forms, with or without 10 | # modification, are permitted provided that the following conditions are met: 11 | # 12 | # * Redistributions of source code must retain the above copyright notice, 13 | # this list of conditions and the following disclaimer. 14 | # * Redistributions in binary form must reproduce the above copyright notice, 15 | # this list of conditions and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # * The name of the author may not be used to endorse or promote products 18 | # derived from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | # POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen### Python template 10 | # Byte-compiled / optimized / DLL files 11 | __pycache__/ 12 | *.py[cod] 13 | *$py.class 14 | 15 | # C extensions 16 | *.so 17 | 18 | # Distribution / packaging 19 | .Python 20 | env/ 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *,cover 55 | .hypothesis/ 56 | 57 | # Translations 58 | *.mo 59 | #*.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # IPython Notebook 79 | .ipynb_checkpoints 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # dotenv 88 | .env 89 | 90 | # virtualenv 91 | venv/ 92 | ENV/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | /temp/ 101 | -------------------------------------------------------------------------------- /oletools/doc/oledir.md: -------------------------------------------------------------------------------- 1 | oledir 2 | ====== 3 | 4 | oledir is a script to display all the directory entries of an OLE file, 5 | including free and orphaned entries. 6 | 7 | It can be used either as a command-line tool, or as a python module from your own applications. 8 | 9 | It is part of the [python-oletools](http://www.decalage.info/python/oletools) package. 10 | 11 | ## Usage 12 | 13 | ```text 14 | Usage: oledir [options] [filename2 ...] 15 | 16 | Options: 17 | -h, --help show this help message and exit 18 | -r find files recursively in subdirectories. 19 | -z ZIP_PASSWORD, --zip=ZIP_PASSWORD 20 | if the file is a zip archive, open all files from it, 21 | using the provided password (requires Python 2.6+) 22 | -f ZIP_FNAME, --zipfname=ZIP_FNAME 23 | if the file is a zip archive, file(s) to be opened 24 | within the zip. Wildcards * and ? are supported. 25 | (default:*) 26 | ``` 27 | 28 | ### Examples 29 | 30 | Scan a single file: 31 | 32 | ```text 33 | oledir file.doc 34 | ``` 35 | 36 | ![](oledir.png) 37 | 38 | 39 | -------------------------------------------------------------------------- 40 | 41 | ## How to use oledir in Python applications 42 | 43 | TODO 44 | 45 | -------------------------------------------------------------------------- 46 | 47 | python-oletools documentation 48 | ----------------------------- 49 | 50 | - [[Home]] 51 | - [[License]] 52 | - [[Install]] 53 | - [[Contribute]], Suggest Improvements or Report Issues 54 | - Tools: 55 | - [[mraptor]] 56 | - [[msodde]] 57 | - [[olebrowse]] 58 | - [[oledir]] 59 | - [[oleid]] 60 | - [[olemap]] 61 | - [[olemeta]] 62 | - [[oleobj]] 63 | - [[oletimes]] 64 | - [[olevba]] 65 | - [[pyxswf]] 66 | - [[rtfobj]] 67 | -------------------------------------------------------------------------------- /tests/test-data/rtfobj/issue_251.rtf: -------------------------------------------------------------------------------- 1 | {\rtf2 DOCUMENT IS PROTECTED, DOWNLOAD AND ENABLE EDITING TO VIEW\ansi \par \par \par \ansicpg1181\deff0\nouicompat\deflang1094{\fonttbl{\f0\fnil\fcharset0 Calibri;}} 2 | {\*\generator Slimh20 6.186.5500}\viewkind2\uc3 3 | \pard\sa119\sl187\slmult1\f0\fs122\lang5{\object\objemb\objupdate{\*\objclass Equation.3}\objw105\objh245{\* 4 | \footerr 5 | { 6 | \bin\binN\rsidN\binN\rsidN\binN\binN\revdttmN\rsidN\binN\revdttmN\rsidN\object\binN\bin\binN\bin\revdttmN\bin\revdttmN\bin\binN\binN\rsidN\objhtml\rsidN\revdttmN\rsidN\bin\binN\rsidN\bin\bin\binN\bin\objupdate\bin\rsidN\binN\bin\rsidN\bin\revdttmN\bin\rsidN} 7 | \footers 8 | { 9 | \revdttmN\binN\binN\bin\binN\rsidN\rsidN\binN\rsidN\bin\binN\revdttmN\object\revdttmN\revdttmN\revdttmN\rsidN\revdttmN\revdttmN\rsidN\revdttmN\bin\bin\bin\objhtml\binN\rsidN\bin\binN\binN\revdttmN\bin\revdttmN\rsidN\binN\objupdate\binN\rsidN\revdttmN\binN\bin\binN\bin\binN\bin} 10 | \.\objdata 11 11 | \fnil 12 | 050000020000000B0000006571756174696F6E2E33000000000000000000000 13 | C0000D{\proptype\staticval 14 | abc} 15 | 0CF11E0A1B11AE1 16 | } 17 | { \result{\pict{\*\picprop}\wmetafile4\picw158\pich121\picwgoal344\pichgoal241 18 | 0100090000039e00000002001c0000000000050000000902000000000500000002010100000005 19 | 0000000102ffffff00050000002e0118000000050000000b0200000000050000000c02a0016002 20 | 1200000026060f001a00ffffffff000010000000c0ffffffc6ffffff20020000660100000b0000 21 | 0026060f000c004d61746854797065000020001c000000fb0280fe000000000000900100000000 22 | 0402001054696d6573204e657720526f6d616e00feffffff5f2d0a6500000a0000000000040000 23 | 002d01000009000000320a6001100003000000323232000a00000026060f000a00ffffffff0100 24 | 000000001c000000fb021000070000000000bc02000000000102022253797374656d000048008a 25 | 0100000a000600000048008a01ffffffff6ce21800040000002d01010004000000f00100000300 26 | 00000000 27 | }}} 28 | \par} -------------------------------------------------------------------------------- /oletools/common/errors.py: -------------------------------------------------------------------------------- 1 | """ 2 | Errors used in several tools to avoid duplication 3 | 4 | .. codeauthor:: Intra2net AG 5 | """ 6 | 7 | class CryptoErrorBase(ValueError): 8 | """Base class for crypto-based exceptions.""" 9 | pass 10 | 11 | 12 | class CryptoLibNotImported(CryptoErrorBase, ImportError): 13 | """Exception thrown if msoffcrypto is needed but could not be imported.""" 14 | 15 | def __init__(self): 16 | super(CryptoLibNotImported, self).__init__( 17 | 'msoffcrypto-tools is not installed. Please run "pip install msoffcrypto-tool" or see https://github.com/nolze/msoffcrypto-tool') 18 | 19 | 20 | class UnsupportedEncryptionError(CryptoErrorBase): 21 | """Exception thrown if file is encrypted and cannot deal with it.""" 22 | def __init__(self, filename=None): 23 | super(UnsupportedEncryptionError, self).__init__( 24 | 'Office file {}is encrypted, not yet supported' 25 | .format('' if filename is None else filename + ' ')) 26 | 27 | 28 | class WrongEncryptionPassword(CryptoErrorBase): 29 | """Exception thrown if encryption could be handled but passwords wrong.""" 30 | def __init__(self, filename=None): 31 | super(WrongEncryptionPassword, self).__init__( 32 | 'Given passwords could not decrypt office file{}, use option -p to specify the password' 33 | .format('' if filename is None else ' ' + filename)) 34 | 35 | 36 | class MaxCryptoNestingReached(CryptoErrorBase): 37 | """ 38 | Exception thrown if decryption is too deeply layered. 39 | 40 | (...or decrypt code creates inf loop) 41 | """ 42 | def __init__(self, n_layers, filename=None): 43 | super(MaxCryptoNestingReached, self).__init__( 44 | 'Encountered more than {} layers of encryption for office file{}' 45 | .format(n_layers, '' if filename is None else ' ' + filename)) 46 | -------------------------------------------------------------------------------- /oletools/doc/olebrowse.md: -------------------------------------------------------------------------------- 1 | olebrowse 2 | ========= 3 | 4 | olebrowse is a simple GUI to browse OLE files (e.g. MS Word, Excel, Powerpoint documents), to 5 | view and extract individual data streams. 6 | 7 | It is part of the [python-oletools](http://www.decalage.info/python/oletools) package. 8 | 9 | Dependencies 10 | ------------ 11 | 12 | olebrowse requires [Tkinter](https://en.wikipedia.org/wiki/Tkinter). 13 | On Windows and MacOSX, it should be installed with Python, and 14 | olebrowse should work out of the box. 15 | 16 | However, on Linux it might be necessary to install the tkinter 17 | package for Python separately. For example, on Ubuntu this is done with the 18 | following command: 19 | 20 | ``` 21 | sudo apt-get install python-tk 22 | ``` 23 | 24 | And for Python 3: 25 | 26 | ``` 27 | sudo apt-get install python3-tk 28 | ``` 29 | 30 | 31 | Usage 32 | ----- 33 | ``` 34 | olebrowse [file] 35 | ``` 36 | If you provide a file it will be opened, else a dialog will allow you to browse 37 | folders to open a file. Then if it is a valid OLE file, the list of data streams 38 | will be displayed. You can select a stream, and then either view its content 39 | in a builtin hexadecimal viewer, or save it to a file for further analysis. 40 | 41 | Screenshots 42 | ----------- 43 | 44 | Main menu, showing all streams in the OLE file: 45 | 46 | ![](olebrowse1_menu.png) 47 | 48 | Menu with actions for a stream: 49 | 50 | ![](olebrowse2_stream.png) 51 | 52 | Hex view for a stream: 53 | 54 | ![](olebrowse3_hexview.png) 55 | 56 | -------------------------------------------------------------------------- 57 | 58 | python-oletools documentation 59 | ----------------------------- 60 | 61 | - [[Home]] 62 | - [[License]] 63 | - [[Install]] 64 | - [[Contribute]], Suggest Improvements or Report Issues 65 | - Tools: 66 | - [[mraptor]] 67 | - [[msodde]] 68 | - [[olebrowse]] 69 | - [[oledir]] 70 | - [[oleid]] 71 | - [[olemap]] 72 | - [[olemeta]] 73 | - [[oleobj]] 74 | - [[oletimes]] 75 | - [[olevba]] 76 | - [[pyxswf]] 77 | - [[rtfobj]] 78 | -------------------------------------------------------------------------------- /oletools/doc/oleobj.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Untitled 8 | 14 | 17 | 18 | 19 |

oleobj

20 |

oleobj is a script to extract embedded objects from OLE files.

21 |

It can be used either as a command-line tool, or as a python module from your own applications.

22 |

It is part of the python-oletools package.

23 |

Usage

24 |
TODO
25 |
26 |

How to use oleobj in Python applications

27 |

See rtfobj.py source code.

28 |

TODO

29 |
30 |

python-oletools documentation

31 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /oletools/doc/olemeta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Untitled 8 | 14 | 17 | 18 | 19 |

olemeta

20 |

olemeta is a script to parse OLE files such as MS Office documents (e.g. Word, Excel), to extract all standard properties present in the OLE file.

21 |

It is part of the python-oletools package.

22 |

Usage

23 |
olemeta <file>
24 |

Example

25 |

26 |

How to use olemeta in Python applications

27 |

TODO

28 |
29 |

python-oletools documentation

30 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /oletools/common/log_helper/_logger_adapter.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from . import _root_logger_wrapper 3 | 4 | 5 | class OletoolsLoggerAdapter(logging.LoggerAdapter): 6 | """ 7 | Adapter class for all loggers returned by the logging module. 8 | """ 9 | _json_enabled = None 10 | 11 | def print_str(self, message, **kwargs): 12 | """ 13 | This function replaces normal print() calls so we can format them as JSON 14 | when needed or just print them right away otherwise. 15 | """ 16 | if self._json_enabled and self._json_enabled(): 17 | # Messages from this function should always be printed, 18 | # so when using JSON we log using the same level that set. 19 | # Additional information in kwargs is added to LogRecord 20 | self.log(_root_logger_wrapper.level(), message, extra=kwargs) 21 | else: 22 | print(message) 23 | 24 | def log(self, lvl, msg, *args, **kwargs): 25 | """ 26 | Run :py:meth:`process` on kwargs, then forward to actual logger. 27 | 28 | This is based on the logging cookbox, section "Using LoggerAdapter to 29 | impart contextual information". 30 | """ 31 | msg, kwargs = self.process(msg, kwargs) 32 | self.logger.log(lvl, msg, *args, **kwargs) 33 | 34 | def process(self, msg, kwargs): 35 | """ 36 | Ensure `kwargs['extra']['type']` exists, init with given arg `type`. 37 | 38 | The `type` field will be added to the :py:class:`logging.LogRecord` and 39 | is used by the :py:class:`JsonFormatter`. 40 | """ 41 | if 'extra' not in kwargs: 42 | kwargs['extra'] = {} 43 | if 'type' in kwargs: 44 | kwargs['extra']['type'] = kwargs['type'] 45 | del kwargs['type'] # downstream loggers cannot deal with this 46 | if 'type' not in kwargs['extra']: 47 | kwargs['extra']['type'] = 'msg' # type will be added to LogRecord 48 | return msg, kwargs 49 | 50 | def set_json_enabled_function(self, json_enabled): 51 | """ 52 | Set a function to be called to check whether JSON output is enabled. 53 | """ 54 | self._json_enabled = json_enabled 55 | 56 | def level(self): 57 | return self.logger.level 58 | -------------------------------------------------------------------------------- /tests/common/log_helper/log_helper_test_main.py: -------------------------------------------------------------------------------- 1 | """ Test log_helpers """ 2 | 3 | import sys 4 | from tests.common.log_helper import log_helper_test_imported 5 | from oletools.common.log_helper import log_helper 6 | 7 | DEBUG_MESSAGE = 'main: debug log' 8 | INFO_MESSAGE = 'main: info log' 9 | WARNING_MESSAGE = 'main: warning log' 10 | ERROR_MESSAGE = 'main: error log' 11 | CRITICAL_MESSAGE = 'main: critical log' 12 | RESULT_MESSAGE = 'main: result log' 13 | RESULT_TYPE = 'main: result' 14 | 15 | logger = log_helper.get_or_create_silent_logger('test_main') 16 | 17 | 18 | def init_logging_and_log(args): 19 | """ 20 | Try to cover possible logging scenarios. For each scenario covered, here's the expected args and outcome: 21 | - Log without enabling: [''] 22 | * logging when being imported - should never print 23 | - Log as JSON without enabling: ['as-json', ''] 24 | * logging as JSON when being imported - should never print 25 | - Enable and log: ['enable', ''] 26 | * logging when being run as script - should log messages 27 | - Enable and log as JSON: ['as-json', 'enable', ''] 28 | * logging as JSON when being run as script - should log messages as JSON 29 | - Enable, log as JSON and throw: ['enable', 'as-json', 'throw', ''] 30 | * should produce JSON-compatible output, even after an unhandled exception 31 | """ 32 | 33 | # the level should always be the last argument passed 34 | level = args[-1] 35 | use_json = 'as-json' in args 36 | throw = 'throw' in args 37 | percent_autoformat = '%-autoformat' in args 38 | 39 | if 'enable' in args: 40 | log_helper.enable_logging(use_json, level, stream=sys.stdout) 41 | 42 | _log() 43 | 44 | if percent_autoformat: 45 | logger.info('The %s is %d.', 'answer', 47) 46 | 47 | if throw: 48 | raise Exception('An exception occurred before ending the logging') 49 | 50 | log_helper.end_logging() 51 | 52 | 53 | def _log(): 54 | logger.debug(DEBUG_MESSAGE) 55 | logger.info(INFO_MESSAGE) 56 | logger.warning(WARNING_MESSAGE) 57 | logger.error(ERROR_MESSAGE) 58 | logger.critical(CRITICAL_MESSAGE) 59 | logger.info(RESULT_MESSAGE, type=RESULT_TYPE) 60 | log_helper_test_imported.log() 61 | 62 | 63 | if __name__ == '__main__': 64 | init_logging_and_log(sys.argv[1:]) 65 | -------------------------------------------------------------------------------- /oletools/doc/olemap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Untitled 8 | 14 | 17 | 18 | 19 |

olemap

20 |

olemap is a script to display a map of all the sectors in an OLE file.

21 |

It can be used either as a command-line tool, or as a python module from your own applications.

22 |

It is part of the python-oletools package.

23 |

Usage

24 |
Usage: olemap <filename>
25 |

Examples

26 |

Scan a single file:

27 |
olemap file.doc
28 |

29 |

30 |
31 |

How to use olemap in Python applications

32 |

TODO

33 |
34 |

python-oletools documentation

35 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /oletools/doc/Contribute.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Untitled 8 | 14 | 17 | 18 | 19 |

How to Suggest Improvements, Report Issues or Contribute

20 |

This is a personal open-source project, developed on my spare time. Any contribution, suggestion, feedback or bug report is welcome.

21 |

To suggest improvements, report a bug or any issue, please use the issue reporting page, and provide all the information and files to reproduce the problem.

22 |

You may also contact the author directly to send feedback.

23 |

The code is available in a repository on GitHub. You may use it to submit enhancements using forks and pull requests.

24 |
25 |

python-oletools documentation

26 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This license applies to the python-oletools package, apart from the thirdparty folder which contains third-party files 2 | published with their own license. 3 | 4 | The python-oletools package is copyright (c) 2012-2019 Philippe Lagadec (http://www.decalage.info) 5 | 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | 29 | ---------- 30 | 31 | olevba contains modified source code from the officeparser project, published 32 | under the following MIT License (MIT): 33 | 34 | officeparser is copyright (c) 2014 John William Davison 35 | 36 | Permission is hereby granted, free of charge, to any person obtaining a copy 37 | of this software and associated documentation files (the "Software"), to deal 38 | in the Software without restriction, including without limitation the rights 39 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 40 | copies of the Software, and to permit persons to whom the Software is 41 | furnished to do so, subject to the following conditions: 42 | 43 | The above copyright notice and this permission notice shall be included in all 44 | copies or substantial portions of the Software. 45 | 46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 47 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 48 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 49 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 50 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 51 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 52 | SOFTWARE. 53 | -------------------------------------------------------------------------------- /oletools/LICENSE.txt: -------------------------------------------------------------------------------- 1 | LICENSE for the python-oletools package: 2 | 3 | This license applies to the python-oletools package, apart from the thirdparty 4 | folder which contains third-party files published with their own license. 5 | 6 | The python-oletools package is copyright (c) 2012-2019 Philippe Lagadec (http://www.decalage.info) 7 | 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without modification, 11 | are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, this 14 | list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | 31 | ---------- 32 | 33 | olevba contains modified source code from the officeparser project, published 34 | under the following MIT License (MIT): 35 | 36 | officeparser is copyright (c) 2014 John William Davison 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in all 46 | copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. 55 | -------------------------------------------------------------------------------- /tests/rtfobj/test_is_rtf.py: -------------------------------------------------------------------------------- 1 | """ Test rtfobj.is_rtf """ 2 | 3 | from __future__ import print_function 4 | 5 | import unittest 6 | from os.path import join 7 | from os import walk 8 | 9 | from oletools.rtfobj import is_rtf, RTF_MAGIC 10 | 11 | # Directory with test data, independent of current working directory 12 | from tests.test_utils import DATA_BASE_DIR 13 | 14 | 15 | class TestIsRtf(unittest.TestCase): 16 | """ Tests rtfobj.is_rtf """ 17 | 18 | def test_bytearray(self): 19 | """ test that is_rtf works with bytearray """ 20 | self.assertTrue(is_rtf(bytearray(RTF_MAGIC + b'asdfasdfasdfasdfasdf'))) 21 | self.assertFalse(is_rtf(bytearray(RTF_MAGIC.upper() + b'asdfasdasdff'))) 22 | self.assertFalse(is_rtf(bytearray(b'asdfasdfasdfasdfasdfasdfsdfsdfa'))) 23 | 24 | def test_bytes(self): 25 | """ test that is_rtf works with bytearray """ 26 | self.assertTrue(is_rtf(RTF_MAGIC + b'asasdffdfasdfasdfasdfasdf', True)) 27 | self.assertFalse(is_rtf(RTF_MAGIC.upper() + b'asdffasdfasdasdff', True)) 28 | self.assertFalse(is_rtf(b'asdfasdfasdfasdfasdfasdasdfffsdfsdfa', True)) 29 | 30 | def test_tuple(self): 31 | """ test that is_rtf works with byte tuples """ 32 | data = tuple(byte_char for byte_char in RTF_MAGIC + b'asdfasfadfdfsdf') 33 | self.assertTrue(is_rtf(data)) 34 | 35 | data = tuple(byte_char for byte_char in RTF_MAGIC.upper() + b'asfasdf') 36 | self.assertFalse(is_rtf(data)) 37 | 38 | data = tuple(byte_char for byte_char in b'asdfasfassdfsdsfeereasdfwdf') 39 | self.assertFalse(is_rtf(data)) 40 | 41 | def test_iterable(self): 42 | """ test that is_rtf works with byte iterables """ 43 | data = (byte_char for byte_char in RTF_MAGIC + b'asdfasfasasdfasdfddf') 44 | self.assertTrue(is_rtf(data)) 45 | 46 | data = (byte_char for byte_char in RTF_MAGIC.upper() + b'asdfassfasdf') 47 | self.assertFalse(is_rtf(data)) 48 | 49 | data = (byte_char for byte_char in b'asdfasfasasdfasdfasdfsdfdwerwedf') 50 | self.assertFalse(is_rtf(data)) 51 | 52 | def test_files(self): 53 | """ test on real files """ 54 | for base_dir, _, files in walk(DATA_BASE_DIR): 55 | for filename in files: 56 | full_path = join(base_dir, filename) 57 | expect = filename.endswith('.rtf') 58 | self.assertEqual(is_rtf(full_path), expect, 59 | 'is_rtf({0}) did not return {1}' 60 | .format(full_path, expect)) 61 | with open(full_path, 'rb') as handle: 62 | self.assertEqual(is_rtf(handle), expect, 63 | 'is_rtf(open({0})) did not return {1}' 64 | .format(full_path, expect)) 65 | 66 | 67 | # just in case somebody calls this file as a script 68 | if __name__ == '__main__': 69 | unittest.main() 70 | -------------------------------------------------------------------------------- /oletools/doc/oletimes.md: -------------------------------------------------------------------------------- 1 | oletimes 2 | ======== 3 | 4 | oletimes is a script to parse OLE files such as MS Office documents (e.g. Word, 5 | Excel), to extract creation and modification times of all streams and storages 6 | in the OLE file. 7 | 8 | It is part of the [python-oletools](http://www.decalage.info/python/oletools) package. 9 | 10 | ## Usage 11 | 12 | ```text 13 | oletimes 14 | ``` 15 | 16 | ### Example 17 | 18 | Checking the malware sample [DIAN_caso-5415.doc](https://malwr.com/analysis/M2I4YWRhM2IwY2QwNDljN2E3ZWFjYTg3ODk4NmZhYmE/): 19 | 20 | ```text 21 | >oletimes DIAN_caso-5415.doc 22 | 23 | +----------------------------+---------------------+---------------------+ 24 | | Stream/Storage name | Modification Time | Creation Time | 25 | +----------------------------+---------------------+---------------------+ 26 | | Root | 2014-05-14 12:45:24 | None | 27 | | '\x01CompObj' | None | None | 28 | | '\x05DocumentSummaryInform | None | None | 29 | | ation' | | | 30 | | '\x05SummaryInformation' | None | None | 31 | | '1Table' | None | None | 32 | | 'Data' | None | None | 33 | | 'Macros' | 2014-05-14 12:45:24 | 2014-05-14 12:45:24 | 34 | | 'Macros/PROJECT' | None | None | 35 | | 'Macros/PROJECTwm' | None | None | 36 | | 'Macros/VBA' | 2014-05-14 12:45:24 | 2014-05-14 12:45:24 | 37 | | 'Macros/VBA/ThisDocument' | None | None | 38 | | 'Macros/VBA/_VBA_PROJECT' | None | None | 39 | | 'Macros/VBA/__SRP_0' | None | None | 40 | | 'Macros/VBA/__SRP_1' | None | None | 41 | | 'Macros/VBA/__SRP_2' | None | None | 42 | | 'Macros/VBA/__SRP_3' | None | None | 43 | | 'Macros/VBA/dir' | None | None | 44 | | 'WordDocument' | None | None | 45 | +----------------------------+---------------------+---------------------+ 46 | ``` 47 | 48 | ## How to use oletimes in Python applications 49 | 50 | TODO 51 | 52 | -------------------------------------------------------------------------- 53 | 54 | python-oletools documentation 55 | ----------------------------- 56 | 57 | - [[Home]] 58 | - [[License]] 59 | - [[Install]] 60 | - [[Contribute]], Suggest Improvements or Report Issues 61 | - Tools: 62 | - [[mraptor]] 63 | - [[msodde]] 64 | - [[olebrowse]] 65 | - [[oledir]] 66 | - [[oleid]] 67 | - [[olemap]] 68 | - [[olemeta]] 69 | - [[oleobj]] 70 | - [[oletimes]] 71 | - [[olevba]] 72 | - [[pyxswf]] 73 | - [[rtfobj]] 74 | -------------------------------------------------------------------------------- /oletools/doc/oledir.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Untitled 8 | 14 | 17 | 18 | 19 |

oledir

20 |

oledir is a script to display all the directory entries of an OLE file, including free and orphaned entries.

21 |

It can be used either as a command-line tool, or as a python module from your own applications.

22 |

It is part of the python-oletools package.

23 |

Usage

24 |
Usage: oledir [options] <filename> [filename2 ...]
25 | 
26 | Options:
27 |   -h, --help            show this help message and exit
28 |   -r                    find files recursively in subdirectories.
29 |   -z ZIP_PASSWORD, --zip=ZIP_PASSWORD
30 |                         if the file is a zip archive, open all files from it,
31 |                         using the provided password (requires Python 2.6+)
32 |   -f ZIP_FNAME, --zipfname=ZIP_FNAME
33 |                         if the file is a zip archive, file(s) to be opened
34 |                         within the zip. Wildcards * and ? are supported.
35 |                         (default:*)
36 |

Examples

37 |

Scan a single file:

38 |
oledir file.doc
39 |

40 |
41 |

How to use oledir in Python applications

42 |

TODO

43 |
44 |

python-oletools documentation

45 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /tests/test_utils/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """Utils generally useful for unittests.""" 4 | 5 | import sys 6 | import os 7 | from os.path import dirname, join, abspath 8 | from subprocess import check_output, PIPE, STDOUT, CalledProcessError 9 | 10 | 11 | # Base dir of project, contains subdirs "tests" and "oletools" and README.md 12 | PROJECT_ROOT = dirname(dirname(dirname(abspath(__file__)))) 13 | 14 | # Directory with test data, independent of current working directory 15 | DATA_BASE_DIR = join(PROJECT_ROOT, 'tests', 'test-data') 16 | 17 | # Directory with source code 18 | SOURCE_BASE_DIR = join(PROJECT_ROOT, 'oletools') 19 | 20 | 21 | def call_and_capture(module, args=None, accept_nonzero_exit=False, 22 | exclude_stderr=False): 23 | """ 24 | Run module as script, capturing and returning output and return code. 25 | 26 | This is the best way to capture a module's stdout and stderr; trying to 27 | modify sys.stdout/sys.stderr to StringIO-Buffers frequently causes trouble. 28 | 29 | Only drawback sofar: stdout and stderr are merged into one (which is 30 | what users see on their shell as well). When testing for json-compatible 31 | output you should `exclude_stderr` to `False` since logging ignores stderr, 32 | so unforseen warnings (e.g. issued by pypy) would mess up your json. 33 | 34 | :param str module: name of module to test, e.g. `olevba` 35 | :param args: arguments for module's main function 36 | :param bool fail_nonzero: Raise error if command returns non-0 return code 37 | :param bool exclude_stderr: Exclude output to `sys.stderr` from output 38 | (e.g. if parsing output through json) 39 | :returns: ret_code, output 40 | :rtype: int, str 41 | """ 42 | # create a PYTHONPATH environment var to prefer our current code 43 | env = os.environ.copy() 44 | try: 45 | env['PYTHONPATH'] = SOURCE_BASE_DIR + os.pathsep + \ 46 | os.environ['PYTHONPATH'] 47 | except KeyError: 48 | env['PYTHONPATH'] = SOURCE_BASE_DIR 49 | 50 | # hack: in python2 output encoding (sys.stdout.encoding) was None 51 | # although sys.getdefaultencoding() and sys.getfilesystemencoding were ok 52 | # TODO: maybe can remove this once branch 53 | # "encoding-for-non-unicode-environments" is merged 54 | if 'PYTHONIOENCODING' not in env: 55 | env['PYTHONIOENCODING'] = 'utf8' 56 | 57 | # ensure args is a tuple 58 | my_args = tuple(args) if args else () 59 | 60 | ret_code = -1 61 | try: 62 | output = check_output((sys.executable, '-m', module) + my_args, 63 | universal_newlines=True, env=env, 64 | stderr=PIPE if exclude_stderr else STDOUT) 65 | ret_code = 0 66 | 67 | except CalledProcessError as err: 68 | if accept_nonzero_exit: 69 | ret_code = err.returncode 70 | output = err.output 71 | else: 72 | print(err.output) 73 | raise 74 | 75 | return output, ret_code 76 | -------------------------------------------------------------------------------- /tests/olevba/test_crypto.py: -------------------------------------------------------------------------------- 1 | """Check decryption of files from olevba works.""" 2 | 3 | import sys 4 | import unittest 5 | from os.path import basename, join as pjoin 6 | import json 7 | from collections import OrderedDict 8 | 9 | from tests.test_utils import DATA_BASE_DIR, call_and_capture 10 | 11 | from oletools import crypto 12 | 13 | 14 | @unittest.skipIf(not crypto.check_msoffcrypto(), 15 | 'Module msoffcrypto not installed for {}' 16 | .format(basename(sys.executable))) 17 | class OlevbaCryptoWriteProtectTest(unittest.TestCase): 18 | """ 19 | Test documents that are 'write-protected' through encryption. 20 | 21 | Excel has a way to 'write-protect' documents by encrypting them with a 22 | hard-coded standard password. When looking at the file-structure you see 23 | an OLE-file with streams `EncryptedPackage`, `StrongEncryptionSpace`, and 24 | `EncryptionInfo`. Contained in the first is the actual file. When opening 25 | such a file in excel, it is decrypted without the user noticing. 26 | 27 | Olevba should detect such encryption, try to decrypt with the standard 28 | password and look for VBA code in the decrypted file. 29 | 30 | All these tests are skipped if the module `msoffcrypto-tools` is not 31 | installed. 32 | """ 33 | def test_autostart(self): 34 | """Check that autostart macro is found in xls[mb] sample file.""" 35 | for suffix in 'xlsm', 'xlsb': 36 | example_file = pjoin( 37 | DATA_BASE_DIR, 'encrypted', 38 | 'autostart-encrypt-standardpassword.' + suffix) 39 | output, _ = call_and_capture('olevba', args=('-j', example_file), 40 | exclude_stderr=True) 41 | data = json.loads(output, object_pairs_hook=OrderedDict) 42 | # debug: json.dump(data, sys.stdout, indent=4) 43 | self.assertEqual(len(data), 4) 44 | self.assertIn('script_name', data[0]) 45 | self.assertIn('version', data[0]) 46 | self.assertEqual(data[0]['type'], 'MetaInformation') 47 | self.assertIn('return_code', data[-1]) 48 | self.assertEqual(data[-1]['type'], 'MetaInformation') 49 | self.assertEqual(data[1]['container'], None) 50 | self.assertEqual(data[1]['file'], example_file) 51 | self.assertEqual(data[1]['analysis'], None) 52 | self.assertEqual(data[1]['macros'], []) 53 | self.assertEqual(data[1]['type'], 'OLE') 54 | self.assertEqual(data[2]['container'], example_file) 55 | self.assertNotEqual(data[2]['file'], example_file) 56 | self.assertEqual(data[2]['type'], "OpenXML") 57 | analysis = data[2]['analysis'] 58 | self.assertEqual(analysis[0]['type'], 'AutoExec') 59 | self.assertEqual(analysis[0]['keyword'], 'Auto_Open') 60 | macros = data[2]['macros'] 61 | self.assertEqual(macros[0]['vba_filename'], 'Modul1.bas') 62 | self.assertIn('Sub Auto_Open()', macros[0]['code']) 63 | 64 | 65 | if __name__ == '__main__': 66 | unittest.main() 67 | -------------------------------------------------------------------------------- /oletools/doc/olebrowse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Untitled 8 | 14 | 17 | 18 | 19 |

olebrowse

20 |

olebrowse is a simple GUI to browse OLE files (e.g. MS Word, Excel, Powerpoint documents), to view and extract individual data streams.

21 |

It is part of the python-oletools package.

22 |

Dependencies

23 |

olebrowse requires Tkinter. On Windows and MacOSX, it should be installed with Python, and olebrowse should work out of the box.

24 |

However, on Linux it might be necessary to install the tkinter package for Python separately. For example, on Ubuntu this is done with the following command:

25 |
sudo apt-get install python-tk
26 |

And for Python 3:

27 |
sudo apt-get install python3-tk
28 |

Usage

29 |
olebrowse [file]
30 |

If you provide a file it will be opened, else a dialog will allow you to browse folders to open a file. Then if it is a valid OLE file, the list of data streams will be displayed. You can select a stream, and then either view its content in a builtin hexadecimal viewer, or save it to a file for further analysis.

31 |

Screenshots

32 |

Main menu, showing all streams in the OLE file:

33 |

34 |

Menu with actions for a stream:

35 |

36 |

Hex view for a stream:

37 |

38 |
39 |

python-oletools documentation

40 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /install.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem INSTALL.BAT - Easy installer for Python modules on Windows 3 | 4 | rem version 0.04 2014-02-24 Philippe Lagadec - http://www.decalage.info 5 | 6 | rem License: 7 | rem This file install.bat can freely used, modified and redistributed, as 8 | rem long as credit to the author is kept intact. Please send any feedback, 9 | rem issues or improvements to decalage at laposte.net. 10 | 11 | rem CHANGELOG: 12 | rem 2007-09-04 v0.01 PL: - first version, for Python 2.3 to 2.5 13 | rem 2009-02-27 v0.02 PL: - added support for Python 2.6 14 | rem 2013-05-07 v0.03 PL: - added support for Python 2.7 15 | rem 2014-02-24 v0.04 PL: - added support for py.exe 16 | 17 | rem 1) test if py.exe or python.exe is in the path: 18 | rem (py.exe is better because it can select python 2 or 3 according to shebang lines) 19 | 20 | py.exe --version >NUL 2>&1 21 | if errorlevel 1 goto notpy 22 | echo py.exe found in the path. 23 | py.exe setup.py install 24 | if errorlevel 1 goto error 25 | goto end 26 | :NOTPY 27 | 28 | python.exe --version >NUL 2>&1 29 | if errorlevel 1 goto notpath 30 | echo Python.exe found in the path. 31 | python setup.py install 32 | if errorlevel 1 goto error 33 | goto end 34 | :NOTPATH 35 | 36 | rem 2) test for usual python.exe paths: 37 | 38 | REM Python 2.7: 39 | c:\python27\python.exe --version >NUL 2>&1 40 | if errorlevel 1 goto notpy27 41 | echo Python.exe found in C:\Python27 42 | c:\python27\python.exe setup.py install 43 | if errorlevel 1 goto error 44 | goto end 45 | :NOTPY27 46 | 47 | REM Python 2.6: 48 | c:\python26\python.exe --version >NUL 2>&1 49 | if errorlevel 1 goto notpy26 50 | echo Python.exe found in C:\Python26 51 | c:\python26\python.exe setup.py install 52 | if errorlevel 1 goto error 53 | goto end 54 | :NOTPY26 55 | 56 | c:\python25\python.exe --version >NUL 2>&1 57 | if errorlevel 1 goto notpy25 58 | echo Python.exe found in C:\Python25 59 | c:\python25\python.exe setup.py install 60 | if errorlevel 1 goto error 61 | goto end 62 | :NOTPY25 63 | 64 | c:\python24\python.exe --version >NUL 2>&1 65 | if errorlevel 1 goto notpy24 66 | echo Python.exe found in C:\Python24 67 | c:\python24\python.exe setup.py install 68 | if errorlevel 1 goto error 69 | goto end 70 | :NOTPY24 71 | 72 | c:\python23\python.exe --version >NUL 2>&1 73 | if errorlevel 1 goto notpy23 74 | echo Python.exe found in C:\Python23 75 | c:\python23\python.exe setup.py install 76 | if errorlevel 1 goto error 77 | goto end 78 | :NOTPY23 79 | 80 | "c:\program files\python\python.exe" --version >NUL 2>&1 81 | if errorlevel 1 goto notpf 82 | echo Python.exe found in C:\Program Files\Python 83 | "c:\program files\python\python.exe" setup.py install 84 | if errorlevel 1 goto error 85 | goto end 86 | :NOTPF 87 | 88 | rem 3) last we just try to launch the script, if .py is associated to python.exe 89 | echo Python.exe not found, trying to launch setup.py directly. 90 | setup.py install 91 | if errorlevel 1 goto error 92 | goto end 93 | 94 | :ERROR 95 | echo. 96 | echo If the installation is not successful, try to run "python setup.py install" 97 | echo or simply "setup.py install" in the script directory. 98 | echo You can also copy files by hand in the site-package directory of your 99 | echo Python directory. 100 | REM pause 101 | 102 | :END 103 | pause 104 | -------------------------------------------------------------------------------- /oletools/doc/Home.md: -------------------------------------------------------------------------------- 1 | python-oletools v0.55 documentation 2 | =================================== 3 | 4 | This is the home page of the documentation for python-oletools. The latest version can be found 5 | [online](https://github.com/decalage2/oletools/wiki), otherwise a copy is provided in the doc subfolder of the package. 6 | 7 | [python-oletools](http://www.decalage.info/python/oletools) is a package of python tools to analyze 8 | [Microsoft OLE2 files](http://en.wikipedia.org/wiki/Compound_File_Binary_Format) 9 | (also called Structured Storage, Compound File Binary Format or Compound Document File Format), 10 | such as Microsoft Office documents or Outlook messages, mainly for malware analysis, forensics and debugging. 11 | It is based on the [olefile](http://www.decalage.info/olefile) parser. 12 | See [http://www.decalage.info/python/oletools](http://www.decalage.info/python/oletools) for more info. 13 | 14 | **Quick links:** 15 | [Home page](http://www.decalage.info/python/oletools) - 16 | [Download/Install](https://github.com/decalage2/oletools/wiki/Install) - 17 | [Documentation](https://github.com/decalage2/oletools/wiki) - 18 | [Report Issues/Suggestions/Questions](https://github.com/decalage2/oletools/issues) - 19 | [Contact the Author](http://decalage.info/contact) - 20 | [Repository](https://github.com/decalage2/oletools) - 21 | [Updates on Twitter](https://twitter.com/decalage2) 22 | 23 | Note: python-oletools is not related to OLETools published by BeCubed Software. 24 | 25 | Tools in python-oletools: 26 | ------------------------- 27 | 28 | ### Tools to analyze malicious documents 29 | 30 | - **[[oleid]]**: to analyze OLE files to detect specific characteristics usually found in malicious files. 31 | - **[[olevba]]**: to extract and analyze VBA Macro source code from MS Office documents (OLE and OpenXML). 32 | - **[[mraptor]]**: to detect malicious VBA Macros 33 | - **[[msodde]]**: to detect and extract DDE/DDEAUTO links from MS Office documents, RTF and CSV 34 | - **[[pyxswf]]**: to detect, extract and analyze Flash objects (SWF) that may 35 | be embedded in files such as MS Office documents (e.g. Word, Excel) and RTF, 36 | which is especially useful for malware analysis. 37 | - **[[oleobj]]**: to extract embedded objects from OLE files. 38 | - **[[rtfobj]]**: to extract embedded objects from RTF files. 39 | 40 | ### Tools to analyze the structure of OLE files 41 | 42 | - **[[olebrowse]]**: A simple GUI to browse OLE files (e.g. MS Word, Excel, Powerpoint documents), to 43 | view and extract individual data streams. 44 | - **[[olemeta]]**: to extract all standard properties (metadata) from OLE files. 45 | - **[[oletimes]]**: to extract creation and modification timestamps of all streams and storages. 46 | - **[[oledir]]**: to display all the directory entries of an OLE file, including free and orphaned entries. 47 | - **[[olemap]]**: to display a map of all the sectors in an OLE file. 48 | - and a few others (coming soon) 49 | 50 | -------------------------------------------------------------------------- 51 | 52 | python-oletools documentation 53 | ----------------------------- 54 | 55 | - [[Home]] 56 | - [[License]] 57 | - [[Install]] 58 | - [[Contribute]], Suggest Improvements or Report Issues 59 | - Tools: 60 | - [[mraptor]] 61 | - [[msodde]] 62 | - [[olebrowse]] 63 | - [[oledir]] 64 | - [[oleid]] 65 | - [[olemap]] 66 | - [[olemeta]] 67 | - [[oleobj]] 68 | - [[oletimes]] 69 | - [[olevba]] 70 | - [[pyxswf]] 71 | - [[rtfobj]] 72 | -------------------------------------------------------------------------------- /oletools/doc/License.md: -------------------------------------------------------------------------------- 1 | License for python-oletools 2 | =========================== 3 | 4 | This license applies to the [python-oletools](http://www.decalage.info/python/oletools) package, apart from the 5 | thirdparty folder which contains third-party files published with their own license. 6 | 7 | The python-oletools package is copyright (c) 2012-2019 Philippe Lagadec ([http://www.decalage.info](http://www.decalage.info)) 8 | 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without modification, 12 | are permitted provided that the following conditions are met: 13 | 14 | * Redistributions of source code must retain the above copyright notice, this 15 | list of conditions and the following disclaimer. 16 | * Redistributions in binary form must reproduce the above copyright notice, 17 | this list of conditions and the following disclaimer in the documentation 18 | and/or other materials provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | 32 | ---------- 33 | License for officeparser 34 | ------------------------ 35 | 36 | olevba contains modified source code from the [officeparser](https://github.com/unixfreak0037/officeparser) project, published 37 | under the following MIT License (MIT): 38 | 39 | officeparser is copyright (c) 2014 John William Davison 40 | 41 | Permission is hereby granted, free of charge, to any person obtaining a copy 42 | of this software and associated documentation files (the "Software"), to deal 43 | in the Software without restriction, including without limitation the rights 44 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 45 | copies of the Software, and to permit persons to whom the Software is 46 | furnished to do so, subject to the following conditions: 47 | 48 | The above copyright notice and this permission notice shall be included in all 49 | copies or substantial portions of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 52 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 53 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 54 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 55 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 56 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 57 | SOFTWARE. 58 | 59 | -------------------------------------------------------------------------- 60 | 61 | python-oletools documentation 62 | ----------------------------- 63 | 64 | - [[Home]] 65 | - [[License]] 66 | - [[Install]] 67 | - [[Contribute]], Suggest Improvements or Report Issues 68 | - Tools: 69 | - [[mraptor]] 70 | - [[msodde]] 71 | - [[olebrowse]] 72 | - [[oledir]] 73 | - [[oleid]] 74 | - [[olemap]] 75 | - [[olemeta]] 76 | - [[oleobj]] 77 | - [[oletimes]] 78 | - [[olevba]] 79 | - [[pyxswf]] 80 | - [[rtfobj]] 81 | -------------------------------------------------------------------------------- /oletools/doc/mraptor.md: -------------------------------------------------------------------------------- 1 | mraptor (MacroRaptor) 2 | ===================== 3 | 4 | mraptor is a tool designed to detect most malicious VBA Macros using 5 | generic heuristics. Unlike antivirus engines, it does not rely on signatures. 6 | 7 | In a nutshell, mraptor detects keywords corresponding to the three 8 | following types of behaviour that are present in clear text in almost 9 | any macro malware: 10 | - A: Auto-execution trigger 11 | - W: Write to the file system or memory 12 | - X: Execute a file or any payload outside the VBA context 13 | 14 | mraptor considers that a macro is suspicious when A and (W or X) is true. 15 | 16 | For more information about mraptor's detection algorithm, see the article 17 | [How to detect most malicious macros without an antivirus](http://www.decalage.info/mraptor). 18 | 19 | mraptor can be used either as a command-line tool, or as a python module 20 | from your own applications. 21 | 22 | It is part of the [python-oletools](http://www.decalage.info/python/oletools) package. 23 | 24 | ## Usage 25 | 26 | ```text 27 | Usage: mraptor [options] [filename2 ...] 28 | 29 | Options: 30 | -h, --help show this help message and exit 31 | -r find files recursively in subdirectories. 32 | -z ZIP_PASSWORD, --zip=ZIP_PASSWORD 33 | if the file is a zip archive, open all files from it, 34 | using the provided password (requires Python 2.6+) 35 | -f ZIP_FNAME, --zipfname=ZIP_FNAME 36 | if the file is a zip archive, file(s) to be opened 37 | within the zip. Wildcards * and ? are supported. 38 | (default:*) 39 | -l LOGLEVEL, --loglevel=LOGLEVEL 40 | logging level debug/info/warning/error/critical 41 | (default=warning) 42 | -m, --matches Show matched strings. 43 | 44 | An exit code is returned based on the analysis result: 45 | - 0: No Macro 46 | - 1: Not MS Office 47 | - 2: Macro OK 48 | - 10: ERROR 49 | - 20: SUSPICIOUS 50 | ``` 51 | 52 | ### Examples 53 | 54 | Scan a single file: 55 | 56 | ```text 57 | mraptor file.doc 58 | ``` 59 | 60 | Scan a single file, stored in a Zip archive with password "infected": 61 | 62 | ```text 63 | mraptor malicious_file.xls.zip -z infected 64 | ``` 65 | 66 | Scan a collection of files stored in a folder: 67 | 68 | ```text 69 | mraptor "MalwareZoo/VBA/*" 70 | ``` 71 | 72 | **Important**: on Linux/MacOSX, always add double quotes around a file name when you use 73 | wildcards such as `*` and `?`. Otherwise, the shell may replace the argument with the actual 74 | list of files matching the wildcards before starting the script. 75 | 76 | ![](mraptor1.png) 77 | 78 | ## Python 3 support - mraptor3 79 | 80 | Since v0.54, mraptor is fully compatible with both Python 2 and 3. 81 | There is no need to use mraptor3 anymore, however it is still present for backward compatibility. 82 | 83 | 84 | -------------------------------------------------------------------------- 85 | 86 | ## How to use mraptor in Python applications 87 | 88 | TODO 89 | 90 | 91 | -------------------------------------------------------------------------- 92 | 93 | python-oletools documentation 94 | ----------------------------- 95 | 96 | - [[Home]] 97 | - [[License]] 98 | - [[Install]] 99 | - [[Contribute]], Suggest Improvements or Report Issues 100 | - Tools: 101 | - [[mraptor]] 102 | - [[msodde]] 103 | - [[olebrowse]] 104 | - [[oledir]] 105 | - [[oleid]] 106 | - [[olemap]] 107 | - [[olemeta]] 108 | - [[oleobj]] 109 | - [[oletimes]] 110 | - [[olevba]] 111 | - [[pyxswf]] 112 | - [[rtfobj]] 113 | -------------------------------------------------------------------------------- /oletools/doc/rtfobj.md: -------------------------------------------------------------------------------- 1 | rtfobj 2 | ====== 3 | 4 | rtfobj is a Python module to detect and extract embedded objects stored 5 | in RTF files, such as OLE objects. It can also detect OLE Package objects, 6 | and extract the embedded files. 7 | 8 | Since v0.50, rtfobj contains a custom RTF parser that has been designed to 9 | match MS Word's behaviour, in order to handle obfuscated RTF files. See my 10 | article ["Anti-Analysis Tricks in Weaponized RTF"](http://decalage.info/rtf_tricks) 11 | for some concrete examples. 12 | 13 | rtfobj can be used as a Python library or a command-line tool. 14 | 15 | It is part of the [python-oletools](http://www.decalage.info/python/oletools) package. 16 | 17 | ## Usage 18 | 19 | ```text 20 | rtfobj [options] [filename2 ...] 21 | 22 | Options: 23 | -h, --help show this help message and exit 24 | -r find files recursively in subdirectories. 25 | -z ZIP_PASSWORD, --zip=ZIP_PASSWORD 26 | if the file is a zip archive, open first file from it, 27 | using the provided password (requires Python 2.6+) 28 | -f ZIP_FNAME, --zipfname=ZIP_FNAME 29 | if the file is a zip archive, file(s) to be opened 30 | within the zip. Wildcards * and ? are supported. 31 | (default:*) 32 | -l LOGLEVEL, --loglevel=LOGLEVEL 33 | logging level debug/info/warning/error/critical 34 | (default=warning) 35 | -s SAVE_OBJECT, --save=SAVE_OBJECT 36 | Save the object corresponding to the provided number 37 | to a file, for example "-s 2". Use "-s all" to save 38 | all objects at once. 39 | -d OUTPUT_DIR use specified directory to save output files. 40 | ``` 41 | 42 | rtfobj displays a list of the OLE and Package objects that have been detected, 43 | with their attributes such as class and filename. 44 | 45 | When an OLE Package object contains an executable file or script, it is 46 | highlighted as such. For example: 47 | 48 | ![](rtfobj1.png) 49 | 50 | To extract an object or file, use the option -s followed by the object number 51 | as shown in the table. 52 | 53 | Example: 54 | 55 | ```text 56 | rtfobj -s 0 57 | ``` 58 | 59 | It extracts and decodes the corresponding object, and saves it as a file 60 | named "object_xxxx.bin", xxxx being the location of the object in the RTF file. 61 | 62 | 63 | ## How to use rtfobj in Python applications 64 | 65 | As of v0.50, the API has changed significantly and it is not final yet. 66 | For now, see the class RtfObjectParser in the code. 67 | 68 | ### Deprecated API (still functional): 69 | 70 | rtf_iter_objects(filename) is an iterator which yields a tuple 71 | (index, orig_len, object) providing the index of each hexadecimal stream 72 | in the RTF file, and the corresponding decoded object. 73 | 74 | Example: 75 | 76 | ```python 77 | from oletools import rtfobj 78 | for index, orig_len, data in rtfobj.rtf_iter_objects("myfile.rtf"): 79 | print('found object size %d at index %08X' % (len(data), index)) 80 | ``` 81 | 82 | -------------------------------------------------------------------------- 83 | 84 | python-oletools documentation 85 | ----------------------------- 86 | 87 | - [[Home]] 88 | - [[License]] 89 | - [[Install]] 90 | - [[Contribute]], Suggest Improvements or Report Issues 91 | - Tools: 92 | - [[mraptor]] 93 | - [[msodde]] 94 | - [[olebrowse]] 95 | - [[oledir]] 96 | - [[oleid]] 97 | - [[olemap]] 98 | - [[olemeta]] 99 | - [[oleobj]] 100 | - [[oletimes]] 101 | - [[olevba]] 102 | - [[pyxswf]] 103 | - [[rtfobj]] 104 | -------------------------------------------------------------------------------- /tests/test-data/msodde/dde-in-excel2003.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | user 10 | user 11 | 2018-01-04T17:55:47Z 12 | 11.9999 13 | 14 | 15 | 16 | cmd|/c calc.exe 17 | 18 | A1 19 | 20 | 21 | 22 | StdDocumentName 23 | 24 | 25 | 26 | 27 | 12840 28 | 15315 29 | 360 30 | 105 31 | False 32 | False 33 | 34 | 35 | 43 | 44 | 45 | 47 | 48 | This spread sheet has a dde link which starts calc.exe 49 | 50 | 51 | #REF! 52 | 53 |
54 | 55 | 56 |
57 |