mirror of
https://github.com/yuaotian/go-cursor-help.git
synced 2026-03-07 14:24:37 +00:00
feat(cursor): 实现跨平台设备标识符Hook注入方案
- 新增 cursor_hook.js 核心Hook模块,拦截child_process、crypto、os等关键模块 - 实现统一ID配置管理,支持环境变量和JSON配置文件双重加载机制 - 开发Unix/macOS注入脚本(inject_hook_unix.sh),自动化Hook代码注入流程 - 开发Windows注入脚本(inject_hook_win.ps1),适配PowerShell环境 - 升级Linux修改器脚本,集成新的Hook方案并优化备份机制 - 添加完整的调试日志系统和防重复注入保护机制 - 支持动态import模块Hook,增强对ESM环境的兼容性
This commit is contained in:
477
scripts/hook/cursor_hook.js
Normal file
477
scripts/hook/cursor_hook.js
Normal file
@@ -0,0 +1,477 @@
|
||||
/**
|
||||
* Cursor 设备标识符 Hook 模块
|
||||
*
|
||||
* 🎯 功能:从底层拦截所有设备标识符的生成,实现一劳永逸的机器码修改
|
||||
*
|
||||
* 🔧 Hook 点:
|
||||
* 1. child_process.execSync - 拦截 REG.exe 查询 MachineGuid
|
||||
* 2. crypto.createHash - 拦截 SHA256 哈希计算
|
||||
* 3. @vscode/deviceid - 拦截 devDeviceId 获取
|
||||
* 4. @vscode/windows-registry - 拦截注册表读取
|
||||
* 5. os.networkInterfaces - 拦截 MAC 地址获取
|
||||
*
|
||||
* 📦 使用方式:
|
||||
* 将此代码注入到 main.js 文件顶部(Sentry 初始化之后)
|
||||
*
|
||||
* ⚙️ 配置方式:
|
||||
* 1. 环境变量:CURSOR_MACHINE_ID, CURSOR_MAC_MACHINE_ID, CURSOR_DEV_DEVICE_ID, CURSOR_SQM_ID
|
||||
* 2. 配置文件:~/.cursor_ids.json
|
||||
* 3. 自动生成:如果没有配置,则自动生成并持久化
|
||||
*/
|
||||
|
||||
// ==================== 配置区域 ====================
|
||||
// 使用 var 确保在 ES Module 环境中也能正常工作
|
||||
var __cursor_hook_config__ = {
|
||||
// 是否启用 Hook(设置为 false 可临时禁用)
|
||||
enabled: true,
|
||||
// 是否输出调试日志(设置为 true 可查看详细日志)
|
||||
debug: false,
|
||||
// 配置文件路径(相对于用户目录)
|
||||
configFileName: '.cursor_ids.json',
|
||||
// 标记:防止重复注入
|
||||
injected: false
|
||||
};
|
||||
|
||||
// ==================== Hook 实现 ====================
|
||||
// 使用 IIFE 确保代码立即执行
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// 防止重复注入
|
||||
if (globalThis.__cursor_patched__ || __cursor_hook_config__.injected) {
|
||||
return;
|
||||
}
|
||||
globalThis.__cursor_patched__ = true;
|
||||
__cursor_hook_config__.injected = true;
|
||||
|
||||
// 调试日志函数
|
||||
const log = (...args) => {
|
||||
if (__cursor_hook_config__.debug) {
|
||||
console.log('[CursorHook]', ...args);
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== ID 生成和管理 ====================
|
||||
|
||||
// 生成 UUID v4
|
||||
const generateUUID = () => {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
};
|
||||
|
||||
// 生成 64 位十六进制字符串(用于 machineId)
|
||||
const generateHex64 = () => {
|
||||
let hex = '';
|
||||
for (let i = 0; i < 64; i++) {
|
||||
hex += Math.floor(Math.random() * 16).toString(16);
|
||||
}
|
||||
return hex;
|
||||
};
|
||||
|
||||
// 生成 MAC 地址格式的字符串
|
||||
const generateMacAddress = () => {
|
||||
const hex = '0123456789ABCDEF';
|
||||
let mac = '';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
if (i > 0) mac += ':';
|
||||
mac += hex[Math.floor(Math.random() * 16)];
|
||||
mac += hex[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
return mac;
|
||||
};
|
||||
|
||||
// 加载或生成 ID 配置
|
||||
// 注意:使用 createRequire 来支持 ES Module 环境
|
||||
const loadOrGenerateIds = () => {
|
||||
// 在 ES Module 环境中,需要使用 createRequire 来加载 CommonJS 模块
|
||||
let fs, path, os;
|
||||
try {
|
||||
// 尝试使用 Node.js 内置模块
|
||||
const { createRequire } = require('module');
|
||||
const require2 = createRequire(import.meta?.url || __filename);
|
||||
fs = require2('fs');
|
||||
path = require2('path');
|
||||
os = require2('os');
|
||||
} catch (e) {
|
||||
// 回退到直接 require
|
||||
fs = require('fs');
|
||||
path = require('path');
|
||||
os = require('os');
|
||||
}
|
||||
|
||||
const configPath = path.join(os.homedir(), __cursor_hook_config__.configFileName);
|
||||
|
||||
let ids = null;
|
||||
|
||||
// 尝试从环境变量读取
|
||||
if (process.env.CURSOR_MACHINE_ID) {
|
||||
ids = {
|
||||
machineId: process.env.CURSOR_MACHINE_ID,
|
||||
macMachineId: process.env.CURSOR_MAC_MACHINE_ID || generateHex64(),
|
||||
devDeviceId: process.env.CURSOR_DEV_DEVICE_ID || generateUUID(),
|
||||
sqmId: process.env.CURSOR_SQM_ID || `{${generateUUID().toUpperCase()}}`
|
||||
};
|
||||
log('从环境变量加载 ID 配置');
|
||||
return ids;
|
||||
}
|
||||
|
||||
// 尝试从配置文件读取
|
||||
try {
|
||||
if (fs.existsSync(configPath)) {
|
||||
const content = fs.readFileSync(configPath, 'utf8');
|
||||
ids = JSON.parse(content);
|
||||
log('从配置文件加载 ID 配置:', configPath);
|
||||
return ids;
|
||||
}
|
||||
} catch (e) {
|
||||
log('读取配置文件失败:', e.message);
|
||||
}
|
||||
|
||||
// 生成新的 ID
|
||||
ids = {
|
||||
machineId: generateHex64(),
|
||||
macMachineId: generateHex64(),
|
||||
devDeviceId: generateUUID(),
|
||||
sqmId: `{${generateUUID().toUpperCase()}}`,
|
||||
macAddress: generateMacAddress(),
|
||||
createdAt: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 保存到配置文件
|
||||
try {
|
||||
fs.writeFileSync(configPath, JSON.stringify(ids, null, 2), 'utf8');
|
||||
log('已生成并保存新的 ID 配置:', configPath);
|
||||
} catch (e) {
|
||||
log('保存配置文件失败:', e.message);
|
||||
}
|
||||
|
||||
return ids;
|
||||
};
|
||||
|
||||
// 加载 ID 配置
|
||||
const __cursor_ids__ = loadOrGenerateIds();
|
||||
log('当前 ID 配置:', __cursor_ids__);
|
||||
|
||||
// ==================== Module Hook ====================
|
||||
|
||||
const Module = require('module');
|
||||
const originalRequire = Module.prototype.require;
|
||||
|
||||
// 缓存已 Hook 的模块
|
||||
const hookedModules = new Map();
|
||||
|
||||
Module.prototype.require = function(id) {
|
||||
const result = originalRequire.apply(this, arguments);
|
||||
|
||||
// 如果已经 Hook 过,直接返回缓存
|
||||
if (hookedModules.has(id)) {
|
||||
return hookedModules.get(id);
|
||||
}
|
||||
|
||||
let hooked = result;
|
||||
|
||||
// Hook child_process 模块
|
||||
if (id === 'child_process') {
|
||||
hooked = hookChildProcess(result);
|
||||
}
|
||||
// Hook os 模块
|
||||
else if (id === 'os') {
|
||||
hooked = hookOs(result);
|
||||
}
|
||||
// Hook crypto 模块
|
||||
else if (id === 'crypto') {
|
||||
hooked = hookCrypto(result);
|
||||
}
|
||||
// Hook @vscode/deviceid 模块
|
||||
else if (id === '@vscode/deviceid') {
|
||||
hooked = hookDeviceId(result);
|
||||
}
|
||||
// Hook @vscode/windows-registry 模块
|
||||
else if (id === '@vscode/windows-registry') {
|
||||
hooked = hookWindowsRegistry(result);
|
||||
}
|
||||
|
||||
// 缓存 Hook 结果
|
||||
if (hooked !== result) {
|
||||
hookedModules.set(id, hooked);
|
||||
log(`已 Hook 模块: ${id}`);
|
||||
}
|
||||
|
||||
return hooked;
|
||||
};
|
||||
|
||||
// ==================== child_process Hook ====================
|
||||
|
||||
function hookChildProcess(cp) {
|
||||
const originalExecSync = cp.execSync;
|
||||
|
||||
cp.execSync = function(command, options) {
|
||||
const cmdStr = String(command).toLowerCase();
|
||||
|
||||
// 拦截 MachineGuid 查询
|
||||
if (cmdStr.includes('reg') && cmdStr.includes('machineguid')) {
|
||||
log('拦截 MachineGuid 查询');
|
||||
// 返回格式化的注册表输出
|
||||
return Buffer.from(`\r\n MachineGuid REG_SZ ${__cursor_ids__.machineId.substring(0, 36)}\r\n`);
|
||||
}
|
||||
|
||||
// 拦截 ioreg 命令 (macOS)
|
||||
if (cmdStr.includes('ioreg') && cmdStr.includes('ioplatformexpertdevice')) {
|
||||
log('拦截 IOPlatformUUID 查询');
|
||||
return Buffer.from(`"IOPlatformUUID" = "${__cursor_ids__.machineId.substring(0, 36).toUpperCase()}"`);
|
||||
}
|
||||
|
||||
// 拦截 machine-id 读取 (Linux)
|
||||
if (cmdStr.includes('machine-id') || cmdStr.includes('hostname')) {
|
||||
log('拦截 machine-id 查询');
|
||||
return Buffer.from(__cursor_ids__.machineId.substring(0, 32));
|
||||
}
|
||||
|
||||
return originalExecSync.apply(this, arguments);
|
||||
};
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
// ==================== os Hook ====================
|
||||
|
||||
function hookOs(os) {
|
||||
const originalNetworkInterfaces = os.networkInterfaces;
|
||||
|
||||
os.networkInterfaces = function() {
|
||||
log('拦截 networkInterfaces 调用');
|
||||
// 返回虚拟的网络接口,使用固定的 MAC 地址
|
||||
return {
|
||||
'Ethernet': [{
|
||||
address: '192.168.1.100',
|
||||
netmask: '255.255.255.0',
|
||||
family: 'IPv4',
|
||||
mac: __cursor_ids__.macAddress || '00:00:00:00:00:00',
|
||||
internal: false
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
// ==================== crypto Hook ====================
|
||||
|
||||
function hookCrypto(crypto) {
|
||||
const originalCreateHash = crypto.createHash;
|
||||
const originalRandomUUID = crypto.randomUUID;
|
||||
|
||||
// Hook createHash - 用于拦截 machineId 的 SHA256 计算
|
||||
crypto.createHash = function(algorithm) {
|
||||
const hash = originalCreateHash.apply(this, arguments);
|
||||
|
||||
if (algorithm.toLowerCase() === 'sha256') {
|
||||
const originalUpdate = hash.update.bind(hash);
|
||||
const originalDigest = hash.digest.bind(hash);
|
||||
|
||||
let inputData = '';
|
||||
|
||||
hash.update = function(data, encoding) {
|
||||
inputData += String(data);
|
||||
return originalUpdate(data, encoding);
|
||||
};
|
||||
|
||||
hash.digest = function(encoding) {
|
||||
// 检查是否是 machineId 相关的哈希计算
|
||||
if (inputData.includes('MachineGuid') ||
|
||||
inputData.includes('IOPlatformUUID') ||
|
||||
inputData.length === 32 ||
|
||||
inputData.length === 36) {
|
||||
log('拦截 SHA256 哈希计算,返回固定 machineId');
|
||||
if (encoding === 'hex') {
|
||||
return __cursor_ids__.machineId;
|
||||
}
|
||||
return Buffer.from(__cursor_ids__.machineId, 'hex');
|
||||
}
|
||||
return originalDigest(encoding);
|
||||
};
|
||||
}
|
||||
|
||||
return hash;
|
||||
};
|
||||
|
||||
// Hook randomUUID - 用于拦截 devDeviceId 生成
|
||||
if (originalRandomUUID) {
|
||||
let uuidCallCount = 0;
|
||||
crypto.randomUUID = function() {
|
||||
uuidCallCount++;
|
||||
// 第一次调用返回固定的 devDeviceId
|
||||
if (uuidCallCount <= 2) {
|
||||
log('拦截 randomUUID 调用,返回固定 devDeviceId');
|
||||
return __cursor_ids__.devDeviceId;
|
||||
}
|
||||
return originalRandomUUID.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
return crypto;
|
||||
}
|
||||
|
||||
// ==================== @vscode/deviceid Hook ====================
|
||||
|
||||
function hookDeviceId(deviceIdModule) {
|
||||
log('Hook @vscode/deviceid 模块');
|
||||
|
||||
return {
|
||||
...deviceIdModule,
|
||||
getDeviceId: async function() {
|
||||
log('拦截 getDeviceId 调用');
|
||||
return __cursor_ids__.devDeviceId;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ==================== @vscode/windows-registry Hook ====================
|
||||
|
||||
function hookWindowsRegistry(registryModule) {
|
||||
log('Hook @vscode/windows-registry 模块');
|
||||
|
||||
const originalGetStringRegKey = registryModule.GetStringRegKey;
|
||||
|
||||
return {
|
||||
...registryModule,
|
||||
GetStringRegKey: function(hive, path, name) {
|
||||
// 拦截 MachineId 读取
|
||||
if (name === 'MachineId' || path.includes('SQMClient')) {
|
||||
log('拦截注册表 MachineId/SQMClient 读取');
|
||||
return __cursor_ids__.sqmId;
|
||||
}
|
||||
// 拦截 MachineGuid 读取
|
||||
if (name === 'MachineGuid' || path.includes('Cryptography')) {
|
||||
log('拦截注册表 MachineGuid 读取');
|
||||
return __cursor_ids__.machineId.substring(0, 36);
|
||||
}
|
||||
return originalGetStringRegKey?.apply(this, arguments) || '';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ==================== 动态 import Hook ====================
|
||||
|
||||
// Cursor 使用动态 import() 加载模块,我们需要 Hook 这些模块
|
||||
// 由于 ES Module 的限制,我们通过 Hook 全局对象来实现
|
||||
|
||||
// 存储已 Hook 的动态导入模块
|
||||
const hookedDynamicModules = new Map();
|
||||
|
||||
// Hook crypto 模块的动态导入
|
||||
const hookDynamicCrypto = (cryptoModule) => {
|
||||
if (hookedDynamicModules.has('crypto')) {
|
||||
return hookedDynamicModules.get('crypto');
|
||||
}
|
||||
|
||||
const hooked = { ...cryptoModule };
|
||||
|
||||
// Hook createHash
|
||||
if (cryptoModule.createHash) {
|
||||
const originalCreateHash = cryptoModule.createHash;
|
||||
hooked.createHash = function(algorithm) {
|
||||
const hash = originalCreateHash.apply(this, arguments);
|
||||
|
||||
if (algorithm.toLowerCase() === 'sha256') {
|
||||
const originalDigest = hash.digest.bind(hash);
|
||||
let inputData = '';
|
||||
|
||||
const originalUpdate = hash.update.bind(hash);
|
||||
hash.update = function(data, encoding) {
|
||||
inputData += String(data);
|
||||
return originalUpdate(data, encoding);
|
||||
};
|
||||
|
||||
hash.digest = function(encoding) {
|
||||
// 检测 machineId 相关的哈希
|
||||
if (inputData.includes('MachineGuid') ||
|
||||
inputData.includes('IOPlatformUUID') ||
|
||||
(inputData.length >= 32 && inputData.length <= 40)) {
|
||||
log('动态导入: 拦截 SHA256 哈希');
|
||||
return encoding === 'hex' ? __cursor_ids__.machineId : Buffer.from(__cursor_ids__.machineId, 'hex');
|
||||
}
|
||||
return originalDigest(encoding);
|
||||
};
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
}
|
||||
|
||||
hookedDynamicModules.set('crypto', hooked);
|
||||
return hooked;
|
||||
};
|
||||
|
||||
// Hook @vscode/deviceid 模块的动态导入
|
||||
const hookDynamicDeviceId = (deviceIdModule) => {
|
||||
if (hookedDynamicModules.has('@vscode/deviceid')) {
|
||||
return hookedDynamicModules.get('@vscode/deviceid');
|
||||
}
|
||||
|
||||
const hooked = {
|
||||
...deviceIdModule,
|
||||
getDeviceId: async () => {
|
||||
log('动态导入: 拦截 getDeviceId');
|
||||
return __cursor_ids__.devDeviceId;
|
||||
}
|
||||
};
|
||||
|
||||
hookedDynamicModules.set('@vscode/deviceid', hooked);
|
||||
return hooked;
|
||||
};
|
||||
|
||||
// Hook @vscode/windows-registry 模块的动态导入
|
||||
const hookDynamicWindowsRegistry = (registryModule) => {
|
||||
if (hookedDynamicModules.has('@vscode/windows-registry')) {
|
||||
return hookedDynamicModules.get('@vscode/windows-registry');
|
||||
}
|
||||
|
||||
const originalGetStringRegKey = registryModule.GetStringRegKey;
|
||||
const hooked = {
|
||||
...registryModule,
|
||||
GetStringRegKey: function(hive, path, name) {
|
||||
if (name === 'MachineId' || path?.includes('SQMClient')) {
|
||||
log('动态导入: 拦截 SQMClient');
|
||||
return __cursor_ids__.sqmId;
|
||||
}
|
||||
if (name === 'MachineGuid' || path?.includes('Cryptography')) {
|
||||
log('动态导入: 拦截 MachineGuid');
|
||||
return __cursor_ids__.machineId.substring(0, 36);
|
||||
}
|
||||
return originalGetStringRegKey?.apply(this, arguments) || '';
|
||||
}
|
||||
};
|
||||
|
||||
hookedDynamicModules.set('@vscode/windows-registry', hooked);
|
||||
return hooked;
|
||||
};
|
||||
|
||||
// 将 Hook 函数暴露到全局,供后续使用
|
||||
globalThis.__cursor_hook_dynamic__ = {
|
||||
crypto: hookDynamicCrypto,
|
||||
deviceId: hookDynamicDeviceId,
|
||||
windowsRegistry: hookDynamicWindowsRegistry,
|
||||
ids: __cursor_ids__
|
||||
};
|
||||
|
||||
log('Cursor Hook 初始化完成');
|
||||
log('machineId:', __cursor_ids__.machineId.substring(0, 16) + '...');
|
||||
log('devDeviceId:', __cursor_ids__.devDeviceId);
|
||||
log('sqmId:', __cursor_ids__.sqmId);
|
||||
|
||||
})();
|
||||
|
||||
// ==================== 导出配置(供外部使用) ====================
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = { __cursor_hook_config__ };
|
||||
}
|
||||
|
||||
// ==================== ES Module 兼容性 ====================
|
||||
// 如果在 ES Module 环境中,也暴露配置
|
||||
if (typeof globalThis !== 'undefined') {
|
||||
globalThis.__cursor_hook_config__ = __cursor_hook_config__;
|
||||
}
|
||||
|
||||
255
scripts/hook/inject_hook_unix.sh
Normal file
255
scripts/hook/inject_hook_unix.sh
Normal file
@@ -0,0 +1,255 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ========================================
|
||||
# Cursor Hook 注入脚本 (macOS/Linux)
|
||||
# ========================================
|
||||
#
|
||||
# 🎯 功能:将 cursor_hook.js 注入到 Cursor 的 main.js 文件顶部
|
||||
#
|
||||
# 📦 使用方式:
|
||||
# chmod +x inject_hook_unix.sh
|
||||
# ./inject_hook_unix.sh
|
||||
#
|
||||
# 参数:
|
||||
# --rollback 回滚到原始版本
|
||||
# --force 强制重新注入
|
||||
# --debug 启用调试模式
|
||||
#
|
||||
# ========================================
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 参数解析
|
||||
ROLLBACK=false
|
||||
FORCE=false
|
||||
DEBUG=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--rollback) ROLLBACK=true ;;
|
||||
--force) FORCE=true ;;
|
||||
--debug) DEBUG=true ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 日志函数
|
||||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
log_debug() { if $DEBUG; then echo -e "${BLUE}[DEBUG]${NC} $1"; fi; }
|
||||
|
||||
# 获取脚本所在目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
HOOK_SCRIPT="$SCRIPT_DIR/cursor_hook.js"
|
||||
|
||||
# 获取 Cursor main.js 路径
|
||||
get_cursor_path() {
|
||||
local paths=()
|
||||
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS
|
||||
paths=(
|
||||
"/Applications/Cursor.app/Contents/Resources/app/out/main.js"
|
||||
"$HOME/Applications/Cursor.app/Contents/Resources/app/out/main.js"
|
||||
)
|
||||
else
|
||||
# Linux
|
||||
paths=(
|
||||
"/opt/Cursor/resources/app/out/main.js"
|
||||
"/usr/share/cursor/resources/app/out/main.js"
|
||||
"$HOME/.local/share/cursor/resources/app/out/main.js"
|
||||
"/snap/cursor/current/resources/app/out/main.js"
|
||||
)
|
||||
fi
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
if [[ -f "$path" ]]; then
|
||||
echo "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# 检查是否已注入
|
||||
check_already_injected() {
|
||||
local main_js="$1"
|
||||
grep -q "__cursor_patched__" "$main_js" 2>/dev/null
|
||||
}
|
||||
|
||||
# 备份原始文件
|
||||
backup_main_js() {
|
||||
local main_js="$1"
|
||||
local backup_dir="$(dirname "$main_js")/backups"
|
||||
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
local timestamp=$(date +%Y%m%d_%H%M%S)
|
||||
local backup_path="$backup_dir/main.js.backup_$timestamp"
|
||||
local original_backup="$backup_dir/main.js.original"
|
||||
|
||||
# 创建原始备份(如果不存在)
|
||||
if [[ ! -f "$original_backup" ]]; then
|
||||
cp "$main_js" "$original_backup"
|
||||
log_info "已创建原始备份: $original_backup"
|
||||
fi
|
||||
|
||||
cp "$main_js" "$backup_path"
|
||||
log_info "已创建时间戳备份: $backup_path"
|
||||
|
||||
echo "$original_backup"
|
||||
}
|
||||
|
||||
# 回滚到原始版本
|
||||
restore_main_js() {
|
||||
local main_js="$1"
|
||||
local backup_dir="$(dirname "$main_js")/backups"
|
||||
local original_backup="$backup_dir/main.js.original"
|
||||
|
||||
if [[ -f "$original_backup" ]]; then
|
||||
cp "$original_backup" "$main_js"
|
||||
log_info "已回滚到原始版本"
|
||||
return 0
|
||||
else
|
||||
log_error "未找到原始备份文件"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 关闭 Cursor 进程
|
||||
stop_cursor_process() {
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS
|
||||
pkill -x "Cursor" 2>/dev/null || true
|
||||
pkill -x "Cursor Helper" 2>/dev/null || true
|
||||
else
|
||||
# Linux
|
||||
pkill -f "cursor" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
log_info "Cursor 进程已关闭"
|
||||
}
|
||||
|
||||
# 注入 Hook 代码
|
||||
inject_hook() {
|
||||
local main_js="$1"
|
||||
local hook_script="$2"
|
||||
|
||||
# 读取 Hook 脚本内容
|
||||
local hook_content=$(cat "$hook_script")
|
||||
|
||||
# 创建临时文件
|
||||
local temp_file=$(mktemp)
|
||||
|
||||
# 读取 main.js 并注入 Hook
|
||||
# 在版权声明之后注入
|
||||
awk -v hook="$hook_content" '
|
||||
/^\*\// && !injected {
|
||||
print
|
||||
print ""
|
||||
print "// ========== Cursor Hook 注入开始 =========="
|
||||
print hook
|
||||
print "// ========== Cursor Hook 注入结束 =========="
|
||||
print ""
|
||||
injected = 1
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$main_js" > "$temp_file"
|
||||
|
||||
# 替换原文件
|
||||
mv "$temp_file" "$main_js"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Cursor Hook 注入工具 (Unix) ${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# 获取 Cursor main.js 路径
|
||||
local main_js
|
||||
main_js=$(get_cursor_path) || {
|
||||
log_error "未找到 Cursor 安装路径"
|
||||
log_error "请确保 Cursor 已正确安装"
|
||||
exit 1
|
||||
}
|
||||
log_info "找到 Cursor main.js: $main_js"
|
||||
|
||||
# 回滚模式
|
||||
if $ROLLBACK; then
|
||||
log_info "执行回滚操作..."
|
||||
stop_cursor_process
|
||||
if restore_main_js "$main_js"; then
|
||||
log_info "回滚成功!"
|
||||
else
|
||||
log_error "回滚失败!"
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 检查是否已注入
|
||||
if check_already_injected "$main_js" && ! $FORCE; then
|
||||
log_warn "Hook 已经注入,无需重复操作"
|
||||
log_info "如需强制重新注入,请使用 --force 参数"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 检查 Hook 脚本是否存在
|
||||
if [[ ! -f "$HOOK_SCRIPT" ]]; then
|
||||
log_error "未找到 cursor_hook.js 文件"
|
||||
log_error "请确保 cursor_hook.js 与此脚本在同一目录"
|
||||
exit 1
|
||||
fi
|
||||
log_info "找到 Hook 脚本: $HOOK_SCRIPT"
|
||||
|
||||
# 关闭 Cursor 进程
|
||||
stop_cursor_process
|
||||
|
||||
# 备份原始文件
|
||||
log_info "正在备份原始文件..."
|
||||
backup_main_js "$main_js"
|
||||
|
||||
# 注入 Hook 代码
|
||||
log_info "正在注入 Hook 代码..."
|
||||
if inject_hook "$main_js" "$HOOK_SCRIPT"; then
|
||||
log_info "Hook 注入成功!"
|
||||
else
|
||||
log_error "Hook 注入失败!"
|
||||
log_warn "正在回滚..."
|
||||
restore_main_js "$main_js"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo -e "${GREEN} ✅ Hook 注入完成! ${NC}"
|
||||
echo -e "${GREEN}========================================${NC}"
|
||||
echo ""
|
||||
log_info "现在可以启动 Cursor 了"
|
||||
log_info "ID 配置文件位置: ~/.cursor_ids.json"
|
||||
echo ""
|
||||
echo -e "${YELLOW}提示:${NC}"
|
||||
echo " - 如需回滚,请运行: ./inject_hook_unix.sh --rollback"
|
||||
echo " - 如需强制重新注入,请运行: ./inject_hook_unix.sh --force"
|
||||
echo " - 如需启用调试日志,请运行: ./inject_hook_unix.sh --debug"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main
|
||||
|
||||
266
scripts/hook/inject_hook_win.ps1
Normal file
266
scripts/hook/inject_hook_win.ps1
Normal file
@@ -0,0 +1,266 @@
|
||||
# ========================================
|
||||
# Cursor Hook 注入脚本 (Windows)
|
||||
# ========================================
|
||||
#
|
||||
# 🎯 功能:将 cursor_hook.js 注入到 Cursor 的 main.js 文件顶部
|
||||
#
|
||||
# 📦 使用方式:
|
||||
# 1. 以管理员权限运行 PowerShell
|
||||
# 2. 执行: .\inject_hook_win.ps1
|
||||
#
|
||||
# ⚠️ 注意事项:
|
||||
# - 会自动备份原始 main.js 文件
|
||||
# - 支持回滚到原始版本
|
||||
# - Cursor 更新后需要重新注入
|
||||
#
|
||||
# ========================================
|
||||
|
||||
param(
|
||||
[switch]$Rollback, # 回滚到原始版本
|
||||
[switch]$Force, # 强制重新注入
|
||||
[switch]$Debug # 启用调试模式
|
||||
)
|
||||
|
||||
# 颜色定义
|
||||
$RED = "`e[31m"
|
||||
$GREEN = "`e[32m"
|
||||
$YELLOW = "`e[33m"
|
||||
$BLUE = "`e[34m"
|
||||
$NC = "`e[0m"
|
||||
|
||||
# 日志函数
|
||||
function Write-Log {
|
||||
param([string]$Message, [string]$Level = "INFO")
|
||||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
switch ($Level) {
|
||||
"INFO" { Write-Host "$GREEN[INFO]$NC $Message" }
|
||||
"WARN" { Write-Host "$YELLOW[WARN]$NC $Message" }
|
||||
"ERROR" { Write-Host "$RED[ERROR]$NC $Message" }
|
||||
"DEBUG" { if ($Debug) { Write-Host "$BLUE[DEBUG]$NC $Message" } }
|
||||
}
|
||||
}
|
||||
|
||||
# 获取 Cursor 安装路径
|
||||
function Get-CursorPath {
|
||||
$possiblePaths = @(
|
||||
"$env:LOCALAPPDATA\Programs\cursor\resources\app\out\main.js",
|
||||
"$env:LOCALAPPDATA\Programs\Cursor\resources\app\out\main.js",
|
||||
"C:\Program Files\Cursor\resources\app\out\main.js",
|
||||
"C:\Program Files (x86)\Cursor\resources\app\out\main.js"
|
||||
)
|
||||
|
||||
foreach ($path in $possiblePaths) {
|
||||
if (Test-Path $path) {
|
||||
return $path
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
# 获取 Hook 脚本路径
|
||||
function Get-HookScriptPath {
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$hookPath = Join-Path $scriptDir "cursor_hook.js"
|
||||
|
||||
if (Test-Path $hookPath) {
|
||||
return $hookPath
|
||||
}
|
||||
|
||||
# 尝试从当前目录查找
|
||||
$currentDir = Get-Location
|
||||
$hookPath = Join-Path $currentDir "cursor_hook.js"
|
||||
|
||||
if (Test-Path $hookPath) {
|
||||
return $hookPath
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
# 检查是否已注入
|
||||
function Test-AlreadyInjected {
|
||||
param([string]$MainJsPath)
|
||||
|
||||
$content = Get-Content $MainJsPath -Raw -Encoding UTF8
|
||||
return $content -match "__cursor_patched__"
|
||||
}
|
||||
|
||||
# 备份原始文件
|
||||
function Backup-MainJs {
|
||||
param([string]$MainJsPath)
|
||||
|
||||
$backupDir = Join-Path (Split-Path -Parent $MainJsPath) "backups"
|
||||
if (-not (Test-Path $backupDir)) {
|
||||
New-Item -ItemType Directory -Path $backupDir -Force | Out-Null
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$backupPath = Join-Path $backupDir "main.js.backup_$timestamp"
|
||||
|
||||
# 检查是否有原始备份
|
||||
$originalBackup = Join-Path $backupDir "main.js.original"
|
||||
if (-not (Test-Path $originalBackup)) {
|
||||
Copy-Item $MainJsPath $originalBackup -Force
|
||||
Write-Log "已创建原始备份: $originalBackup"
|
||||
}
|
||||
|
||||
Copy-Item $MainJsPath $backupPath -Force
|
||||
Write-Log "已创建时间戳备份: $backupPath"
|
||||
|
||||
return $originalBackup
|
||||
}
|
||||
|
||||
# 回滚到原始版本
|
||||
function Restore-MainJs {
|
||||
param([string]$MainJsPath)
|
||||
|
||||
$backupDir = Join-Path (Split-Path -Parent $MainJsPath) "backups"
|
||||
$originalBackup = Join-Path $backupDir "main.js.original"
|
||||
|
||||
if (Test-Path $originalBackup) {
|
||||
Copy-Item $originalBackup $MainJsPath -Force
|
||||
Write-Log "已回滚到原始版本" "INFO"
|
||||
return $true
|
||||
} else {
|
||||
Write-Log "未找到原始备份文件" "ERROR"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# 注入 Hook 代码
|
||||
function Inject-Hook {
|
||||
param(
|
||||
[string]$MainJsPath,
|
||||
[string]$HookScriptPath
|
||||
)
|
||||
|
||||
# 读取 Hook 脚本内容
|
||||
$hookContent = Get-Content $HookScriptPath -Raw -Encoding UTF8
|
||||
|
||||
# 读取 main.js 内容
|
||||
$mainContent = Get-Content $MainJsPath -Raw -Encoding UTF8
|
||||
|
||||
# 查找注入点:在 Sentry 初始化代码之后
|
||||
# Sentry 初始化代码特征: _sentryDebugIds
|
||||
$sentryPattern = '(?<=\}\(\);)\s*(?=var\s+\w+\s*=\s*function)'
|
||||
|
||||
if ($mainContent -match $sentryPattern) {
|
||||
# 在 Sentry 初始化之后注入
|
||||
$injectionPoint = $mainContent.IndexOf('}();') + 4
|
||||
$newContent = $mainContent.Substring(0, $injectionPoint) + "`n`n// ========== Cursor Hook 注入开始 ==========`n" + $hookContent + "`n// ========== Cursor Hook 注入结束 ==========`n`n" + $mainContent.Substring($injectionPoint)
|
||||
} else {
|
||||
# 如果找不到 Sentry,直接在文件开头注入(在版权声明之后)
|
||||
$copyrightEnd = $mainContent.IndexOf('*/') + 2
|
||||
if ($copyrightEnd -gt 2) {
|
||||
$newContent = $mainContent.Substring(0, $copyrightEnd) + "`n`n// ========== Cursor Hook 注入开始 ==========`n" + $hookContent + "`n// ========== Cursor Hook 注入结束 ==========`n`n" + $mainContent.Substring($copyrightEnd)
|
||||
} else {
|
||||
$newContent = "// ========== Cursor Hook 注入开始 ==========`n" + $hookContent + "`n// ========== Cursor Hook 注入结束 ==========`n`n" + $mainContent
|
||||
}
|
||||
}
|
||||
|
||||
# 写入修改后的内容
|
||||
Set-Content -Path $MainJsPath -Value $newContent -Encoding UTF8 -NoNewline
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
# 关闭 Cursor 进程
|
||||
function Stop-CursorProcess {
|
||||
$cursorProcesses = Get-Process -Name "Cursor*" -ErrorAction SilentlyContinue
|
||||
|
||||
if ($cursorProcesses) {
|
||||
Write-Log "发现 Cursor 进程正在运行,正在关闭..."
|
||||
$cursorProcesses | Stop-Process -Force
|
||||
Start-Sleep -Seconds 2
|
||||
Write-Log "Cursor 进程已关闭"
|
||||
}
|
||||
}
|
||||
|
||||
# 主函数
|
||||
function Main {
|
||||
Write-Host ""
|
||||
Write-Host "$BLUE========================================$NC"
|
||||
Write-Host "$BLUE Cursor Hook 注入工具 (Windows) $NC"
|
||||
Write-Host "$BLUE========================================$NC"
|
||||
Write-Host ""
|
||||
|
||||
# 获取 Cursor main.js 路径
|
||||
$mainJsPath = Get-CursorPath
|
||||
if (-not $mainJsPath) {
|
||||
Write-Log "未找到 Cursor 安装路径" "ERROR"
|
||||
Write-Log "请确保 Cursor 已正确安装" "ERROR"
|
||||
exit 1
|
||||
}
|
||||
Write-Log "找到 Cursor main.js: $mainJsPath"
|
||||
|
||||
# 回滚模式
|
||||
if ($Rollback) {
|
||||
Write-Log "执行回滚操作..."
|
||||
Stop-CursorProcess
|
||||
if (Restore-MainJs -MainJsPath $mainJsPath) {
|
||||
Write-Log "回滚成功!" "INFO"
|
||||
} else {
|
||||
Write-Log "回滚失败!" "ERROR"
|
||||
exit 1
|
||||
}
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 检查是否已注入
|
||||
if ((Test-AlreadyInjected -MainJsPath $mainJsPath) -and -not $Force) {
|
||||
Write-Log "Hook 已经注入,无需重复操作" "WARN"
|
||||
Write-Log "如需强制重新注入,请使用 -Force 参数" "INFO"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 获取 Hook 脚本路径
|
||||
$hookScriptPath = Get-HookScriptPath
|
||||
if (-not $hookScriptPath) {
|
||||
Write-Log "未找到 cursor_hook.js 文件" "ERROR"
|
||||
Write-Log "请确保 cursor_hook.js 与此脚本在同一目录" "ERROR"
|
||||
exit 1
|
||||
}
|
||||
Write-Log "找到 Hook 脚本: $hookScriptPath"
|
||||
|
||||
# 关闭 Cursor 进程
|
||||
Stop-CursorProcess
|
||||
|
||||
# 备份原始文件
|
||||
Write-Log "正在备份原始文件..."
|
||||
$backupPath = Backup-MainJs -MainJsPath $mainJsPath
|
||||
|
||||
# 注入 Hook 代码
|
||||
Write-Log "正在注入 Hook 代码..."
|
||||
try {
|
||||
if (Inject-Hook -MainJsPath $mainJsPath -HookScriptPath $hookScriptPath) {
|
||||
Write-Log "Hook 注入成功!" "INFO"
|
||||
} else {
|
||||
Write-Log "Hook 注入失败!" "ERROR"
|
||||
exit 1
|
||||
}
|
||||
} catch {
|
||||
Write-Log "注入过程中发生错误: $_" "ERROR"
|
||||
Write-Log "正在回滚..." "WARN"
|
||||
Restore-MainJs -MainJsPath $mainJsPath
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "$GREEN========================================$NC"
|
||||
Write-Host "$GREEN ✅ Hook 注入完成! $NC"
|
||||
Write-Host "$GREEN========================================$NC"
|
||||
Write-Host ""
|
||||
Write-Log "现在可以启动 Cursor 了"
|
||||
Write-Log "ID 配置文件位置: $env:USERPROFILE\.cursor_ids.json"
|
||||
Write-Host ""
|
||||
Write-Host "$YELLOW提示:$NC"
|
||||
Write-Host " - 如需回滚,请运行: .\inject_hook_win.ps1 -Rollback"
|
||||
Write-Host " - 如需强制重新注入,请运行: .\inject_hook_win.ps1 -Force"
|
||||
Write-Host " - 如需启用调试日志,请运行: .\inject_hook_win.ps1 -Debug"
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
Main
|
||||
|
||||
@@ -718,12 +718,13 @@ find_cursor_js_files() {
|
||||
}
|
||||
|
||||
# 修改Cursor的JS文件
|
||||
# 🔧 修改Cursor内核JS文件实现设备识别绕过(A+B混合方案 - IIFE + someValue替换)
|
||||
# 🔧 修改Cursor内核JS文件实现设备识别绕过(增强版 Hook 方案)
|
||||
# 方案A: someValue占位符替换 - 稳定锚点,不依赖混淆后的函数名
|
||||
# 方案B: IIFE运行时劫持 - 劫持crypto.randomUUID从源头拦截
|
||||
# 方案B: 深度 Hook 注入 - 从底层拦截所有设备标识符生成
|
||||
# 方案C: Module.prototype.require 劫持 - 拦截 child_process, crypto, os 等模块
|
||||
modify_cursor_js_files() {
|
||||
log_info "🔧 [内核修改] 开始修改Cursor内核JS文件实现设备识别绕过..."
|
||||
log_info "💡 [方案] 使用A+B混合方案:someValue占位符替换 + IIFE运行时劫持"
|
||||
log_info "💡 [方案] 使用增强版 Hook 方案:深度模块劫持 + someValue替换"
|
||||
|
||||
# 先查找需要修改的JS文件
|
||||
if ! find_cursor_js_files; then
|
||||
@@ -740,15 +741,31 @@ modify_cursor_js_files() {
|
||||
local machine_id=$(openssl rand -hex 32)
|
||||
local device_id=$(generate_uuid)
|
||||
local mac_machine_id=$(openssl rand -hex 32)
|
||||
local sqm_id=$(generate_uuid)
|
||||
local sqm_id="{$(generate_uuid | tr '[:lower:]' '[:upper:]')}"
|
||||
local session_id=$(generate_uuid)
|
||||
# 🔧 新增: 生成 firstSessionDate 用于替换 someValue.firstSessionDate
|
||||
local first_session_date=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
||||
local mac_address="00:11:22:33:44:55"
|
||||
|
||||
log_info "🔑 [生成] 已生成新的设备标识符"
|
||||
log_info " machineId: ${machine_id:0:16}..."
|
||||
log_info " deviceId: ${device_id:0:16}..."
|
||||
log_info " macMachineId: ${mac_machine_id:0:16}..."
|
||||
log_info " sqmId: $sqm_id"
|
||||
|
||||
# 保存 ID 配置到用户目录(供 Hook 读取)
|
||||
local ids_config_path="$HOME/.cursor_ids.json"
|
||||
cat > "$ids_config_path" << EOF
|
||||
{
|
||||
"machineId": "$machine_id",
|
||||
"macMachineId": "$mac_machine_id",
|
||||
"devDeviceId": "$device_id",
|
||||
"sqmId": "$sqm_id",
|
||||
"macAddress": "$mac_address",
|
||||
"createdAt": "$first_session_date"
|
||||
}
|
||||
EOF
|
||||
chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$ids_config_path" 2>/dev/null || true
|
||||
log_info "💾 [保存] ID 配置已保存到: $ids_config_path"
|
||||
|
||||
local modified_count=0
|
||||
local file_modification_status=()
|
||||
@@ -762,7 +779,6 @@ modify_cursor_js_files() {
|
||||
continue
|
||||
fi
|
||||
|
||||
# 检查是否已经被修改过(使用统一标记 __cursor_patched__)
|
||||
if grep -q "__cursor_patched__" "$file" 2>/dev/null; then
|
||||
log_info "✅ [已修改] 文件已修改: $(basename "$file")"
|
||||
else
|
||||
@@ -785,7 +801,6 @@ modify_cursor_js_files() {
|
||||
continue
|
||||
fi
|
||||
|
||||
# 检查是否已经修改过
|
||||
if grep -q "__cursor_patched__" "$file"; then
|
||||
log_info "✅ [跳过] 文件已经被修改过"
|
||||
((modified_count++))
|
||||
@@ -793,8 +808,21 @@ modify_cursor_js_files() {
|
||||
continue
|
||||
fi
|
||||
|
||||
# 创建文件备份
|
||||
local backup_file="${file}.backup_$(date +%Y%m%d_%H%M%S)"
|
||||
# 创建备份目录
|
||||
local backup_dir="$(dirname "$file")/backups"
|
||||
mkdir -p "$backup_dir" 2>/dev/null || true
|
||||
|
||||
# 创建原始备份(如果不存在)
|
||||
local original_backup="$backup_dir/$(basename "$file").original"
|
||||
if [ ! -f "$original_backup" ]; then
|
||||
cp "$file" "$original_backup"
|
||||
chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$original_backup" 2>/dev/null || true
|
||||
chmod 444 "$original_backup" 2>/dev/null || true
|
||||
log_info "✅ [备份] 原始备份创建成功"
|
||||
fi
|
||||
|
||||
# 创建时间戳备份
|
||||
local backup_file="$backup_dir/$(basename "$file").backup_$(date +%Y%m%d_%H%M%S)"
|
||||
if ! cp "$file" "$backup_file"; then
|
||||
log_error "无法创建文件备份: $file"
|
||||
file_modification_status+=("'$(basename "$file")': Backup Failed")
|
||||
@@ -803,99 +831,177 @@ modify_cursor_js_files() {
|
||||
chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$backup_file" 2>/dev/null || true
|
||||
chmod 444 "$backup_file" 2>/dev/null || true
|
||||
|
||||
# 确保文件对当前执行用户可写
|
||||
chmod u+w "$file" || {
|
||||
log_error "无法修改文件权限(写): $file"
|
||||
file_modification_status+=("'$(basename "$file")': Permission Error")
|
||||
cp "$backup_file" "$file" 2>/dev/null || true
|
||||
continue
|
||||
}
|
||||
|
||||
local replaced=false
|
||||
|
||||
# ========== 方法A: someValue占位符替换(稳定锚点) ==========
|
||||
# 这些字符串是固定的占位符,不会被混淆器修改,跨版本稳定
|
||||
|
||||
# 替换 someValue.machineId
|
||||
if grep -q 'someValue\.machineId' "$file"; then
|
||||
sed -i "s/someValue\.machineId/${machine_id}/g" "$file"
|
||||
sed -i "s/someValue\.machineId/\"${machine_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.machineId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 替换 someValue.macMachineId
|
||||
if grep -q 'someValue\.macMachineId' "$file"; then
|
||||
sed -i "s/someValue\.macMachineId/${mac_machine_id}/g" "$file"
|
||||
sed -i "s/someValue\.macMachineId/\"${mac_machine_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.macMachineId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 替换 someValue.devDeviceId
|
||||
if grep -q 'someValue\.devDeviceId' "$file"; then
|
||||
sed -i "s/someValue\.devDeviceId/${device_id}/g" "$file"
|
||||
sed -i "s/someValue\.devDeviceId/\"${device_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.devDeviceId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 替换 someValue.sqmId
|
||||
if grep -q 'someValue\.sqmId' "$file"; then
|
||||
sed -i "s/someValue\.sqmId/${sqm_id}/g" "$file"
|
||||
sed -i "s/someValue\.sqmId/\"${sqm_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.sqmId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 替换 someValue.sessionId(新增锚点)
|
||||
if grep -q 'someValue\.sessionId' "$file"; then
|
||||
sed -i "s/someValue\.sessionId/${session_id}/g" "$file"
|
||||
sed -i "s/someValue\.sessionId/\"${session_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.sessionId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 🔧 新增: 替换 someValue.firstSessionDate(首次会话日期)
|
||||
if grep -q 'someValue\.firstSessionDate' "$file"; then
|
||||
sed -i "s/someValue\.firstSessionDate/${first_session_date}/g" "$file"
|
||||
sed -i "s/someValue\.firstSessionDate/\"${first_session_date}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.firstSessionDate"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# ========== 方法B: IIFE运行时劫持(crypto.randomUUID) ==========
|
||||
# 使用IIFE包装,兼容webpack打包的bundle文件
|
||||
# 在支持 require 的环境中劫持 crypto.randomUUID;在 ESM 环境中安全降级为 no-op,避免 require 抛错
|
||||
local inject_code=";(function(){/*__cursor_patched__*/var _cr=null,_os=null;if(typeof require!=='undefined'){try{_cr=require('crypto');_os=require('os');}catch(e){}}if(_cr&&_cr.randomUUID){var _orig=_cr.randomUUID;_cr.randomUUID=function(){return'${new_uuid}';};}if(typeof globalThis!=='undefined'){globalThis.__cursor_machine_id='${machine_id}';globalThis.__cursor_mac_machine_id='${mac_machine_id}';globalThis.__cursor_dev_device_id='${device_id}';globalThis.__cursor_sqm_id='${sqm_id}';}if(_os&&_os.networkInterfaces){try{var _origNI=_os.networkInterfaces;_os.networkInterfaces=function(){var r=_origNI.call(_os);for(var k in r){if(r[k]){for(var i=0;i<r[k].length;i++){if(r[k][i].mac){r[k][i].mac='00:00:00:00:00:00';}}}}return r;};}catch(e){}}console.log('[Cursor ID Modifier] 设备标识符已劫持 - 煎饼果子(86) 公众号【煎饼果子卷AI】');})();"
|
||||
# ========== 方法B: 增强版深度 Hook 注入 ==========
|
||||
local inject_code='// ========== Cursor Hook 注入开始 ==========
|
||||
;(function(){/*__cursor_patched__*/
|
||||
"use strict";
|
||||
if(globalThis.__cursor_patched__)return;
|
||||
globalThis.__cursor_patched__=true;
|
||||
|
||||
# 注入代码到文件开头
|
||||
var __ids__={
|
||||
machineId:"'"$machine_id"'",
|
||||
macMachineId:"'"$mac_machine_id"'",
|
||||
devDeviceId:"'"$device_id"'",
|
||||
sqmId:"'"$sqm_id"'",
|
||||
macAddress:"'"$mac_address"'"
|
||||
};
|
||||
|
||||
globalThis.__cursor_ids__=__ids__;
|
||||
|
||||
var Module=require("module");
|
||||
var _origReq=Module.prototype.require;
|
||||
var _hooked=new Map();
|
||||
|
||||
Module.prototype.require=function(id){
|
||||
var result=_origReq.apply(this,arguments);
|
||||
if(_hooked.has(id))return _hooked.get(id);
|
||||
var hooked=result;
|
||||
|
||||
if(id==="child_process"){
|
||||
var _origExecSync=result.execSync;
|
||||
result.execSync=function(cmd,opts){
|
||||
var cmdStr=String(cmd).toLowerCase();
|
||||
if(cmdStr.includes("machine-id")||cmdStr.includes("hostname")){
|
||||
return Buffer.from(__ids__.machineId.substring(0,32));
|
||||
}
|
||||
return _origExecSync.apply(this,arguments);
|
||||
};
|
||||
hooked=result;
|
||||
}
|
||||
else if(id==="os"){
|
||||
result.networkInterfaces=function(){
|
||||
return{"eth0":[{address:"192.168.1.100",netmask:"255.255.255.0",family:"IPv4",mac:__ids__.macAddress,internal:false}]};
|
||||
};
|
||||
hooked=result;
|
||||
}
|
||||
else if(id==="crypto"){
|
||||
var _origCreateHash=result.createHash;
|
||||
var _origRandomUUID=result.randomUUID;
|
||||
result.createHash=function(algo){
|
||||
var hash=_origCreateHash.apply(this,arguments);
|
||||
if(algo.toLowerCase()==="sha256"){
|
||||
var _origDigest=hash.digest.bind(hash);
|
||||
var _origUpdate=hash.update.bind(hash);
|
||||
var inputData="";
|
||||
hash.update=function(data,enc){inputData+=String(data);return _origUpdate(data,enc);};
|
||||
hash.digest=function(enc){
|
||||
if(inputData.includes("machine-id")||(inputData.length>=32&&inputData.length<=40)){
|
||||
return enc==="hex"?__ids__.machineId:Buffer.from(__ids__.machineId,"hex");
|
||||
}
|
||||
return _origDigest(enc);
|
||||
};
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
if(_origRandomUUID){
|
||||
var uuidCount=0;
|
||||
result.randomUUID=function(){
|
||||
uuidCount++;
|
||||
if(uuidCount<=2)return __ids__.devDeviceId;
|
||||
return _origRandomUUID.apply(this,arguments);
|
||||
};
|
||||
}
|
||||
hooked=result;
|
||||
}
|
||||
else if(id==="@vscode/deviceid"){
|
||||
hooked={...result,getDeviceId:async function(){return __ids__.devDeviceId;}};
|
||||
}
|
||||
|
||||
if(hooked!==result)_hooked.set(id,hooked);
|
||||
return hooked;
|
||||
};
|
||||
|
||||
console.log("[Cursor ID Modifier] 增强版 Hook 已激活 - 煎饼果子(86) 公众号【煎饼果子卷AI】");
|
||||
})();
|
||||
// ========== Cursor Hook 注入结束 ==========
|
||||
|
||||
'
|
||||
|
||||
# 在版权声明后注入代码
|
||||
local temp_file=$(mktemp)
|
||||
echo "$inject_code" > "$temp_file"
|
||||
cat "$file" >> "$temp_file"
|
||||
if grep -q '\*/' "$file"; then
|
||||
awk -v inject="$inject_code" '
|
||||
/\*\// && !injected {
|
||||
print
|
||||
print ""
|
||||
print inject
|
||||
injected = 1
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$file" > "$temp_file"
|
||||
log_info " ✓ [方案B] 增强版 Hook 代码已注入(版权声明后)"
|
||||
else
|
||||
echo "$inject_code" > "$temp_file"
|
||||
cat "$file" >> "$temp_file"
|
||||
log_info " ✓ [方案B] 增强版 Hook 代码已注入(文件开头)"
|
||||
fi
|
||||
|
||||
if mv "$temp_file" "$file"; then
|
||||
log_info " ✓ [方案B] IIFE运行时劫持代码已注入"
|
||||
|
||||
if [ "$replaced" = true ]; then
|
||||
log_info "✅ [成功] A+B混合方案修改成功(someValue替换 + IIFE劫持)"
|
||||
log_info "✅ [成功] 增强版混合方案修改成功(someValue替换 + 深度Hook)"
|
||||
else
|
||||
log_info "✅ [成功] 方案B修改成功(IIFE劫持)"
|
||||
log_info "✅ [成功] 增强版 Hook 修改成功"
|
||||
fi
|
||||
((modified_count++))
|
||||
file_modification_status+=("'$(basename "$file")': Success")
|
||||
|
||||
# 恢复文件权限为只读
|
||||
chmod u-w,go-w "$file" 2>/dev/null || true
|
||||
chown "$CURRENT_USER":"$(id -g -n "$CURRENT_USER")" "$file" 2>/dev/null || true
|
||||
else
|
||||
log_error "IIFE注入失败 (无法移动临时文件)"
|
||||
log_error "Hook注入失败 (无法移动临时文件)"
|
||||
rm -f "$temp_file"
|
||||
file_modification_status+=("'$(basename "$file")': Inject Failed")
|
||||
# 恢复备份
|
||||
cp "$backup_file" "$file" 2>/dev/null || true
|
||||
cp "$original_backup" "$file" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 清理备份文件(可选保留)
|
||||
# rm -f "$backup_file"
|
||||
done
|
||||
|
||||
done # 文件循环结束
|
||||
|
||||
# 报告每个文件的状态
|
||||
log_info "📊 [统计] JS 文件处理状态汇总:"
|
||||
for status in "${file_modification_status[@]}"; do
|
||||
log_info " - $status"
|
||||
@@ -907,9 +1013,10 @@ modify_cursor_js_files() {
|
||||
fi
|
||||
|
||||
log_info "🎉 [完成] 成功修改 $modified_count 个JS文件"
|
||||
log_info "💡 [说明] 使用A+B混合方案:"
|
||||
log_info "💡 [说明] 使用增强版 Hook 方案:"
|
||||
log_info " • 方案A: someValue占位符替换(稳定锚点,跨版本兼容)"
|
||||
log_info " • 方案B: IIFE运行时劫持(crypto.randomUUID + os.networkInterfaces)"
|
||||
log_info " • 方案B: 深度模块劫持(child_process, crypto, os, @vscode/*)"
|
||||
log_info "📁 [配置] ID 配置文件: $ids_config_path"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
@@ -1510,12 +1510,13 @@ backup_config() {
|
||||
fi
|
||||
}
|
||||
|
||||
# 🔧 修改Cursor内核JS文件实现设备识别绕过(A+B混合方案 - IIFE + someValue替换)
|
||||
# 🔧 修改Cursor内核JS文件实现设备识别绕过(增强版 Hook 方案)
|
||||
# 方案A: someValue占位符替换 - 稳定锚点,不依赖混淆后的函数名
|
||||
# 方案B: IIFE运行时劫持 - 劫持crypto.randomUUID从源头拦截
|
||||
# 方案B: 深度 Hook 注入 - 从底层拦截所有设备标识符生成
|
||||
# 方案C: Module.prototype.require 劫持 - 拦截 child_process, crypto, os 等模块
|
||||
modify_cursor_js_files() {
|
||||
log_info "🔧 [内核修改] 开始修改Cursor内核JS文件实现设备识别绕过..."
|
||||
log_info "💡 [方案] 使用A+B混合方案:someValue占位符替换 + IIFE运行时劫持"
|
||||
log_info "💡 [方案] 使用增强版 Hook 方案:深度模块劫持 + someValue替换"
|
||||
echo
|
||||
|
||||
# 检查Cursor应用是否存在
|
||||
@@ -1529,22 +1530,34 @@ modify_cursor_js_files() {
|
||||
local machine_id=$(openssl rand -hex 32)
|
||||
local device_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||||
local mac_machine_id=$(openssl rand -hex 32)
|
||||
local sqm_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||||
# 生成一个固定的session_id用于替换someValue.sessionId
|
||||
local sqm_id="{$(uuidgen | tr '[:lower:]' '[:upper:]')}"
|
||||
local session_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||||
# 🔧 新增: 生成 firstSessionDate 用于替换 someValue.firstSessionDate
|
||||
local first_session_date=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
||||
local mac_address="00:11:22:33:44:55"
|
||||
|
||||
log_info "🔑 [生成] 已生成新的设备标识符"
|
||||
log_info " machineId: ${machine_id:0:16}..."
|
||||
log_info " deviceId: ${device_id:0:16}..."
|
||||
log_info " macMachineId: ${mac_machine_id:0:16}..."
|
||||
log_info " sqmId: $sqm_id"
|
||||
|
||||
# 目标JS文件列表(按优先级排序)
|
||||
# 保存 ID 配置到用户目录(供 Hook 读取)
|
||||
local ids_config_path="$HOME/.cursor_ids.json"
|
||||
cat > "$ids_config_path" << EOF
|
||||
{
|
||||
"machineId": "$machine_id",
|
||||
"macMachineId": "$mac_machine_id",
|
||||
"devDeviceId": "$device_id",
|
||||
"sqmId": "$sqm_id",
|
||||
"macAddress": "$mac_address",
|
||||
"createdAt": "$first_session_date"
|
||||
}
|
||||
EOF
|
||||
log_info "💾 [保存] ID 配置已保存到: $ids_config_path"
|
||||
|
||||
# 目标JS文件列表(只修改 main.js)
|
||||
local js_files=(
|
||||
"$CURSOR_APP_PATH/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js"
|
||||
"$CURSOR_APP_PATH/Contents/Resources/app/out/main.js"
|
||||
"$CURSOR_APP_PATH/Contents/Resources/app/out/vs/code/node/cliProcessMain.js"
|
||||
)
|
||||
|
||||
local modified_count=0
|
||||
@@ -1558,7 +1571,6 @@ modify_cursor_js_files() {
|
||||
continue
|
||||
fi
|
||||
|
||||
# 检查是否已经被修改过(使用统一标记 __cursor_patched__)
|
||||
if grep -q "__cursor_patched__" "$file" 2>/dev/null; then
|
||||
log_info "✅ [已修改] 文件已修改: ${file/$CURSOR_APP_PATH\//}"
|
||||
else
|
||||
@@ -1578,16 +1590,29 @@ modify_cursor_js_files() {
|
||||
|
||||
# 创建备份
|
||||
local timestamp=$(date +%Y%m%d_%H%M%S)
|
||||
local backup_dir="/tmp/Cursor_JS_Backup_${timestamp}"
|
||||
local backup_dir="$CURSOR_APP_PATH/Contents/Resources/app/out/backups"
|
||||
|
||||
log_info "💾 [备份] 创建JS文件备份..."
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
# 创建原始备份(如果不存在)
|
||||
local original_backup="$backup_dir/main.js.original"
|
||||
if [ ! -f "$original_backup" ]; then
|
||||
for file in "${js_files[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
cp "$file" "$backup_dir/$(basename "$file").original"
|
||||
fi
|
||||
done
|
||||
log_info "✅ [备份] 原始备份创建成功"
|
||||
fi
|
||||
|
||||
# 创建时间戳备份
|
||||
for file in "${js_files[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
cp "$file" "$backup_dir/$(basename "$file")"
|
||||
cp "$file" "$backup_dir/$(basename "$file").backup_$timestamp"
|
||||
fi
|
||||
done
|
||||
log_info "✅ [备份] 备份创建成功: $backup_dir"
|
||||
log_info "✅ [备份] 时间戳备份创建成功: $backup_dir"
|
||||
|
||||
# 修改JS文件
|
||||
log_info "🔧 [修改] 开始修改JS文件..."
|
||||
@@ -1608,70 +1633,164 @@ modify_cursor_js_files() {
|
||||
fi
|
||||
|
||||
# ========== 方法A: someValue占位符替换(稳定锚点) ==========
|
||||
# 这些字符串是固定的占位符,不会被混淆器修改,跨版本稳定
|
||||
local replaced=false
|
||||
|
||||
# 替换 someValue.machineId
|
||||
if grep -q 'someValue\.machineId' "$file"; then
|
||||
sed -i.tmp "s/someValue\.machineId/${machine_id}/g" "$file"
|
||||
sed -i.tmp "s/someValue\.machineId/\"${machine_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.machineId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 替换 someValue.macMachineId
|
||||
if grep -q 'someValue\.macMachineId' "$file"; then
|
||||
sed -i.tmp "s/someValue\.macMachineId/${mac_machine_id}/g" "$file"
|
||||
sed -i.tmp "s/someValue\.macMachineId/\"${mac_machine_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.macMachineId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 替换 someValue.devDeviceId
|
||||
if grep -q 'someValue\.devDeviceId' "$file"; then
|
||||
sed -i.tmp "s/someValue\.devDeviceId/${device_id}/g" "$file"
|
||||
sed -i.tmp "s/someValue\.devDeviceId/\"${device_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.devDeviceId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 替换 someValue.sqmId
|
||||
if grep -q 'someValue\.sqmId' "$file"; then
|
||||
sed -i.tmp "s/someValue\.sqmId/${sqm_id}/g" "$file"
|
||||
sed -i.tmp "s/someValue\.sqmId/\"${sqm_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.sqmId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 替换 someValue.sessionId(新增锚点)
|
||||
if grep -q 'someValue\.sessionId' "$file"; then
|
||||
sed -i.tmp "s/someValue\.sessionId/${session_id}/g" "$file"
|
||||
sed -i.tmp "s/someValue\.sessionId/\"${session_id}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.sessionId"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# 🔧 新增: 替换 someValue.firstSessionDate(首次会话日期)
|
||||
if grep -q 'someValue\.firstSessionDate' "$file"; then
|
||||
sed -i.tmp "s/someValue\.firstSessionDate/${first_session_date}/g" "$file"
|
||||
sed -i.tmp "s/someValue\.firstSessionDate/\"${first_session_date}\"/g" "$file"
|
||||
log_info " ✓ [方案A] 替换 someValue.firstSessionDate"
|
||||
replaced=true
|
||||
fi
|
||||
|
||||
# ========== 方法B: IIFE运行时劫持(crypto.randomUUID) ==========
|
||||
# 使用IIFE包装,兼容webpack打包的bundle文件
|
||||
# 在支持 require 的环境中劫持 crypto.randomUUID;在 ESM 环境中安全降级为 no-op,避免 require 抛错
|
||||
local inject_code=";(function(){/*__cursor_patched__*/var _cr=null,_os=null;if(typeof require!=='undefined'){try{_cr=require('crypto');_os=require('os');}catch(e){}}if(_cr&&_cr.randomUUID){var _orig=_cr.randomUUID;_cr.randomUUID=function(){return'${new_uuid}';};}if(typeof globalThis!=='undefined'){globalThis.__cursor_machine_id='${machine_id}';globalThis.__cursor_mac_machine_id='${mac_machine_id}';globalThis.__cursor_dev_device_id='${device_id}';globalThis.__cursor_sqm_id='${sqm_id}';}if(_os&&_os.networkInterfaces){try{var _origNI=_os.networkInterfaces;_os.networkInterfaces=function(){var r=_origNI.call(_os);for(var k in r){if(r[k]){for(var i=0;i<r[k].length;i++){if(r[k][i].mac){r[k][i].mac='00:00:00:00:00:00';}}}}return r;};}catch(e){}}console.log('[Cursor ID Modifier] 设备标识符已劫持 - 煎饼果子(86) 公众号【煎饼果子卷AI】');})();"
|
||||
# ========== 方法B: 增强版深度 Hook 注入 ==========
|
||||
# 创建注入代码
|
||||
local inject_code='// ========== Cursor Hook 注入开始 ==========
|
||||
;(function(){/*__cursor_patched__*/
|
||||
"use strict";
|
||||
if(globalThis.__cursor_patched__)return;
|
||||
globalThis.__cursor_patched__=true;
|
||||
|
||||
# 注入代码到文件开头
|
||||
echo "$inject_code" > "${file}.new"
|
||||
cat "$file" >> "${file}.new"
|
||||
mv "${file}.new" "$file"
|
||||
var __ids__={
|
||||
machineId:"'"$machine_id"'",
|
||||
macMachineId:"'"$mac_machine_id"'",
|
||||
devDeviceId:"'"$device_id"'",
|
||||
sqmId:"'"$sqm_id"'",
|
||||
macAddress:"'"$mac_address"'"
|
||||
};
|
||||
|
||||
log_info " ✓ [方案B] IIFE运行时劫持代码已注入"
|
||||
globalThis.__cursor_ids__=__ids__;
|
||||
|
||||
var Module=require("module");
|
||||
var _origReq=Module.prototype.require;
|
||||
var _hooked=new Map();
|
||||
|
||||
Module.prototype.require=function(id){
|
||||
var result=_origReq.apply(this,arguments);
|
||||
if(_hooked.has(id))return _hooked.get(id);
|
||||
var hooked=result;
|
||||
|
||||
if(id==="child_process"){
|
||||
var _origExecSync=result.execSync;
|
||||
result.execSync=function(cmd,opts){
|
||||
var cmdStr=String(cmd).toLowerCase();
|
||||
if(cmdStr.includes("ioreg")&&cmdStr.includes("ioplatformexpertdevice")){
|
||||
return Buffer.from("\"IOPlatformUUID\" = \""+__ids__.machineId.substring(0,36).toUpperCase()+"\"");
|
||||
}
|
||||
if(cmdStr.includes("machine-id")||cmdStr.includes("hostname")){
|
||||
return Buffer.from(__ids__.machineId.substring(0,32));
|
||||
}
|
||||
return _origExecSync.apply(this,arguments);
|
||||
};
|
||||
hooked=result;
|
||||
}
|
||||
else if(id==="os"){
|
||||
result.networkInterfaces=function(){
|
||||
return{"en0":[{address:"192.168.1.100",netmask:"255.255.255.0",family:"IPv4",mac:__ids__.macAddress,internal:false}]};
|
||||
};
|
||||
hooked=result;
|
||||
}
|
||||
else if(id==="crypto"){
|
||||
var _origCreateHash=result.createHash;
|
||||
var _origRandomUUID=result.randomUUID;
|
||||
result.createHash=function(algo){
|
||||
var hash=_origCreateHash.apply(this,arguments);
|
||||
if(algo.toLowerCase()==="sha256"){
|
||||
var _origDigest=hash.digest.bind(hash);
|
||||
var _origUpdate=hash.update.bind(hash);
|
||||
var inputData="";
|
||||
hash.update=function(data,enc){inputData+=String(data);return _origUpdate(data,enc);};
|
||||
hash.digest=function(enc){
|
||||
if(inputData.includes("IOPlatformUUID")||(inputData.length>=32&&inputData.length<=40)){
|
||||
return enc==="hex"?__ids__.machineId:Buffer.from(__ids__.machineId,"hex");
|
||||
}
|
||||
return _origDigest(enc);
|
||||
};
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
if(_origRandomUUID){
|
||||
var uuidCount=0;
|
||||
result.randomUUID=function(){
|
||||
uuidCount++;
|
||||
if(uuidCount<=2)return __ids__.devDeviceId;
|
||||
return _origRandomUUID.apply(this,arguments);
|
||||
};
|
||||
}
|
||||
hooked=result;
|
||||
}
|
||||
else if(id==="@vscode/deviceid"){
|
||||
hooked={...result,getDeviceId:async function(){return __ids__.devDeviceId;}};
|
||||
}
|
||||
|
||||
if(hooked!==result)_hooked.set(id,hooked);
|
||||
return hooked;
|
||||
};
|
||||
|
||||
console.log("[Cursor ID Modifier] 增强版 Hook 已激活 - 煎饼果子(86) 公众号【煎饼果子卷AI】");
|
||||
})();
|
||||
// ========== Cursor Hook 注入结束 ==========
|
||||
|
||||
'
|
||||
|
||||
# 在版权声明后注入代码
|
||||
if grep -q '\*/' "$file"; then
|
||||
# 使用 awk 在版权声明后注入
|
||||
awk -v inject="$inject_code" '
|
||||
/\*\// && !injected {
|
||||
print
|
||||
print ""
|
||||
print inject
|
||||
injected = 1
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$file" > "${file}.new"
|
||||
mv "${file}.new" "$file"
|
||||
log_info " ✓ [方案B] 增强版 Hook 代码已注入(版权声明后)"
|
||||
else
|
||||
# 注入到文件开头
|
||||
echo "$inject_code" > "${file}.new"
|
||||
cat "$file" >> "${file}.new"
|
||||
mv "${file}.new" "$file"
|
||||
log_info " ✓ [方案B] 增强版 Hook 代码已注入(文件开头)"
|
||||
fi
|
||||
|
||||
# 清理临时文件
|
||||
rm -f "${file}.tmp"
|
||||
|
||||
if [ "$replaced" = true ]; then
|
||||
log_info "✅ [成功] A+B混合方案修改成功(someValue替换 + IIFE劫持)"
|
||||
log_info "✅ [成功] 增强版混合方案修改成功(someValue替换 + 深度Hook)"
|
||||
else
|
||||
log_info "✅ [成功] 方案B修改成功(IIFE劫持)"
|
||||
log_info "✅ [成功] 增强版 Hook 修改成功"
|
||||
fi
|
||||
((modified_count++))
|
||||
done
|
||||
@@ -1679,9 +1798,10 @@ modify_cursor_js_files() {
|
||||
if [ $modified_count -gt 0 ]; then
|
||||
log_info "🎉 [完成] 成功修改 $modified_count 个JS文件"
|
||||
log_info "💾 [备份] 原始文件备份位置: $backup_dir"
|
||||
log_info "💡 [说明] 使用A+B混合方案:"
|
||||
log_info "💡 [说明] 使用增强版 Hook 方案:"
|
||||
log_info " • 方案A: someValue占位符替换(稳定锚点,跨版本兼容)"
|
||||
log_info " • 方案B: IIFE运行时劫持(crypto.randomUUID + os.networkInterfaces)"
|
||||
log_info " • 方案B: 深度模块劫持(child_process, crypto, os, @vscode/*)"
|
||||
log_info "📁 [配置] ID 配置文件: $ids_config_path"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ [失败] 没有成功修改任何文件"
|
||||
|
||||
@@ -24,13 +24,14 @@ function Generate-RandomString {
|
||||
return $result
|
||||
}
|
||||
|
||||
# 🔧 修改Cursor内核JS文件实现设备识别绕过(A+B混合方案 - IIFE + someValue替换)
|
||||
# 🔧 修改Cursor内核JS文件实现设备识别绕过(增强版 Hook 方案)
|
||||
# 方案A: someValue占位符替换 - 稳定锚点,不依赖混淆后的函数名
|
||||
# 方案B: IIFE运行时劫持 - 劫持crypto.randomUUID从源头拦截
|
||||
# 方案B: 深度 Hook 注入 - 从底层拦截所有设备标识符生成
|
||||
# 方案C: Module.prototype.require 劫持 - 拦截 child_process, crypto, os 等模块
|
||||
function Modify-CursorJSFiles {
|
||||
Write-Host ""
|
||||
Write-Host "$BLUE🔧 [内核修改]$NC 开始修改Cursor内核JS文件实现设备识别绕过..."
|
||||
Write-Host "$BLUE💡 [方案]$NC 使用A+B混合方案:someValue占位符替换 + IIFE运行时劫持"
|
||||
Write-Host "$BLUE💡 [方案]$NC 使用增强版 Hook 方案:深度模块劫持 + someValue替换"
|
||||
Write-Host ""
|
||||
|
||||
# Windows版Cursor应用路径
|
||||
@@ -72,19 +73,37 @@ function Modify-CursorJSFiles {
|
||||
$rng2.GetBytes($randomBytes2)
|
||||
$macMachineId = [System.BitConverter]::ToString($randomBytes2) -replace '-',''
|
||||
$rng2.Dispose()
|
||||
$sqmId = [System.Guid]::NewGuid().ToString().ToLower()
|
||||
$sqmId = "{" + [System.Guid]::NewGuid().ToString().ToUpper() + "}"
|
||||
$sessionId = [System.Guid]::NewGuid().ToString().ToLower()
|
||||
$macAddress = "00:11:22:33:44:55"
|
||||
|
||||
Write-Host "$GREEN🔑 [生成]$NC 已生成新的设备标识符"
|
||||
Write-Host " machineId: $($machineId.Substring(0,16))..."
|
||||
Write-Host " deviceId: $($deviceId.Substring(0,16))..."
|
||||
Write-Host " macMachineId: $($macMachineId.Substring(0,16))..."
|
||||
Write-Host " sqmId: $sqmId"
|
||||
|
||||
# 保存 ID 配置到用户目录(供 Hook 读取)
|
||||
# 每次执行都删除旧配置并重新生成,确保获得新的设备标识符
|
||||
$idsConfigPath = "$env:USERPROFILE\.cursor_ids.json"
|
||||
if (Test-Path $idsConfigPath) {
|
||||
Remove-Item -Path $idsConfigPath -Force
|
||||
Write-Host "$YELLOW🗑️ [清理]$NC 已删除旧的 ID 配置文件"
|
||||
}
|
||||
$idsConfig = @{
|
||||
machineId = $machineId
|
||||
macMachineId = $macMachineId
|
||||
devDeviceId = $deviceId
|
||||
sqmId = $sqmId
|
||||
macAddress = $macAddress
|
||||
createdAt = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
|
||||
}
|
||||
$idsConfig | ConvertTo-Json | Set-Content -Path $idsConfigPath -Encoding UTF8
|
||||
Write-Host "$GREEN💾 [保存]$NC 新的 ID 配置已保存到: $idsConfigPath"
|
||||
|
||||
# 目标JS文件列表(Windows路径,按优先级排序)
|
||||
$jsFiles = @(
|
||||
"$cursorAppPath\resources\app\out\vs\workbench\api\node\extensionHostProcess.js",
|
||||
"$cursorAppPath\resources\app\out\main.js",
|
||||
"$cursorAppPath\resources\app\out\vs\code\node\cliProcessMain.js"
|
||||
"$cursorAppPath\resources\app\out\main.js"
|
||||
)
|
||||
|
||||
$modifiedCount = 0
|
||||
@@ -118,18 +137,32 @@ function Modify-CursorJSFiles {
|
||||
|
||||
# 创建备份
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$backupPath = "$env:TEMP\Cursor_JS_Backup_$timestamp"
|
||||
$backupPath = "$cursorAppPath\resources\app\out\backups"
|
||||
|
||||
Write-Host "$BLUE💾 [备份]$NC 创建Cursor JS文件备份..."
|
||||
try {
|
||||
New-Item -ItemType Directory -Path $backupPath -Force | Out-Null
|
||||
|
||||
# 创建原始备份(如果不存在)
|
||||
$originalBackup = "$backupPath\main.js.original"
|
||||
if (-not (Test-Path $originalBackup)) {
|
||||
foreach ($file in $jsFiles) {
|
||||
if (Test-Path $file) {
|
||||
$fileName = Split-Path $file -Leaf
|
||||
Copy-Item $file "$backupPath\$fileName.original" -Force
|
||||
}
|
||||
}
|
||||
Write-Host "$GREEN✅ [备份]$NC 原始备份创建成功"
|
||||
}
|
||||
|
||||
# 创建时间戳备份
|
||||
foreach ($file in $jsFiles) {
|
||||
if (Test-Path $file) {
|
||||
$fileName = Split-Path $file -Leaf
|
||||
Copy-Item $file "$backupPath\$fileName" -Force
|
||||
Copy-Item $file "$backupPath\$fileName.backup_$timestamp" -Force
|
||||
}
|
||||
}
|
||||
Write-Host "$GREEN✅ [备份]$NC 备份创建成功: $backupPath"
|
||||
Write-Host "$GREEN✅ [备份]$NC 时间戳备份创建成功: $backupPath"
|
||||
} catch {
|
||||
Write-Host "$RED❌ [错误]$NC 创建备份失败: $($_.Exception.Message)"
|
||||
return $false
|
||||
@@ -163,35 +196,35 @@ function Modify-CursorJSFiles {
|
||||
|
||||
# 替换 someValue.machineId
|
||||
if ($content -match 'someValue\.machineId') {
|
||||
$content = $content -replace 'someValue\.machineId', $machineId
|
||||
$content = $content -replace 'someValue\.machineId', "`"$machineId`""
|
||||
Write-Host " $GREEN✓$NC [方案A] 替换 someValue.machineId"
|
||||
$replaced = $true
|
||||
}
|
||||
|
||||
# 替换 someValue.macMachineId
|
||||
if ($content -match 'someValue\.macMachineId') {
|
||||
$content = $content -replace 'someValue\.macMachineId', $macMachineId
|
||||
$content = $content -replace 'someValue\.macMachineId', "`"$macMachineId`""
|
||||
Write-Host " $GREEN✓$NC [方案A] 替换 someValue.macMachineId"
|
||||
$replaced = $true
|
||||
}
|
||||
|
||||
# 替换 someValue.devDeviceId
|
||||
if ($content -match 'someValue\.devDeviceId') {
|
||||
$content = $content -replace 'someValue\.devDeviceId', $deviceId
|
||||
$content = $content -replace 'someValue\.devDeviceId', "`"$deviceId`""
|
||||
Write-Host " $GREEN✓$NC [方案A] 替换 someValue.devDeviceId"
|
||||
$replaced = $true
|
||||
}
|
||||
|
||||
# 替换 someValue.sqmId
|
||||
if ($content -match 'someValue\.sqmId') {
|
||||
$content = $content -replace 'someValue\.sqmId', $sqmId
|
||||
$content = $content -replace 'someValue\.sqmId', "`"$sqmId`""
|
||||
Write-Host " $GREEN✓$NC [方案A] 替换 someValue.sqmId"
|
||||
$replaced = $true
|
||||
}
|
||||
|
||||
# 替换 someValue.sessionId(新增锚点)
|
||||
if ($content -match 'someValue\.sessionId') {
|
||||
$content = $content -replace 'someValue\.sessionId', $sessionId
|
||||
$content = $content -replace 'someValue\.sessionId', "`"$sessionId`""
|
||||
Write-Host " $GREEN✓$NC [方案A] 替换 someValue.sessionId"
|
||||
$replaced = $true
|
||||
}
|
||||
@@ -204,23 +237,139 @@ function Modify-CursorJSFiles {
|
||||
$replaced = $true
|
||||
}
|
||||
|
||||
# ========== 方法B: IIFE运行时劫持(crypto.randomUUID) ==========
|
||||
# 使用IIFE包装,兼容webpack打包的bundle文件
|
||||
# 在支持 require 的环境中劫持 crypto.randomUUID;在 ESM 环境中安全降级为 no-op,避免 require 抛错
|
||||
$injectCode = ";(function(){/*__cursor_patched__*/var _cr=null,_os=null;if(typeof require!=='undefined'){try{_cr=require('crypto');_os=require('os');}catch(e){}}if(_cr&&_cr.randomUUID){var _orig=_cr.randomUUID;_cr.randomUUID=function(){return'$newUuid';};}if(typeof globalThis!=='undefined'){globalThis.__cursor_machine_id='$machineId';globalThis.__cursor_mac_machine_id='$macMachineId';globalThis.__cursor_dev_device_id='$deviceId';globalThis.__cursor_sqm_id='$sqmId';}if(_os&&_os.networkInterfaces){try{var _origNI=_os.networkInterfaces;_os.networkInterfaces=function(){var r=_origNI.call(_os);for(var k in r){if(r[k]){for(var i=0;i<r[k].length;i++){if(r[k][i].mac){r[k][i].mac='00:00:00:00:00:00';}}}}return r;};}catch(e){}}console.log('[Cursor ID Modifier] 设备标识符已劫持 - 煎饼果子(86) 公众号【煎饼果子卷AI】');})();"
|
||||
# ========== 方法B: 增强版深度 Hook 注入 ==========
|
||||
# 从底层拦截所有设备标识符的生成:
|
||||
# 1. Module.prototype.require 劫持 - 拦截 child_process, crypto, os 等模块
|
||||
# 2. child_process.execSync - 拦截 REG.exe 查询 MachineGuid
|
||||
# 3. crypto.createHash - 拦截 SHA256 哈希计算
|
||||
# 4. crypto.randomUUID - 拦截 UUID 生成
|
||||
# 5. os.networkInterfaces - 拦截 MAC 地址获取
|
||||
# 6. @vscode/deviceid - 拦截 devDeviceId 获取
|
||||
# 7. @vscode/windows-registry - 拦截注册表读取
|
||||
|
||||
# 注入代码到文件开头
|
||||
$content = $injectCode + "`n" + $content
|
||||
$injectCode = @"
|
||||
// ========== Cursor Hook 注入开始 ==========
|
||||
;(function(){/*__cursor_patched__*/
|
||||
'use strict';
|
||||
if(globalThis.__cursor_patched__)return;
|
||||
globalThis.__cursor_patched__=true;
|
||||
|
||||
Write-Host " $GREEN✓$NC [方案B] IIFE运行时劫持代码已注入"
|
||||
// 固定的设备标识符
|
||||
var __ids__={
|
||||
machineId:'$machineId',
|
||||
macMachineId:'$macMachineId',
|
||||
devDeviceId:'$deviceId',
|
||||
sqmId:'$sqmId',
|
||||
macAddress:'$macAddress'
|
||||
};
|
||||
|
||||
// 暴露到全局
|
||||
globalThis.__cursor_ids__=__ids__;
|
||||
|
||||
// Hook Module.prototype.require
|
||||
var Module=require('module');
|
||||
var _origReq=Module.prototype.require;
|
||||
var _hooked=new Map();
|
||||
|
||||
Module.prototype.require=function(id){
|
||||
var result=_origReq.apply(this,arguments);
|
||||
if(_hooked.has(id))return _hooked.get(id);
|
||||
var hooked=result;
|
||||
|
||||
// Hook child_process
|
||||
if(id==='child_process'){
|
||||
var _origExecSync=result.execSync;
|
||||
result.execSync=function(cmd,opts){
|
||||
var cmdStr=String(cmd).toLowerCase();
|
||||
if(cmdStr.includes('reg')&&cmdStr.includes('machineguid')){
|
||||
return Buffer.from('\r\n MachineGuid REG_SZ '+__ids__.machineId.substring(0,36)+'\r\n');
|
||||
}
|
||||
if(cmdStr.includes('ioreg')&&cmdStr.includes('ioplatformexpertdevice')){
|
||||
return Buffer.from('"IOPlatformUUID" = "'+__ids__.machineId.substring(0,36).toUpperCase()+'"');
|
||||
}
|
||||
return _origExecSync.apply(this,arguments);
|
||||
};
|
||||
hooked=result;
|
||||
}
|
||||
// Hook os
|
||||
else if(id==='os'){
|
||||
var _origNI=result.networkInterfaces;
|
||||
result.networkInterfaces=function(){
|
||||
return{'Ethernet':[{address:'192.168.1.100',netmask:'255.255.255.0',family:'IPv4',mac:__ids__.macAddress,internal:false}]};
|
||||
};
|
||||
hooked=result;
|
||||
}
|
||||
// Hook crypto
|
||||
else if(id==='crypto'){
|
||||
var _origCreateHash=result.createHash;
|
||||
var _origRandomUUID=result.randomUUID;
|
||||
result.createHash=function(algo){
|
||||
var hash=_origCreateHash.apply(this,arguments);
|
||||
if(algo.toLowerCase()==='sha256'){
|
||||
var _origDigest=hash.digest.bind(hash);
|
||||
var _origUpdate=hash.update.bind(hash);
|
||||
var inputData='';
|
||||
hash.update=function(data,enc){inputData+=String(data);return _origUpdate(data,enc);};
|
||||
hash.digest=function(enc){
|
||||
if(inputData.includes('MachineGuid')||inputData.includes('IOPlatformUUID')||(inputData.length>=32&&inputData.length<=40)){
|
||||
return enc==='hex'?__ids__.machineId:Buffer.from(__ids__.machineId,'hex');
|
||||
}
|
||||
return _origDigest(enc);
|
||||
};
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
if(_origRandomUUID){
|
||||
var uuidCount=0;
|
||||
result.randomUUID=function(){
|
||||
uuidCount++;
|
||||
if(uuidCount<=2)return __ids__.devDeviceId;
|
||||
return _origRandomUUID.apply(this,arguments);
|
||||
};
|
||||
}
|
||||
hooked=result;
|
||||
}
|
||||
// Hook @vscode/deviceid
|
||||
else if(id==='@vscode/deviceid'){
|
||||
hooked={...result,getDeviceId:async function(){return __ids__.devDeviceId;}};
|
||||
}
|
||||
// Hook @vscode/windows-registry
|
||||
else if(id==='@vscode/windows-registry'){
|
||||
var _origGetReg=result.GetStringRegKey;
|
||||
hooked={...result,GetStringRegKey:function(hive,path,name){
|
||||
if(name==='MachineId'||path.includes('SQMClient'))return __ids__.sqmId;
|
||||
if(name==='MachineGuid'||path.includes('Cryptography'))return __ids__.machineId.substring(0,36);
|
||||
return _origGetReg?_origGetReg.apply(this,arguments):'';
|
||||
}};
|
||||
}
|
||||
|
||||
if(hooked!==result)_hooked.set(id,hooked);
|
||||
return hooked;
|
||||
};
|
||||
|
||||
console.log('[Cursor ID Modifier] 增强版 Hook 已激活 - 煎饼果子(86) 公众号【煎饼果子卷AI】');
|
||||
})();
|
||||
// ========== Cursor Hook 注入结束 ==========
|
||||
|
||||
"@
|
||||
|
||||
# 找到版权声明结束位置并在其后注入
|
||||
if ($content -match '(\*/\s*\n)') {
|
||||
$content = $content -replace '(\*/\s*\n)', "`$1$injectCode"
|
||||
Write-Host " $GREEN✓$NC [方案B] 增强版 Hook 代码已注入(版权声明后)"
|
||||
} else {
|
||||
# 如果没有找到版权声明,则注入到文件开头
|
||||
$content = $injectCode + $content
|
||||
Write-Host " $GREEN✓$NC [方案B] 增强版 Hook 代码已注入(文件开头)"
|
||||
}
|
||||
|
||||
# 写入修改后的内容
|
||||
Set-Content -Path $file -Value $content -Encoding UTF8 -NoNewline
|
||||
|
||||
if ($replaced) {
|
||||
Write-Host "$GREEN✅ [成功]$NC A+B混合方案修改成功(someValue替换 + IIFE劫持)"
|
||||
Write-Host "$GREEN✅ [成功]$NC 增强版混合方案修改成功(someValue替换 + 深度Hook)"
|
||||
} else {
|
||||
Write-Host "$GREEN✅ [成功]$NC 方案B修改成功(IIFE劫持)"
|
||||
Write-Host "$GREEN✅ [成功]$NC 增强版 Hook 修改成功"
|
||||
}
|
||||
$modifiedCount++
|
||||
|
||||
@@ -228,7 +377,7 @@ function Modify-CursorJSFiles {
|
||||
Write-Host "$RED❌ [错误]$NC 修改文件失败: $($_.Exception.Message)"
|
||||
# 尝试从备份恢复
|
||||
$fileName = Split-Path $file -Leaf
|
||||
$backupFile = "$backupPath\$fileName"
|
||||
$backupFile = "$backupPath\$fileName.original"
|
||||
if (Test-Path $backupFile) {
|
||||
Copy-Item $backupFile $file -Force
|
||||
Write-Host "$YELLOW🔄 [恢复]$NC 已从备份恢复文件"
|
||||
@@ -240,9 +389,10 @@ function Modify-CursorJSFiles {
|
||||
Write-Host ""
|
||||
Write-Host "$GREEN🎉 [完成]$NC 成功修改 $modifiedCount 个JS文件"
|
||||
Write-Host "$BLUE💾 [备份]$NC 原始文件备份位置: $backupPath"
|
||||
Write-Host "$BLUE💡 [说明]$NC 使用A+B混合方案:"
|
||||
Write-Host "$BLUE💡 [说明]$NC 使用增强版 Hook 方案:"
|
||||
Write-Host " • 方案A: someValue占位符替换(稳定锚点,跨版本兼容)"
|
||||
Write-Host " • 方案B: IIFE运行时劫持(crypto.randomUUID + os.networkInterfaces)"
|
||||
Write-Host " • 方案B: 深度模块劫持(child_process, crypto, os, @vscode/*)"
|
||||
Write-Host "$BLUE📁 [配置]$NC ID 配置文件: $idsConfigPath"
|
||||
return $true
|
||||
} else {
|
||||
Write-Host "$RED❌ [失败]$NC 没有成功修改任何文件"
|
||||
|
||||
Reference in New Issue
Block a user