@@ -276,6 +276,11 @@ def setup_ui(self):
276276 self .image_size_label = ttk .Label (frame , text = "0 MB" )
277277 self .image_size_label .place (x = 120 , y = 210 )
278278
279+ # Add a progress bar for file processing
280+ self .progress_bar = ttk .Progressbar (
281+ frame , orient = "horizontal" , length = 260 , mode = "determinate" )
282+ self .progress_bar .place (x = 120 , y = 240 )
283+
279284 large_font = ("Arial" , 10 )
280285
281286 ttk .Label (frame , text = "MD5 Hash:" ).place (x = 10 , y = 240 )
@@ -318,52 +323,61 @@ def update_zip_codes(self, event=None):
318323 def browse_image_file (self ):
319324 """Browse for an image file and ensure paths are handled correctly."""
320325 try :
321- if not self .winfo_exists (): # Check if widget still exists
322- return
323-
324326 file_path = filedialog .askopenfilename (
325- title = "Select an Image File" ,
326- filetypes = [ ("Image Files" , "*.img *.jpg *.png *.bmp *.tiff " )]
327+ filetypes = [
328+ ("Image Files" , "*.img;*.dd;*.iso;*.bin;*.001;*.raw" ), ( "All Files" , "*.* " )]
327329 )
328-
329330 if file_path :
330- # Store the absolute path
331- abs_path = os .path .abspath (file_path )
332-
333331 self .image_file_entry .config (state = "normal" )
334332 self .image_file_entry .delete (0 , tk .END )
335- self .image_file_entry .insert (0 , abs_path )
333+ self .image_file_entry .insert (0 , file_path )
336334 self .image_file_entry .config (state = "readonly" )
337-
338- self . update_image_size ( abs_path )
339- self .calculate_hashes ( abs_path )
340-
335+ self . update_image_size ( file_path )
336+ # Calculate hashes in a thread to keep UI responsive
337+ threading . Thread ( target = self .calculate_hashes ,
338+ args = ( file_path ,), daemon = True ). start ()
341339 except Exception as e :
342- messagebox .showerror ("Error" , f"Failed to access file: { str (e )} " )
340+ messagebox .showerror ("Error" , f"Failed to select file: { str (e )} " )
343341
344342 def update_image_size (self , file_path ):
345343 """Update the image size label with the size of the selected file."""
346344 try :
347- file_size = os .path .getsize (file_path )
348- file_size_mb = file_size / (1024 * 1024 )
349- self .image_size_label .config (text = f"{ file_size_mb :.2f} MB" )
345+ size_bytes = os .path .getsize (file_path )
346+ size_mb = size_bytes / (1024 * 1024 )
347+ self .image_size_label .config (text = f"{ size_mb :.2f} MB" )
350348 except Exception as e :
351- self .image_size_label .config (text = "0 MB" )
352- print (f"Error updating image size: { e } " )
349+ self .image_size_label .config (text = "Error" )
353350
354351 def calculate_hashes (self , file_path ):
355- """Calculate and display the MD5 and SHA-256 hashes of the selected file."""
352+ """Calculate and display the MD5 and SHA-256 hashes of the selected file, updating the progress bar ."""
356353 try :
354+ md5 = hashlib .md5 ()
355+ sha256 = hashlib .sha256 ()
356+ file_size = os .path .getsize (file_path )
357+ chunk_size = 1024 * 1024 # 1MB
358+ read_bytes = 0
359+
360+ self .progress_bar ["value" ] = 0
361+ self .progress_bar ["maximum" ] = file_size
362+
357363 with open (file_path , "rb" ) as f :
358- file_data = f .read ()
359- md5_hash = hashlib .md5 (file_data ).hexdigest ()
360- sha256_hash = hashlib .sha256 (file_data ).hexdigest ()
361- self .md5_hash_label .config (text = md5_hash )
362- self .sha256_hash_label .config (text = sha256_hash )
364+ while True :
365+ chunk = f .read (chunk_size )
366+ if not chunk :
367+ break
368+ md5 .update (chunk )
369+ sha256 .update (chunk )
370+ read_bytes += len (chunk )
371+ self .progress_bar ["value" ] = read_bytes
372+ self .progress_bar .update_idletasks ()
373+
374+ self .md5_hash_label .config (text = md5 .hexdigest ())
375+ self .sha256_hash_label .config (text = sha256 .hexdigest ())
376+ self .progress_bar ["value" ] = file_size # Ensure bar is full at end
363377 except Exception as e :
364- self .md5_hash_label .config (text = "" )
365- self .sha256_hash_label .config (text = "" )
366- print ( f"Error calculating hashes: { e } " )
378+ self .md5_hash_label .config (text = "Error " )
379+ self .sha256_hash_label .config (text = "Error " )
380+ self . progress_bar [ "value" ] = 0
367381
368382 def submit_form (self ):
369383 """Submit the form and log the chain of custody."""
@@ -690,21 +704,46 @@ def read_chain_of_custody():
690704 return log_file .read ()
691705
692706
693- def create_disk_image (disk_device , output_image , disk_size_gb , progress_callback , progress_bar , mb_label , speed_label , time_label ):
694- """Create a forensic disk image using dd."""
707+ def create_disk_image (disk_device , output_image , progress_callback , progress_bar , mb_label , speed_label , time_label ):
708+ """Create a forensic disk image using dd, auto-detecting device size ."""
695709 try :
696-
697710 log_chain_of_custody ("Disk Imaging Started" ,
698711 f"Device: { disk_device } , Output: { output_image } " )
712+
713+ # --- Get device size in bytes ---
714+ size_bytes = 0
715+ try :
716+ # Use diskutil info for macOS
717+ output = subprocess .check_output (
718+ ["diskutil" , "info" , disk_device ], text = True )
719+ print ("DISKUTIL OUTPUT:\n " , output ) # Add this line for debugging
720+ for line in output .splitlines ():
721+ if "disk size:" in line .lower () and "bytes" in line .lower ():
722+ # Find the part like (15728640000 Bytes)
723+ parts = line .split ("(" )
724+ for part in parts :
725+ if "bytes" in part .lower ():
726+ size_str = part .split (" " )[0 ].replace ("," , "" )
727+ size_bytes = int (size_str )
728+ break
729+ if size_bytes > 0 :
730+ break
731+ except Exception as e :
732+ progress_callback (f"Could not determine device size: { e } " )
733+ return
734+
735+ if size_bytes == 0 :
736+ progress_callback ("Could not determine device size." )
737+ return
738+
739+ total_size_mb = size_bytes / (1024 * 1024 )
740+
699741 command = ["sudo" , "dd" , f"if={ disk_device } " ,
700742 f"of={ output_image } " , "bs=4M" , "status=progress" ]
701743 process = subprocess .Popen (
702744 command , stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True )
703745
704746 start_time = datetime .now ()
705- total_size_bytes = disk_size_gb * 1024 * 1024 * 1024
706- total_size_mb = disk_size_gb * 1024
707-
708747 progress = 0
709748
710749 while True :
@@ -717,7 +756,7 @@ def create_disk_image(disk_device, output_image, disk_size_gb, progress_callback
717756 copied_bytes = int (match .group (1 ))
718757 copied_mb = copied_bytes / (1024 * 1024 )
719758
720- progress = (copied_bytes / total_size_bytes ) * 100
759+ progress = (copied_bytes / size_bytes ) * 100
721760 progress_bar ["value" ] = progress
722761
723762 elapsed_time = datetime .now () - start_time
@@ -732,7 +771,7 @@ def create_disk_image(disk_device, output_image, disk_size_gb, progress_callback
732771
733772 if copied_bytes > 0 and elapsed_time .total_seconds () > 0 :
734773 remaining_time = (elapsed_time .total_seconds (
735- ) / copied_bytes ) * (total_size_bytes - copied_bytes )
774+ ) / copied_bytes ) * (size_bytes - copied_bytes )
736775 time_label .config (
737776 text = f"Estimated Time Remaining: { str (timedelta (seconds = int (remaining_time )))} " )
738777 else :
@@ -1198,11 +1237,12 @@ def setup_disk_imaging_tab(self):
11981237 highlightthickness = 0 )
11991238 browse_btn .place (x = 700 , y = 350 )
12001239
1201- tk .Label (self .tab1 , text = "Disk Size (GB):" ,
1202- font = label_font , bg = "#530a0a" , fg = label_color ).place (x = 330 , y = 400 )
1203-
1204- self .disk_size_entry = tk .Entry (self .tab1 , width = 20 , font = label_font )
1205- self .disk_size_entry .place (x = 480 , y = 400 )
1240+ # --- REMOVE Disk Size (GB) FIELD ---
1241+ # tk.Label(self.tab1, text="Disk Size (GB):",
1242+ # font=label_font, bg="#530a0a", fg=label_color).place(x=330, y=400)
1243+ # self.disk_size_entry = tk.Entry(self.tab1, width=20, font=label_font)
1244+ # self.disk_size_entry.place(x=480, y=400)
1245+ # ------------------------------------
12061246
12071247 self .progress_label = tk .Label (self .tab1 , text = "" ,
12081248 font = label_font , bg = "#530a0a" , fg = label_color )
@@ -1243,17 +1283,10 @@ def start_disk_imaging(self):
12431283 """Start the disk imaging process in a background thread."""
12441284 disk_device = self .drive_var .get ()
12451285 output_image = self .output_image_entry .get ()
1246- disk_size_gb = self .disk_size_entry .get ()
12471286
1248- if not disk_device or not output_image or not disk_size_gb :
1287+ if not disk_device or not output_image :
12491288 messagebox .showerror (
1250- "Error" , "Please select a flash drive, provide an output image path, and enter the disk size." )
1251- return
1252-
1253- try :
1254- disk_size_gb = float (disk_size_gb )
1255- except ValueError :
1256- messagebox .showerror ("Error" , "Disk size must be a valid number." )
1289+ "Error" , "Please select a flash drive and provide an output image path." )
12571290 return
12581291
12591292 self .mb_label .place (x = 460 , y = 450 )
@@ -1268,7 +1301,7 @@ def start_disk_imaging(self):
12681301
12691302 imaging_thread = threading .Thread (
12701303 target = create_disk_image ,
1271- args = (disk_device , output_image , disk_size_gb , self .update_progress ,
1304+ args = (disk_device , output_image , self .update_progress ,
12721305 self .progress_bar , self .mb_label , self .speed_label , self .time_label ),
12731306 daemon = True
12741307 )
0 commit comments