Commit 2fa4c06cebce8d2893543af11aa4a5dba3c9751f

Authored by Philippe Lagadec
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__':
... ...