Commit 209688eb7ca9580c3e8264865193f2a595d9972f

Authored by Philippe Lagadec
1 parent 31ce789a

rtfobj: added option -d to set the output directory (contribution by Thomas Jarosch)

Showing 1 changed file with 22 additions and 8 deletions
oletools/rtfobj.py
@@ -48,8 +48,10 @@ http://www.decalage.info/python/oletools @@ -48,8 +48,10 @@ http://www.decalage.info/python/oletools
48 # - extract files from OLE Package objects 48 # - extract files from OLE Package objects
49 # 2016-04-01 v0.04 PL: - fixed logging output to use stdout instead of stderr 49 # 2016-04-01 v0.04 PL: - fixed logging output to use stdout instead of stderr
50 # 2016-04-07 v0.45 PL: - improved parsing to handle some malware tricks 50 # 2016-04-07 v0.45 PL: - improved parsing to handle some malware tricks
  51 +# 2016-05-06 v0.47 TJ: - added option -d to set the output directory
  52 +# (contribution by Thomas Jarosch)
51 53
52 -__version__ = '0.45' 54 +__version__ = '0.47'
53 55
54 #------------------------------------------------------------------------------ 56 #------------------------------------------------------------------------------
55 # TODO: 57 # TODO:
@@ -60,7 +62,7 @@ __version__ = '0.45' @@ -60,7 +62,7 @@ __version__ = '0.45'
60 62
61 #=== IMPORTS ================================================================= 63 #=== IMPORTS =================================================================
62 64
63 -import re, sys, string, binascii, logging, optparse 65 +import re, os, sys, string, binascii, logging, optparse
64 66
65 from thirdparty.xglob import xglob 67 from thirdparty.xglob import xglob
66 from oleobj import OleObject, OleNativeStream 68 from oleobj import OleObject, OleNativeStream
@@ -280,7 +282,16 @@ def rtf_iter_objects (data, min_size=32): @@ -280,7 +282,16 @@ def rtf_iter_objects (data, min_size=32):
280 match = re_hexblock.search(data, pos=current) 282 match = re_hexblock.search(data, pos=current)
281 283
282 284
283 -def process_file(container, filename, data): 285 +def process_file(container, filename, data, output_dir=None):
  286 + if output_dir:
  287 + if not os.path.isdir(output_dir):
  288 + log.info('creating output directory %s' % output_dir)
  289 + os.mkdir(output_dir)
  290 +
  291 + fname_prefix = os.path.join(output_dir, os.path.basename(filename))
  292 + else:
  293 + fname_prefix = filename
  294 +
284 # TODO: option to extract objects to files (false by default) 295 # TODO: option to extract objects to files (false by default)
285 if data is None: 296 if data is None:
286 data = open(filename, 'rb').read() 297 data = open(filename, 'rb').read()
@@ -288,7 +299,7 @@ def process_file(container, filename, data): @@ -288,7 +299,7 @@ def process_file(container, filename, data):
288 print 'File: %r - %d bytes' % (filename, len(data)) 299 print 'File: %r - %d bytes' % (filename, len(data))
289 for index, orig_len, objdata in rtf_iter_objects(data): 300 for index, orig_len, objdata in rtf_iter_objects(data):
290 print 'found object size %d at index %08X - end %08X' % (len(objdata), index, index+orig_len) 301 print 'found object size %d at index %08X - end %08X' % (len(objdata), index, index+orig_len)
291 - fname = '%s_object_%08X.raw' % (filename, index) 302 + fname = '%s_object_%08X.raw' % (fname_prefix, index)
292 print 'saving object to file %s' % fname 303 print 'saving object to file %s' % fname
293 open(fname, 'wb').write(objdata) 304 open(fname, 'wb').write(objdata)
294 # TODO: check if all hex data is extracted properly 305 # TODO: check if all hex data is extracted properly
@@ -308,7 +319,8 @@ def process_file(container, filename, data): @@ -308,7 +319,8 @@ def process_file(container, filename, data):
308 ext = 'package' 319 ext = 'package'
309 else: 320 else:
310 ext = 'bin' 321 ext = 'bin'
311 - fname = '%s_object_%08X.%s' % (filename, index, ext) 322 +
  323 + fname = '%s_object_%08X.%s' % (fname_prefix, index, ext)
312 print 'saving to file %s' % fname 324 print 'saving to file %s' % fname
313 open(fname, 'wb').write(obj.data) 325 open(fname, 'wb').write(obj.data)
314 if obj.class_name.lower() == 'package': 326 if obj.class_name.lower() == 'package':
@@ -318,9 +330,9 @@ def process_file(container, filename, data): @@ -318,9 +330,9 @@ def process_file(container, filename, data):
318 print 'Source path = %r' % opkg.src_path 330 print 'Source path = %r' % opkg.src_path
319 print 'Temp path = %r' % opkg.temp_path 331 print 'Temp path = %r' % opkg.temp_path
320 if opkg.filename: 332 if opkg.filename:
321 - fname = '%s_%s' % (filename, opkg.filename) 333 + fname = '%s_%s' % (fname_prefix, opkg.filename)
322 else: 334 else:
323 - fname = '%s_object_%08X.noname' % (filename, index) 335 + fname = '%s_object_%08X.noname' % (fname_prefix, index)
324 print 'saving to file %s' % fname 336 print 'saving to file %s' % fname
325 open(fname, 'wb').write(opkg.data) 337 open(fname, 'wb').write(opkg.data)
326 except: 338 except:
@@ -354,6 +366,8 @@ if __name__ == '__main__': @@ -354,6 +366,8 @@ if __name__ == '__main__':
354 # help='export results to a CSV file') 366 # help='export results to a CSV file')
355 parser.add_option("-r", action="store_true", dest="recursive", 367 parser.add_option("-r", action="store_true", dest="recursive",
356 help='find files recursively in subdirectories.') 368 help='find files recursively in subdirectories.')
  369 + parser.add_option("-d", type="str", dest="output_dir",
  370 + help='use specified directory to output files.', default=None)
357 parser.add_option("-z", "--zip", dest='zip_password', type='str', default=None, 371 parser.add_option("-z", "--zip", dest='zip_password', type='str', default=None,
358 help='if the file is a zip archive, open first file from it, using the provided password (requires Python 2.6+)') 372 help='if the file is a zip archive, open first file from it, using the provided password (requires Python 2.6+)')
359 parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*', 373 parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*',
@@ -384,7 +398,7 @@ if __name__ == '__main__': @@ -384,7 +398,7 @@ if __name__ == '__main__':
384 # ignore directory names stored in zip files: 398 # ignore directory names stored in zip files:
385 if container and filename.endswith('/'): 399 if container and filename.endswith('/'):
386 continue 400 continue
387 - process_file(container, filename, data) 401 + process_file(container, filename, data, options.output_dir)
388 402
389 403
390 404