Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
# start_gui.py # Версия 4.1 (macOS): Исправлены пути, os.startfile и ошибка stop_proxy import tkinter as tk from tkinter import messagebox, ttk import subprocess import os import sys import threading import asyncio from pathlib import Path import importlib.util import socket # --- Mitmproxy imports --- try: from mitmproxy.options import Options from mitmproxy.tools.dump import DumpMaster except ImportError: messagebox.showerror("Ошибка импорта", "Не найден модуль mitmproxy.\n" "Пожалуйста, установите его: pip3 install mitmproxy") sys.exit(1) # --- Глобальные переменные --- mitm_thread = None mitm_master = None mitm_loop = None # Для корректной остановки # --- Функции --- def resource_path(relative_path): if getattr(sys, 'frozen', False): base_dir = Path(sys.executable).parent.resolve() else: base_dir = Path(__file__).parent.resolve() return os.path.join(base_dir, relative_path) def find_chrome_path(): """Ищет Chrome в стандартных местах (macOS/Windows).""" if sys.platform == "darwin": # 'darwin' - это macOS return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" elif sys.platform == "win32": possible_paths = [ r"C:\Program Files\Google\Chrome\Application\chrome.exe", r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", os.path.expanduser(r"~\AppData\Local\Google\Chrome\Application\chrome.exe") ] for path in possible_paths: if os.path.exists(path): return path return "chrome.exe" else: return "google-chrome" # Для Linux def get_lan_ip(): s = None try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('8.8.8.8', 1)) ip = s.getsockname()[0] except Exception: ip = '127.0.0.1' finally: if s: s.close() return ip def show_lan_help(): port = port_entry.get() ip = get_lan_ip() if ip == '127.0.0.1': messagebox.showwarning("LAN IP не найден", "Не удалось определить ваш LAN IP. Убедитесь, что вы подключены к сети.") return # НОВОЕ: Инструкция для macOS if sys.platform == "darwin": fw_help = (f"2. На этом Mac: Откройте 'Системные настройки' -> 'Сеть' -> 'Брандмауэр' (Firewall). " f"Нажмите 'Параметры...' и добавьте (+) 'Python' (или это приложение), разрешив входящие подключения.") elif sys.platform == "win32": fw_help = (f"2. На этом ПК: Откройте Брандмауэр Windows и создайте 'Правило для входящих подключений', " f"чтобы 'Разрешить' 'TCP' трафик на порт {port}.") else: fw_help = f"2. На этом ПК: Убедитесь, что брандмауэр разрешает входящие подключения на порт {port}." help_text = ( f"Чтобы подключиться с другого устройства:\n\n" f"1. Устройство должно быть в той же Wi-Fi/LAN сети.\n\n" f"{fw_help}\n\n" f"3. На другом устройстве: В настройках Wi-Fi укажите прокси:\n" f" - Хост/Сервер: {ip}\n" f" - Порт: {port}\n\n" f"4. На другом устройстве: Установите сертификат mitmproxy. Самый простой способ - открыть в браузере http://mitm.it (когда прокси уже включен)." ) messagebox.showinfo("Как подключиться по LAN", help_text) async def run_mitmproxy(port, script_file, tk_root, host): global mitm_master conf_dir = os.path.join(os.path.abspath("."), ".mitmproxy") os.makedirs(conf_dir, exist_ok=True) opts = Options( listen_host=host, listen_port=int(port), ssl_insecure=True, confdir=conf_dir ) try: mitm_master = DumpMaster(opts, with_termlog=False, with_dumper=False) # Динамическая загрузка скрипта spec = importlib.util.spec_from_file_location("sni_override", script_file) if spec is None: raise ImportError(f"Не удалось загрузить модуль из {script_file}") module = importlib.util.module_from_spec(spec) sys.modules["sni_override"] = module spec.loader.exec_module(module) if hasattr(module, "addons"): for addon in module.addons: mitm_master.addons.add(addon) print(f"Загружен аддон: {type(addon).__name__}") else: raise ValueError(f"В модуле {script_file} не найден список 'addons'") await mitm_master.run() except Exception as e: print(f"!!! Ошибка Mitmproxy: {e}") if "mitm_loop" in globals() and mitm_loop and mitm_loop.is_running(): tk_root.after(0, lambda: messagebox.showerror("Ошибка Mitmproxy", str(e))) tk_root.after(0, stop_proxy) finally: print("--- Поток Mitmproxy завершен ---") async def shutdown_mitmproxy(): global mitm_master if mitm_master: try: mitm_master.shutdown() await asyncio.sleep(0.1) except Exception as e: print(f"!!! Ошибка при асинхронной остановке mitmproxy: {e}") def run_proxy_server(port, script_file, tk_root, host): global mitm_master, mitm_loop mitm_loop = asyncio.new_event_loop() asyncio.set_event_loop(mitm_loop) try: mitm_loop.run_until_complete(run_mitmproxy(port, script_file, tk_root, host)) except Exception as e: print(f"!!! Ошибка в run_proxy_server: {e}") finally: try: if mitm_loop.is_running(): mitm_loop.run_until_complete(mitm_loop.shutdown_asyncgens()) mitm_loop.run_until_complete(mitm_loop.shutdown_default_executor()) except Exception as e: print(f"!!! Ошибка при очистке цикла событий: {e}") finally: mitm_loop.close() print("--- Цикл событий закрыт ---") mitm_loop = None # mitm_master будет None после stop_proxy def start_proxy(): global mitm_thread if mitm_thread and mitm_thread.is_alive(): messagebox.showwarning("Внимание", "Прокси уже запущен!") return port = port_entry.get() if not port.isdigit() or not (1024 < int(port) < 65535): messagebox.showerror("Ошибка", f"Введите корректный порт (например, 8087).") return try: script_path = resource_path("sni_override.py") # Используем твое имя файла if not os.path.exists(script_path): messagebox.showerror("Ошибка", f"Не найден файл аддона: {script_path}") return host = listen_mode_var.get() mitm_thread = threading.Thread( target=run_proxy_server, args=(port, script_path, root, host), daemon=True ) mitm_thread.start() status_label.config(text=f"Прокси запущен на {host}:{port}", fg="green") start_button.config(state=tk.DISABLED) stop_button.config(state=tk.NORMAL) local_radio.config(state=tk.DISABLED) lan_radio.config(state=tk.DISABLED) except Exception as e: messagebox.showerror("Ошибка запуска", f"Не удалось запустить прокси-поток:\n{e}") # ИСПРАВЛЕННАЯ ФУНКЦИЯ ОСТАНОВКИ def stop_proxy(): global mitm_master, mitm_thread, mitm_loop if mitm_master and mitm_loop and mitm_loop.is_running(): print("--- Запрос на остановку прокси ---") try: # Потокобезопасно просим цикл событий запустить shutdown mitm_loop.call_soon_threadsafe( lambda: mitm_loop.create_task(shutdown_mitmproxy()) ) # Ждем, пока поток (в котором живет цикл) завершится сам mitm_thread.join(timeout=2.0) print("--- Поток прокси завершен ---") except Exception as e: print(f"!!! Ошибка при остановке прокси: {e}") finally: mitm_thread = None mitm_master = None mitm_loop = None # Обновляем GUI в любом случае status_label.config(text="Прокси остановлен", fg="red") start_button.config(state=tk.NORMAL) stop_button.config(state=tk.DISABLED) local_radio.config(state=tk.NORMAL) lan_radio.config(state=tk.NORMAL) def launch_browser(): port = port_entry.get() temp_dir = os.getenv("TEMP") or "/tmp" # НОВОЕ: /tmp для macOS/Linux if not temp_dir: messagebox.showerror("Ошибка", "Не удалось определить временную папку.") return profile_dir = os.path.join(temp_dir, "chrome-mitm-test") os.makedirs(profile_dir, exist_ok=True) chrome_path = find_chrome_path() if not os.path.exists(chrome_path): messagebox.showerror("Ошибка", f"Не удалось найти Google Chrome по пути: {chrome_path}\nУстановите его через Homebrew: brew install --cask google-chrome") return cmd = [ chrome_path, f"--proxy-server=127.0.0.1:{port}", "--disable-quic", f"--user-data-dir={profile_dir}", ] try: subprocess.Popen(cmd) except Exception as e: messagebox.showerror("Ошибка запуска", f"Не удалось запустить Chrome:\n{e}") def install_cert(): cert_path_on_disk = os.path.join(os.path.abspath("."), ".mitmproxy", "mitmproxy-ca-cert.cer") if not os.path.exists(cert_path_on_disk): messagebox.showwarning("Внимание", "Файл сертификата 'mitmproxy-ca-cert.cer' не найден.\n\n" "1. Сначала нажмите 'Start Proxy'.\n" "2. Прокси запустится и сгенерирует файл.\n" "3. Затем снова нажмите 'Установить сертификат'.") return try: # НОВОЕ: Кроссплатформенный запуск if sys.platform == "win32": os.startfile(cert_path_on_disk, 'open') messagebox.showinfo("Сертификат (Windows)", "Открыто окно установки сертификата.\n\n" "1. Нажмите 'Установить сертификат...'\n" "2. Выберите 'Локальный компьютер'\n" "3. Выберите 'Поместить... в следующее хранилище' -> 'Обзор'\n" "4. Выберите 'Доверенные корневые центры сертификации'\n" "5. Нажмите 'Далее' и 'Готово'.") elif sys.platform == "darwin": subprocess.run(["open", cert_path_on_disk]) messagebox.showinfo("Сертификат (macOS)", "Открылась 'Связка ключей' (Keychain Access).\n\n" "1. Найдите сертификат 'mitmproxy' (он может быть во вкладке 'login' или 'Вход').\n" "2. Дважды кликните на него.\n" "3. Разверните секцию 'Доверие' (Trust).\n" "4. В 'При использовании...' выберите 'Всегда доверять' (Always Trust).\n" "5. Закройте окно (потребуется пароль).") else: subprocess.run(["open", cert_path_on_disk]) # Для Linux except Exception as e: messagebox.showerror("Ошибка", f"Не удалось открыть файл сертификата:\n{cert_path_on_disk}\n\n{e}") def on_closing(): """Вызывается при закрытии окна""" if mitm_master: stop_proxy() root.destroy() # --- Создание GUI --- root = tk.Tk() root.title("SNI Proxy Manager (v4.0 macOS)") root.geometry("430x250") root.resizable(False, False) frame = tk.Frame(root, padx=10, pady=10) frame.pack(expand=True, fill=tk.BOTH) # --- Строка Порта --- port_frame = tk.Frame(frame) port_frame.pack(fill=tk.X, pady=5) port_label = tk.Label(port_frame, text="Порт прокси:", width=15, anchor="w") port_label.pack(side=tk.LEFT) port_entry = tk.Entry(port_frame, width=10) port_entry.pack(side=tk.LEFT, padx=5) port_entry.insert(0, "8087") # --- Блок выбора режима --- mode_frame = ttk.Labelframe(frame, text="Режим прослушивания", padding=5) mode_frame.pack(fill=tk.X, pady=(5,0)) listen_mode_var = tk.StringVar(value="127.0.0.1") local_radio = ttk.Radiobutton(mode_frame, text="Только локально (127.0.0.1)", variable=listen_mode_var, value="127.0.0.1") local_radio.pack(side=tk.LEFT, padx=5) local_ip = get_lan_ip() lan_radio = ttk.Radiobutton(mode_frame, text=f"Для LAN ({local_ip})", variable=listen_mode_var, value="0.0.0.0") lan_radio.pack(side=tk.LEFT, padx=5) lan_help_button = ttk.Button(mode_frame, text="?", width=3, command=show_lan_help) lan_help_button.pack(side=tk.RIGHT, padx=5) # --- Кнопки управления --- proxy_frame = tk.Frame(frame) proxy_frame.pack(fill=tk.X, pady=10) start_button = tk.Button(proxy_frame, text="Start Proxy", command=start_proxy, width=15) start_button.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=2) stop_button = tk.Button(proxy_frame, text="Stop Proxy", command=stop_proxy, state=tk.DISABLED, width=15) stop_button.pack(side=tk.LEFT, expand=True, fill=tk.X, padx=2) # --- Кнопки утилит --- utils_frame = tk.Frame(frame) utils_frame.pack(fill=tk.X, pady=5) cert_button = tk.Button(utils_frame, text="Установить сертификат", command=install_cert) cert_button.pack(expand=True, fill=tk.X, pady=2) browser_button = tk.Button(utils_frame, text="Запустить Chrome (с прокси)", command=launch_browser) browser_button.pack(expand=True, fill=tk.X, pady=2) # --- Строка статуса --- status_frame = tk.Frame(frame, relief=tk.SUNKEN, bd=1) status_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=(10,0)) status_label = tk.Label(status_frame, text="Прокси остановлен", fg="red", anchor="w") status_label.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) root.protocol("WM_DELETE_WINDOW", on_closing) root.mainloop()
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)