cursor_auto_register/cursor_shadow_patcher.py
2025-05-11 23:06:57 +08:00

289 lines
7.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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("按回车键退出...")