From 955e636d5f8b176273ea7395c84358e0eaf7cf10 Mon Sep 17 00:00:00 2001 From: Zhe Yuan Date: Mon, 13 Apr 2026 12:48:46 -0400 Subject: [PATCH] refactor: enhance auto-login functionality and add MAC address randomization --- README.md | Bin 1024 -> 1472 bytes main.py | 288 +++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 231 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 06d7405020018ddf3cacee90fd4af10487da3d20..6f4fb4a354eb8dd7e831e59d95327b50b09ebf6f 100644 GIT binary patch literal 1472 zcmZuxTTdHT5PsiZF@l%Yk?kh%6seJ-s8u5+3JO7ah$`)3mUtzzYk7}pk$7+j#<;96 zluNNOp$;}sLUy4g0pr_`>7KJ^pZE)zW$ogi`o%t;IWzNJ&J4q|@d+49PR5i7yi|fe zD)2B#Gh;0MdJba>S={9e>F*m4mb zNpXV3VN6R9DF#@N?Nr%u$Elj!tkK^Merib|q8e09w%-%AUED2kJk9u<d%s{FxOK+1@-Q#V2d_W< zFU%2x!GxqvX*0nYDI#KSkJfC?Ef-kjk{X+R=eJjBu8Mwzrw<=NU&&>+l0u#WfgYsQ zEw=O4EfoU+7$RM5;oQ5)r-4LAxJV?woBlXsf{1J6Mcc z^_wgB#(bCqx9p3L-U>UnXlK#BxU}1UdUecqZYk|n8_u@rhpv6+4PvA*3ysCH2ME!x zeWPSgy7-);;`p(coIpi1Ri0L2^4I=!)^~CHdF~OfeJlSei(t2N_GKQ#DMcq?M6_+< LHnuNTJSM*Y!zKPC literal 1024 ScmZQz7zLvtFd70QH3R?z00031 diff --git a/main.py b/main.py index 464f21d..5852ae0 100644 --- a/main.py +++ b/main.py @@ -1,74 +1,248 @@ -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.edge.service import Service as EdgeService -from selenium.webdriver.edge.options import Options - -# 如果你安装了 webdriver_manager,取消下面这行的注释 -# from webdriver_manager.microsoft import EdgeChromiumDriverManager +import argparse +import random +import subprocess +import sys +import time import requests +import winreg +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.edge.options import Options +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait + +# Captive Portal 触发 URL 列表,按优先级依次尝试 +CAPTIVE_PORTAL_URLS = [ + "http://captive.apple.com/", + "http://www.msftconnecttest.com/redirect", + "http://detectportal.firefox.com/", +] + +MAX_LOGIN_RETRIES = 3 + def is_connected(): + """通过 HTTP 204 检测网络是否连通。""" + test_urls = [ + ("http://clients3.google.com/generate_204", 204), + ("http://www.msftconnecttest.com/connecttest.txt", 200), + ] + for url, expected_status in test_urls: + try: + r = requests.get(url, timeout=3) + if r.status_code == expected_status: + return True + except Exception: + continue + return False + + +def get_wifi_adapter_name(): + """获取当前活跃的 Wi-Fi 网卡名称。""" + result = subprocess.run( + ["netsh", "wlan", "show", "interfaces"], + capture_output=True, text=True, encoding="gbk", + ) + for line in result.stdout.splitlines(): + line = line.strip() + if line.startswith("名称") or line.startswith("Name"): + return line.split(":", 1)[1].strip() + return None + + +def generate_random_mac(): + """生成一个随机的本地管理单播 MAC 地址。""" + mac = [random.randint(0x00, 0xFF) for _ in range(6)] + mac[0] = (mac[0] & 0xFC) | 0x02 # 本地管理、单播 + return mac + + +def find_adapter_registry_key(adapter_name): + """在注册表中找到匹配 adapter_name 的网卡子键路径。""" + base_path = r"SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}" try: - # 尝试访问一个极其稳定的网站,设置短超时 - requests.get("https://www.google.com", timeout=3) - return True - except: + base_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, base_path) + except OSError: + return None + + index = 0 + while True: + try: + subkey_name = winreg.EnumKey(base_key, index) + index += 1 + except OSError: + break + try: + subkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, f"{base_path}\\{subkey_name}") + driver_desc, _ = winreg.QueryValueEx(subkey, "DriverDesc") + if adapter_name.lower() in driver_desc.lower(): + winreg.CloseKey(subkey) + winreg.CloseKey(base_key) + return f"{base_path}\\{subkey_name}" + winreg.CloseKey(subkey) + except OSError: + continue + + winreg.CloseKey(base_key) + return None + + +def randomize_mac(): + """随机化当前 Wi-Fi 网卡的 MAC 地址(需要管理员权限)。""" + adapter_name = get_wifi_adapter_name() + if not adapter_name: + print("错误:未找到活跃的 Wi-Fi 适配器。") return False -def auto_login_wifi(): - # 配置 Edge 选项 - edge_options = Options() - edge_options.add_argument("--start-maximized") # 最大化窗口 - edge_options.add_argument("--ignore-certificate-errors") # 忽略证书错误(公共Wi-Fi常见) - edge_options.add_argument("--headless") # 如果不想看到浏览器界面,取消注释这一行 + print(f"找到 Wi-Fi 适配器: {adapter_name}") + + reg_path = find_adapter_registry_key(adapter_name) + if not reg_path: + print(f"错误:未在注册表中找到适配器 '{adapter_name}' 的条目。") + return False + + mac = generate_random_mac() + mac_str = "".join(f"{b:02X}" for b in mac) + mac_display = ":".join(f"{b:02X}" for b in mac) try: - # 初始化 Edge 驱动 - # 如果安装了 webdriver_manager,使用下面这行: - # driver = webdriver.Edge(service=EdgeService(EdgeChromiumDriverManager().install()), options=edge_options) - - # 如果没有安装 webdriver_manager,且驱动已在环境变量中,直接使用下面这行: - driver = webdriver.Edge(options=edge_options) + key = winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, reg_path, 0, winreg.KEY_SET_VALUE, + ) + winreg.SetValueEx(key, "NetworkAddress", 0, winreg.REG_SZ, mac_str) + winreg.CloseKey(key) + except PermissionError: + print("错误:需要管理员权限来修改 MAC 地址。请以管理员身份运行。") + return False - print("正在打开浏览器并访问触发页面...") - # 1. 访问触发页面,这会自动重定向到 OSU 的登录页 - driver.get("http://captive.apple.com/") + print(f"正在将 MAC 地址更改为: {mac_display}") - # 设置显式等待,最长等待 20 秒,确保页面元素加载出来 - wait = WebDriverWait(driver, 20) + # 获取网络适配器的接口名称(用于 netsh) + interface_name = get_netsh_interface_name() + if not interface_name: + interface_name = adapter_name - print("等待跳转至登录页面...") - - # 2. 定位并勾选 "I accept the terms of use" - # 使用 name="visitor_accept_terms" 定位,这比包含随机数字的 ID 更稳定 - checkbox = wait.until(EC.element_to_be_clickable((By.NAME, "visitor_accept_terms"))) - - if not checkbox.is_selected(): - checkbox.click() - print("已勾选同意协议。") - - # 3. 定位并点击 "Log In" 按钮 - # 同样避开动态 ID,使用 XPath 查找 value 为 "Log In" 的提交按钮 - login_button = driver.find_element(By.XPATH, "//input[@type='submit' and @value='Log In']") - login_button.click() - print("已点击登录按钮。") + print("正在重启网卡以应用新 MAC 地址...") + subprocess.run( + ["netsh", "interface", "set", "interface", interface_name, "disable"], + capture_output=True, + ) + time.sleep(2) + subprocess.run( + ["netsh", "interface", "set", "interface", interface_name, "enable"], + capture_output=True, + ) + time.sleep(3) + print(f"MAC 地址已更改为: {mac_display}") + return True - # 保持窗口打开一小会儿以确认连接成功(根据需要调整) - import time - time.sleep(5) - print("操作完成。") - except Exception as e: - print(f"发生错误: {e}") - finally: - # 如果你想让浏览器保持打开状态,注释掉下面这行 - driver.quit() +def get_netsh_interface_name(): + """获取 Wi-Fi 网络接口的名称(用于 netsh interface 命令)。""" + result = subprocess.run( + ["netsh", "interface", "show", "interface"], + capture_output=True, text=True, encoding="gbk", + ) + for line in result.stdout.splitlines(): + if "Wi-Fi" in line or "WLAN" in line or "Wireless" in line: + parts = line.split() + if len(parts) >= 4: + return " ".join(parts[3:]) + return None + + +def auto_login_wifi(): + """自动登录 OSU 公共 Wi-Fi,支持重试。""" + edge_options = Options() + edge_options.add_argument("--start-maximized") + edge_options.add_argument("--ignore-certificate-errors") + edge_options.add_argument("--headless") + + for attempt in range(1, MAX_LOGIN_RETRIES + 1): + driver = None + try: + driver = webdriver.Edge(options=edge_options) + print(f"第 {attempt} 次尝试登录...") + + # 依次尝试不同的 Captive Portal 触发 URL + portal_reached = False + for url in CAPTIVE_PORTAL_URLS: + print(f" 访问触发页面: {url}") + driver.get(url) + try: + wait = WebDriverWait(driver, 10) + wait.until(EC.element_to_be_clickable((By.NAME, "visitor_accept_terms"))) + portal_reached = True + break + except Exception: + print(f" 未能通过 {url} 跳转到登录页,尝试下一个...") + continue + + if not portal_reached: + print(f" 第 {attempt} 次尝试未能到达登录页面。") + continue + + # 勾选同意协议 + checkbox = driver.find_element(By.NAME, "visitor_accept_terms") + if not checkbox.is_selected(): + checkbox.click() + print(" 已勾选同意协议。") + + time.sleep(1) + + # 点击登录按钮 + login_button = driver.find_element( + By.XPATH, "//input[@type='submit' and @value='Log In']", + ) + login_button.click() + print(" 已点击登录按钮。") + + # 轮询验证是否真正连上网络 + for i in range(10): + time.sleep(2) + if is_connected(): + print("登录成功!网络已连通。") + return + print(f" 第 {attempt} 次尝试:点击登录后网络仍未连通。") + + except Exception as e: + print(f" 第 {attempt} 次尝试出错: {e}") + finally: + if driver: + driver.quit() + + if attempt < MAX_LOGIN_RETRIES: + print(" 等待 3 秒后重试...") + time.sleep(3) + + print("多次尝试后仍未能成功登录。") + + +def main(): + parser = argparse.ArgumentParser(description="OSU 公共 Wi-Fi 自动登录工具") + parser.add_argument( + "--random-mac", + action="store_true", + help="登录前随机化 Wi-Fi 网卡的 MAC 地址(需要管理员权限)", + ) + args = parser.parse_args() -if __name__ == "__main__": if is_connected(): print("网络已连接,无需登录。") - else: - auto_login_wifi() \ No newline at end of file + return + + if args.random_mac: + print("正在随机化 MAC 地址...") + if not randomize_mac(): + print("MAC 地址随机化失败,继续尝试登录...") + else: + # MAC 更改后需要等待 Wi-Fi 重新连接 + print("等待 Wi-Fi 重新连接...") + time.sleep(5) + + auto_login_wifi() + + +if __name__ == "__main__": + main() \ No newline at end of file