├── README.md ├── linux └── bash.sh ├── reset.ps1 └── reset.sh /README.md: -------------------------------------------------------------------------------- 1 | # Cursor Reset Script 2 | 3 | [English Version](#usage) 4 | 5 | 这是一个用于重置 Cursor IDE 设备标识的 PowerShell 脚本。该脚本支持 Cursor 0.45.x 版本(已在 0.45.8 版本上测试通过)。 6 | 7 | ## ⚠️ 免责声明 8 | 9 | 本项目仅供学习和研究使用,旨在研究 Cursor IDE 的设备标识机制。**强烈建议您购买 [Cursor](https://cursor.sh/) 的正版授权**以支持开发者。 10 | 11 | 使用本脚本可能违反 Cursor 的使用条款。作者不对使用本脚本导致的任何问题负责,包括但不限于: 12 | 13 | - 软件授权失效 14 | - 账号封禁 15 | - 其他未知风险 16 | 17 | 如果您认可 Cursor 的价值,请支持正版,为软件开发者的工作付费。 18 | 19 | ## 使用方法 20 | 21 | ⚠️ 为避免新账号立即失效,请严格按照以下步骤操作: 22 | 23 | ### Windows 24 | 25 | 1. 在 Cursor IDE 中退出当前登录的账号 26 | 2. 完全关闭 Cursor IDE 27 | 3. 以管理员身份打开命令提示符或 PowerShell 28 | 4. 复制粘贴执行以下命令: 29 | 30 | ```batch 31 | powershell -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; iwr -Uri 'https://raw.githubusercontent.com/hamflx/cursor-reset/main/reset.ps1' -UseBasicParsing | iex" 32 | ``` 33 | 34 | 5. 重置完成后打开 Cursor IDE,使用新的账号登录(不要使用之前的账号) 35 | 36 | 如果脚本卡在"正在等待 Cursor 进程退出...",可以在管理员权限的命令行中执行以下命令强制结束所有 Cursor 进程: 37 | 38 | ```powershell 39 | taskkill /f /im cursor.exe 40 | ``` 41 | 42 | ### macOS 43 | 44 | 1. 在 Cursor IDE 中退出当前登录的账号 45 | 2. 完全关闭 Cursor IDE 46 | 3. 打开终端,执行以下命令: 47 | 48 | ```bash 49 | curl -fsSL https://raw.githubusercontent.com/hamflx/cursor-reset/main/reset.sh | bash 50 | ``` 51 | 52 | 4. 启动 Cursor 并使用新账号登录(不要使用之前的账号) 53 | 54 | 如果需要恢复到原始状态,可以使用以下命令: 55 | 56 | ```bash 57 | curl -fsSL https://raw.githubusercontent.com/hamflx/cursor-reset/main/reset.sh | bash -s -- --restore 58 | ``` 59 | 60 | 如果脚本卡在"正在等待 Cursor 进程退出...",可以在终端中执行以下命令强制结束 Cursor 进程: 61 | 62 | ```bash 63 | pkill -9 Cursor 64 | ``` 65 | 66 | ### Linux 67 | 68 | 1. 在 Cursor IDE 中退出当前登录的账号 69 | 2. 完全关闭 Cursor IDE 70 | 3. 打开终端,执行以下命令: 71 | 72 | ```bash 73 | curl -fsSL https://raw.githubusercontent.com/hamflx/cursor-reset/main/linux/bash.sh | bash -s -- --appimage /path/to/cursor.AppImage 74 | ``` 75 | 76 | 将 `/path/to/cursor.AppImage` 替换为你的 Cursor AppImage 文件路径。 77 | 78 | 4. 启动 Cursor 并使用新账号登录(不要使用之前的账号) 79 | 80 | 如果脚本卡在"正在等待 Cursor 进程退出...",可以在终端中执行以下命令强制结束 Cursor 进程: 81 | 82 | ```bash 83 | pkill -9 Cursor 84 | ``` 85 | 86 | ## ⚠️ 重要注意事项 87 | 88 | ### Windows 89 | 90 | 脚本会修改系统注册表中的 `HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid`,这个值可能被其他软件用作设备标识,如果你购买了 Cursor 的正版授权或其他使用此注册表项作为设备标识的正版软件,修改后可能会导致这些软件的授权失效。 91 | 92 | 原始的 MachineGuid 会被自动备份到 `%USERPROFILE%\MachineGuid_Backups` 目录下,如果需要恢复原始 MachineGuid,可以从备份目录中找到对应的备份文件,然后通过注册表编辑器恢复该值。 93 | 94 | ## 系统要求 95 | 96 | ### Windows 97 | 98 | - Windows 操作系统 99 | - PowerShell 100 | - 管理员权限 101 | - Cursor IDE 0.45.x 版本(已在 0.45.8 版本测试通过) 102 | 103 | ### macOS 104 | 105 | - macOS 10.13 或更高版本 106 | - Cursor IDE 0.45.x 版本 107 | 108 | ### Linux 109 | 110 | - Linux 操作系统 111 | - Python 3 112 | - Cursor IDE 0.45.x 版本(仅支持 AppImage 安装方式) 113 | - appimagetool(用于重新打包 AppImage) 114 | - 安装路径必须为 `/opt/cursor-bin/cursor-bin.AppImage` 115 | 116 | --- 117 | 118 | This is a PowerShell script for resetting Cursor IDE device identifiers. The script supports Cursor 0.45.x. 119 | 120 | ## ⚠️ Disclaimer 121 | 122 | This project is for educational and research purposes only, aimed at studying the device identification mechanism of Cursor IDE. **It is strongly recommended to purchase a [Cursor](https://cursor.sh/) license** to support the developers. 123 | 124 | Using this script may violate Cursor's terms of service. The author assumes no responsibility for any issues arising from the use of this script, including but not limited to: 125 | 126 | - Software license invalidation 127 | - Account suspension 128 | - Other unknown risks 129 | 130 | If you value Cursor, please support the official version and pay for the developers' work. 131 | 132 | ## Usage 133 | 134 | ⚠️ To prevent the new account from being immediately invalidated, please follow these steps strictly: 135 | 136 | ### Windows 137 | 138 | 1. Sign out of your current account in Cursor IDE 139 | 2. Completely close Cursor IDE 140 | 3. Open Command Prompt or PowerShell as Administrator 141 | 4. Copy and paste the following command: 142 | 143 | ```batch 144 | powershell -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; iwr -Uri 'https://raw.githubusercontent.com/hamflx/cursor-reset/main/reset.ps1' -UseBasicParsing | iex" 145 | ``` 146 | 147 | 5. After reset is complete, open Cursor IDE and sign in with a new account (do not use the previous account) 148 | 149 | If the script is stuck at "Waiting for Cursor process to exit...", you can force kill Cursor processes by running the following command in the terminal: 150 | 151 | ```powershell 152 | taskkill /f /im cursor.exe 153 | ``` 154 | 155 | ### macOS 156 | 157 | 1. Sign out of your current account in Cursor IDE 158 | 2. Completely close Cursor IDE 159 | 3. Open terminal and execute the following command: 160 | 161 | ```bash 162 | curl -fsSL https://raw.githubusercontent.com/hamflx/cursor-reset/main/reset.sh | bash 163 | ``` 164 | 165 | 4. Start Cursor and sign in with a new account (do not use the previous account) 166 | 167 | To restore to the original state, you can use the following command: 168 | 169 | ```bash 170 | curl -fsSL https://raw.githubusercontent.com/hamflx/cursor-reset/main/reset.sh | bash -s -- --restore 171 | ``` 172 | 173 | If the script is stuck at "Cursor is running", you can force kill Cursor processes by running the following command in the terminal: 174 | 175 | ```bash 176 | pkill -9 Cursor 177 | ``` 178 | 179 | ### Linux 180 | 181 | 1. Sign out of your current account in Cursor IDE 182 | 2. Completely close Cursor IDE 183 | 3. Open terminal and execute the following command: 184 | 185 | ```bash 186 | curl -fsSL https://raw.githubusercontent.com/hamflx/cursor-reset/main/linux/bash.sh | bash -s -- --appimage /path/to/cursor.AppImage 187 | ``` 188 | 189 | Replace `/path/to/cursor.AppImage` with the path to your Cursor AppImage file. 190 | 191 | 4. Start Cursor and sign in with a new account (do not use the previous account) 192 | 193 | If the script is stuck at "Waiting for Cursor process to exit...", you can force kill Cursor processes by running the following command in the terminal: 194 | 195 | ```bash 196 | pkill -9 Cursor 197 | ``` 198 | 199 | ## ⚠️ Important Notes 200 | 201 | ### Windows 202 | 203 | The script modifies the system registry key `HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid`, which may be used by other software as a device identifier. If you have purchased a license for Cursor or other software that uses this registry key for device identification, modifying it may invalidate these software licenses. 204 | 205 | The original MachineGuid will be automatically backed up to the `%USERPROFILE%\MachineGuid_Backups` directory. If you need to restore the original MachineGuid, you can find the corresponding backup file in this directory and restore it using the registry editor. 206 | 207 | ## System Requirements 208 | 209 | ### Windows 210 | 211 | - Windows OS 212 | - PowerShell 213 | - Administrator privileges 214 | - Cursor IDE 0.45.x (tested on version 0.45.8) 215 | 216 | ### macOS 217 | 218 | - macOS 10.13 or higher 219 | - Cursor IDE 0.45.x 220 | 221 | ### Linux 222 | 223 | - Linux operating system 224 | - Cursor IDE 0.45.x (AppImage format) 225 | - appimagetool (will be automatically downloaded if not present) 226 | -------------------------------------------------------------------------------- /linux/bash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Parse command line arguments 4 | APPIMAGE_PATH="" 5 | while [[ $# -gt 0 ]]; do 6 | case $1 in 7 | --appimage) 8 | APPIMAGE_PATH="$2" 9 | shift 2 10 | ;; 11 | *) 12 | echo "Unknown option: $1" 13 | echo "Usage: $0 --appimage /path/to/cursor.AppImage" 14 | exit 1 15 | ;; 16 | esac 17 | done 18 | 19 | # Validate AppImage path 20 | if [ -z "$APPIMAGE_PATH" ]; then 21 | echo "Error: AppImage path is required" 22 | echo "Usage: $0 --appimage /path/to/cursor.AppImage" 23 | exit 1 24 | fi 25 | 26 | if [ ! -f "$APPIMAGE_PATH" ]; then 27 | echo "Error: AppImage file not found at $APPIMAGE_PATH" 28 | exit 1 29 | fi 30 | 31 | # Get the real user 32 | REAL_USER=$(whoami) 33 | if [ -z "$REAL_USER" ]; then 34 | echo "Error: Unable to determine the user." 35 | exit 1 36 | fi 37 | REAL_HOME=$(eval echo "~$REAL_USER") 38 | 39 | # Check for required commands 40 | for cmd in uuidgen; do 41 | if ! command -v $cmd &> /dev/null; then 42 | echo "Error: Required command $cmd not found." 43 | exit 1 44 | fi 45 | done 46 | 47 | # Generate a macMachineId-like ID 48 | generate_mac_machine_id() { 49 | # Generate a UUID and ensure 13th char is 4 and 17th is 8-b 50 | uuid=$(uuidgen | tr '[:upper:]' '[:lower:]') 51 | # Ensure 13th char is 4 52 | uuid=$(echo $uuid | sed 's/.\{12\}\(.\)/4/') 53 | # Ensure 17th char is 8-b (via random) 54 | random_hex=$(echo $RANDOM | md5sum | cut -c1) 55 | random_num=$((16#$random_hex)) 56 | new_char=$(printf '%x' $(( ($random_num & 0x3) | 0x8 ))) 57 | uuid=$(echo $uuid | sed "s/.\{16\}\(.\)/$new_char/") 58 | echo $uuid 59 | } 60 | 61 | # Generate a 64-bit random ID 62 | generate_random_id() { 63 | uuid1=$(uuidgen | tr -d '-') 64 | uuid2=$(uuidgen | tr -d '-') 65 | echo "${uuid1}${uuid2}" 66 | } 67 | 68 | # Check if Cursor process is running (case-insensitive match) 69 | while pgrep -i -x "Cursor" > /dev/null || pgrep -i -f "Cursor.app" > /dev/null; do 70 | echo "Detected that Cursor is running. Please close Cursor to continue..." 71 | echo "Waiting for Cursor to exit..." 72 | sleep 1 73 | done 74 | 75 | echo "Cursor is not running. Continuing execution..." 76 | 77 | # Update storage.json with new telemetry IDs 78 | STORAGE_JSON="$REAL_HOME/.config/Cursor/User/globalStorage/storage.json" 79 | NEW_MACHINE_ID=$(generate_random_id) 80 | NEW_MAC_MACHINE_ID=$(generate_mac_machine_id) 81 | NEW_DEV_DEVICE_ID=$(uuidgen) 82 | NEW_SQM_ID="{$(uuidgen | tr '[:lower:]' '[:upper:]')}" 83 | 84 | if [ -f "$STORAGE_JSON" ]; then 85 | cp "$STORAGE_JSON" "${STORAGE_JSON}.bak" || { 86 | echo "Error: Unable to backup storage.json." 87 | exit 1 88 | } 89 | 90 | # Use jq to update the JSON file if available 91 | if command -v jq &> /dev/null; then 92 | jq --arg mid "$NEW_MACHINE_ID" \ 93 | --arg mmid "$NEW_MAC_MACHINE_ID" \ 94 | --arg did "$NEW_DEV_DEVICE_ID" \ 95 | --arg sid "$NEW_SQM_ID" \ 96 | '.["telemetry.machineId"]=$mid | .["telemetry.macMachineId"]=$mmid | .["telemetry.devDeviceId"]=$did | .["telemetry.sqmId"]=$sid' \ 97 | "$STORAGE_JSON" > "${STORAGE_JSON}.tmp" && \ 98 | mv "${STORAGE_JSON}.tmp" "$STORAGE_JSON" || { 99 | echo "Error: Failed to update storage.json" 100 | exit 1 101 | } 102 | else 103 | echo "Warning: jq not found. Skipping storage.json update." 104 | fi 105 | fi 106 | 107 | echo "Successfully updated all IDs:" 108 | echo "New telemetry.machineId: $NEW_MACHINE_ID" 109 | echo "New telemetry.macMachineId: $NEW_MAC_MACHINE_ID" 110 | echo "New telemetry.devDeviceId: $NEW_DEV_DEVICE_ID" 111 | echo "New telemetry.sqmId: $NEW_SQM_ID" 112 | echo "" 113 | 114 | TEMP_DIR=$(mktemp -d) 115 | # Create temporary directory for download 116 | if [ -z "$TEMP_DIR" ] || [ ! -d "$TEMP_DIR" ]; then 117 | echo "Error: Failed to create temporary directory" 118 | exit 1 119 | fi 120 | 121 | # After updating PATH configuration, extract the AppImage 122 | APPIMAGE_DIR="$(dirname "$APPIMAGE_PATH")" 123 | cd "$APPIMAGE_DIR" || { echo "Error: Unable to change to AppImage directory"; exit 1; } 124 | 125 | # Create squashfs-root in temporary directory 126 | cd "$TEMP_DIR" || { echo "Error: Unable to change to temporary directory"; exit 1; } 127 | 128 | echo "Extracting AppImage..." 129 | if [ ! -d "squashfs-root" ]; then 130 | "$APPIMAGE_PATH" --appimage-extract >/dev/null || { 131 | echo "Error: Extraction failed." 132 | rm -rf "$TEMP_DIR" 133 | exit 1 134 | } 135 | fi 136 | echo "Extracted AppImage to: $TEMP_DIR/squashfs-root" 137 | 138 | # Modify the files inside the extracted AppImage 139 | FILES=( 140 | "$TEMP_DIR/squashfs-root/resources/app/out/main.js" 141 | "$TEMP_DIR/squashfs-root/resources/app/out/vs/code/node/cliProcessMain.js" 142 | ) 143 | 144 | # Process each file 145 | for file in "${FILES[@]}"; do 146 | if [ ! -f "$file" ]; then 147 | echo "Warning: File $file not found" 148 | continue 149 | fi 150 | 151 | # Ensure we have write permissions 152 | chmod -R u+w "$TEMP_DIR/squashfs-root" || { 153 | echo "Error: Unable to set permissions" 154 | rm -rf "$TEMP_DIR" 155 | exit 1 156 | } 157 | 158 | # Replace machine-id related code using sed 159 | sed -i 's/"[^"]*\/etc\/machine-id[^"]*"/"uuidgen"/g' "$file" || { 160 | echo "Error: Failed to modify $file" 161 | rm -rf "$TEMP_DIR" 162 | exit 1 163 | } 164 | 165 | echo "Successfully modified $file" 166 | done 167 | 168 | # Export the temporary directory path for later use 169 | APPIMAGETOOL_PATH="/tmp/appimagetool" 170 | 171 | # Function to download and setup appimagetool 172 | setup_appimagetool() { 173 | echo "appimagetool not found, attempting to download..." 174 | 175 | # Download latest continuous build 176 | if command -v curl &> /dev/null; then 177 | curl -sL -o "$APPIMAGETOOL_PATH" "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage" || { 178 | echo "Error: Failed to download appimagetool" 179 | exit 1 180 | } 181 | elif command -v wget &> /dev/null; then 182 | wget -O "$APPIMAGETOOL_PATH" "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage" || { 183 | echo "Error: Failed to download appimagetool" 184 | exit 1 185 | } 186 | else 187 | echo "Error: Neither curl nor wget is available" 188 | exit 1 189 | fi 190 | 191 | # Make it executable 192 | chmod +x "$APPIMAGETOOL_PATH" || { 193 | echo "Error: Failed to make appimagetool executable" 194 | exit 1 195 | } 196 | 197 | echo "Successfully downloaded appimagetool to $APPIMAGETOOL_PATH" 198 | } 199 | 200 | # Check and setup appimagetool if needed 201 | if [ ! -f "$APPIMAGETOOL_PATH" ]; then 202 | setup_appimagetool || { 203 | echo "Error: Failed to setup appimagetool" 204 | rm -rf "$TEMP_DIR" 205 | exit 1 206 | } 207 | fi 208 | 209 | # Repack the AppImage in-place (replacing the original) 210 | echo "Repacking AppImage..." 211 | ARCH=x86_64 "$APPIMAGETOOL_PATH" -n ./squashfs-root >/dev/null 2>&1 || { 212 | echo "Error: Repacking failed." 213 | rm -rf "$TEMP_DIR" 214 | exit 1 215 | } 216 | 217 | NEW_IMAGE=$(ls -t Cursor-*.AppImage 2>/dev/null | head -n1) 218 | if [ -z "$NEW_IMAGE" ]; then 219 | echo "Error: No repacked AppImage found." 220 | rm -rf "$TEMP_DIR" 221 | exit 1 222 | fi 223 | 224 | mv -f "$NEW_IMAGE" "$APPIMAGE_PATH" || { 225 | echo "Error: Overwriting failed." 226 | rm -rf "$TEMP_DIR" 227 | exit 1 228 | } 229 | echo "Repacked AppImage updated at $APPIMAGE_PATH" 230 | 231 | # Cleanup temporary directory 232 | rm -rf "$TEMP_DIR" 233 | 234 | echo "Reset complete! Please launch Cursor using $APPIMAGE_PATH" 235 | 236 | 237 | -------------------------------------------------------------------------------- /reset.ps1: -------------------------------------------------------------------------------- 1 | # 生成类似 macMachineId 的格式 2 | function New-MacMachineId { 3 | $template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" 4 | $result = "" 5 | $random = [Random]::new() 6 | 7 | foreach ($char in $template.ToCharArray()) { 8 | if ($char -eq 'x' -or $char -eq 'y') { 9 | $r = $random.Next(16) 10 | $v = if ($char -eq "x") { $r } else { ($r -band 0x3) -bor 0x8 } 11 | $result += $v.ToString("x") 12 | } else { 13 | $result += $char 14 | } 15 | } 16 | return $result 17 | } 18 | 19 | # 生成64位随机ID 20 | function New-RandomId { 21 | $uuid1 = [guid]::NewGuid().ToString("N") 22 | $uuid2 = [guid]::NewGuid().ToString("N") 23 | return $uuid1 + $uuid2 24 | } 25 | 26 | # 等待 Cursor 进程退出 27 | $cursorProcesses = Get-Process "cursor" -ErrorAction SilentlyContinue 28 | if ($cursorProcesses) { 29 | Write-Host "检测到 Cursor 正在运行。请关闭 Cursor 后继续..." 30 | Write-Host "正在等待 Cursor 进程退出..." 31 | 32 | while ($true) { 33 | $cursorProcesses = Get-Process "cursor" -ErrorAction SilentlyContinue 34 | if (-not $cursorProcesses) { 35 | Write-Host "Cursor 已关闭,继续执行..." 36 | break 37 | } 38 | Start-Sleep -Seconds 1 39 | } 40 | } 41 | 42 | # 备份 MachineGuid 43 | $backupDir = Join-Path $HOME "MachineGuid_Backups" 44 | if (-not (Test-Path $backupDir)) { 45 | New-Item -ItemType Directory -Path $backupDir | Out-Null 46 | } 47 | 48 | $currentValue = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography" -Name MachineGuid 49 | $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" 50 | $backupFile = Join-Path $backupDir "MachineGuid_$timestamp.txt" 51 | $counter = 0 52 | 53 | while (Test-Path $backupFile) { 54 | $counter++ 55 | $backupFile = Join-Path $backupDir "MachineGuid_${timestamp}_$counter.txt" 56 | } 57 | 58 | $currentValue.MachineGuid | Out-File $backupFile 59 | 60 | # 使用环境变量构建 storage.json 路径 61 | $storageJsonPath = Join-Path $env:APPDATA "Cursor\User\globalStorage\storage.json" 62 | $newMachineId = New-RandomId 63 | $newMacMachineId = New-MacMachineId 64 | $newDevDeviceId = [guid]::NewGuid().ToString() 65 | $newSqmId = "{$([guid]::NewGuid().ToString().ToUpper())}" 66 | 67 | if (Test-Path $storageJsonPath) { 68 | # 保存原始文件属性 69 | $originalAttributes = (Get-ItemProperty $storageJsonPath).Attributes 70 | 71 | # 移除只读属性 72 | Set-ItemProperty $storageJsonPath -Name IsReadOnly -Value $false 73 | 74 | # 更新文件内容 75 | $jsonContent = Get-Content $storageJsonPath -Raw -Encoding UTF8 76 | $data = $jsonContent | ConvertFrom-Json 77 | 78 | # 检查并更新或添加属性 79 | $properties = @{ 80 | "telemetry.machineId" = $newMachineId 81 | "telemetry.macMachineId" = $newMacMachineId 82 | "telemetry.devDeviceId" = $newDevDeviceId 83 | "telemetry.sqmId" = $newSqmId 84 | } 85 | 86 | foreach ($prop in $properties.Keys) { 87 | if (-not (Get-Member -InputObject $data -Name $prop -MemberType Properties)) { 88 | $data | Add-Member -NotePropertyName $prop -NotePropertyValue $properties[$prop] 89 | } else { 90 | $data.$prop = $properties[$prop] 91 | } 92 | } 93 | 94 | $newJson = $data | ConvertTo-Json -Depth 100 95 | 96 | # 使用 StreamWriter 保存文件,确保 UTF-8 无 BOM 且使用 LF 换行符 97 | $utf8NoBom = New-Object System.Text.UTF8Encoding $false 98 | [System.IO.File]::WriteAllText($storageJsonPath, $newJson.Replace("`r`n", "`n"), $utf8NoBom) 99 | 100 | # 恢复原始文件属性 101 | Set-ItemProperty $storageJsonPath -Name Attributes -Value $originalAttributes 102 | } 103 | 104 | # 更新注册表 MachineGuid 105 | $newMachineGuid = [guid]::NewGuid().ToString() 106 | Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Cryptography" -Name "MachineGuid" -Value $newMachineGuid 107 | 108 | Write-Host "Successfully updated all IDs:" 109 | Write-Host "Backup file created at: $backupFile" 110 | Write-Host "New MachineGuid: $newMachineGuid" 111 | Write-Host "New telemetry.machineId: $newMachineId" 112 | Write-Host "New telemetry.macMachineId: $newMacMachineId" 113 | Write-Host "New telemetry.devDeviceId: $newDevDeviceId" 114 | Write-Host "New telemetry.sqmId: $newSqmId" -------------------------------------------------------------------------------- /reset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 获取实际用户信息 4 | if [ -n "$SUDO_USER" ]; then 5 | REAL_USER="$SUDO_USER" 6 | elif [ -n "$DOAS_USER" ]; then 7 | REAL_USER="$DOAS_USER" 8 | else 9 | REAL_USER=$(who am i | awk '{print $1}') 10 | if [ -z "$REAL_USER" ]; then 11 | REAL_USER=$(logname) 12 | fi 13 | fi 14 | 15 | if [ -z "$REAL_USER" ]; then 16 | echo "错误: 无法确定实际用户" 17 | exit 1 18 | fi 19 | 20 | REAL_HOME=$(eval echo ~$REAL_USER) 21 | 22 | # 检查必要的命令 23 | for cmd in uuidgen ioreg; do 24 | if ! command -v $cmd &> /dev/null; then 25 | echo "错误: 需要 $cmd 但未找到" 26 | exit 1 27 | fi 28 | done 29 | 30 | # 生成类似 macMachineId 的格式 31 | generate_mac_machine_id() { 32 | # 使用 uuidgen 生成基础 UUID,然后确保第 13 位是 4,第 17 位是 8-b 33 | uuid=$(uuidgen | tr '[:upper:]' '[:lower:]') 34 | # 确保第 13 位是 4 35 | uuid=$(echo $uuid | sed 's/.\{12\}\(.\)/4/') 36 | # 确保第 17 位是 8-b (通过随机数) 37 | random_hex=$(echo $RANDOM | md5 | cut -c1) 38 | random_num=$((16#$random_hex)) 39 | new_char=$(printf '%x' $(( ($random_num & 0x3) | 0x8 ))) 40 | uuid=$(echo $uuid | sed "s/.\{16\}\(.\)/$new_char/") 41 | echo $uuid 42 | } 43 | 44 | # 生成64位随机ID 45 | generate_random_id() { 46 | uuid1=$(uuidgen | tr -d '-') 47 | uuid2=$(uuidgen | tr -d '-') 48 | echo "${uuid1}${uuid2}" 49 | } 50 | 51 | # 检查 Cursor 进程 52 | if pgrep -x "Cursor" > /dev/null || pgrep -f "Cursor.app" > /dev/null; then 53 | echo "检测到 Cursor 正在运行。请关闭 Cursor 后继续..." 54 | echo "正在等待 Cursor 进程退出..." 55 | while pgrep -x "Cursor" > /dev/null || pgrep -f "Cursor.app" > /dev/null; do 56 | sleep 1 57 | done 58 | fi 59 | 60 | echo "Cursor 已关闭,继续执行..." 61 | 62 | # 定义文件路径 63 | STORAGE_JSON="$REAL_HOME/Library/Application Support/Cursor/User/globalStorage/storage.json" 64 | FILES=( 65 | "/Applications/Cursor.app/Contents/Resources/app/out/main.js" 66 | "/Applications/Cursor.app/Contents/Resources/app/out/vs/code/node/cliProcessMain.js" 67 | ) 68 | 69 | # 恢复功能 70 | restore_files() { 71 | # 恢复 storage.json 72 | if [ -f "${STORAGE_JSON}.bak" ]; then 73 | cp "${STORAGE_JSON}.bak" "$STORAGE_JSON" && { 74 | echo "已恢复 storage.json" 75 | # 确保恢复后的文件权限正确 76 | chown $REAL_USER:staff "$STORAGE_JSON" 77 | chmod 644 "$STORAGE_JSON" 78 | } || echo "错误: 恢复 storage.json 失败" 79 | else 80 | echo "警告: storage.json 的备份文件不存在" 81 | fi 82 | 83 | # 恢复应用程序 84 | if [ -d "/Applications/Cursor.backup.app" ]; then 85 | echo "正在恢复 Cursor.app..." 86 | # 关闭应用 87 | osascript -e 'tell application "Cursor" to quit' || true 88 | sleep 2 89 | 90 | # 删除当前应用并恢复备份 91 | rm -rf "/Applications/Cursor.app" 92 | mv "/Applications/Cursor.backup.app" "/Applications/Cursor.app" && { 93 | echo "已恢复 Cursor.app" 94 | } || echo "错误: 恢复 Cursor.app 失败" 95 | else 96 | echo "警告: Cursor.app 的备份不存在" 97 | fi 98 | 99 | echo "恢复操作完成" 100 | exit 0 101 | } 102 | 103 | # 检查是否为恢复模式 104 | if [ "$1" = "--restore" ]; then 105 | restore_files 106 | fi 107 | 108 | # 更新 storage.json 109 | NEW_MACHINE_ID=$(generate_random_id) 110 | NEW_MAC_MACHINE_ID=$(generate_mac_machine_id) 111 | NEW_DEV_DEVICE_ID=$(uuidgen) 112 | NEW_SQM_ID="{$(uuidgen | tr '[:lower:]' '[:upper:]')}" 113 | 114 | if [ -f "$STORAGE_JSON" ]; then 115 | # 备份原始文件 116 | cp "$STORAGE_JSON" "${STORAGE_JSON}.bak" || { 117 | echo "错误: 无法备份 storage.json" 118 | exit 1 119 | } 120 | 121 | # 确保备份文件的所有权正确 122 | chown $REAL_USER:staff "${STORAGE_JSON}.bak" 123 | chmod 644 "${STORAGE_JSON}.bak" 124 | 125 | # 使用 osascript 更新 JSON 文件 126 | osascript -l JavaScript << EOF 127 | function run() { 128 | const fs = $.NSFileManager.defaultManager; 129 | const path = '$STORAGE_JSON'; 130 | const nsdata = fs.contentsAtPath(path); 131 | const nsstr = $.NSString.alloc.initWithDataEncoding(nsdata, $.NSUTF8StringEncoding); 132 | const content = nsstr.js; 133 | const data = JSON.parse(content); 134 | 135 | data['telemetry.machineId'] = '$NEW_MACHINE_ID'; 136 | data['telemetry.macMachineId'] = '$NEW_MAC_MACHINE_ID'; 137 | data['telemetry.devDeviceId'] = '$NEW_DEV_DEVICE_ID'; 138 | data['telemetry.sqmId'] = '$NEW_SQM_ID'; 139 | 140 | const newContent = JSON.stringify(data, null, 2); 141 | const newData = $.NSString.alloc.initWithUTF8String(newContent); 142 | newData.writeToFileAtomicallyEncodingError(path, true, $.NSUTF8StringEncoding, null); 143 | 144 | return "success"; 145 | } 146 | EOF 147 | 148 | if [ $? -ne 0 ]; then 149 | echo "错误: 更新 storage.json 失败" 150 | exit 1 151 | fi 152 | 153 | # 确保修改后的文件所有权正确 154 | chown $REAL_USER:staff "$STORAGE_JSON" 155 | chmod 644 "$STORAGE_JSON" 156 | fi 157 | 158 | echo "Successfully updated all IDs:" 159 | echo "Backup file created at: $BACKUP_FILE" 160 | echo "New telemetry.machineId: $NEW_MACHINE_ID" 161 | echo "New telemetry.macMachineId: $NEW_MAC_MACHINE_ID" 162 | echo "New telemetry.devDeviceId: $NEW_DEV_DEVICE_ID" 163 | echo "New telemetry.sqmId: $NEW_SQM_ID" 164 | echo "" 165 | 166 | # 在处理文件之前,先复制整个应用到临时目录 167 | echo "正在复制 Cursor.app 到临时目录..." 168 | TIMESTAMP=$(date +%Y%m%d_%H%M%S) 169 | TEMP_DIR="/tmp/cursor_reset_${TIMESTAMP}" 170 | TEMP_APP="$TEMP_DIR/Cursor.app" 171 | 172 | # 确保临时目录不存在 173 | if [ -d "$TEMP_DIR" ]; then 174 | echo "清理已存在的临时目录..." 175 | rm -rf "$TEMP_DIR" 176 | fi 177 | 178 | # 创建临时目录 179 | mkdir -p "$TEMP_DIR" || { 180 | echo "错误: 无法创建临时目录" 181 | exit 1 182 | } 183 | 184 | # 复制应用到临时目录 185 | cp -R "/Applications/Cursor.app" "$TEMP_DIR" || { 186 | echo "错误: 无法复制应用到临时目录" 187 | rm -rf "$TEMP_DIR" 188 | exit 1 189 | } 190 | 191 | # 确保临时目录的权限正确 192 | chown -R $REAL_USER:staff "$TEMP_DIR" 193 | chmod -R 755 "$TEMP_DIR" 194 | 195 | echo "正在移除临时应用的签名..." 196 | codesign --remove-signature "$TEMP_APP" || { 197 | echo "警告: 移除应用签名失败" 198 | } 199 | 200 | # 移除所有相关组件的签名 201 | components=( 202 | "$TEMP_APP/Contents/Frameworks/Cursor Helper.app" 203 | "$TEMP_APP/Contents/Frameworks/Cursor Helper (GPU).app" 204 | "$TEMP_APP/Contents/Frameworks/Cursor Helper (Plugin).app" 205 | "$TEMP_APP/Contents/Frameworks/Cursor Helper (Renderer).app" 206 | ) 207 | 208 | for component in "${components[@]}"; do 209 | if [ -e "$component" ]; then 210 | echo "正在移除签名: $component" 211 | codesign --remove-signature "$component" || { 212 | echo "警告: 移除组件签名失败: $component" 213 | } 214 | fi 215 | done 216 | 217 | # 修改临时应用中的文件 218 | FILES=( 219 | "$TEMP_APP/Contents/Resources/app/out/main.js" 220 | "$TEMP_APP/Contents/Resources/app/out/vs/code/node/cliProcessMain.js" 221 | ) 222 | 223 | # 处理每个文件 224 | for file in "${FILES[@]}"; do 225 | if [ ! -f "$file" ]; then 226 | echo "警告: 文件 $file 不存在" 227 | continue 228 | fi 229 | 230 | # 创建备份 231 | backup_file="${file}.bak" 232 | cp "$file" "$backup_file" || { 233 | echo "错误: 无法备份文件 $file" 234 | continue 235 | } 236 | 237 | # 读取文件内容 238 | content=$(cat "$file") 239 | 240 | # 查找 IOPlatformUUID 的位置 241 | uuid_pos=$(printf "%s" "$content" | grep -b -o "IOPlatformUUID" | cut -d: -f1) 242 | if [ -z "$uuid_pos" ]; then 243 | echo "警告: 在 $file 中未找到 IOPlatformUUID" 244 | continue 245 | fi 246 | 247 | # 从 UUID 位置向前查找 switch 248 | before_uuid=${content:0:$uuid_pos} 249 | switch_pos=$(printf "%s" "$before_uuid" | grep -b -o "switch" | tail -n1 | cut -d: -f1) 250 | if [ -z "$switch_pos" ]; then 251 | echo "警告: 在 $file 中未找到 switch 关键字" 252 | continue 253 | fi 254 | 255 | # 构建新的文件内容 256 | printf "%sreturn crypto.randomUUID();\n%s" "${content:0:$switch_pos}" "${content:$switch_pos}" > "$file" || { 257 | echo "错误: 无法写入文件 $file" 258 | continue 259 | } 260 | 261 | echo "成功修改文件: $file" 262 | done 263 | 264 | # 重新签名临时应用 265 | echo "正在重新签名临时应用..." 266 | codesign --sign - "$TEMP_APP" --force --deep || { 267 | echo "警告: 重新签名失败" 268 | } 269 | 270 | # 关闭原应用 271 | echo "正在关闭 Cursor..." 272 | osascript -e 'tell application "Cursor" to quit' || true 273 | sleep 2 274 | 275 | # 备份原应用 276 | echo "备份原应用..." 277 | if [ -d "/Applications/Cursor.backup.app" ]; then 278 | rm -rf "/Applications/Cursor.backup.app" 279 | fi 280 | mv "/Applications/Cursor.app" "/Applications/Cursor.backup.app" || { 281 | echo "错误: 无法备份原应用" 282 | rm -rf "$TEMP_DIR" 283 | exit 1 284 | } 285 | 286 | # 移动修改后的应用到应用程序文件夹 287 | echo "安装修改后的应用..." 288 | mv "$TEMP_APP" "/Applications/" || { 289 | echo "错误: 无法安装修改后的应用" 290 | mv "/Applications/Cursor.backup.app" "/Applications/Cursor.app" 291 | rm -rf "$TEMP_DIR" 292 | exit 1 293 | } 294 | 295 | # 清理临时目录 296 | rm -rf "$TEMP_DIR" 297 | 298 | echo "应用修改完成!原应用已备份为 /Applications/Cursor.backup.app" 299 | echo "所有操作完成" 300 | --------------------------------------------------------------------------------