Commit 209688eb7ca9580c3e8264865193f2a595d9972f
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 | 48 | # - extract files from OLE Package objects |
| 49 | 49 | # 2016-04-01 v0.04 PL: - fixed logging output to use stdout instead of stderr |
| 50 | 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 | 57 | # TODO: |
| ... | ... | @@ -60,7 +62,7 @@ __version__ = '0.45' |
| 60 | 62 | |
| 61 | 63 | #=== IMPORTS ================================================================= |
| 62 | 64 | |
| 63 | -import re, sys, string, binascii, logging, optparse | |
| 65 | +import re, os, sys, string, binascii, logging, optparse | |
| 64 | 66 | |
| 65 | 67 | from thirdparty.xglob import xglob |
| 66 | 68 | from oleobj import OleObject, OleNativeStream |
| ... | ... | @@ -280,7 +282,16 @@ def rtf_iter_objects (data, min_size=32): |
| 280 | 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 | 295 | # TODO: option to extract objects to files (false by default) |
| 285 | 296 | if data is None: |
| 286 | 297 | data = open(filename, 'rb').read() |
| ... | ... | @@ -288,7 +299,7 @@ def process_file(container, filename, data): |
| 288 | 299 | print 'File: %r - %d bytes' % (filename, len(data)) |
| 289 | 300 | for index, orig_len, objdata in rtf_iter_objects(data): |
| 290 | 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 | 303 | print 'saving object to file %s' % fname |
| 293 | 304 | open(fname, 'wb').write(objdata) |
| 294 | 305 | # TODO: check if all hex data is extracted properly |
| ... | ... | @@ -308,7 +319,8 @@ def process_file(container, filename, data): |
| 308 | 319 | ext = 'package' |
| 309 | 320 | else: |
| 310 | 321 | ext = 'bin' |
| 311 | - fname = '%s_object_%08X.%s' % (filename, index, ext) | |
| 322 | + | |
| 323 | + fname = '%s_object_%08X.%s' % (fname_prefix, index, ext) | |
| 312 | 324 | print 'saving to file %s' % fname |
| 313 | 325 | open(fname, 'wb').write(obj.data) |
| 314 | 326 | if obj.class_name.lower() == 'package': |
| ... | ... | @@ -318,9 +330,9 @@ def process_file(container, filename, data): |
| 318 | 330 | print 'Source path = %r' % opkg.src_path |
| 319 | 331 | print 'Temp path = %r' % opkg.temp_path |
| 320 | 332 | if opkg.filename: |
| 321 | - fname = '%s_%s' % (filename, opkg.filename) | |
| 333 | + fname = '%s_%s' % (fname_prefix, opkg.filename) | |
| 322 | 334 | else: |
| 323 | - fname = '%s_object_%08X.noname' % (filename, index) | |
| 335 | + fname = '%s_object_%08X.noname' % (fname_prefix, index) | |
| 324 | 336 | print 'saving to file %s' % fname |
| 325 | 337 | open(fname, 'wb').write(opkg.data) |
| 326 | 338 | except: |
| ... | ... | @@ -354,6 +366,8 @@ if __name__ == '__main__': |
| 354 | 366 | # help='export results to a CSV file') |
| 355 | 367 | parser.add_option("-r", action="store_true", dest="recursive", |
| 356 | 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 | 371 | parser.add_option("-z", "--zip", dest='zip_password', type='str', default=None, |
| 358 | 372 | help='if the file is a zip archive, open first file from it, using the provided password (requires Python 2.6+)') |
| 359 | 373 | parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*', |
| ... | ... | @@ -384,7 +398,7 @@ if __name__ == '__main__': |
| 384 | 398 | # ignore directory names stored in zip files: |
| 385 | 399 | if container and filename.endswith('/'): |
| 386 | 400 | continue |
| 387 | - process_file(container, filename, data) | |
| 401 | + process_file(container, filename, data, options.output_dir) | |
| 388 | 402 | |
| 389 | 403 | |
| 390 | 404 | ... | ... |