Commit 49b8af644038bf4ceaa3a7c51f057f5f111c0346
1 parent
4b4775d3
olevba: added new option --reveal
Showing
1 changed file
with
32 additions
and
4 deletions
oletools/olevba.py
| ... | ... | @@ -151,8 +151,9 @@ https://github.com/unixfreak0037/officeparser |
| 151 | 151 | # 2015-09-15 PL: - remove duplicate IOCs from results |
| 152 | 152 | # 2015-09-16 PL: - join long VBA lines ending with underscore before scan |
| 153 | 153 | # - disabled unused option --each |
| 154 | +# 2015-09-22 v0.41 PL: - added new option --reveal | |
| 154 | 155 | |
| 155 | -__version__ = '0.40' | |
| 156 | +__version__ = '0.41' | |
| 156 | 157 | |
| 157 | 158 | #------------------------------------------------------------------------------ |
| 158 | 159 | # TODO: |
| ... | ... | @@ -2095,9 +2096,30 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2095 | 2096 | print 'No suspicious keyword or IOC found.' |
| 2096 | 2097 | |
| 2097 | 2098 | |
| 2099 | + def reveal(self): | |
| 2100 | + print 'MACRO SOURCE CODE WITH DEOBFUSCATED VBA STRINGS (EXPERIMENTAL):\n' | |
| 2101 | + # we only want printable strings: | |
| 2102 | + analysis = self.analyze_macros(show_decoded_strings=False) | |
| 2103 | + # to avoid replacing short strings contained into longer strings, we sort the analysis results | |
| 2104 | + # based on the length of the encoded string, in reverse order: | |
| 2105 | + analysis = sorted(analysis, key=lambda type_decoded_encoded: len(type_decoded_encoded[2]), reverse=True) | |
| 2106 | + # normally now self.vba_code_all_modules contains source code from all modules | |
| 2107 | + deobf_code = self.vba_code_all_modules | |
| 2108 | + for kw_type, decoded, encoded in analysis: | |
| 2109 | + if kw_type == 'VBA string': | |
| 2110 | + #print '%3d occurences: %r => %r' % (deobf_code.count(encoded), encoded, decoded) | |
| 2111 | + # need to add double quotes around the decoded strings | |
| 2112 | + # after escaping double-quotes as double-double-quotes for VBA: | |
| 2113 | + decoded = decoded.replace('"', '""') | |
| 2114 | + deobf_code = deobf_code.replace(encoded, '"%s"' % decoded) | |
| 2115 | + print '' | |
| 2116 | + print deobf_code | |
| 2117 | + #TODO: repasser l'analyse plusieurs fois si des chaines hex ou base64 sont revelees | |
| 2118 | + | |
| 2119 | + | |
| 2098 | 2120 | def process_file(self, show_decoded_strings=False, |
| 2099 | 2121 | display_code=True, global_analysis=True, hide_attributes=True, |
| 2100 | - vba_code_only=False): | |
| 2122 | + vba_code_only=False, show_deobfuscated_code=False): | |
| 2101 | 2123 | """ |
| 2102 | 2124 | Process a single file |
| 2103 | 2125 | |
| ... | ... | @@ -2150,6 +2172,8 @@ class VBA_Parser_CLI(VBA_Parser): |
| 2150 | 2172 | if global_analysis and not vba_code_only: |
| 2151 | 2173 | # analyse the code from all modules at once: |
| 2152 | 2174 | self.print_analysis(show_decoded_strings) |
| 2175 | + if show_deobfuscated_code: | |
| 2176 | + self.reveal() | |
| 2153 | 2177 | else: |
| 2154 | 2178 | print 'No VBA macros found.' |
| 2155 | 2179 | except: #TypeError: |
| ... | ... | @@ -2260,6 +2284,8 @@ def main(): |
| 2260 | 2284 | help='display all the obfuscated strings with their decoded content (Hex, Base64, StrReverse, Dridex, VBA).') |
| 2261 | 2285 | parser.add_option("--attr", action="store_false", dest="hide_attributes", default=True, |
| 2262 | 2286 | help='display the attribute lines at the beginning of VBA source code') |
| 2287 | + parser.add_option("--reveal", action="store_true", dest="show_deobfuscated_code", | |
| 2288 | + help='display the macro source code after replacing all the obfuscated strings by their decoded content.') | |
| 2263 | 2289 | |
| 2264 | 2290 | # Disabled options: |
| 2265 | 2291 | # parser.add_option("--each", action="store_false", dest="global_analysis", default=True, |
| ... | ... | @@ -2316,7 +2342,8 @@ def main(): |
| 2316 | 2342 | # fully detailed output |
| 2317 | 2343 | vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, |
| 2318 | 2344 | display_code=options.display_code, global_analysis=True, #options.global_analysis, |
| 2319 | - hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only) | |
| 2345 | + hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, | |
| 2346 | + show_deobfuscated_code=options.show_deobfuscated_code) | |
| 2320 | 2347 | else: |
| 2321 | 2348 | # print container name when it changes: |
| 2322 | 2349 | if container != previous_container: |
| ... | ... | @@ -2335,7 +2362,8 @@ def main(): |
| 2335 | 2362 | # if options -t and -d were not specified and it's a single file, print details: |
| 2336 | 2363 | vba_parser.process_file(show_decoded_strings=options.show_decoded_strings, |
| 2337 | 2364 | display_code=options.display_code, global_analysis=True, #options.global_analysis, |
| 2338 | - hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only) | |
| 2365 | + hide_attributes=options.hide_attributes, vba_code_only=options.vba_code_only, | |
| 2366 | + show_deobfuscated_code=options.show_deobfuscated_code) | |
| 2339 | 2367 | |
| 2340 | 2368 | |
| 2341 | 2369 | if __name__ == '__main__': | ... | ... |