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,6 +139,7 @@ https://github.com/unixfreak0037/officeparser
139 # 2015-05-29 v0.30 PL: - added suspicious keywords suggested by @ozhermit, 139 # 2015-05-29 v0.30 PL: - added suspicious keywords suggested by @ozhermit,
140 # Davy Douhine (issue #9), issue #13 140 # Davy Douhine (issue #9), issue #13
141 # 2015-06-16 v0.31 PL: - added generic VBA expression deobfuscation (chr,asc,etc) 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 __version__ = '0.31' 144 __version__ = '0.31'
144 145
@@ -1857,7 +1858,9 @@ def print_analysis(vba_code, show_decoded_strings=False): @@ -1857,7 +1858,9 @@ def print_analysis(vba_code, show_decoded_strings=False):
1857 print 'No suspicious keyword or IOC found.' 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 Process a single file 1865 Process a single file
1863 1866
@@ -1866,8 +1869,15 @@ def process_file(container, filename, data, show_decoded_strings=False): @@ -1866,8 +1869,15 @@ def process_file(container, filename, data, show_decoded_strings=False):
1866 :param filename: str, path and filename of file on disk, or within the container. 1869 :param filename: str, path and filename of file on disk, or within the container.
1867 :param data: bytes, content of the file if it is in a container, None if it is a file on disk. 1870 :param data: bytes, content of the file if it is in a container, None if it is a file on disk.
1868 :param show_decoded_strings: bool, if True hex-encoded strings will be displayed with their decoded content. 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 #TODO: replace print by writing to a provided output file (sys.stdout by default) 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 if container: 1881 if container:
1872 display_filename = '%s in %s' % (filename, container) 1882 display_filename = '%s in %s' % (filename, container)
1873 else: 1883 else:
@@ -1880,23 +1890,34 @@ def process_file(container, filename, data, show_decoded_strings=False): @@ -1880,23 +1890,34 @@ def process_file(container, filename, data, show_decoded_strings=False):
1880 print 'Type:', vba.type 1890 print 'Type:', vba.type
1881 if vba.detect_vba_macros(): 1891 if vba.detect_vba_macros():
1882 #print 'Contains VBA Macros:' 1892 #print 'Contains VBA Macros:'
  1893 + # variable to merge source code from all modules:
  1894 + vba_code_all_modules = ''
1883 for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros(): 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 print '-' * 79 1901 print '-' * 79
1888 print 'VBA MACRO %s ' % vba_filename 1902 print 'VBA MACRO %s ' % vba_filename
1889 print 'in file: %s - OLE stream: %s' % (subfilename, repr(stream_path)) 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 print '- ' * 39 1912 print '- ' * 39
1897 print 'ANALYSIS:' 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 print_analysis(vba_code_filtered, show_decoded_strings) 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 else: 1921 else:
1901 print 'No VBA macros found.' 1922 print 'No VBA macros found.'
1902 except: #TypeError: 1923 except: #TypeError:
@@ -2025,14 +2046,22 @@ def main(): @@ -2025,14 +2046,22 @@ def main():
2025 help='if the file is a zip archive, open first file from it, using the provided password (requires Python 2.6+)') 2046 help='if the file is a zip archive, open first file from it, using the provided password (requires Python 2.6+)')
2026 parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*', 2047 parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*',
2027 help='if the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)') 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 help='triage mode, display results as a summary table (default for multiple files)') 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 help='detailed mode, display full results (default for single file)') 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 parser.add_option("-i", "--input", dest='input', type='str', default=None, 2057 parser.add_option("-i", "--input", dest='input', type='str', default=None,
2033 help='input file containing VBA source code to be analyzed (no parsing)') 2058 help='input file containing VBA source code to be analyzed (no parsing)')
2034 parser.add_option("--decode", action="store_true", dest="show_decoded_strings", 2059 parser.add_option("--decode", action="store_true", dest="show_decoded_strings",
2035 help='display all the obfuscated strings with their decoded content (Hex, Base64, StrReverse, Dridex, VBA).') 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 # TODO: --novba to disable VBA expressions parsing 2066 # TODO: --novba to disable VBA expressions parsing
2038 2067
@@ -2074,7 +2103,9 @@ def main(): @@ -2074,7 +2103,9 @@ def main():
2074 continue 2103 continue
2075 if options.detailed_mode and not options.triage_mode: 2104 if options.detailed_mode and not options.triage_mode:
2076 # fully detailed output 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 else: 2109 else:
2079 # print container name when it changes: 2110 # print container name when it changes:
2080 if container != previous_container: 2111 if container != previous_container:
@@ -2092,7 +2123,9 @@ def main(): @@ -2092,7 +2123,9 @@ def main():
2092 if count == 1 and not options.triage_mode and not options.detailed_mode: 2123 if count == 1 and not options.triage_mode and not options.detailed_mode:
2093 # if options -t and -d were not specified and it's a single file, print details: 2124 # if options -t and -d were not specified and it's a single file, print details:
2094 #TODO: avoid doing the analysis twice by storing results 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 if __name__ == '__main__': 2131 if __name__ == '__main__':