289 lines
7.8 KiB
Python
289 lines
7.8 KiB
Python
|
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("按回车键退出...")
|