@@ -41,7 +41,7 @@ def time_to_int(str_time):
4141 dt = time .mktime (datetime .datetime .strptime (str_time , "%Y-%m-%dT%H:%M:%S" ).timetuple ())
4242 return dt
4343
44- def write_to_spreadsheet (disk_result , spreadsheet_path ):
44+ def write_to_spreadsheet (disk_result , spreadsheet_path , exportall ):
4545 """append info for current disk to analysis CSV"""
4646
4747 # open description spreadsheet
@@ -52,7 +52,6 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
5252 number_files = 0
5353 total_bytes = 0
5454 mtimes = []
55- atimes = []
5655 ctimes = []
5756 crtimes = []
5857
@@ -71,6 +70,12 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
7170 # skip directories and links
7271 if obj .name_type != "r" :
7372 continue
73+
74+ # skip unallocated if args.exportall is False
75+ if exportall == False :
76+ if obj .unalloc :
77+ if obj .unalloc == 1 :
78+ continue
7479
7580 # gather info
7681 number_files += 1
@@ -82,13 +87,6 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
8287 except :
8388 pass
8489
85- try :
86- atime = obj .atime
87- atime = str (atime )
88- atimes .append (atime )
89- except :
90- pass
91-
9290 try :
9391 ctime = obj .ctime
9492 ctime = str (ctime )
@@ -106,7 +104,7 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
106104 total_bytes += obj .filesize
107105
108106 # filter 'None' values from date lists
109- for date_list in mtimes , atimes , ctimes , crtimes :
107+ for date_list in mtimes , ctimes , crtimes :
110108 while 'None' in date_list :
111109 date_list .remove ('None' )
112110
@@ -122,8 +120,6 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
122120 # determine earliest and latest MAC dates from lists
123121 date_earliest_m = ""
124122 date_latest_m = ""
125- date_earliest_a = ""
126- date_latest_a = ""
127123 date_earliest_c = ""
128124 date_latest_c = ""
129125 date_earliest_cr = ""
@@ -133,9 +129,6 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
133129 if mtimes :
134130 date_earliest_m = min (mtimes )
135131 date_latest_m = max (mtimes )
136- if atimes :
137- date_earliest_a = min (atimes )
138- date_latest_a = max (atimes )
139132 if ctimes :
140133 date_earliest_c = min (ctimes )
141134 date_latest_c = max (ctimes )
@@ -144,7 +137,6 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
144137 date_latest_cr = max (crtimes )
145138
146139 # determine which set of dates to use (logic: use set with earliest start date)
147- use_atimes = False
148140 use_ctimes = False
149141 use_crtimes = False
150142
@@ -153,29 +145,19 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
153145 date_latest_m = "N/A"
154146 date_to_use = date_earliest_m # default to date modified
155147
156- if date_earliest_a :
157- if date_earliest_a < date_to_use :
158- date_to_use = date_earliest_a
159- use_atimes = True
160148 if date_earliest_c :
161149 if date_earliest_c < date_to_use :
162150 date_to_use = date_earliest_c
163- use_atimes = False
164151 use_ctimes = True
165152 if date_earliest_cr :
166153 if date_earliest_cr < date_to_use :
167154 date_to_use = date_earliest_cr
168- use_atimes = False
169155 use_ctimes = False
170156 use_crtimes = True
171157
172158 # store date_earliest and date_latest values based on datetype & record datetype
173159 date_type = 'Modified'
174- if use_atimes == True :
175- date_earliest = date_earliest_a [:10 ]
176- date_latest = date_latest_a [:10 ]
177- date_type = 'Accessed'
178- elif use_ctimes == True :
160+ if use_ctimes == True :
179161 date_earliest = date_earliest_c [:10 ]
180162 date_latest = date_latest_c [:10 ]
181163 date_type = 'Created'
@@ -250,7 +232,9 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
250232
251233# parse arguments
252234parser = argparse .ArgumentParser ()
253- parser .add_argument ("-f" , "--forensic" , help = "Use fiwalk and tsk_recover" , action = "store_true" )
235+ parser .add_argument ("-e" , "--exportall" , help = "Export all (not only allocated) with tsk_recover" , action = "store_true" )
236+ parser .add_argument ("-k" , "--keepfiles" , help = "Retain exported logical files from each disk" , action = "store_true" )
237+ parser .add_argument ("-r" , "--resforks" , help = "Export AppleDouble resource forks from HFS-formatted disks" , action = "store_true" )
254238parser .add_argument ("source" , help = "Path to folder containing disk images" )
255239parser .add_argument ("destination" , help = "Output destination" )
256240args = parser .parse_args ()
@@ -262,8 +246,9 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
262246if not os .path .exists (destination ):
263247 os .makedirs (destination )
264248diskimages_dir = os .path .join (destination , 'diskimages' )
249+ files_dir = os .path .join (destination , 'files' )
265250results_dir = os .path .join (destination , 'reports' )
266- for new_dir in diskimages_dir , results_dir :
251+ for new_dir in diskimages_dir , files_dir , results_dir :
267252 os .makedirs (new_dir )
268253
269254# make list for unanalyzed disks
@@ -334,16 +319,23 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
334319 try :
335320 subprocess .check_output (['fiwalk' , '-X' , fiwalk_file , diskimage ])
336321 except subprocess .CalledProcessError as e :
337- logandprint ('ERROR: Fiwalk could not create DFXML for disk. STDERR: %s' % (e .output ))
322+ print ('ERROR: Fiwalk could not create DFXML for disk. STDERR: %s' % (e .output ))
338323
339324 # carve files
340- temp_dir = os .path .join (disk_dir , 'temp' )
341- if not os .path .exists (temp_dir ):
342- os .makedirs (temp_dir )
343- try :
344- subprocess .check_output (['tsk_recover' , '-a' , diskimage , temp_dir ])
345- except subprocess .CalledProcessError as e :
346- logandprint ('ERROR: tsk_recover could not carve allocated files from disk. STDERR: %s' % (e .output ))
325+ disk_files_dir = os .path .join (files_dir , file )
326+ if not os .path .exists (disk_files_dir ):
327+ os .makedirs (disk_files_dir )
328+ # carve allocated or all files depending on option selected
329+ if args .exportall == True :
330+ try :
331+ subprocess .check_output (['tsk_recover' , '-e' , diskimage , disk_files_dir ])
332+ except subprocess .CalledProcessError as e :
333+ print ('ERROR: tsk_recover could not carve all files from disk. STDERR: %s' % (e .output ))
334+ else :
335+ try :
336+ subprocess .check_output (['tsk_recover' , '-a' , diskimage , disk_files_dir ])
337+ except subprocess .CalledProcessError as e :
338+ print ('ERROR: tsk_recover could not carve allocated files from disk. STDERR: %s' % (e .output ))
347339
348340 # rewrite last modified dates of carved files based on values in DFXML
349341 for (event , obj ) in Objects .iterparse (fiwalk_file ):
@@ -385,15 +377,16 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
385377 continue
386378
387379 # rewrite last modified date of corresponding file in objects/files
388- exported_filepath = os .path .join (temp_dir , dfxml_filename )
380+ exported_filepath = os .path .join (disk_files_dir , dfxml_filename )
389381 if os .path .isfile (exported_filepath ):
390382 os .utime (exported_filepath , (dfxml_filedate , dfxml_filedate ))
391383
392384 # run brunnhilde
393- subprocess .call ("brunnhilde.py -zwb '%s' '%s' brunnhilde" % (temp_dir , disk_dir ), shell = True )
385+ subprocess .call ("brunnhilde.py -zwb '%s' '%s' brunnhilde" % (disk_files_dir , disk_dir ), shell = True )
394386
395- # remove tmpdir
396- shutil .rmtree (temp_dir )
387+ # remove disk_files_dir unless keepfiles option selected
388+ if args .keepfiles == False :
389+ shutil .rmtree (disk_files_dir )
397390
398391 elif ('hfs' in disk_fs .lower ()) and ('hfs+' not in disk_fs .lower ()):
399392 # mount disk image
@@ -404,14 +397,32 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
404397 try :
405398 subprocess .call ("cd /mnt/diskid/ && python3 /usr/share/ccatools/diskimageprocessor/walk_to_dfxml.py > '%s'" % (dfxml_file ), shell = True )
406399 except :
407- logandprint ('ERROR: walk_to_dfxml.py unable to generate DFXML for disk %s' % (diskimage ))
400+ print ('ERROR: walk_to_dfxml.py unable to generate DFXML for disk %s' % (diskimage ))
408401
409402 # run brunnhilde
410403 subprocess .call ("brunnhilde.py -zwb /mnt/diskid/ '%s' brunnhilde" % (disk_dir ), shell = True )
411404
412405 # unmount disk image
413406 subprocess .call ('sudo umount /mnt/diskid' , shell = True )
414407
408+ # export files to disk_files_dir if keepfiles selected
409+ if args .keepfiles == True :
410+ disk_files_dir = os .path .join (files_dir , file )
411+ if not os .path .exists (disk_files_dir ):
412+ os .makedirs (disk_files_dir )
413+ # carve with or without resource forks depending on option selected
414+ if args .resforks == True :
415+ try :
416+ subprocess .check_output (['bash' , '/usr/share/hfsexplorer/bin/unhfs' , '-v' , '-resforks' , 'APPLEDOUBLE' , '-o' , disk_files_dir , diskimage ])
417+ except subprocess .CalledProcessError as e :
418+ print ('ERROR: HFS Explorer could not carve the following files from image: %s' % (e .output ))
419+ else :
420+ try :
421+ subprocess .check_output (['bash' , '/usr/share/hfsexplorer/bin/unhfs' , '-v' , '-o' , disk_files_dir , diskimage ])
422+ except subprocess .CalledProcessError as e :
423+ print ('ERROR: HFS Explorer could not carve the following files from image: %s' % (e .output ))
424+
425+
415426 elif 'udf' in disk_fs .lower ():
416427 # mount image
417428 subprocess .call ("sudo mount -t udf -o loop '%s' /mnt/diskid/" % (diskimage ), shell = True )
@@ -421,32 +432,34 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
421432 try :
422433 subprocess .call ("cd /mnt/diskid/ && python3 /usr/share/ccatools/diskimageprocessor/walk_to_dfxml.py > '%s'" % (dfxml_file ), shell = True )
423434 except :
424- logandprint ('ERROR: walk_to_dfxml.py unable to generate DFXML for disk %s' % (diskimage ))
435+ print ('ERROR: walk_to_dfxml.py unable to generate DFXML for disk %s' % (diskimage ))
425436
426437 # write files to tempdir
427- temp_dir = os .path .join (disk_dir , 'temp' )
428- shutil .copytree ('/mnt/diskid/' , temp_dir , symlinks = False , ignore = None )
438+ disk_files_dir = os .path .join (files_dir , file )
439+ shutil .copytree ('/mnt/diskid/' , disk_files_dir , symlinks = False , ignore = None )
429440
430- # change file permissions in temp_dir
431- subprocess .call ("find '%s' -type d -exec chmod 755 {} \;" % (temp_dir ), shell = True )
432- subprocess .call ("find '%s' -type f -exec chmod 644 {} \;" % (temp_dir ), shell = True )
441+ # change file permissions in disk_files_dir
442+ subprocess .call ("find '%s' -type d -exec chmod 755 {} \;" % (disk_files_dir ), shell = True )
443+ subprocess .call ("find '%s' -type f -exec chmod 644 {} \;" % (disk_files_dir ), shell = True )
433444
434445 # unmount disk image
435446 subprocess .call ('sudo umount /mnt/diskid' , shell = True )
436447
437448 # run brunnhilde
438- subprocess .call ("brunnhilde.py -zwb '%s' '%s' brunnhilde" % (temp_dir , disk_dir ), shell = True )
449+ subprocess .call ("brunnhilde.py -zwb '%s' '%s' brunnhilde" % (disk_files_dir , disk_dir ), shell = True )
439450
440- # delete tempdir
441- shutil .rmtree (temp_dir )
451+ # remove disk_files_dir unless keepfiles option selected
452+ if args .keepfiles == False :
453+ shutil .rmtree (disk_files_dir )
442454
443455 else :
444456 # add disk to unanalyzed list
445- unanalyzed .append (diskimage )
446-
457+ unanalyzed .append (diskimage )
447458
448- # delete disk images
459+ # delete temp directories
449460shutil .rmtree (diskimages_dir )
461+ if args .keepfiles == False :
462+ shutil .rmtree (files_dir )
450463
451464# create analysis spreadsheet
452465spreadsheet_path = os .path .join (destination , 'analysis.csv' )
@@ -462,7 +475,7 @@ def write_to_spreadsheet(disk_result, spreadsheet_path):
462475# add info to description spreadsheet
463476for item in sorted (os .listdir (results_dir )):
464477 disk_result = os .path .join (results_dir , item )
465- write_to_spreadsheet (disk_result , spreadsheet_path )
478+ write_to_spreadsheet (disk_result , spreadsheet_path , args . exportall )
466479
467480# write closing message
468481if unanalyzed :
0 commit comments