Skip to content

Instantly share code, notes, and snippets.

@0PandaDEV
Last active March 21, 2025 17:04
Show Gist options
  • Save 0PandaDEV/b81ec008a75fcb85afe32142c65d294b to your computer and use it in GitHub Desktop.
Save 0PandaDEV/b81ec008a75fcb85afe32142c65d294b to your computer and use it in GitHub Desktop.
#!/bin/bash
# Set error handling
set -e
# Define log file path
LOG_FILE="/tmp/cursor_mac_id_modifier.log"
# Initialize log file
initialize_log() {
echo "========== Cursor ID Modification Tool Log Start $(date) ==========" > "$LOG_FILE"
chmod 644 "$LOG_FILE"
}
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Log functions - output to both terminal and log file
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
log_debug() {
echo -e "${BLUE}[DEBUG]${NC} $1"
echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
# Log command output to log file
log_cmd_output() {
local cmd="$1"
local msg="$2"
echo "[CMD] $(date '+%Y-%m-%d %H:%M:%S') Execute command: $cmd" >> "$LOG_FILE"
echo "[CMD] $msg:" >> "$LOG_FILE"
eval "$cmd" 2>&1 | tee -a "$LOG_FILE"
echo "" >> "$LOG_FILE"
}
# Get current user
get_current_user() {
if [ "$EUID" -eq 0 ]; then
echo "$SUDO_USER"
else
echo "$USER"
fi
}
CURRENT_USER=$(get_current_user)
if [ -z "$CURRENT_USER" ]; then
log_error "Unable to get username"
exit 1
fi
# Define configuration file paths
STORAGE_FILE="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json"
BACKUP_DIR="$HOME/Library/Application Support/Cursor/User/globalStorage/backups"
# Define Cursor application path
CURSOR_APP_PATH="/Applications/Cursor.app"
# Check permissions
check_permissions() {
if [ "$EUID" -ne 0 ]; then
log_error "Please run this script with sudo"
echo "Example: sudo $0"
exit 1
fi
}
# Check and kill Cursor process
check_and_kill_cursor() {
log_info "Checking Cursor process..."
local attempt=1
local max_attempts=5
# Function: Get process details
get_process_details() {
local process_name="$1"
log_debug "Getting $process_name process details:"
ps aux | grep -i "/Applications/Cursor.app" | grep -v grep
}
while [ $attempt -le $max_attempts ]; do
# Use more precise matching to get Cursor process
CURSOR_PIDS=$(ps aux | grep -i "/Applications/Cursor.app" | grep -v grep | awk '{print $2}')
if [ -z "$CURSOR_PIDS" ]; then
log_info "No running Cursor process found"
return 0
fi
log_warn "Cursor process is running"
get_process_details "cursor"
log_warn "Attempting to close Cursor process..."
if [ $attempt -eq $max_attempts ]; then
log_warn "Attempting to force terminate process..."
kill -9 $CURSOR_PIDS 2>/dev/null || true
else
kill $CURSOR_PIDS 2>/dev/null || true
fi
sleep 1
# Also use more precise matching to check if the process is still running
if ! ps aux | grep -i "/Applications/Cursor.app" | grep -v grep > /dev/null; then
log_info "Cursor process successfully closed"
return 0
fi
log_warn "Waiting for process to close, attempt $attempt/$max_attempts..."
((attempt++))
done
log_error "Unable to close Cursor process after $max_attempts attempts"
get_process_details "cursor"
log_error "Please manually close the process and try again"
exit 1
}
# Backup system ID
backup_system_id() {
log_info "Backing up system ID..."
local system_id_file="$BACKUP_DIR/system_id.backup_$(date +%Y%m%d_%H%M%S)"
# Get and backup IOPlatformExpertDevice information
{
echo "# Original System ID Backup" > "$system_id_file"
echo "## IOPlatformExpertDevice Info:" >> "$system_id_file"
ioreg -rd1 -c IOPlatformExpertDevice >> "$system_id_file"
chmod 444 "$system_id_file"
chown "$CURRENT_USER" "$system_id_file"
log_info "System ID backed up to: $system_id_file"
} || {
log_error "Failed to backup system ID"
return 1
}
}
# Backup configuration file
backup_config() {
if [ ! -f "$STORAGE_FILE" ]; then
log_warn "Configuration file does not exist, skipping backup"
return 0
fi
mkdir -p "$BACKUP_DIR"
local backup_file="$BACKUP_DIR/storage.json.backup_$(date +%Y%m%d_%H%M%S)"
if cp "$STORAGE_FILE" "$backup_file"; then
chmod 644 "$backup_file"
chown "$CURRENT_USER" "$backup_file"
log_info "Configuration backed up to: $backup_file"
else
log_error "Backup failed"
exit 1
fi
}
# Generate random ID
generate_random_id() {
# Generate 32 bytes (64 hexadecimal characters) of random data
openssl rand -hex 32
}
# Generate random UUID
generate_uuid() {
uuidgen | tr '[:upper:]' '[:lower:]'
}
# Modify existing file
modify_or_add_config() {
local key="$1"
local value="$2"
local file="$3"
if [ ! -f "$file" ]; then
log_error "File does not exist: $file"
return 1
fi
# Ensure file is writable
chmod 644 "$file" || {
log_error "Unable to modify file permissions: $file"
return 1
}
# Create temporary file
local temp_file=$(mktemp)
# Check if key exists
if grep -q "\"$key\":" "$file"; then
# Key exists, perform replacement
sed "s/\"$key\":[[:space:]]*\"[^\"]*\"/\"$key\": \"$value\"/" "$file" > "$temp_file" || {
log_error "Failed to modify configuration: $key"
rm -f "$temp_file"
return 1
}
else
# Key doesn't exist, add new key-value pair
sed "s/}$/,\n \"$key\": \"$value\"\n}/" "$file" > "$temp_file" || {
log_error "Failed to add configuration: $key"
rm -f "$temp_file"
return 1
}
fi
# Check if temporary file is empty
if [ ! -s "$temp_file" ]; then
log_error "Generated temporary file is empty"
rm -f "$temp_file"
return 1
fi
# Use cat to replace original file content
cat "$temp_file" > "$file" || {
log_error "Unable to write to file: $file"
rm -f "$temp_file"
return 1
}
rm -f "$temp_file"
# Restore file permissions
chmod 444 "$file"
return 0
}
# Generate new configuration
generate_new_config() {
# Modify system ID
log_info "Modifying system ID..."
echo "[CONFIG] Starting system ID modification" >> "$LOG_FILE"
# Backup current system ID
backup_system_id
# Generate new system UUID
local new_system_uuid=$(uuidgen)
echo "[CONFIG] Generated new system UUID: $new_system_uuid" >> "$LOG_FILE"
# Modify system UUID
sudo nvram SystemUUID="$new_system_uuid"
echo "[CONFIG] System UUID has been set" >> "$LOG_FILE"
printf "${YELLOW}System UUID has been updated to: $new_system_uuid${NC}\n"
printf "${YELLOW}Please restart the system for changes to take effect${NC}\n"
# Convert auth0|user_ to hexadecimal byte array
local prefix_hex=$(echo -n "auth0|user_" | xxd -p)
local random_part=$(generate_random_id)
local machine_id="${prefix_hex}${random_part}"
local mac_machine_id=$(generate_random_id)
local device_id=$(generate_uuid | tr '[:upper:]' '[:lower:]')
local sqm_id="{$(generate_uuid | tr '[:lower:]' '[:upper:]')}"
echo "[CONFIG] Generated IDs:" >> "$LOG_FILE"
echo "[CONFIG] machine_id: $machine_id" >> "$LOG_FILE"
echo "[CONFIG] mac_machine_id: $mac_machine_id" >> "$LOG_FILE"
echo "[CONFIG] device_id: $device_id" >> "$LOG_FILE"
echo "[CONFIG] sqm_id: $sqm_id" >> "$LOG_FILE"
log_info "Modifying configuration file..."
# Check if configuration file exists
if [ ! -f "$STORAGE_FILE" ]; then
log_error "Configuration file not found: $STORAGE_FILE"
log_warn "Please install and run Cursor once before using this script"
exit 1
fi
# Ensure configuration file directory exists
mkdir -p "$(dirname "$STORAGE_FILE")" || {
log_error "Unable to create configuration directory"
exit 1
}
# If file doesn't exist, create a basic JSON structure
if [ ! -s "$STORAGE_FILE" ]; then
echo '{}' > "$STORAGE_FILE" || {
log_error "Unable to initialize configuration file"
exit 1
}
fi
# Modify existing file
modify_or_add_config "telemetry.machineId" "$machine_id" "$STORAGE_FILE" || exit 1
modify_or_add_config "telemetry.macMachineId" "$mac_machine_id" "$STORAGE_FILE" || exit 1
modify_or_add_config "telemetry.devDeviceId" "$device_id" "$STORAGE_FILE" || exit 1
modify_or_add_config "telemetry.sqmId" "$sqm_id" "$STORAGE_FILE" || exit 1
# Set file permissions and owner
chmod 444 "$STORAGE_FILE" # Change to read-only permissions
chown "$CURRENT_USER" "$STORAGE_FILE"
# Verify permission settings
if [ -w "$STORAGE_FILE" ]; then
log_warn "Unable to set read-only permissions, trying other methods..."
chattr +i "$STORAGE_FILE" 2>/dev/null || true
else
log_info "Successfully set file to read-only"
fi
echo
log_info "Updated configuration: $STORAGE_FILE"
log_debug "machineId: $machine_id"
log_debug "macMachineId: $mac_machine_id"
log_debug "devDeviceId: $device_id"
log_debug "sqmId: $sqm_id"
}
# Clean up previous Cursor modifications
clean_cursor_app() {
log_info "Attempting to clean up previous Cursor modifications..."
# If backup exists, restore directly
local latest_backup=""
# Find the latest backup
latest_backup=$(find /tmp -name "Cursor.app.backup_*" -type d -print 2>/dev/null | sort -r | head -1)
if [ -n "$latest_backup" ] && [ -d "$latest_backup" ]; then
log_info "Found existing backup: $latest_backup"
log_info "Restoring original version..."
# Stop Cursor process
check_and_kill_cursor
# Restore backup
sudo rm -rf "$CURSOR_APP_PATH"
sudo cp -R "$latest_backup" "$CURSOR_APP_PATH"
sudo chown -R "$CURRENT_USER:staff" "$CURSOR_APP_PATH"
sudo chmod -R 755 "$CURSOR_APP_PATH"
log_info "Original version restored"
return 0
else
log_warn "No existing backup found, attempting to reinstall Cursor..."
echo "You can download and reinstall Cursor from https://cursor.sh"
echo "Or continue with this script, which will attempt to fix the existing installation"
# Logic for re-downloading and installing can be added here
return 1
fi
}
# Modify Cursor main program files (safe mode)
modify_cursor_app_files() {
log_info "Safely modifying Cursor main program files..."
log_info "Detailed logs will be recorded to: $LOG_FILE"
# Clean up previous modifications first
clean_cursor_app
# Verify application exists
if [ ! -d "$CURSOR_APP_PATH" ]; then
log_error "Cursor.app not found, please confirm installation path: $CURSOR_APP_PATH"
return 1
fi
# Define target files
local target_files=(
"${CURSOR_APP_PATH}/Contents/Resources/app/out/main.js"
"${CURSOR_APP_PATH}/Contents/Resources/app/out/vs/code/node/cliProcessMain.js"
)
# Check if files exist and if they've been modified
local need_modification=false
local missing_files=false
log_debug "Checking target files..."
for file in "${target_files[@]}"; do
if [ ! -f "$file" ]; then
log_warn "File does not exist: ${file/$CURSOR_APP_PATH\//}"
echo "[FILE_CHECK] File does not exist: $file" >> "$LOG_FILE"
missing_files=true
continue
fi
echo "[FILE_CHECK] File exists: $file ($(wc -c < "$file") bytes)" >> "$LOG_FILE"
if ! grep -q "return crypto.randomUUID()" "$file" 2>/dev/null; then
log_info "File needs modification: ${file/$CURSOR_APP_PATH\//}"
grep -n "IOPlatformUUID" "$file" | head -3 >> "$LOG_FILE" || echo "[FILE_CHECK] IOPlatformUUID not found" >> "$LOG_FILE"
need_modification=true
break
else
log_info "File already modified: ${file/$CURSOR_APP_PATH\//}"
fi
done
# If all files are already modified or don't exist, exit
if [ "$missing_files" = true ]; then
log_error "Some target files don't exist, please confirm Cursor installation is complete"
return 1
fi
if [ "$need_modification" = false ]; then
log_info "All target files have already been modified, no need to repeat"
return 0
fi
# Create temporary working directory
local timestamp=$(date +%Y%m%d_%H%M%S)
local temp_dir="/tmp/cursor_reset_${timestamp}"
local temp_app="${temp_dir}/Cursor.app"
local backup_app="/tmp/Cursor.app.backup_${timestamp}"
log_debug "Creating temporary directory: $temp_dir"
echo "[TEMP_DIR] Creating temporary directory: $temp_dir" >> "$LOG_FILE"
# Clean up any existing old temporary directories
if [ -d "$temp_dir" ]; then
log_info "Cleaning up existing temporary directory..."
rm -rf "$temp_dir"
fi
# Create new temporary directory
mkdir -p "$temp_dir" || {
log_error "Unable to create temporary directory: $temp_dir"
echo "[ERROR] Unable to create temporary directory: $temp_dir" >> "$LOG_FILE"
return 1
}
# Backup original application
log_info "Backing up original application..."
echo "[BACKUP] Starting backup: $CURSOR_APP_PATH -> $backup_app" >> "$LOG_FILE"
cp -R "$CURSOR_APP_PATH" "$backup_app" || {
log_error "Unable to create application backup"
echo "[ERROR] Backup failed: $CURSOR_APP_PATH -> $backup_app" >> "$LOG_FILE"
rm -rf "$temp_dir"
return 1
}
echo "[BACKUP] Backup complete" >> "$LOG_FILE"
# Copy application to temporary directory
log_info "Creating temporary working copy..."
echo "[COPY] Starting copy: $CURSOR_APP_PATH -> $temp_dir" >> "$LOG_FILE"
cp -R "$CURSOR_APP_PATH" "$temp_dir" || {
log_error "Unable to copy application to temporary directory"
echo "[ERROR] Copy failed: $CURSOR_APP_PATH -> $temp_dir" >> "$LOG_FILE"
rm -rf "$temp_dir" "$backup_app"
return 1
}
echo "[COPY] Copy complete" >> "$LOG_FILE"
# Ensure temporary directory has correct permissions
chown -R "$CURRENT_USER:staff" "$temp_dir"
chmod -R 755 "$temp_dir"
# Remove signature (for better compatibility)
log_info "Removing application signature..."
echo "[CODESIGN] Removing signature: $temp_app" >> "$LOG_FILE"
codesign --remove-signature "$temp_app" 2>> "$LOG_FILE" || {
log_warn "Failed to remove application signature"
echo "[WARN] Failed to remove signature: $temp_app" >> "$LOG_FILE"
}
# Remove signatures for all related components
local components=(
"$temp_app/Contents/Frameworks/Cursor Helper.app"
"$temp_app/Contents/Frameworks/Cursor Helper (GPU).app"
"$temp_app/Contents/Frameworks/Cursor Helper (Plugin).app"
"$temp_app/Contents/Frameworks/Cursor Helper (Renderer).app"
)
for component in "${components[@]}"; do
if [ -e "$component" ]; then
log_info "Removing signature: $component"
codesign --remove-signature "$component" || {
log_warn "Failed to remove component signature: $component"
}
fi
done
# Modify target files
local modified_count=0
local files=(
"${temp_app}/Contents/Resources/app/out/main.js"
"${temp_app}/Contents/Resources/app/out/vs/code/node/cliProcessMain.js"
)
for file in "${files[@]}"; do
if [ ! -f "$file" ]; then
log_warn "File does not exist: ${file/$temp_dir\//}"
continue
fi
log_debug "Processing file: ${file/$temp_dir\//}"
echo "[PROCESS] Starting to process file: $file" >> "$LOG_FILE"
echo "[PROCESS] File size: $(wc -c < "$file") bytes" >> "$LOG_FILE"
# Output part of file content to log
echo "[FILE_CONTENT] First 100 lines of file:" >> "$LOG_FILE"
head -100 "$file" 2>/dev/null | grep -v "^$" | head -50 >> "$LOG_FILE"
echo "[FILE_CONTENT] ..." >> "$LOG_FILE"
# Create file backup
cp "$file" "${file}.bak" || {
log_error "Unable to create file backup: ${file/$temp_dir\//}"
echo "[ERROR] Unable to create file backup: $file" >> "$LOG_FILE"
continue
}
# Use sed for replacement instead of string operations
if grep -q "IOPlatformUUID" "$file"; then
log_debug "Found IOPlatformUUID keyword"
echo "[FOUND] Found IOPlatformUUID keyword" >> "$LOG_FILE"
grep -n "IOPlatformUUID" "$file" | head -5 >> "$LOG_FILE"
# Locate IOPlatformUUID related function
if grep -q "function a\$" "$file"; then
# Check if already modified
if grep -q "return crypto.randomUUID()" "$file"; then
log_info "File already contains randomUUID call, skipping modification"
((modified_count++))
continue
fi
# Modify for code structure found in main.js
if sed -i.tmp 's/function a\$(t){switch/function a\$(t){return crypto.randomUUID(); switch/' "$file"; then
log_debug "Successfully injected randomUUID call to a\$ function"
((modified_count++))
log_info "Successfully modified file: ${file/$temp_dir\//}"
else
log_error "Failed to modify a\$ function"
cp "${file}.bak" "$file"
fi
elif grep -q "async function v5" "$file"; then
# Check if already modified
if grep -q "return crypto.randomUUID()" "$file"; then
log_info "File already contains randomUUID call, skipping modification"
((modified_count++))
continue
fi
# Alternative method - modify v5 function
if sed -i.tmp 's/async function v5(t){let e=/async function v5(t){return crypto.randomUUID(); let e=/' "$file"; then
log_debug "Successfully injected randomUUID call to v5 function"
((modified_count++))
log_info "Successfully modified file: ${file/$temp_dir\//}"
else
log_error "Failed to modify v5 function"
cp "${file}.bak" "$file"
fi
else
# Check if custom code has already been injected
if grep -q "// Cursor ID Modification Tool Injection" "$file"; then
log_info "File already contains custom injected code, skipping modification"
((modified_count++))
continue
fi
# Use more generic injection method
log_warn "Specific function not found, trying generic modification method"
inject_code="
// Cursor ID Modification Tool Injection - $(date +%Y%m%d%H%M%S)
// Random Device ID Generator Injection - $(date +%s)
const randomDeviceId_$(date +%s) = () => {
try {
return require('crypto').randomUUID();
} catch (e) {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
};
"
# Inject code at the beginning of the file
echo "$inject_code" > "${file}.new"
cat "$file" >> "${file}.new"
mv "${file}.new" "$file"
# Replace call points
sed -i.tmp 's/await v5(!1)/randomDeviceId_'"$(date +%s)"'()/g' "$file"
sed -i.tmp 's/a\$(t)/randomDeviceId_'"$(date +%s)"'()/g' "$file"
log_debug "Generic modification complete"
((modified_count++))
log_info "Successfully modified file using generic method: ${file/$temp_dir\//}"
fi
else
# IOPlatformUUID not found, file structure might have changed
log_warn "IOPlatformUUID not found, trying alternative methods"
# Check if already injected or modified
if grep -q "return crypto.randomUUID()" "$file" || grep -q "// Cursor ID Modification Tool Injection" "$file"; then
log_info "File has already been modified, skipping"
((modified_count++))
continue
fi
# Try to find other key functions like getMachineId or getDeviceId
if grep -q "function t\$()" "$file" || grep -q "async function y5" "$file"; then
log_debug "Found device ID related functions"
# Modify MAC address retrieval function
if grep -q "function t\$()" "$file"; then
sed -i.tmp 's/function t\$(){/function t\$(){return "00:00:00:00:00:00";/' "$file"
log_debug "Successfully modified MAC address retrieval function"
fi
# Modify device ID retrieval function
if grep -q "async function y5" "$file"; then
sed -i.tmp 's/async function y5(t){/async function y5(t){return crypto.randomUUID();/' "$file"
log_debug "Successfully modified device ID retrieval function"
fi
((modified_count++))
log_info "Successfully modified file using alternative method: ${file/$temp_dir\//}"
else
# Last resort generic method - insert function definition overrides at the top of the file
log_warn "No known functions found, using most generic method"
inject_universal_code="
// Cursor ID Modification Tool Injection - $(date +%Y%m%d%H%M%S)
// Global Device Identifier Interception - $(date +%s)
const originalRequire_$(date +%s) = require;
require = function(module) {
const result = originalRequire_$(date +%s)(module);
if (module === 'crypto' && result.randomUUID) {
const originalRandomUUID_$(date +%s) = result.randomUUID;
result.randomUUID = function() {
return '${new_uuid}';
};
}
return result;
};
// Override all possible system ID retrieval functions
global.getMachineId = function() { return '${machine_id}'; };
global.getDeviceId = function() { return '${device_id}'; };
global.macMachineId = '${mac_machine_id}';
"
# Inject code at the beginning of the file
local new_uuid=$(uuidgen | tr '[:upper:]' '[:lower:]')
local machine_id="auth0|user_$(openssl rand -hex 16)"
local device_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
local mac_machine_id=$(openssl rand -hex 32)
inject_universal_code=${inject_universal_code//\$\{new_uuid\}/$new_uuid}
inject_universal_code=${inject_universal_code//\$\{machine_id\}/$machine_id}
inject_universal_code=${inject_universal_code//\$\{device_id\}/$device_id}
inject_universal_code=${inject_universal_code//\$\{mac_machine_id\}/$mac_machine_id}
echo "$inject_universal_code" > "${file}.new"
cat "$file" >> "${file}.new"
mv "${file}.new" "$file"
log_debug "Universal override complete"
((modified_count++))
log_info "Successfully modified file using most generic method: ${file/$temp_dir\//}"
fi
fi
# Add logging after key operations
echo "[MODIFIED] File content after modification:" >> "$LOG_FILE"
grep -n "return crypto.randomUUID()" "$file" | head -3 >> "$LOG_FILE"
# Clean up temporary files
rm -f "${file}.tmp" "${file}.bak"
echo "[PROCESS] File processing completed: $file" >> "$LOG_FILE"
done
if [ "$modified_count" -eq 0 ]; then
log_error "Failed to modify any files"
rm -rf "$temp_dir"
return 1
fi
# Re-sign application (with retry mechanism)
local max_retry=3
local retry_count=0
local sign_success=false
while [ $retry_count -lt $max_retry ]; do
((retry_count++))
log_info "Attempting to sign (Attempt $retry_count)..."
# Use more detailed signing parameters
if codesign --sign - --force --deep --preserve-metadata=entitlements,identifier,flags "$temp_app" 2>&1 | tee /tmp/codesign.log; then
# Verify signature
if codesign --verify -vvvv "$temp_app" 2>/dev/null; then
sign_success=true
log_info "Application signature verification passed"
break
else
log_warn "Signature verification failed, error log:"
cat /tmp/codesign.log
fi
else
log_warn "Signing failed, error log:"
cat /tmp/codesign.log
fi
sleep 1
done
if ! $sign_success; then
log_error "Failed to complete signing after $max_retry attempts"
log_error "Please manually execute the following command to complete signing:"
echo -e "${BLUE}sudo codesign --sign - --force --deep '${temp_app}'${NC}"
echo -e "${YELLOW}After completion, please manually copy the application to the original path:${NC}"
echo -e "${BLUE}sudo cp -R '${temp_app}' '/Applications/'${NC}"
log_info "Temporary files retained at: ${temp_dir}"
return 1
fi
# Replace original application
log_info "Installing modified application..."
if ! sudo rm -rf "$CURSOR_APP_PATH" || ! sudo cp -R "$temp_app" "/Applications/"; then
log_error "Application replacement failed, restoring..."
sudo rm -rf "$CURSOR_APP_PATH"
sudo cp -R "$backup_app" "$CURSOR_APP_PATH"
rm -rf "$temp_dir" "$backup_app"
return 1
fi
# Clean up temporary files
rm -rf "$temp_dir" "$backup_app"
# Set permissions
sudo chown -R "$CURRENT_USER:staff" "$CURSOR_APP_PATH"
sudo chmod -R 755 "$CURSOR_APP_PATH"
log_info "Cursor main program file modification complete! Original backup at: ${backup_app/$HOME/\~}"
return 0
}
# Display file tree structure
show_file_tree() {
local base_dir=$(dirname "$STORAGE_FILE")
echo
log_info "File structure:"
echo -e "${BLUE}$base_dir${NC}"
echo "├── globalStorage"
echo "│ ├── storage.json (modified)"
echo "│ └── backups"
# List backup files
if [ -d "$BACKUP_DIR" ]; then
local backup_files=("$BACKUP_DIR"/*)
if [ ${#backup_files[@]} -gt 0 ]; then
for file in "${backup_files[@]}"; do
if [ -f "$file" ]; then
echo "│ └── $(basename "$file")"
fi
done
else
echo "│ └── (empty)"
fi
fi
echo
}
# Display follow information
show_follow_info() {
echo
echo -e "${GREEN}================================${NC}"
echo -e "${YELLOW} Follow WeChat Official Account [JianBingGuoZiJuanAI] to discuss more Cursor tips and AI knowledge (script is free, follow for more tips and experts in group) ${NC}"
echo -e "${GREEN}================================${NC}"
echo
}
# Disable auto update
disable_auto_update() {
local updater_path="$HOME/Library/Application Support/Caches/cursor-updater"
local app_update_yml="/Applications/Cursor.app/Contents/Resources/app-update.yml"
echo
log_info "Disabling Cursor auto-update..."
# Backup and clear app-update.yml
if [ -f "$app_update_yml" ]; then
log_info "Backing up and modifying app-update.yml..."
if ! sudo cp "$app_update_yml" "${app_update_yml}.bak" 2>/dev/null; then
log_warn "Failed to backup app-update.yml, continuing..."
fi
if sudo bash -c "echo '' > \"$app_update_yml\"" && \
sudo chmod 444 "$app_update_yml"; then
log_info "Successfully disabled app-update.yml"
else
log_error "Failed to modify app-update.yml, please manually execute:"
echo -e "${BLUE}sudo cp \"$app_update_yml\" \"${app_update_yml}.bak\"${NC}"
echo -e "${BLUE}sudo bash -c 'echo \"\" > \"$app_update_yml\"'${NC}"
echo -e "${BLUE}sudo chmod 444 \"$app_update_yml\"${NC}"
fi
else
log_warn "app-update.yml file not found"
fi
# Also handle cursor-updater
log_info "Processing cursor-updater..."
if sudo rm -rf "$updater_path" && \
sudo touch "$updater_path" && \
sudo chmod 444 "$updater_path"; then
log_info "Successfully disabled cursor-updater"
else
log_error "Failed to disable cursor-updater, please manually execute:"
echo -e "${BLUE}sudo rm -rf \"$updater_path\" && sudo touch \"$updater_path\" && sudo chmod 444 \"$updater_path\"${NC}"
fi
echo
log_info "Verification method:"
echo "1. Run command: ls -l \"$updater_path\""
echo " Confirm file permissions show as: r--r--r--"
echo "2. Run command: ls -l \"$app_update_yml\""
echo " Confirm file permissions show as: r--r--r--"
echo
log_info "Please restart Cursor after completion"
}
# Generate random MAC address
generate_random_mac() {
# Generate random MAC address, keeping second bit of first byte as 0 (ensure unicast)
printf '02:%02x:%02x:%02x:%02x:%02x' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))
}
# Get network interfaces list
get_network_interfaces() {
networksetup -listallhardwareports | awk '/Hardware Port|Ethernet Address/ {print $NF}' | paste - - | grep -v 'N/A'
}
# Backup MAC addresses
backup_mac_addresses() {
log_info "Backing up MAC addresses..."
local backup_file="$BACKUP_DIR/mac_addresses.backup_$(date +%Y%m%d_%H%M%S)"
{
echo "# Original MAC Addresses Backup - $(date)" > "$backup_file"
echo "## Network Interfaces:" >> "$backup_file"
networksetup -listallhardwareports >> "$backup_file"
chmod 444 "$backup_file"
chown "$CURRENT_USER" "$backup_file"
log_info "MAC addresses backed up to: $backup_file"
} || {
log_error "Failed to backup MAC addresses"
return 1
}
}
# Modify MAC address
modify_mac_address() {
log_info "Getting network interface information..."
# Backup current MAC addresses
backup_mac_addresses
# Get all network interfaces
local interfaces=$(get_network_interfaces)
if [ -z "$interfaces" ]; then
log_error "No available network interfaces found"
return 1
fi
echo
log_info "Found following network interfaces:"
echo "$interfaces" | nl -w2 -s') '
echo
echo -n "Please select interface number (press Enter to skip): "
read -r choice
if [ -z "$choice" ]; then
log_info "Skipping MAC address modification"
return 0
fi
# Get selected interface name
local selected_interface=$(echo "$interfaces" | sed -n "${choice}p" | awk '{print $1}')
if [ -z "$selected_interface" ]; then
log_error "Invalid selection"
return 1
fi
# Generate new MAC address
local new_mac=$(generate_random_mac)
log_info "Modifying MAC address for interface $selected_interface..."
# Disable network interface
sudo ifconfig "$selected_interface" down || {
log_error "Unable to disable network interface"
return 1
}
# Modify MAC address
if sudo ifconfig "$selected_interface" ether "$new_mac"; then
# Re-enable network interface
sudo ifconfig "$selected_interface" up
log_info "Successfully changed MAC address to: $new_mac"
echo
log_warn "Note: MAC address change may require reconnecting to network to take effect"
else
log_error "Failed to modify MAC address"
# Try to restore network interface
sudo ifconfig "$selected_interface" up
return 1
fi
}
# New restore feature option
restore_feature() {
# Check if backup directory exists
if [ ! -d "$BACKUP_DIR" ]; then
log_warn "Backup directory does not exist"
return 1
fi
# Use find command to get backup files list and store in array
backup_files=()
while IFS= read -r file; do
[ -f "$file" ] && backup_files+=("$file")
done < <(find "$BACKUP_DIR" -name "*.backup_*" -type f 2>/dev/null | sort)
# Check if any backup files found
if [ ${#backup_files[@]} -eq 0 ]; then
log_warn "No backup files found"
return 1
fi
echo
log_info "Available backup files:"
echo "0) Exit (default)"
# Display backup files list
for i in "${!backup_files[@]}"; do
echo "$((i+1))) $(basename "${backup_files[$i]}")"
done
echo
echo -n "Please select backup file number to restore [0-${#backup_files[@]}] (default: 0): "
read -r choice
# Handle user input
if [ -z "$choice" ] || [ "$choice" = "0" ]; then
log_info "Skipping restore operation"
return 0
fi
# Validate input
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -gt "${#backup_files[@]}" ]; then
log_error "Invalid selection"
return 1
fi
# Get selected backup file
local selected_backup="${backup_files[$((choice-1))]}"
# Verify file existence and readability
if [ ! -f "$selected_backup" ] || [ ! -r "$selected_backup" ]; then
log_error "Cannot access selected backup file"
return 1
fi
# Attempt to restore configuration
if cp "$selected_backup" "$STORAGE_FILE"; then
chmod 644 "$STORAGE_FILE"
chown "$CURRENT_USER" "$STORAGE_FILE"
log_info "Configuration restored from backup file: $(basename "$selected_backup")"
return 0
else
log_error "Failed to restore configuration"
return 1
fi
}
# Fix "App is damaged and can't be opened" issue
fix_damaged_app() {
log_info "Fixing 'App is damaged' issue..."
# Check if Cursor app exists
if [ ! -d "$CURSOR_APP_PATH" ]; then
log_error "Cursor app not found: $CURSOR_APP_PATH"
return 1
fi
log_info "Attempting to remove quarantine attribute..."
if sudo xattr -rd com.apple.quarantine "$CURSOR_APP_PATH" 2>/dev/null; then
log_info "Successfully removed quarantine attribute"
else
log_warn "Failed to remove quarantine attribute, trying other methods..."
fi
log_info "Attempting to re-sign application..."
if sudo codesign --force --deep --sign - "$CURSOR_APP_PATH" 2>/dev/null; then
log_info "Application re-signing successful"
else
log_warn "Application re-signing failed"
fi
echo
log_info "Fix complete! Please try reopening Cursor application"
echo
echo -e "${YELLOW}If still unable to open, you can try these methods:${NC}"
echo "1. In System Preferences -> Security & Privacy, click 'Open Anyway' button"
echo "2. Temporarily disable Gatekeeper (not recommended): sudo spctl --master-disable"
echo "3. Re-download and install Cursor application"
echo
echo -e "${BLUE}Reference link: https://sysin.org/blog/macos-if-crashes-when-opening/${NC}"
return 0
}
# Main function
main() {
# Initialize log file
initialize_log
log_info "Script started..."
# Record system information
log_info "System info: $(uname -a)"
log_info "Current user: $CURRENT_USER"
log_cmd_output "sw_vers" "macOS version info"
log_cmd_output "which codesign" "codesign path"
log_cmd_output "ls -la \"$CURSOR_APP_PATH\"" "Cursor app info"
# Environment check
if [[ $(uname) != "Darwin" ]]; then
log_error "This script only supports macOS"
exit 1
fi
clear
# Display Logo
echo -e "
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
"
echo -e "${BLUE}================================${NC}"
echo -e "${GREEN} Cursor Device ID Modification Tool ${NC}"
echo -e "${YELLOW} Follow WeChat: JianBingGuoZiJuanAI ${NC}"
echo -e "${YELLOW} Join us to discuss more Cursor tips and AI knowledge (Script is free, follow WeChat to join group for more tips) ${NC}"
echo -e "${BLUE}================================${NC}"
echo
echo -e "${YELLOW}[Important Notice]${NC} This tool supports Cursor v0.47.x"
echo -e "${YELLOW}[Important Notice]${NC} This tool is free, if it helps you, please follow WeChat: JianBingGuoZiJuanAI"
echo
# Execute main functions
check_permissions
check_and_kill_cursor
backup_config
generate_new_config
# Ask user whether to modify main program files
echo
log_warn "Do you want to modify Cursor program files?"
echo "0) No - Only modify config files (Safer but may need more frequent resets)"
echo "1) Yes - Also modify program files (More persistent but small risk of instability)"
echo ""
printf "Enter choice [0-1] (default 1): "
# Read user input robustly
app_choice=""
# Clear input buffer
while read -r -t 0.1; do read -r; done 2>/dev/null
exec <&-
exec < /dev/tty
app_choice=$(read -r choice; echo "$choice")
if [ -z "$app_choice" ]; then
if [ -e "/dev/tty" ] && [ -r "/dev/tty" ] && [ -w "/dev/tty" ]; then
app_choice=$(head -n 1 < /dev/tty 2>/dev/null)
fi
fi
echo "[INPUT_DEBUG] Choice read: '$app_choice'" >> "$LOG_FILE"
set +e
if [ "$app_choice" = "0" ]; then
log_info "You chose to skip program file modification"
log_info "Program file modification skipped"
else
log_info "Executing program file modification..."
(
if modify_cursor_app_files; then
log_info "Program file modification successful!"
else
log_warn "Program file modification failed, but config changes may have succeeded"
log_warn "If Cursor still shows device disabled after restart, please run this script again"
fi
)
fi
set -e
# MAC address modification option
echo
log_warn "Do you want to modify MAC address?"
echo "0) No - Keep default settings (default)"
echo "1) Yes - Modify MAC address"
echo ""
printf "Enter choice [0-1] (default 0): "
mac_choice=""
while read -r -t 0.1; do read -r; done 2>/dev/null
exec <&-
exec < /dev/tty
mac_choice=$(read -r choice; echo "$choice")
if [ -z "$mac_choice" ]; then
if [ -e "/dev/tty" ] && [ -r "/dev/tty" ] && [ -w "/dev/tty" ]; then
mac_choice=$(head -n 1 < /dev/tty 2>/dev/null)
fi
fi
echo "[INPUT_DEBUG] MAC choice: '$mac_choice'" >> "$LOG_FILE"
set +e
if [ "$mac_choice" = "1" ]; then
log_info "You chose to modify MAC address"
(
if modify_mac_address; then
log_info "MAC address modification complete!"
else
log_error "MAC address modification failed"
fi
)
else
log_info "MAC address modification skipped"
fi
set -e
show_file_tree
show_follow_info
disable_auto_update
log_info "Please restart Cursor to apply new configuration"
show_follow_info
# Repair options
echo
log_warn "Cursor Repair Options"
echo "0) Skip - Don't perform repairs (default)"
echo "1) Repair Mode - Restore original Cursor installation, fix errors from previous modifications"
echo ""
printf "Do you need to restore original Cursor? [0-1] (default 0): "
fix_choice=""
while read -r -t 0.1; do read -r; done 2>/dev/null
exec <&-
exec < /dev/tty
fix_choice=$(read -r choice; echo "$choice")
if [ -z "$fix_choice" ]; then
if [ -e "/dev/tty" ] && [ -r "/dev/tty" ] && [ -w "/dev/tty" ]; then
fix_choice=$(head -n 1 < /dev/tty 2>/dev/null)
fi
fi
echo "[INPUT_DEBUG] Repair choice: '$fix_choice'" >> "$LOG_FILE"
set +e
if [ "$fix_choice" = "1" ]; then
log_info "You chose repair mode"
(
if clean_cursor_app; then
log_info "Cursor restored to original state"
log_info "If you need ID modification, please run this script again"
else
log_warn "No backup found, cannot auto-restore"
log_warn "Recommend reinstalling Cursor"
fi
)
else
log_info "Repair operation skipped"
fi
set -e
log_info "Script execution complete"
echo "========== Cursor ID Modification Tool Log End $(date) ==========" >> "$LOG_FILE"
echo
log_info "Detailed log saved to: $LOG_FILE"
echo "If you encounter issues, please provide this log file to developers for troubleshooting"
echo
# "App is damaged" repair option
echo
log_warn "App Repair Options"
echo "0) Skip - Don't perform repairs (default)"
echo "1) Fix 'App is damaged' issue - Resolve macOS warning about damaged app"
echo ""
printf "Do you need to fix 'App is damaged' issue? [0-1] (default 0): "
damaged_choice=""
while read -r -t 0.1; do read -r; done 2>/dev/null
exec <&-
exec < /dev/tty
damaged_choice=$(read -r choice; echo "$choice")
if [ -z "$damaged_choice" ]; then
if [ -e "/dev/tty" ] && [ -r "/dev/tty" ] && [ -w "/dev/tty" ]; then
damaged_choice=$(head -n 1 < /dev/tty 2>/dev/null)
fi
fi
echo "[INPUT_DEBUG] App repair choice: '$damaged_choice'" >> "$LOG_FILE"
set +e
if [ "$damaged_choice" = "1" ]; then
log_info "You chose to fix 'App is damaged' issue"
(
if fix_damaged_app; then
log_info "Fixed 'App is damaged' issue"
else
log_warn "Failed to fix 'App is damaged' issue"
fi
)
else
log_info "App repair operation skipped"
fi
set -e
}
# Execute main function
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment