Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 243 additions & 0 deletions gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import os
import sys
import json
import ctypes
import threading
import subprocess
import customtkinter as ctk
import tkinter.messagebox as messagebox

def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False

if not is_admin():
exe = sys.executable.replace("python.exe", "pythonw.exe") if "python.exe" in sys.executable else sys.executable
script_path = f'"{os.path.abspath(__file__)}"'
ctypes.windll.shell32.ShellExecuteW(None, "runas", exe, script_path, None, 1)
sys.exit()

ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")

CONFIG_FILE = "config.json"
DEFAULT_CONFIG = {
"LISTEN_HOST": "0.0.0.0",
"LISTEN_PORT": 40443,
"CONNECT_IP": "188.114.98.0",
"CONNECT_PORT": 443,
"FAKE_SNI": "auth.vercel.com"
}

class SNISpooferGUI(ctk.CTk):
def __init__(self):
super().__init__()

self.title("SNI-Spoofing Controller")
self.geometry("600x700")
self.minsize(500, 600)

self.process = None
self.is_running = False

self.frame_top = ctk.CTkFrame(self, fg_color="transparent")
self.frame_top.pack(pady=20, padx=20, fill="x")

self.btn_toggle = ctk.CTkButton(
self.frame_top,
text="START SPOOFING",
font=("Arial", 20, "bold"),
height=60,
fg_color="#1f6aa5",
hover_color="#144870",
command=self.toggle_process
)
self.btn_toggle.pack(fill="x")

self.frame_config = ctk.CTkFrame(self)
self.frame_config.pack(pady=10, padx=20, fill="x")

self.entries = {}
row = 0
for key in DEFAULT_CONFIG.keys():
lbl = ctk.CTkLabel(self.frame_config, text=key + ":", font=("Arial", 12, "bold"))
lbl.grid(row=row, column=0, padx=10, pady=5, sticky="w")

ent = ctk.CTkEntry(self.frame_config, width=250)
ent.grid(row=row, column=1, padx=10, pady=5, sticky="ew")
self.entries[key] = ent
row += 1

self.frame_config.columnconfigure(1, weight=1)

self.frame_config_btns = ctk.CTkFrame(self, fg_color="transparent")
self.frame_config_btns.pack(pady=5, padx=20, fill="x")

self.btn_save = ctk.CTkButton(self.frame_config_btns, text="Save", command=self.save_config, width=100)
self.btn_save.pack(side="left", padx=5)

self.btn_clear = ctk.CTkButton(self.frame_config_btns, text="Clear", command=self.clear_config, width=100, fg_color="#8B0000", hover_color="#5C0000")
self.btn_clear.pack(side="left", padx=5)

self.btn_import = ctk.CTkButton(self.frame_config_btns, text="Import JSON", command=self.import_clipboard)
self.btn_import.pack(side="right", padx=5)

self.btn_export = ctk.CTkButton(self.frame_config_btns, text="Export JSON", command=self.export_clipboard)
self.btn_export.pack(side="right", padx=5)

self.frame_log = ctk.CTkFrame(self)
self.frame_log.pack(pady=10, padx=20, fill="both", expand=True)

lbl_log = ctk.CTkLabel(self.frame_log, text="Logs Output:", font=("Arial", 12, "bold"))
lbl_log.pack(anchor="w", padx=10, pady=(10,0))

self.textbox_log = ctk.CTkTextbox(self.frame_log, state="disabled", font=("Consolas", 12))
self.textbox_log.pack(padx=10, pady=10, fill="both", expand=True)

self.load_config()

def load_config(self):
config_data = DEFAULT_CONFIG.copy()
if os.path.exists(CONFIG_FILE):
try:
with open(CONFIG_FILE, "r") as f:
data = json.load(f)
config_data.update(data)
except Exception as e:
self.log(f"[!] Error loading config: {e}")

self.clear_config()
for key, value in config_data.items():
if key in self.entries:
self.entries[key].insert(0, str(value))

def save_config(self):
new_config = {}
for key, ent in self.entries.items():
val = ent.get().strip()
if "PORT" in key:
val = int(val) if val.isdigit() else val
new_config[key] = val

try:
with open(CONFIG_FILE, "w") as f:
json.dump(new_config, f, indent=2)
self.log("[+] Config saved successfully.")
except Exception as e:
self.log(f"[!] Error saving config: {e}")

def clear_config(self):
for ent in self.entries.values():
ent.delete(0, "end")

def import_clipboard(self):
try:
clipboard_data = self.clipboard_get()
data = json.loads(clipboard_data)
self.clear_config()
for key, value in data.items():
if key in self.entries:
self.entries[key].insert(0, str(value))
self.log("[+] Config imported from clipboard.")
except Exception as e:
messagebox.showerror("Import Error", f"Invalid JSON format in clipboard.\n{e}")

def export_clipboard(self):
new_config = {}
for key, ent in self.entries.items():
val = ent.get().strip()
if "PORT" in key:
val = int(val) if val.isdigit() else val
new_config[key] = val

self.clipboard_clear()
self.clipboard_append(json.dumps(new_config, indent=2))
self.log("[+] Config exported to clipboard.")

def log(self, message):
self.textbox_log.configure(state="normal")
self.textbox_log.insert("end", message + "\n")
self.textbox_log.see("end")
self.textbox_log.configure(state="disabled")

def toggle_process(self):
if not self.is_running:
self.start_process()
else:
self.stop_process()

def start_process(self):
self.save_config()
self.textbox_log.configure(state="normal")
self.textbox_log.delete("1.0", "end")
self.textbox_log.configure(state="disabled")

self.log("[*] Starting main.py...")

custom_env = os.environ.copy()
custom_env["PYTHONIOENCODING"] = "utf-8"
custom_env["PYTHONUTF8"] = "1"

startup_info = None
if os.name == 'nt':
startup_info = subprocess.STARTUPINFO()
startup_info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startup_info.wShowWindow = 0

try:
python_exe = sys.executable.replace("python.exe", "pythonw.exe") if "python.exe" in sys.executable else sys.executable

self.process = subprocess.Popen(
[python_exe, "main.py"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
encoding="utf-8",
errors="replace",
bufsize=1,
creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0,
startupinfo=startup_info,
env=custom_env
)

self.is_running = True
self.btn_toggle.configure(text="STOP SPOOFING", fg_color="#8B0000", hover_color="#5C0000")

threading.Thread(target=self.read_process_output, daemon=True).start()

except Exception as e:
self.log(f"[!] Failed to start process: {e}")

def stop_process(self):
if self.process:
self.log("[*] Stopping process...")
self.process.terminate()
self.process = None

self.is_running = False
self.btn_toggle.configure(text="START SPOOFING", fg_color="#1f6aa5", hover_color="#144870")
self.log("[+] Process stopped.")

def read_process_output(self):
if self.process:
try:
for line in iter(self.process.stdout.readline, ''):
if line:
self.after(0, self.log, line.strip())
except Exception as e:
self.after(0, self.log, f"[!] Log reading error: {e}")
finally:
if self.process and self.process.stdout:
self.process.stdout.close()
if self.is_running:
self.after(0, self.stop_process)

def destroy(self):
self.stop_process()
super().destroy()

if __name__ == "__main__":
app = SNISpooferGUI()
app.mainloop()
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@

pydivert>=3.1.0
pydivert>=3.1.0
customtkinter>=5.2.2