import os import re import random import shutil import pathlib import platform from uuid import uuid4 from logger import info, warning, error from config import CURSOR_PATH # 颜色常量定义,保留用于日志输出 GREEN = "\033[92m" RED = "\033[91m" YELLOW = "\033[93m" BLUE = "\033[96m" PURPLE = "\033[95m" RESET = "\033[0m" SYSTEM = platform.system() if SYSTEM not in ("Windows", "Linux", "Darwin"): raise OSError(f"不支持的操作系统: {SYSTEM}") def uuid(): """生成随机UUID""" return str(uuid4()) def path(path_str): """获取绝对路径""" return pathlib.Path(path_str).resolve() def randomuuid(randomuuid_str): """获取随机UUID,如果提供则使用提供的值""" if not randomuuid_str: randomuuid_str = uuid() return randomuuid_str def random_mac(): """生成随机MAC地址""" mac = [ 0x00, 0x16, 0x3E, random.randint(0x00, 0x7F), random.randint(0x00, 0xFF), random.randint(0x00, 0xFF), ] return ":".join(map(lambda x: "%02x" % x, mac)) def load(file_path: pathlib.Path): """加载文件内容""" with open(file_path, "rb") as f: return f.read() def save(file_path: pathlib.Path, data: bytes): """保存文件内容""" with open(file_path, "wb") as f: f.write(data) def backup(file_path: pathlib.Path): """备份文件""" backup_path = file_path.with_suffix(file_path.suffix + ".bak") if not backup_path.exists(): shutil.copy2(file_path, backup_path) print(f"已备份 {file_path} -> {backup_path}") def replace(data: bytes, pattern: str, replace_str: str, probe: str = None) -> bytes: """替换文件内容""" pattern_bytes = pattern.encode() if isinstance(pattern, str) else pattern replace_bytes = ( replace_str.encode() if isinstance(replace_str, str) else replace_str ) if probe: probe_bytes = probe.encode() if isinstance(probe, str) else probe if re.search(probe_bytes, data): print("检测到已经被修补的代码,跳过...") return data return re.sub(pattern_bytes, replace_bytes, data) def find_main_js(): """查找Cursor的main.js文件""" error(f"SYSTEM: {SYSTEM}") if SYSTEM == "Windows": localappdata = os.getenv("LOCALAPPDATA") if not localappdata: raise OSError("环境变量 %LOCALAPPDATA% 不存在") # 使用本地变量保存路径 cursor_path = CURSOR_PATH if not cursor_path: error("当前windows系统, 环境变量 CURSOR_PATH 不存在,使用默认路径") cursor_path = os.getenv("LOCALAPPDATA", "") else: info(f"当前windows系统, CURSOR_PATH: {cursor_path}") # 常见的Cursor安装路径 paths = [ path(os.path.join(cursor_path, "resources", "app", "out", "main.js")), path( os.path.join( localappdata, "Programs", "cursor", "resources", "app", "out", "main.js", ) ), path( os.path.join( localappdata, "cursor", "resources", "app", "out", "main.js" ) ), ] for p in paths: info(f"检查路径: {p}") if p.exists(): info(f"找到main.js: {p}") return p else: warning(f"路径不存在: {p}") elif SYSTEM == "Darwin": # macOS paths = [ path("/Applications/Cursor.app/Contents/Resources/app/out/main.js"), path( os.path.expanduser( "~/Applications/Cursor.app/Contents/Resources/app/out/main.js" ) ), ] for p in paths: if p.exists(): return p elif SYSTEM == "Linux": # Linux上常见的安装路径 paths = [ path("/usr/share/cursor/resources/app/out/main.js"), path(os.path.expanduser("~/.local/share/cursor/resources/app/out/main.js")), ] for p in paths: if p.exists(): return p raise FileNotFoundError("无法找到Cursor的main.js文件,请手动指定路径") def patch_cursor( js_path=None, machine_id=None, mac_addr=None, sqm_id=None, dev_id=None ): """ 修补Cursor的main.js文件,替换机器ID等识别信息 参数: js_path: main.js文件路径,如果为None则自动查找 machine_id: 机器ID,如果为None则随机生成 mac_addr: MAC地址,如果为None则随机生成 sqm_id: Windows SQM ID,如果为None则使用空字符串 dev_id: 设备ID,如果为None则随机生成 返回: bool: 是否成功 """ try: # 查找main.js文件 if not js_path: js_path = find_main_js() else: js_path = path(js_path) # 如果找不到main.js文件 if not js_path.exists(): print(f"错误: 找不到文件 {js_path}") return False print(f"找到main.js文件: {js_path}") # 随机生成ID machine_id = randomuuid(machine_id) mac_addr = mac_addr or random_mac() sqm_id = sqm_id or "" dev_id = randomuuid(dev_id) # 加载文件内容 data = load(js_path) # 备份文件 backup(js_path) # 替换机器ID data = replace( data, r"=.{0,50}timeout.{0,10}5e3.*?,", f'=/*csp1*/"{machine_id}"/*1csp*/,', r"=/\*csp1\*/.*?/\*1csp\*/,", ) # 替换MAC地址 data = replace( data, r"(function .{0,50}\{).{0,300}Unable to retrieve mac address.*?(\})", f'\\1return/*csp2*/"{mac_addr}"/*2csp*/;\\2', r"()return/\*csp2\*/.*?/\*2csp\*/;()", ) # 替换SQM ID data = replace( data, r'return.{0,50}\.GetStringRegKey.*?HKEY_LOCAL_MACHINE.*?MachineId.*?\|\|.*?""', f'return/*csp3*/"{sqm_id}"/*3csp*/', r"return/\*csp3\*/.*?/\*3csp\*/", ) # 替换设备ID data = replace( data, r"return.{0,50}vscode\/deviceid.*?getDeviceId\(\)", f'return/*csp4*/"{dev_id}"/*4csp*/', r"return/\*csp4\*/.*?/\*4csp\*/", ) # 保存修改后的文件 save(js_path, data) print(f"成功修补 {js_path}") print(f"机器ID: {machine_id}") print(f"MAC地址: {mac_addr}") print(f"SQM ID: {sqm_id}") print(f"设备ID: {dev_id}") return True except Exception as e: print(f"错误: {str(e)}") import traceback traceback.print_exc() return False class CursorShadowPatcher: """Cursor机器标识修改器""" @staticmethod def reset_machine_ids(): """重置所有机器标识""" return patch_cursor() if __name__ == "__main__": # 作为独立脚本运行时,执行交互式修补 print(f"\n{'=' * 50}") print("Cursor 机器标识重置工具 (Shadow Patch 增强版)") print(f"{'=' * 50}") js_path = input("请输入main.js路径 (留空=自动检测): ") machine_id = input("机器ID (留空=随机生成): ") mac_addr = input("MAC地址 (留空=随机生成): ") sqm_id = input("Windows SQM ID (留空=使用空值): ") dev_id = input("设备ID (留空=随机生成): ") success = patch_cursor(js_path, machine_id, mac_addr, sqm_id, dev_id) if success: print(f"\n{'=' * 50}") print("修补成功!") else: print(f"\n{'=' * 50}") print("修补失败!") input("按回车键退出...")