@@ -201,14 +201,14 @@ def extract_and_save_pmkids(self):
201201 bssid = pmkid_data .get ('bssid' , '' ).upper ()
202202 essid = pmkid_data .get ('essid' , '' )
203203 pmkid_hash = pmkid_data .get ('hash' , '' )
204-
204+
205205 # Skip if we already have this BSSID
206206 if bssid in self .captured_pmkids :
207207 continue
208-
208+
209209 # Save the PMKID hash to file
210210 pmkid_file = self .save_pmkid_hash (bssid , essid , pmkid_hash )
211-
211+
212212 if pmkid_file :
213213 # Add to captured_pmkids dictionary
214214 self .captured_pmkids [bssid ] = {
@@ -218,130 +218,130 @@ def extract_and_save_pmkids(self):
218218 'captured_at' : time .time ()
219219 }
220220 new_pmkids += 1
221-
221+
222222 # Notify TUI if available
223223 if self .tui_view :
224224 self .tui_view .add_pmkid_captured (essid , bssid )
225-
225+
226226 # Update statistics
227227 self .statistics ['networks_detected' ] = len (pmkids )
228228 self .statistics ['pmkids_captured' ] = len (self .captured_pmkids )
229229 self .statistics ['last_extraction' ] = time .time ()
230-
230+
231231 # Update TUI or display in classic mode
232232 if self .tui_view :
233233 # Update TUI view with new statistics
234234 self .update_tui_statistics ()
235235 elif new_pmkids > 0 :
236236 # Classic mode - only show if new PMKIDs captured
237237 Color .pl ('{+} {G}Captured %d new PMKID(s)!{W}' % new_pmkids )
238-
238+
239239 except Exception as e :
240240 error_msg = f'Error during hash extraction: { str (e )} '
241241 if self .tui_view :
242242 self .tui_view .add_log (f'[red]✗[/red] { error_msg } ' )
243243 else :
244244 Color .pl ('{!} {R}%s{W}' % error_msg )
245-
245+
246246 if Configuration .verbose > 0 :
247247 import traceback
248248 if self .tui_view :
249249 self .tui_view .add_log (traceback .format_exc ())
250250 else :
251251 Color .pl ('{!} {R}%s{W}' % traceback .format_exc ())
252-
252+
253253 def save_pmkid_hash (self , bssid , essid , pmkid_hash ):
254254 """
255255 Save a single PMKID hash to file.
256-
256+
257257 Generates filename with format: pmkid_{essid}_{bssid}_{timestamp}.22000
258258 Checks for existing file with same BSSID to prevent duplicates.
259259 Saves hash to Configuration.wpa_handshake_dir.
260-
260+
261261 Args:
262262 bssid (str): Target BSSID
263263 essid (str): Target ESSID
264264 pmkid_hash (str): PMKID hash in .22000 format
265-
265+
266266 Returns:
267267 str: File path of saved hash, or None if save failed
268268 """
269269 # Create handshake directory if it doesn't exist
270270 if not os .path .exists (Configuration .wpa_handshake_dir ):
271271 os .makedirs (Configuration .wpa_handshake_dir )
272-
272+
273273 # Generate filesystem-safe filename
274274 essid_safe = re .sub ('[^a-zA-Z0-9]' , '' , essid ) if essid else 'hidden'
275275 bssid_safe = bssid .replace (':' , '-' )
276276 timestamp = time .strftime ('%Y-%m-%dT%H-%M-%S' )
277-
277+
278278 filename = f'pmkid_{ essid_safe } _{ bssid_safe } _{ timestamp } .22000'
279279 pmkid_file = os .path .join (Configuration .wpa_handshake_dir , filename )
280-
280+
281281 # Check for existing file with same BSSID to prevent duplicates
282282 # This is a simple check - more sophisticated duplicate detection
283283 # could be added by checking file contents
284284 import glob
285285 pattern = os .path .join (Configuration .wpa_handshake_dir , f'pmkid_*_{ bssid_safe } _*.22000' )
286286 existing_files = glob .glob (pattern )
287-
287+
288288 if existing_files :
289289 # Already have a PMKID for this BSSID
290290 if Configuration .verbose > 1 :
291291 Color .pl ('{+} {D}Skipping duplicate PMKID for {C}%s{W}' % bssid )
292292 return None
293-
293+
294294 try :
295295 # Save hash to file
296296 with open (pmkid_file , 'w' ) as f :
297297 f .write (pmkid_hash )
298298 f .write ('\n ' )
299-
299+
300300 if self .tui_view :
301301 # TUI mode - log is added by add_pmkid_captured in extract_and_save_pmkids
302302 pass
303303 else :
304304 # Classic mode
305305 Color .pl ('{+} {G}Saved PMKID:{W} {C}%s{W} ({C}%s{W})' % (essid if essid else '<hidden>' , bssid ))
306306 Color .pl ('{+} File: {C}%s{W}' % pmkid_file )
307-
307+
308308 return pmkid_file
309-
309+
310310 except Exception as e :
311311 error_msg = f'Error saving PMKID: { str (e )} '
312312 if self .tui_view :
313313 self .tui_view .add_log (f'[red]✗[/red] { error_msg } ' )
314314 else :
315315 Color .pl ('{!} {R}%s{W}' % error_msg )
316316 return None
317-
317+
318318 def update_tui_statistics (self ):
319319 """
320320 Update TUI view with current statistics.
321-
321+
322322 Called periodically to refresh the TUI display with latest capture data.
323323 """
324324 if not self .tui_view :
325325 return
326-
326+
327327 # Calculate capture file size
328328 if os .path .exists (self .pcapng_file ):
329329 size_bytes = os .path .getsize (self .pcapng_file )
330330 else :
331331 size_bytes = 0
332-
332+
333333 # Update TUI view
334334 self .tui_view .update_capture_status (
335335 networks_detected = self .statistics ['networks_detected' ],
336336 pmkids_captured = self .statistics ['pmkids_captured' ],
337337 capture_file_size = size_bytes ,
338338 last_extraction = self .statistics .get ('last_extraction' )
339339 )
340-
340+
341341 def display_statistics (self ):
342342 """
343343 Display real-time capture statistics.
344-
344+
345345 Shows networks detected, PMKIDs captured, capture duration, and file size.
346346 Updates display periodically without clearing screen.
347347 Uses Color class for formatted output in classic mode.
@@ -350,14 +350,14 @@ def display_statistics(self):
350350 # Calculate duration
351351 if self .statistics ['start_time' ]:
352352 self .statistics ['duration_seconds' ] = int (time .time () - self .statistics ['start_time' ])
353-
353+
354354 # Calculate capture file size
355355 if os .path .exists (self .pcapng_file ):
356356 size_bytes = os .path .getsize (self .pcapng_file )
357357 self .statistics ['capture_size_mb' ] = size_bytes / (1024 * 1024 )
358358 else :
359359 size_bytes = 0
360-
360+
361361 if self .tui_view :
362362 # TUI mode - update the view
363363 self .tui_view .update_capture_status (
@@ -376,7 +376,7 @@ def display_statistics(self):
376376 minutes = (duration % 3600 ) // 60
377377 seconds = duration % 60
378378 duration_str = f'{ hours :02d} :{ minutes :02d} :{ seconds :02d} '
379-
379+
380380 # Display statistics
381381 Color .clear_entire_line ()
382382 Color .p ('\r {+} {C}Networks:{W} {G}%d{W} | {C}PMKIDs:{W} {G}%d{W} | {C}Duration:{W} {G}%s{W} | {C}Size:{W} {G}%.2f MB{W}' % (
@@ -385,69 +385,69 @@ def display_statistics(self):
385385 duration_str ,
386386 self .statistics ['capture_size_mb' ]
387387 ))
388-
388+
389389 def run (self ):
390390 """
391391 Main entry point for passive PMKID attack.
392-
392+
393393 Validates dependencies before starting.
394394 Starts passive capture with HcxDumpToolPassive.
395395 Starts monitoring thread.
396396 Enters statistics display loop with keyboard interrupt handling.
397397 Handles duration timeout if configured.
398398 Calls cleanup on exit.
399399 """
400-
400+
401401 # Validate dependencies
402402 if not self .validate_dependencies ():
403403 return False
404-
404+
405405 try :
406406 # Start TUI view if available
407407 if self .tui_view :
408408 self .tui_view .start ()
409-
409+
410410 # Start passive capture
411411 with self .start_passive_capture () as dumptool :
412412 self .dumptool = dumptool
413-
413+
414414 # Record start time
415415 self .statistics ['start_time' ] = time .time ()
416-
416+
417417 # Start monitoring thread
418418 self .start_monitoring_thread ()
419-
419+
420420 # Calculate end time if duration is specified
421421 end_time = None
422422 if Configuration .pmkid_passive_duration > 0 :
423423 end_time = time .time () + Configuration .pmkid_passive_duration
424-
424+
425425 # Main statistics display loop
426426 try :
427427 while True :
428428 # Display statistics
429429 self .display_statistics ()
430-
430+
431431 # Check if duration timeout reached
432432 if end_time and time .time () >= end_time :
433433 if self .tui_view :
434434 self .tui_view .add_log ('Duration timeout reached' )
435435 else :
436436 Color .pl ('\n {+} {G}Duration timeout reached{W}' )
437437 break
438-
438+
439439 # Sleep briefly
440440 time .sleep (1 )
441-
441+
442442 except KeyboardInterrupt :
443443 if self .tui_view :
444444 self .tui_view .add_log ('Interrupted by user' )
445445 else :
446446 Color .pl ('\n {!} {O}Interrupted by user{W}' )
447-
447+
448448 # Cleanup
449449 self .cleanup ()
450-
450+
451451 except Exception as e :
452452 error_msg = f'Error during passive capture: { str (e )} '
453453 if self .tui_view :
@@ -466,13 +466,13 @@ def run(self):
466466 # Stop TUI view if it was started
467467 if self .tui_view :
468468 self .tui_view .stop ()
469-
469+
470470 return True
471-
471+
472472 def cleanup (self ):
473473 """
474474 Stop capture and perform final extraction.
475-
475+
476476 Stops monitoring thread gracefully.
477477 Stops hcxdumptool process (handled by context manager).
478478 Performs final hash extraction from capture file.
@@ -483,7 +483,7 @@ def cleanup(self):
483483 self .tui_view .add_log ('Cleaning up...' )
484484 else :
485485 Color .pl ('\n {+} {C}Cleaning up...{W}' )
486-
486+
487487 # Stop monitoring thread
488488 if self .monitor_thread :
489489 self .monitor_thread .stop ()
0 commit comments