Fixed scan crash: all tkinter widget access now happens on main thread (Tcl NOT thread-safe). _log() routes GUI updates via root.after(). _scan_worker reads all tkinter vars on main thread. Removed unused _spray_queue.
This commit is contained in:
64
gui.py
64
gui.py
@@ -94,7 +94,6 @@ class FastRDPGUI:
|
||||
self.users_file = os.path.join(WORDLISTS_DIR, "users.txt")
|
||||
self.passwords_file = os.path.join(WORDLISTS_DIR, "passwords.txt")
|
||||
self._metric_labels: dict = {}
|
||||
self._spray_queue: asyncio.Queue = None
|
||||
|
||||
# Proxy manager
|
||||
self.proxy_manager = ProxyManager()
|
||||
@@ -758,11 +757,8 @@ class FastRDPGUI:
|
||||
# ── LOGGING ────────────────────────────────────────────
|
||||
def _log(self, msg: str, tag: str = "info"):
|
||||
ts = time.strftime("%H:%M:%S")
|
||||
try:
|
||||
self.log_text.insert("end", f"[{ts}] {msg}\n", tag)
|
||||
self.log_text.see("end")
|
||||
except tk.TclError:
|
||||
pass
|
||||
# Schedule GUI update on main thread (tkinter is NOT thread-safe)
|
||||
self.root.after(0, lambda m=msg, t=tag, ts=ts: self._log_gui(m, t, ts))
|
||||
tag_colors = {
|
||||
"hit": "\033[92m",
|
||||
"info": "\033[94m",
|
||||
@@ -777,6 +773,14 @@ class FastRDPGUI:
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def _log_gui(self, msg: str, tag: str, ts: str):
|
||||
"""Thread-safe GUI log append (runs on main thread via after())."""
|
||||
try:
|
||||
self.log_text.insert("end", f"[{ts}] {msg}\n", tag)
|
||||
self.log_text.see("end")
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
# ── RDP CONNECT FEATURE ────────────────────────────────
|
||||
def _rdp_connect(self, ip: str, port: int, username: str = "", password: str = ""):
|
||||
"""Launch Windows mstsc (or store creds + launch) for given host."""
|
||||
@@ -979,7 +983,22 @@ class FastRDPGUI:
|
||||
print(_c("\033[96m", " \u2502 ") + _c("\033[91m", "REAPER v2.0 :: RDP Reaper :: No Mercy") + _c("\033[96m", " \u2502"))
|
||||
print(_c("\033[96m", " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"))
|
||||
|
||||
thread = threading.Thread(target=self._scan_worker, daemon=True)
|
||||
# Read tkinter vars on MAIN thread (Tcl NOT thread-safe)
|
||||
scan_speed = self.speed_var.get()
|
||||
scan_timeout = self.timeout_var.get()
|
||||
scan_ports = [p for p, v in self.port_vars.items() if v.get()]
|
||||
scan_randomize = self.randomize_var.get()
|
||||
spray_enabled = self.spray_var.get()
|
||||
ranges_path = self.ranges_entry.get().strip() if self.ranges_entry.get().strip() else ""
|
||||
users_file = self.users_entry.get().strip() if self.users_entry.get().strip() else ""
|
||||
pass_file = self.pass_entry.get().strip() if self.pass_entry.get().strip() else ""
|
||||
|
||||
thread = threading.Thread(
|
||||
target=self._scan_worker,
|
||||
args=(scan_speed, scan_timeout, scan_ports, scan_randomize, spray_enabled,
|
||||
ranges_path, users_file, pass_file),
|
||||
daemon=True
|
||||
)
|
||||
thread.start()
|
||||
|
||||
def _stop_scan(self):
|
||||
@@ -997,19 +1016,25 @@ class FastRDPGUI:
|
||||
self.log_text.delete("1.0", "end")
|
||||
self.stats = ScanStats()
|
||||
|
||||
def _load_wordlists(self):
|
||||
def _load_wordlists(self, users_path="", pass_path=""):
|
||||
"""Load usernames and passwords from files (or use defaults)."""
|
||||
usernames = TOP_USERNAMES
|
||||
passwords = TOP50_PASSWORDS
|
||||
|
||||
uf = self.users_entry.get().strip()
|
||||
if users_path:
|
||||
uf = users_path
|
||||
else:
|
||||
uf = self.users_entry.get().strip()
|
||||
if os.path.exists(uf):
|
||||
with open(uf, 'r', errors='ignore') as f:
|
||||
cu = [l.strip() for l in f if l.strip()]
|
||||
if cu:
|
||||
usernames = cu
|
||||
|
||||
pf = self.pass_entry.get().strip()
|
||||
if pass_path:
|
||||
pf = pass_path
|
||||
else:
|
||||
pf = self.pass_entry.get().strip()
|
||||
if os.path.exists(pf):
|
||||
with open(pf, 'r', errors='ignore') as f:
|
||||
cp = [l.strip() for l in f if l.strip()]
|
||||
@@ -1018,14 +1043,16 @@ class FastRDPGUI:
|
||||
|
||||
return usernames, passwords
|
||||
|
||||
def _scan_worker(self):
|
||||
"""Background thread: scan + spray run CONCURRENTLY."""
|
||||
def _scan_worker(self, speed, timeout, active_ports, randomize, spray_enabled,
|
||||
ranges_path="", users_path="", pass_path=""):
|
||||
"""Background thread: scan + spray run CONCURRENTLY.
|
||||
All tkinter values are passed from main thread (Tcl NOT thread-safe).
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
||||
ranges_path = self.ranges_entry.get().strip()
|
||||
if not os.path.exists(ranges_path):
|
||||
if not ranges_path or not os.path.exists(ranges_path):
|
||||
self._log(f"\u274c Ranges file not found: {ranges_path}", "error")
|
||||
self._scan_done()
|
||||
return
|
||||
@@ -1040,15 +1067,12 @@ class FastRDPGUI:
|
||||
self._scan_done()
|
||||
return
|
||||
|
||||
if self.randomize_var.get():
|
||||
if randomize:
|
||||
random.shuffle(ips)
|
||||
|
||||
active_ports = [p for p, v in self.port_vars.items() if v.get()]
|
||||
if not active_ports:
|
||||
active_ports = [3389]
|
||||
|
||||
speed = self.speed_var.get()
|
||||
timeout = self.timeout_var.get()
|
||||
self.stats.total = len(ips) * len(active_ports)
|
||||
|
||||
self._log(f"\U0001f680 Scanning {len(ips):,} IPs x {len(active_ports)} ports "
|
||||
@@ -1059,7 +1083,7 @@ class FastRDPGUI:
|
||||
print(_c("\033[90m", " [+] " + "\u2500" * 55))
|
||||
|
||||
# Load wordlists now (in the background thread, before async loop)
|
||||
usernames, passwords = self._load_wordlists()
|
||||
usernames, passwords = self._load_wordlists(users_path, pass_path)
|
||||
self._log(f"\U0001f4cb Loaded {len(usernames)} username(s) x {len(passwords)} password(s)", "system")
|
||||
|
||||
# ── Streaming design ─────────────────────────────
|
||||
@@ -1078,7 +1102,7 @@ class FastRDPGUI:
|
||||
self._update_host_status(ip, port, "\U0001f50d RDP Open \u2014 spraying...")
|
||||
print(_c("\033[92m", f" [{time.strftime('%H:%M:%S')}] \u2714 {ip}:{port} \u2014 LIVE, spraying NOW"))
|
||||
|
||||
if self.spray_var.get():
|
||||
if spray_enabled:
|
||||
# Create an asyncio task for this host's spray
|
||||
task = asyncio.create_task(
|
||||
self._spray_host(loop, ip, port, usernames, passwords)
|
||||
|
||||
Reference in New Issue
Block a user