微信免扫码登录

查看 84|回复 11
作者:不羁de流年   
看到大佬的
微信免扫码多开(支持3.xx和4.xx版本)
https://www.52pojie.cn/thread-2052070-1-1.html
(出处: 吾爱破解论坛)
心血来潮弄了个python版本的,并添加了部分功能:
1. 支持删除账号
2. 支持设置自定义的微信和config.data路径
3. 支持覆盖保存已经存在的账号


image.png (11.86 KB, 下载次数: 1)
下载附件
2025-8-11 10:00 上传



image.png (53.08 KB, 下载次数: 1)
下载附件
2025-8-11 10:03 上传

#!/usr/bin/env python3
# wechat_multi_gui.py
"""
import os
import json
import shutil
import subprocess
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog
CONFIG_FILE = Path(__file__).with_name("wechat_cli_config.json")
def load_config():
    if CONFIG_FILE.exists():
        try:
            return json.loads(CONFIG_FILE.read_text(encoding="utf-8"))
        except Exception:
            return {}
    return {}
def save_config(cfg: dict):
    try:
        CONFIG_FILE.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8")
    except Exception as e:
        messagebox.showerror("错误", f"保存配置文件失败: {e}")
def ensure_backup_dir(config_path):
    cfg_parent = Path(config_path).parent
    backup_dir = cfg_parent / "config_backup"
    backup_dir.mkdir(parents=True, exist_ok=True)
    return backup_dir
def list_saved_accounts(backup_dir):
    accounts = []
    for p in backup_dir.iterdir():
        if p.is_dir() and p.name.isdigit():
            display = f"账号 {p.name}"
            meta = p / "account.meta"
            if meta.exists():
                try:
                    j = json.loads(meta.read_text(encoding="utf-8"))
                    display = j.get("displayName", display)
                except Exception:
                    pass
            accounts.append({"id": int(p.name), "folder": p, "displayName": display})
    accounts.sort(key=lambda x: x["id"])
    return accounts
def save_current_account(config_path, backup_dir, display_name=None):
    # 如果同名则覆盖更新
    if display_name:
        for acc in list_saved_accounts(backup_dir):
            if acc["displayName"] == display_name:
                shutil.copy2(config_path, acc["folder"] / "config.data")
                meta = {"displayName": display_name}
                (acc["folder"] / "account.meta").write_text(json.dumps(meta, ensure_ascii=False), encoding="utf-8")
                return acc["id"]
    # 否则新建
    ids = [int(p.name) for p in backup_dir.iterdir() if p.is_dir() and p.name.isdigit()]
    new_id = max(ids) + 1 if ids else 1
    new_dir = backup_dir / str(new_id)
    new_dir.mkdir(parents=True, exist_ok=True)
    shutil.copy2(config_path, new_dir / "config.data")
    meta = {"displayName": display_name or f"账号 {new_id}"}
    (new_dir / "account.meta").write_text(json.dumps(meta, ensure_ascii=False), encoding="utf-8")
    return new_id
def start_wechat_with_config(exe_path, config_path, backup_dir, account_folder=None):
    if not Path(config_path).exists():
        raise FileNotFoundError("目标 config.data 不存在")
    # 备份当前
    shutil.copy2(config_path, backup_dir / "current_config.data")
    if account_folder is None:
        raise ValueError("account_folder 必须提供")
    src = Path(account_folder) / "config.data"
    if not src.exists():
        raise FileNotFoundError("选定账号的 config.data 丢失")
    shutil.copy2(src, config_path)
    subprocess.Popen([exe_path], shell=False)
class WeChatGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("微信多账号免密启动")
        self.config = load_config()
        # 路径输入
        tk.Label(root, text="微信 EXE 路径:").grid(row=0, column=0, sticky="e")
        self.exe_var = tk.StringVar(value=self.config.get("ExePath", ""))
        tk.Entry(root, textvariable=self.exe_var, width=50).grid(row=0, column=1)
        tk.Button(root, text="选择", command=self.select_exe).grid(row=0, column=2)
        tk.Label(root, text="config.data 路径:").grid(row=1, column=0, sticky="e")
        self.config_var = tk.StringVar(value=self.config.get("ConfigPath", ""))
        tk.Entry(root, textvariable=self.config_var, width=50).grid(row=1, column=1)
        tk.Button(root, text="选择", command=self.select_config).grid(row=1, column=2)
        tk.Button(root, text="保存设置", command=self.save_settings).grid(row=2, column=0, columnspan=3, pady=5)
        # 账号列表
        self.account_listbox = tk.Listbox(root, height=10, width=50)
        self.account_listbox.grid(row=3, column=0, columnspan=3, pady=5)
        tk.Button(root, text="启动选中账号", command=self.start_selected).grid(row=4, column=0)
        tk.Button(root, text="保存当前账号", command=self.save_current).grid(row=4, column=1)
        tk.Button(root, text="删除选中账号", command=self.delete_selected).grid(row=4, column=2)
        tk.Button(root, text="刷新列表", command=self.refresh_list).grid(row=5, column=0, columnspan=3, pady=5)
        self.refresh_list()
    def select_exe(self):
        path = filedialog.askopenfilename(title="选择 WeChat.exe", filetypes=[("微信程序", "*.exe")])
        if path:
            self.exe_var.set(path)
    def select_config(self):
        path = filedialog.askopenfilename(title="选择 config.data", filetypes=[("config.data", "config.data")])
        if path:
            self.config_var.set(path)
    def save_settings(self):
        exe_path = self.exe_var.get()
        cfg_path = self.config_var.get()
        if not Path(exe_path).exists():
            messagebox.showerror("错误", "微信 exe 路径无效")
            return
        if not Path(cfg_path).exists():
            messagebox.showerror("错误", "config.data 路径无效")
            return
        self.config["ExePath"] = exe_path
        self.config["ConfigPath"] = cfg_path
        save_config(self.config)
        messagebox.showinfo("提示", "设置已保存")
    def refresh_list(self):
        self.account_listbox.delete(0, tk.END)
        cfg_path = self.config.get("ConfigPath")
        if not cfg_path or not Path(cfg_path).exists():
            self.account_listbox.insert(tk.END, "⚠ 请先设置正确的 config.data 路径")
            self.accounts = []
            return
        backup_dir = ensure_backup_dir(cfg_path)
        accounts = list_saved_accounts(backup_dir)
        for acc in accounts:
            self.account_listbox.insert(tk.END, f"{acc['displayName']} (ID: {acc['id']})")
        self.accounts = accounts
    def start_selected(self):
        if not hasattr(self, "accounts") or not self.accounts:
            return
        sel = self.account_listbox.curselection()
        if not sel:
            messagebox.showwarning("提示", "请先选择一个账号")
            return
        idx = sel[0]
        account = self.accounts[idx]
        try:
            start_wechat_with_config(self.config["ExePath"], self.config["ConfigPath"],
                                     ensure_backup_dir(self.config["ConfigPath"]), account["folder"])
            messagebox.showinfo("提示", "已启动微信(如无异常会免密登录)")
        except Exception as e:
            messagebox.showerror("错误", str(e))
    def save_current(self):
        cfg_path = self.config.get("ConfigPath")
        if not cfg_path or not Path(cfg_path).exists():
            messagebox.showerror("错误", "config.data 路径无效")
            return
        name = simpledialog.askstring("保存账号", "输入显示名称(可留空使用默认)")
        try:
            new_id = save_current_account(cfg_path, ensure_backup_dir(cfg_path), name)
            messagebox.showinfo("提示", f"账号已保存/更新(ID: {new_id})")
            self.refresh_list()
        except Exception as e:
            messagebox.showerror("错误", str(e))
    def delete_selected(self):
        if not hasattr(self, "accounts") or not self.accounts:
            return
        sel = self.account_listbox.curselection()
        if not sel:
            messagebox.showwarning("提示", "请先选择一个账号")
            return
        idx = sel[0]
        account = self.accounts[idx]
        confirm = messagebox.askyesno("确认删除", f"确定要删除账号 {account['displayName']} 吗?")
        if confirm:
            try:
                shutil.rmtree(account["folder"])
                messagebox.showinfo("提示", "账号已删除")
                self.refresh_list()
            except Exception as e:
                messagebox.showerror("错误", str(e))
if __name__ == "__main__":
    root = tk.Tk()
    app = WeChatGUI(root)
    root.mainloop()

账号, 路径

SSS785399141   

[Asm] 纯文本查看 复制代码#!/usr/bin/env python3
# wechat_multi_gui.py
"""微信多账号免密启动工具"""
import sys  # 新增:用于动态获取程序运行路径(打包后关键)
import os
import json
import shutil
import subprocess
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog
# -------------------------- 核心修改:动态定位配置文件路径 --------------------------
def get_config_file_path():
    """
    动态获取配置文件路径:
    - 打包后(EXE运行):读取EXE所在目录的wechat_cli_config.json
    - 未打包时(脚本运行):读取脚本所在目录的wechat_cli_config.json
    """
    if hasattr(sys, '_MEIPASS'):
        # 打包后:sys.argv[0]是EXE文件路径,取其所在目录(如dist目录)
        exe_directory = Path(sys.argv[0]).parent
    else:
        # 未打包时:取当前脚本文件所在目录(开发阶段使用)
        exe_directory = Path(__file__).parent
   
    # 配置文件固定放在程序所在目录,文件名不变
    return exe_directory / "wechat_cli_config.json"
# 全局配置文件路径(使用动态获取的路径)
CONFIG_FILE = get_config_file_path()
# ----------------------------------------------------------------------------------
def load_config():
    """加载配置文件(读取微信路径和config.data路径)"""
    if CONFIG_FILE.exists():
        try:
            return json.loads(CONFIG_FILE.read_text(encoding="utf-8"))
        except Exception:
            # 配置文件损坏时返回空字典
            return {}
    return {}
def save_config(cfg: dict):
    """保存配置文件(将当前设置的路径写入文件)"""
    try:
        CONFIG_FILE.write_text(
            json.dumps(cfg, ensure_ascii=False, indent=2),
            encoding="utf-8"
        )
    except Exception as e:
        messagebox.showerror("错误", f"保存配置文件失败: {e}")
def ensure_backup_dir(config_path):
    """确保账号配置备份目录存在(用于存储多账号的config.data)"""
    cfg_parent = Path(config_path).parent
    backup_dir = cfg_parent / "config_backup"
    backup_dir.mkdir(parents=True, exist_ok=True)  # 不存在则创建,存在则忽略
    return backup_dir
def list_saved_accounts(backup_dir):
    """列出所有已保存的微信账号(从备份目录读取)"""
    accounts = []
    for folder in backup_dir.iterdir():
        # 账号文件夹以数字命名(如1、2、3...)
        if folder.is_dir() and folder.name.isdigit():
            # 默认显示名称为“账号+ID”
            display_name = f"账号 {folder.name}"
            # 读取账号元数据(如果有自定义名称)
            meta_file = folder / "account.meta"
            if meta_file.exists():
                try:
                    meta_data = json.loads(meta_file.read_text(encoding="utf-8"))
                    display_name = meta_data.get("displayName", display_name)
                except Exception:
                    # 元数据损坏时使用默认名称
                    pass
            # 添加账号信息到列表
            accounts.append({
                "id": int(folder.name),
                "folder": folder,
                "displayName": display_name
            })
    # 按账号ID升序排序
    accounts.sort(key=lambda x: x["id"])
    return accounts
def save_current_account(config_path, backup_dir, display_name=None):
    """保存当前登录的微信账号(复制当前config.data到备份目录)"""
    # 1. 检查是否有同名账号,有则覆盖更新
    if display_name:
        existing_accounts = list_saved_accounts(backup_dir)
        for account in existing_accounts:
            if account["displayName"] == display_name:
                # 覆盖该账号的config.data
                shutil.copy2(config_path, account["folder"] / "config.data")
                # 更新账号元数据(确保名称一致)
                meta_data = {"displayName": display_name}
                (account["folder"] / "account.meta").write_text(
                    json.dumps(meta_data, ensure_ascii=False),
                    encoding="utf-8"
                )
                return account["id"]  # 返回已存在的账号ID
    # 2. 无同名账号则新建账号文件夹
    # 获取已有的账号ID列表(用于生成新ID)
    existing_ids = [
        int(f.name) for f in backup_dir.iterdir()
        if f.is_dir() and f.name.isdigit()
    ]
    new_id = max(existing_ids) + 1 if existing_ids else 1  # 新ID=最大ID+1,无则为1
    new_account_folder = backup_dir / str(new_id)
    new_account_folder.mkdir(parents=True, exist_ok=True)  # 创建新账号文件夹
    # 复制当前config.data到新账号文件夹
    shutil.copy2(config_path, new_account_folder / "config.data")
    # 写入账号元数据(自定义名称或默认名称)
    meta_data = {"displayName": display_name or f"账号 {new_id}"}
    (new_account_folder / "account.meta").write_text(
        json.dumps(meta_data, ensure_ascii=False),
        encoding="utf-8"
    )
    return new_id  # 返回新创建的账号ID
def start_wechat_with_config(exe_path, config_path, backup_dir, account_folder=None):
    """使用指定账号的配置启动微信(免密登录)"""
    # 检查目标config.data是否存在
    if not Path(config_path).exists():
        raise FileNotFoundError("目标 config.data 不存在")
    # 检查账号文件夹是否提供
    if account_folder is None:
        raise ValueError("account_folder 必须提供")
   
    # 1. 备份当前的config.data(防止覆盖原账号)
    current_backup_path = backup_dir / "current_config.data"
    shutil.copy2(config_path, current_backup_path)
   
    # 2. 复制选中账号的config.data到微信配置目录
    target_config = Path(account_folder) / "config.data"
    if not target_config.exists():
        raise FileNotFoundError("选定账号的 config.data 丢失")
    shutil.copy2(target_config, config_path)
   
    # 3. 启动微信(不显示控制台窗口)
    subprocess.Popen([exe_path], shell=False)
class WeChatGUI:
    """微信多账号管理图形界面(所有交互功能)"""
    def __init__(self, root):
        self.root = root
        self.root.title("微信多账号免密启动")
        self.config = load_config()  # 加载已保存的路径配置
        # -------------------------- 路径输入区域 --------------------------
        # 微信EXE路径输入
        tk.Label(root, text="微信 EXE 路径:").grid(row=0, column=0, sticky="e", padx=5, pady=3)
        self.exe_var = tk.StringVar(value=self.config.get("ExePath", ""))  # 填充已保存的路径
        tk.Entry(root, textvariable=self.exe_var, width=50).grid(row=0, column=1, padx=5, pady=3)
        tk.Button(root, text="选择", command=self.select_exe).grid(row=0, column=2, padx=5, pady=3)
        # config.data路径输入
        tk.Label(root, text="config.data 路径:").grid(row=1, column=0, sticky="e", padx=5, pady=3)
        self.config_var = tk.StringVar(value=self.config.get("ConfigPath", ""))  # 填充已保存的路径
        tk.Entry(root, textvariable=self.config_var, width=50).grid(row=1, column=1, padx=5, pady=3)
        tk.Button(root, text="选择", command=self.select_config).grid(row=1, column=2, padx=5, pady=3)
        # 保存设置按钮
        tk.Button(
            root, text="保存设置", command=self.save_settings
        ).grid(row=2, column=0, columnspan=3, pady=5)
        # -------------------------- 账号列表区域 --------------------------
        self.account_listbox = tk.Listbox(root, height=10, width=50)
        self.account_listbox.grid(row=3, column=0, columnspan=3, pady=5, padx=5)
        # -------------------------- 操作按钮区域 --------------------------
        # 启动选中账号
        tk.Button(
            root, text="启动选中账号", command=self.start_selected
        ).grid(row=4, column=0, padx=5, pady=3)
        # 保存当前账号
        tk.Button(
            root, text="保存当前账号", command=self.save_current
        ).grid(row=4, column=1, padx=5, pady=3)
        # 删除选中账号
        tk.Button(
            root, text="删除选中账号", command=self.delete_selected
        ).grid(row=4, column=2, padx=5, pady=3)
        # 刷新列表按钮
        tk.Button(
            root, text="刷新列表", command=self.refresh_list
        ).grid(row=5, column=0, columnspan=3, pady=5)
        # 初始加载账号列表
        self.refresh_list()
    def select_exe(self):
        """打开文件选择器,选择微信WeChat.exe路径"""
        path = filedialog.askopenfilename(
            title="选择 WeChat.exe",
            filetypes=[("微信程序", "*.exe")],  # 只显示EXE文件
            initialdir="C:\\Program Files\\Tencent\\WeChat"  # 默认打开微信安装目录
        )
        if path:
            self.exe_var.set(path)
    def select_config(self):
        """打开文件选择器,选择微信config.data路径"""
        # 微信config.data默认路径(提前定位到常见目录)
        default_dir = Path(os.environ.get("APPDATA", "")) / "Tencent" / "WeChat" / "All Users"
        path = filedialog.askopenfilename(
            title="选择 config.data",
            filetypes=[("config.data文件", "config.data")],  # 只显示config.data
            initialdir=str(default_dir) if default_dir.exists() else ""
        )
        if path:
            self.config_var.set(path)
    def save_settings(self):
        """验证并保存当前设置的路径(微信EXE+config.data)"""
        exe_path = self.exe_var.get().strip()
        cfg_path = self.config_var.get().strip()
        # 验证路径有效性
        if not Path(exe_path).exists():
            messagebox.showerror("错误", "微信 exe 路径无效(文件不存在)")
            return
        if not Path(cfg_path).exists():
            messagebox.showerror("错误", "config.data 路径无效(文件不存在)")
            return
        # 保存到配置文件
        self.config["ExePath"] = exe_path
        self.config["ConfigPath"] = cfg_path
        save_config(self.config)
        messagebox.showinfo("提示", "设置已保存(下次启动自动加载)")
    def refresh_list(self):
        """刷新账号列表(从备份目录读取最新账号)"""
        self.account_listbox.delete(0, tk.END)  # 清空现有列表
        cfg_path = self.config.get("ConfigPath")
        # 检查config.data路径是否有效
        if not cfg_path or not Path(cfg_path).exists():
            self.account_listbox.insert(tk.END, "⚠ 请先设置正确的 config.data 路径")
            self.accounts = []  # 清空账号列表缓存
            return
        # 读取备份目录的账号
        backup_dir = ensure_backup_dir(cfg_path)
        self.accounts = list_saved_accounts(backup_dir)
        # 填充账号到列表框
        if not self.accounts:
            self.account_listbox.insert(tk.END, "暂无已保存的账号(可点击'保存当前账号'添加)")
        else:
            for account in self.accounts:
                self.account_listbox.insert(
                    tk.END, f"{account['displayName']} (ID: {account['id']})"
                )
    def start_selected(self):
        """启动选中的微信账号(免密登录)"""
        # 检查账号列表是否为空
        if not hasattr(self, "accounts") or not self.accounts:
            messagebox.showwarning("提示", "暂无已保存的账号")
            return
        # 检查是否选择了账号
        selected_indices = self.account_listbox.curselection()
        if not selected_indices:
            messagebox.showwarning("提示", "请先选择一个账号")
            return
        # 获取选中的账号信息
        selected_idx = selected_indices[0]
        selected_account = self.accounts[selected_idx]
        try:
            # 启动微信(使用选中账号的配置)
            start_wechat_with_config(
                exe_path=self.config["ExePath"],
                config_path=self.config["ConfigPath"],
                backup_dir=ensure_backup_dir(self.config["ConfigPath"]),
                account_folder=selected_account["folder"]
            )
            messagebox.showinfo("提示", f"已启动账号:{selected_account['displayName']}\n(如无异常会免密登录)")
        except Exception as e:
            messagebox.showerror("启动失败", str(e))
    def save_current(self):
        """保存当前登录的微信账号(基于当前config.data)"""
        cfg_path = self.config.get("ConfigPath")
        # 检查config.data路径是否有效
        if not cfg_path or not Path(cfg_path).exists():
            messagebox.showerror("错误", "请先设置正确的 config.data 路径")
            return
        # 询问用户账号显示名称(可留空)
        display_name = simpledialog.askstring(
            "保存账号",
            "请输入账号显示名称(可留空,默认格式:账号+ID):",
            parent=self.root
        )
        # 用户点击取消则不保存
        if display_name is None:
            return
        try:
            # 保存账号并获取账号ID
            backup_dir = ensure_backup_dir(cfg_path)
            new_account_id = save_current_account(cfg_path, backup_dir, display_name.strip() if display_name else None)
            messagebox.showinfo("提示", f"账号保存成功!\n账号ID:{new_account_id}\n显示名称:{display_name or f'账号 {new_account_id}'}")
            self.refresh_list()  # 刷新列表显示新账号
        except Exception as e:
            messagebox.showerror("保存失败", str(e))
    def delete_selected(self):
        """删除选中的微信账号(从备份目录删除)"""
        # 检查账号列表是否为空
        if not hasattr(self, "accounts") or not self.accounts:
            messagebox.showwarning("提示", "暂无已保存的账号")
            return
        # 检查是否选择了账号
        selected_indices = self.account_listbox.curselection()
        if not selected_indices:
            messagebox.showwarning("提示", "请先选择一个要删除的账号")
            return
        # 获取选中的账号信息
        selected_idx = selected_indices[0]
        selected_account = self.accounts[selected_idx]
        # 二次确认删除(防止误操作)
        confirm = messagebox.askyesno(
            "确认删除",
            f"确定要删除账号「{selected_account['displayName']}(ID: {selected_account['id']})」吗?\n删除后无法恢复!",
            parent=self.root
        )
        if not confirm:
            return
        try:
            # 删除账号文件夹(包含config.data和元数据)
            shutil.rmtree(selected_account["folder"])
            messagebox.showinfo("提示", f"账号「{selected_account['displayName']}」已成功删除")
            self.refresh_list()  # 刷新列表移除已删除账号
        except Exception as e:
            messagebox.showerror("删除失败", str(e))
if __name__ == "__main__":
    # 启动GUI程序
    root = tk.Tk()
    app = WeChatGUI(root)
    root.mainloop()
SSS785399141   

通过网盘分享的文件:微信多账号免密启动.exe
链接: https://pan.baidu.com/s/1bo38hyrD0buzKIJktzfzeg?pwd=52pj 提取码: 52pj
--来自百度网盘超级会员v6的分享
xwlsjs   

这个,感觉很骚气的同时,又少了安全性呀
danshiyuan   

能把编译好的放出来么?
xfmiao   

可以吧同事电脑的config.data弄过来了,直接登录了
m-chris   

a ?还能这么玩么,不是还得各种验证呢么
daymissed   

还能这么玩真是没有想到,感谢分享。
so1474764500   

会不会封号
bokxp   

估计不好使,我的电脑todesk登录后,经常性大概率的下线要重新扫码才能进微信。
您需要登录后才可以回帖 登录 | 立即注册

返回顶部