Commit 2fa4c06cebce8d2893543af11aa4a5dba3c9751f
1 parent
ac8bddb7
olevba: added options -a, -c, --each, --attr
Showing
1 changed file
with
48 additions
and
15 deletions
oletools/olevba.py
| ... | ... | @@ -139,6 +139,7 @@ https://github.com/unixfreak0037/officeparser |
| 139 | 139 | # 2015-05-29 v0.30 PL: - added suspicious keywords suggested by @ozhermit, |
| 140 | 140 | # Davy Douhine (issue #9), issue #13 |
| 141 | 141 | # 2015-06-16 v0.31 PL: - added generic VBA expression deobfuscation (chr,asc,etc) |
| 142 | +# 2015-06-19 PL: - added options -a, -c, --each, --attr | |
| 142 | 143 | |
| 143 | 144 | __version__ = '0.31' |
| 144 | 145 | |
| ... | ... | @@ -1857,7 +1858,9 @@ def print_analysis(vba_code, show_decoded_strings=False): |
| 1857 | 1858 | print 'No suspicious keyword or IOC found.' |
| 1858 | 1859 | |
| 1859 | 1860 | |
| 1860 | -def process_file(container, filename, data, show_decoded_strings=False): | |
| 1861 | +def process_file(container, filename, data, show_decoded_strings=False, | |
| 1862 | + display_code=True, global_analysis=True, hide_attributes=True, | |
| 1863 | + vba_code_only=False): | |
| 1861 | 1864 | """ |
| 1862 | 1865 | Process a single file |
| 1863 | 1866 | |
| ... | ... | @@ -1866,8 +1869,15 @@ def process_file(container, filename, data, show_decoded_strings=False): |
| 1866 | 1869 | :param filename: str, path and filename of file on disk, or within the container. |
| 1867 | 1870 | :param data: bytes, content of the file if it is in a container, None if it is a file on disk. |
| 1868 | 1871 | :param show_decoded_strings: bool, if True hex-encoded strings will be displayed with their decoded content. |
| 1872 | + :param display_code: bool, if False VBA source code is not displayed (default True) | |
| 1873 | + :param global_analysis: bool, if True all modules are merged for a single analysis (default), | |
| 1874 | + otherwise each module is analyzed separately (old behaviour) | |
| 1875 | + :param hide_attributes: bool, if True the first lines starting with "Attribute VB" are hidden (default) | |
| 1869 | 1876 | """ |
| 1870 | 1877 | #TODO: replace print by writing to a provided output file (sys.stdout by default) |
| 1878 | + # fix conflicting parameters: | |
| 1879 | + if vba_code_only and not display_code: | |
| 1880 | + display_code = True | |
| 1871 | 1881 | if container: |
| 1872 | 1882 | display_filename = '%s in %s' % (filename, container) |
| 1873 | 1883 | else: |
| ... | ... | @@ -1880,23 +1890,34 @@ def process_file(container, filename, data, show_decoded_strings=False): |
| 1880 | 1890 | print 'Type:', vba.type |
| 1881 | 1891 | if vba.detect_vba_macros(): |
| 1882 | 1892 | #print 'Contains VBA Macros:' |
| 1893 | + # variable to merge source code from all modules: | |
| 1894 | + vba_code_all_modules = '' | |
| 1883 | 1895 | for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): |
| 1884 | - # hide attribute lines: | |
| 1885 | - #TODO: option to disable attribute filtering | |
| 1886 | - vba_code_filtered = filter_vba(vba_code) | |
| 1896 | + if hide_attributes: | |
| 1897 | + # hide attribute lines: | |
| 1898 | + vba_code_filtered = filter_vba(vba_code) | |
| 1899 | + else: | |
| 1900 | + vba_code_filtered = vba_code | |
| 1887 | 1901 | print '-' * 79 |
| 1888 | 1902 | print 'VBA MACRO %s ' % vba_filename |
| 1889 | 1903 | print 'in file: %s - OLE stream: %s' % (subfilename, repr(stream_path)) |
| 1890 | - print '- ' * 39 | |
| 1891 | - # detect empty macros: | |
| 1892 | - if vba_code_filtered.strip() == '': | |
| 1893 | - print '(empty macro)' | |
| 1894 | - else: | |
| 1895 | - print vba_code_filtered | |
| 1904 | + if display_code: | |
| 1905 | + print '- ' * 39 | |
| 1906 | + # detect empty macros: | |
| 1907 | + if vba_code_filtered.strip() == '': | |
| 1908 | + print '(empty macro)' | |
| 1909 | + else: | |
| 1910 | + print vba_code_filtered | |
| 1911 | + if not global_analysis and not vba_code_only: | |
| 1896 | 1912 | print '- ' * 39 |
| 1897 | 1913 | print 'ANALYSIS:' |
| 1898 | - # analyse the whole code, filtered to avoid false positives: | |
| 1914 | + # analyse each module's code, filtered to avoid false positives: | |
| 1899 | 1915 | print_analysis(vba_code_filtered, show_decoded_strings) |
| 1916 | + else: | |
| 1917 | + vba_code_all_modules += vba_code_filtered + '\n' | |
| 1918 | + if global_analysis and not vba_code_only: | |
| 1919 | + # analyse the code from all modules at once: | |
| 1920 | + print_analysis(vba_code_all_modules, show_decoded_strings) | |
| 1900 | 1921 | else: |
| 1901 | 1922 | print 'No VBA macros found.' |
| 1902 | 1923 | except: #TypeError: |
| ... | ... | @@ -2025,14 +2046,22 @@ def main(): |
| 2025 | 2046 | help='if the file is a zip archive, open first file from it, using the provided password (requires Python 2.6+)') |
| 2026 | 2047 | parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*', |
| 2027 | 2048 | help='if the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)') |
| 2028 | - parser.add_option("-t", action="store_true", dest="triage_mode", | |
| 2049 | + parser.add_option("-t", '--triage', action="store_true", dest="triage_mode", | |
| 2029 | 2050 | help='triage mode, display results as a summary table (default for multiple files)') |
| 2030 | - parser.add_option("-d", action="store_true", dest="detailed_mode", | |
| 2051 | + parser.add_option("-d", '--detailed', action="store_true", dest="detailed_mode", | |
| 2031 | 2052 | help='detailed mode, display full results (default for single file)') |
| 2053 | + parser.add_option("-a", '--analysis', action="store_false", dest="display_code", default=True, | |
| 2054 | + help='display only analysis results, not the macro source code') | |
| 2055 | + parser.add_option("-c", '--code', action="store_true", dest="vba_code_only", default=False, | |
| 2056 | + help='display only VBA source code, do not analyze it') | |
| 2032 | 2057 | parser.add_option("-i", "--input", dest='input', type='str', default=None, |
| 2033 | 2058 | help='input file containing VBA source code to be analyzed (no parsing)') |
| 2034 | 2059 | parser.add_option("--decode", action="store_true", dest="show_decoded_strings", |
| 2035 | 2060 | help='display all the obfuscated strings with their decoded content (Hex, Base64, StrReverse, Dridex, VBA).') |
| 2061 | + parser.add_option("--attr", action="store_false", dest="hide_attributes", default=True, | |
| 2062 | + help='display the attribute lines at the beginning of VBA source code') | |
| 2063 | + parser.add_option("--each", action="store_false", dest="global_analysis", default=True, | |
| 2064 | + help='analyze each VBA module separately') | |
| 2036 | 2065 | |
| 2037 | 2066 | # TODO: --novba to disable VBA expressions parsing |
| 2038 | 2067 | |
| ... | ... | @@ -2074,7 +2103,9 @@ def main(): |
| 2074 | 2103 | continue |
| 2075 | 2104 | if options.detailed_mode and not options.triage_mode: |
| 2076 | 2105 | # fully detailed output |
| 2077 | - process_file(container, filename, data, show_decoded_strings=options.show_decoded_strings) | |
| 2106 | + process_file(container, filename, data, show_decoded_strings=options.show_decoded_strings, | |
| 2107 | + display_code=options.display_code, global_analysis=options.global_analysis, | |
| 2108 | + hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only) | |
| 2078 | 2109 | else: |
| 2079 | 2110 | # print container name when it changes: |
| 2080 | 2111 | if container != previous_container: |
| ... | ... | @@ -2092,7 +2123,9 @@ def main(): |
| 2092 | 2123 | if count == 1 and not options.triage_mode and not options.detailed_mode: |
| 2093 | 2124 | # if options -t and -d were not specified and it's a single file, print details: |
| 2094 | 2125 | #TODO: avoid doing the analysis twice by storing results |
| 2095 | - process_file(container, filename, data, show_decoded_strings=options.show_decoded_strings) | |
| 2126 | + process_file(container, filename, data, show_decoded_strings=options.show_decoded_strings, | |
| 2127 | + display_code=options.display_code, global_analysis=options.global_analysis, | |
| 2128 | + hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only) | |
| 2096 | 2129 | |
| 2097 | 2130 | |
| 2098 | 2131 | if __name__ == '__main__': | ... | ... |