Commit c203e02dc9bab6fb7affc6012d80e27a024abdd3
1 parent
efe28a7b
mraptor: now returns an exit code based on the overall result
Showing
1 changed file
with
63 additions
and
26 deletions
oletools/mraptor.py
| ... | ... | @@ -48,8 +48,9 @@ http://www.decalage.info/python/oletools |
| 48 | 48 | # CHANGELOG: |
| 49 | 49 | # 2016-02-23 v0.01 PL: - first version |
| 50 | 50 | # 2016-02-29 v0.02 PL: - added Workbook_Activate, FileSaveAs |
| 51 | +# 2016-03-04 v0.03 PL: - returns an exit code based on the overall result | |
| 51 | 52 | |
| 52 | -__version__ = '0.01' | |
| 53 | +__version__ = '0.03' | |
| 53 | 54 | |
| 54 | 55 | #------------------------------------------------------------------------------ |
| 55 | 56 | # TODO: |
| ... | ... | @@ -113,6 +114,36 @@ TYPE2TAG = { |
| 113 | 114 | |
| 114 | 115 | # === CLASSES ================================================================= |
| 115 | 116 | |
| 117 | +class Result_NoMacro(object): | |
| 118 | + exit_code = 0 | |
| 119 | + color = 'green' | |
| 120 | + name = 'No Macro' | |
| 121 | + | |
| 122 | + | |
| 123 | +class Result_NotMSOffice(object): | |
| 124 | + exit_code = 1 | |
| 125 | + color = 'green' | |
| 126 | + name = 'Not MS Office' | |
| 127 | + | |
| 128 | + | |
| 129 | +class Result_MacroOK(object): | |
| 130 | + exit_code = 2 | |
| 131 | + color = 'cyan' | |
| 132 | + name = 'Macro OK' | |
| 133 | + | |
| 134 | + | |
| 135 | +class Result_Error(object): | |
| 136 | + exit_code = 10 | |
| 137 | + color = 'yellow' | |
| 138 | + name = 'ERROR' | |
| 139 | + | |
| 140 | + | |
| 141 | +class Result_Suspicious(object): | |
| 142 | + exit_code = 20 | |
| 143 | + color = 'red' | |
| 144 | + name = 'SUSPICIOUS' | |
| 145 | + | |
| 146 | + | |
| 116 | 147 | class MacroRaptor(object): |
| 117 | 148 | """ |
| 118 | 149 | class to scan VBA macro code to detect if it is malicious |
| ... | ... | @@ -202,6 +233,9 @@ def main(): |
| 202 | 233 | if len(args) == 0: |
| 203 | 234 | print __doc__ |
| 204 | 235 | parser.print_help() |
| 236 | + print '\nAn exit code is returned based on the analysis result:' | |
| 237 | + for result in (Result_NoMacro, Result_NotMSOffice, Result_MacroOK, Result_Error, Result_Suspicious): | |
| 238 | + print ' - %d: %s' % (result.exit_code, result.name) | |
| 205 | 239 | sys.exit() |
| 206 | 240 | |
| 207 | 241 | # print banner with version |
| ... | ... | @@ -216,6 +250,8 @@ def main(): |
| 216 | 250 | header_row=['Result', 'Flags', 'Type', 'File'], |
| 217 | 251 | column_width=[10, 5, 4, 56]) |
| 218 | 252 | |
| 253 | + exitcode = -1 | |
| 254 | + global_result = None | |
| 219 | 255 | # TODO: handle errors in xglob, to continue processing the next files |
| 220 | 256 | for container, filename, data in xglob.iter_files(args, recursive=options.recursive, |
| 221 | 257 | zip_password=options.zip_password, zip_fname=options.zip_fname): |
| ... | ... | @@ -231,24 +267,24 @@ def main(): |
| 231 | 267 | # log.exception('Error when opening file %r' % full_name) |
| 232 | 268 | # continue |
| 233 | 269 | if isinstance(data, Exception): |
| 234 | - result = '* ERROR *' | |
| 235 | - result_color = 'yellow' | |
| 236 | - t.write_row([result, '', '', full_name], | |
| 237 | - colors=[result_color, None, None, None]) | |
| 270 | + result = Result_Error | |
| 271 | + t.write_row([result.name, '', '', full_name], | |
| 272 | + colors=[result.color, None, None, None]) | |
| 238 | 273 | t.write_row(['', '', '', str(data)], |
| 239 | - colors=[None, None, None, result_color]) | |
| 274 | + colors=[None, None, None, result.color]) | |
| 240 | 275 | else: |
| 276 | + filetype = '???' | |
| 241 | 277 | try: |
| 242 | 278 | vba_parser = olevba.VBA_Parser(filename=filename, data=data, container=container) |
| 243 | 279 | filetype = TYPE2TAG[vba_parser.type] |
| 244 | 280 | except Exception as e: |
| 245 | 281 | # log.error('Error when parsing VBA macros from file %r' % full_name) |
| 246 | - result = '* ERROR *' | |
| 247 | - result_color = 'yellow' | |
| 248 | - t.write_row([result, '', TYPE2TAG[vba_parser.type], full_name], | |
| 249 | - colors=[result_color, None, None, None]) | |
| 282 | + # TODO: distinguish actual errors from non-MSOffice files | |
| 283 | + result = Result_Error | |
| 284 | + t.write_row([result.name, '', filetype, full_name], | |
| 285 | + colors=[result.color, None, None, None]) | |
| 250 | 286 | t.write_row(['', '', '', str(e)], |
| 251 | - colors=[None, None, None, result_color]) | |
| 287 | + colors=[None, None, None, result.color]) | |
| 252 | 288 | continue |
| 253 | 289 | if vba_parser.detect_vba_macros(): |
| 254 | 290 | vba_code_all_modules = '' |
| ... | ... | @@ -257,33 +293,34 @@ def main(): |
| 257 | 293 | vba_code_all_modules += vba_code + '\n' |
| 258 | 294 | except Exception as e: |
| 259 | 295 | # log.error('Error when parsing VBA macros from file %r' % full_name) |
| 260 | - result = '* ERROR *' | |
| 261 | - result_color = 'yellow' | |
| 262 | - t.write_row([result, '', TYPE2TAG[vba_parser.type], full_name], | |
| 263 | - colors=[result_color, None, None, None]) | |
| 296 | + result = Result_Error | |
| 297 | + t.write_row([result.name, '', TYPE2TAG[vba_parser.type], full_name], | |
| 298 | + colors=[result.color, None, None, None]) | |
| 264 | 299 | t.write_row(['', '', '', str(e)], |
| 265 | - colors=[None, None, None, result_color]) | |
| 300 | + colors=[None, None, None, result.color]) | |
| 266 | 301 | continue |
| 267 | 302 | mraptor = MacroRaptor(vba_code_all_modules) |
| 268 | 303 | mraptor.scan() |
| 269 | 304 | if mraptor.suspicious: |
| 270 | - result = 'SUSPICIOUS' | |
| 271 | - result_color = 'red' | |
| 305 | + result = Result_Suspicious | |
| 272 | 306 | else: |
| 273 | - result = 'Macro OK' | |
| 274 | - result_color = 'cyan' | |
| 275 | - t.write_row([result, mraptor.get_flags(), filetype, full_name], | |
| 276 | - colors=[result_color, None, None, None]) | |
| 307 | + result = Result_MacroOK | |
| 308 | + t.write_row([result.name, mraptor.get_flags(), filetype, full_name], | |
| 309 | + colors=[result.color, None, None, None]) | |
| 277 | 310 | if mraptor.matches and options.show_matches: |
| 278 | 311 | t.write_row(['', '', '', 'Matches: %r' % mraptor.matches]) |
| 279 | 312 | else: |
| 280 | - result = 'No Macro' | |
| 281 | - result_color = 'green' | |
| 282 | - t.write_row([result, '', filetype, full_name], | |
| 283 | - colors=[result_color, None, None, None]) | |
| 313 | + result = Result_NoMacro | |
| 314 | + t.write_row([result.name, '', filetype, full_name], | |
| 315 | + colors=[result.color, None, None, None]) | |
| 316 | + if result.exit_code > exitcode: | |
| 317 | + global_result = result | |
| 318 | + exitcode = result.exit_code | |
| 284 | 319 | |
| 285 | 320 | print '' |
| 286 | 321 | print 'Flags: A=AutoExec, W=Write, X=Execute' |
| 322 | + print 'Exit code: %d - %s' % (exitcode, global_result.name) | |
| 323 | + sys.exit(exitcode) | |
| 287 | 324 | |
| 288 | 325 | if __name__ == '__main__': |
| 289 | 326 | main() | ... | ... |