#Requires -RunAsAdministrator <# .SYNOPSIS Master installation script for SolidWorks Privacy Lockdown. .DESCRIPTION Runs all privacy lockdown components in sequence: 1. Blocks telemetry domains via hosts file 2. Disables update/background services 3. Configures firewall rules 4. Disables in-app telemetry via registry 5. Verifies the lockdown Generates a detailed report of all changes made. .NOTES Author: Atomaste Solution Requires: Administrator privileges Run this script to apply all privacy protections at once. .EXAMPLE .\00-install-privacy-lockdown.ps1 Applies all privacy protections. .EXAMPLE .\00-install-privacy-lockdown.ps1 -Undo Removes all privacy protections and restores original settings. .EXAMPLE .\00-install-privacy-lockdown.ps1 -DryRun Shows what would be changed without making any modifications. #> param( [switch]$Undo, # Remove all protections [switch]$DryRun, # Preview changes without applying [switch]$SkipVerify, # Skip verification step [string]$InstallPath = "C:\Program Files\SOLIDWORKS Corp", [string]$ReportPath # Custom report path (default: script directory) ) $scriptDir = $PSScriptRoot $timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" $reportDir = if ($ReportPath) { $ReportPath } else { Join-Path $scriptDir "reports" } $reportFile = Join-Path $reportDir "lockdown-report_$timestamp.txt" # Global report content $script:report = @() $script:changes = @{ HostsFile = @() Services = @() Firewall = @() Registry = @() } function Initialize-Report { # Create reports directory if needed if (-not (Test-Path $reportDir)) { New-Item -Path $reportDir -ItemType Directory -Force | Out-Null } $header = @" ================================================================================ SOLIDWORKS PRIVACY LOCKDOWN REPORT Generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Computer: $env:COMPUTERNAME User: $env:USERNAME Mode: $(if ($Undo) { "UNINSTALL" } elseif ($DryRun) { "DRY RUN (preview)" } else { "INSTALL" }) ================================================================================ "@ $script:report += $header Write-Host $header -ForegroundColor Cyan } function Add-ReportSection { param( [string]$Title, [string]$Content ) $section = @" -------------------------------------------------------------------------------- $Title -------------------------------------------------------------------------------- $Content "@ $script:report += $section } function Add-ReportLine { param([string]$Line) $script:report += $Line } function Write-Log { param( [string]$Message, [string]$Level = "INFO", # INFO, OK, WARN, ERROR, CHANGE [string]$Category # HostsFile, Services, Firewall, Registry ) $prefix = switch ($Level) { "OK" { "[OK] "; $color = "Green" } "WARN" { "[WARN] "; $color = "Yellow" } "ERROR" { "[ERROR] "; $color = "Red" } "CHANGE" { "[CHANGE]"; $color = "Magenta" } "SKIP" { "[SKIP] "; $color = "Gray" } "BEFORE" { "[BEFORE]"; $color = "DarkGray" } "AFTER" { "[AFTER] "; $color = "White" } default { "[INFO] "; $color = "White" } } $logLine = "$prefix $Message" Write-Host $logLine -ForegroundColor $color Add-ReportLine $logLine # Track changes by category if ($Category -and $Level -eq "CHANGE") { $script:changes[$Category] += $Message } } function Save-Report { # Add summary section $summary = @" ================================================================================ CHANGES SUMMARY ================================================================================ HOSTS FILE CHANGES ($($script:changes.HostsFile.Count)): "@ if ($script:changes.HostsFile.Count -eq 0) { $summary += "`n (no changes)" } else { foreach ($change in $script:changes.HostsFile) { $summary += "`n - $change" } } $summary += @" SERVICES CHANGES ($($script:changes.Services.Count)): "@ if ($script:changes.Services.Count -eq 0) { $summary += "`n (no changes)" } else { foreach ($change in $script:changes.Services) { $summary += "`n - $change" } } $summary += @" FIREWALL CHANGES ($($script:changes.Firewall.Count)): "@ if ($script:changes.Firewall.Count -eq 0) { $summary += "`n (no changes)" } else { foreach ($change in $script:changes.Firewall) { $summary += "`n - $change" } } $summary += @" REGISTRY CHANGES ($($script:changes.Registry.Count)): "@ if ($script:changes.Registry.Count -eq 0) { $summary += "`n (no changes)" } else { foreach ($change in $script:changes.Registry) { $summary += "`n - $change" } } $totalChanges = $script:changes.HostsFile.Count + $script:changes.Services.Count + $script:changes.Firewall.Count + $script:changes.Registry.Count $summary += @" ================================================================================ TOTAL CHANGES: $totalChanges REPORT SAVED: $reportFile ================================================================================ "@ $script:report += $summary # Write report to file $script:report -join "`n" | Set-Content -Path $reportFile -Force -Encoding UTF8 Write-Host "" Write-Host "========================================" -ForegroundColor Green Write-Host " REPORT SAVED" -ForegroundColor Green Write-Host "========================================" -ForegroundColor Green Write-Host " Location: $reportFile" -ForegroundColor White Write-Host " Total changes: $totalChanges" -ForegroundColor White Write-Host "" } function Test-IsAdmin { $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($currentUser) return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function Get-SystemState { Add-ReportSection -Title "SYSTEM STATE BEFORE CHANGES" -Content "" # Hosts file state Write-Log "Checking hosts file..." -Level "INFO" $hostsPath = "C:\Windows\System32\drivers\etc\hosts" $hostsContent = Get-Content $hostsPath -Raw -ErrorAction SilentlyContinue if ($hostsContent -match "SOLIDWORKS TELEMETRY BLOCK") { Write-Log "Hosts file: SolidWorks block already present" -Level "BEFORE" } else { Write-Log "Hosts file: No SolidWorks block found" -Level "BEFORE" } # Services state Write-Log "Checking services..." -Level "INFO" $services = Get-Service -DisplayName "*SOLIDWORKS*","*3DEXPERIENCE*","*Dassault*" -ErrorAction SilentlyContinue if ($services) { foreach ($svc in $services) { Write-Log "Service: $($svc.DisplayName) - Status: $($svc.Status), StartType: $($svc.StartType)" -Level "BEFORE" } } else { Write-Log "Services: No SolidWorks services found" -Level "BEFORE" } # Firewall rules state Write-Log "Checking firewall rules..." -Level "INFO" $rules = Get-NetFirewallRule -DisplayName "SolidWorks Privacy -*" -ErrorAction SilentlyContinue if ($rules) { foreach ($rule in $rules) { Write-Log "Firewall: $($rule.DisplayName) - Action: $($rule.Action), Enabled: $($rule.Enabled)" -Level "BEFORE" } } else { Write-Log "Firewall: No SolidWorks Privacy rules found" -Level "BEFORE" } # Registry state Write-Log "Checking registry..." -Level "INFO" $swBasePath = "HKCU:\Software\SolidWorks" if (Test-Path $swBasePath) { $versions = Get-ChildItem -Path $swBasePath -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -like "SOLIDWORKS *" } foreach ($version in $versions) { Write-Log "Registry: Found $($version.PSChildName)" -Level "BEFORE" $perfPath = Join-Path $version.PSPath "Performance" if (Test-Path $perfPath) { $props = Get-ItemProperty -Path $perfPath -ErrorAction SilentlyContinue if ($props.PSObject.Properties.Name -contains "OptInStatus") { Write-Log " CEIP OptInStatus = $($props.OptInStatus)" -Level "BEFORE" } } } } else { Write-Log "Registry: No SolidWorks keys found (SW may not be installed)" -Level "BEFORE" } Add-ReportLine "" } function Apply-HostsFileBlock { Add-ReportSection -Title "HOSTS FILE MODIFICATIONS" -Content "" $hostsPath = "C:\Windows\System32\drivers\etc\hosts" $backupPath = "C:\Windows\System32\drivers\etc\hosts.backup.solidworks.$timestamp" $blockDomains = @( "api.3ds.com", "www.3ds.com", "swym.3ds.com", "iam.3ds.com", "cas.3ds.com", "eu1-ds-iam.3dexperience.3ds.com", "eu1-ds.3dexperience.3ds.com", "update.solidworks.com", "www.solidworks.com", "sentry.io", "o136956.ingest.sentry.io", "telemetry.solidworks.com", "analytics.3ds.com", "collect.3ds.com", "ifwe.3ds.com", "eu1-ifwe.3dexperience.3ds.com", "passport.3ds.com", "3dswym.3ds.com" ) $licensingDomains = @( "activation.solidworks.com", "license.solidworks.com", "licensing.solidworks.com" ) Write-Log "Licensing domains (preserved):" -Level "INFO" foreach ($domain in $licensingDomains) { Write-Log " ALLOW: $domain" -Level "OK" } Write-Log "" -Level "INFO" Write-Log "Telemetry domains to block:" -Level "INFO" if ($DryRun) { foreach ($domain in $blockDomains) { Write-Log " Would block: $domain" -Level "INFO" } Write-Log "[DRY RUN] No changes made to hosts file" -Level "WARN" return } # Backup Copy-Item -Path $hostsPath -Destination $backupPath -Force Write-Log "Backup created: $backupPath" -Level "OK" $content = Get-Content $hostsPath -Raw $markerStart = "# === SOLIDWORKS TELEMETRY BLOCK START ===" $markerEnd = "# === SOLIDWORKS TELEMETRY BLOCK END ===" # Remove existing block if present if ($content -match [regex]::Escape($markerStart)) { $pattern = "$([regex]::Escape($markerStart))[\s\S]*?$([regex]::Escape($markerEnd))\r?\n?" $content = $content -replace $pattern, "" Write-Log "Removed existing block" -Level "INFO" } # Build new block $blockContent = @() $blockContent += "" $blockContent += $markerStart $blockContent += "# Blocks telemetry/analytics while preserving licensing" $blockContent += "# Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" $blockContent += "#" foreach ($domain in $blockDomains) { $blockContent += "127.0.0.1 $domain" Write-Log "BLOCKED: $domain -> 127.0.0.1" -Level "CHANGE" -Category "HostsFile" } $blockContent += $markerEnd # Apply Set-Content -Path $hostsPath -Value $content.TrimEnd() -Force -NoNewline Add-Content -Path $hostsPath -Value ($blockContent -join "`r`n") -Force Write-Log "Hosts file updated successfully" -Level "OK" } function Remove-HostsFileBlock { Add-ReportSection -Title "HOSTS FILE RESTORATION" -Content "" $hostsPath = "C:\Windows\System32\drivers\etc\hosts" $content = Get-Content $hostsPath -Raw $markerStart = "# === SOLIDWORKS TELEMETRY BLOCK START ===" $markerEnd = "# === SOLIDWORKS TELEMETRY BLOCK END ===" if ($DryRun) { if ($content -match [regex]::Escape($markerStart)) { Write-Log "[DRY RUN] Would remove SolidWorks block from hosts file" -Level "WARN" } else { Write-Log "[DRY RUN] No SolidWorks block found to remove" -Level "INFO" } return } if ($content -match [regex]::Escape($markerStart)) { $pattern = "$([regex]::Escape($markerStart))[\s\S]*?$([regex]::Escape($markerEnd))\r?\n?" $newContent = $content -replace $pattern, "" Set-Content -Path $hostsPath -Value $newContent.TrimEnd() -Force -NoNewline Add-Content -Path $hostsPath -Value "" Write-Log "Removed SolidWorks telemetry block from hosts file" -Level "CHANGE" -Category "HostsFile" } else { Write-Log "No SolidWorks block found in hosts file" -Level "SKIP" } } function Manage-Services { param([switch]$Restore) $title = if ($Restore) { "SERVICES RESTORATION" } else { "SERVICES MODIFICATIONS" } Add-ReportSection -Title $title -Content "" $stateFile = "$env:USERPROFILE\solidworks-services-backup.json" $services = Get-Service -DisplayName "*SOLIDWORKS*","*3DEXPERIENCE*","*Dassault*" -ErrorAction SilentlyContinue if (-not $services -or $services.Count -eq 0) { Write-Log "No SolidWorks services found on this system" -Level "INFO" return } if ($Restore) { if ($DryRun) { Write-Log "[DRY RUN] Would restore services from: $stateFile" -Level "WARN" return } if (Test-Path $stateFile) { $states = Get-Content -Path $stateFile -Raw | ConvertFrom-Json foreach ($serviceName in $states.PSObject.Properties.Name) { $originalState = $states.$serviceName try { Set-Service -Name $serviceName -StartupType $originalState.StartType -ErrorAction Stop Write-Log "Restored: $($originalState.DisplayName) -> $($originalState.StartType)" -Level "CHANGE" -Category "Services" } catch { Write-Log "Failed to restore $serviceName : $_" -Level "ERROR" } } } else { Write-Log "No backup file found: $stateFile" -Level "WARN" } return } # Save current states $states = @{} foreach ($svc in $services) { $states[$svc.Name] = @{ DisplayName = $svc.DisplayName Status = $svc.Status.ToString() StartType = $svc.StartType.ToString() } } if (-not $DryRun) { $states | ConvertTo-Json -Depth 3 | Set-Content -Path $stateFile -Force Write-Log "Service states backed up to: $stateFile" -Level "OK" } foreach ($svc in $services) { $isUpdateService = $svc.DisplayName -match "update|download|background|3dexperience" if ($isUpdateService) { if ($DryRun) { Write-Log "[DRY RUN] Would disable: $($svc.DisplayName) (currently: $($svc.StartType))" -Level "WARN" } else { try { if ($svc.Status -eq "Running") { Stop-Service -Name $svc.Name -Force -ErrorAction Stop Write-Log "Stopped: $($svc.DisplayName)" -Level "OK" } Set-Service -Name $svc.Name -StartupType Disabled -ErrorAction Stop Write-Log "Disabled: $($svc.DisplayName) (was: $($svc.StartType))" -Level "CHANGE" -Category "Services" } catch { Write-Log "Failed to disable $($svc.DisplayName): $_" -Level "ERROR" } } } else { Write-Log "Preserved: $($svc.DisplayName) ($($svc.StartType))" -Level "SKIP" } } } function Manage-Firewall { param([switch]$Remove) $title = if ($Remove) { "FIREWALL RULES REMOVAL" } else { "FIREWALL RULES CONFIGURATION" } Add-ReportSection -Title $title -Content "" $rulePrefix = "SolidWorks Privacy - " if ($Remove) { $rules = Get-NetFirewallRule -DisplayName "$rulePrefix*" -ErrorAction SilentlyContinue if ($DryRun) { if ($rules) { Write-Log "[DRY RUN] Would remove $($rules.Count) firewall rule(s)" -Level "WARN" } else { Write-Log "[DRY RUN] No firewall rules to remove" -Level "INFO" } return } if ($rules) { foreach ($rule in $rules) { $rules | Remove-NetFirewallRule Write-Log "Removed rule: $($rule.DisplayName)" -Level "CHANGE" -Category "Firewall" } } else { Write-Log "No SolidWorks firewall rules found" -Level "SKIP" } return } # Find executables to block $execsToBlock = @( "SOLIDWORKS\swScheduler\dxttasks.exe", "SOLIDWORKS\swScheduler\DXTTask.exe", "SOLIDWORKS Installation Manager\sldIM.exe", "SOLIDWORKS Installation Manager\sldDownloader.exe" ) if (-not (Test-Path $InstallPath)) { Write-Log "SolidWorks installation not found at: $InstallPath" -Level "WARN" Write-Log "Firewall rules will be created when SolidWorks is installed" -Level "INFO" return } foreach ($exe in $execsToBlock) { $fullPath = Join-Path $InstallPath $exe $exeName = Split-Path $fullPath -Leaf $ruleName = "$rulePrefix Block $exeName" if (-not (Test-Path $fullPath)) { Write-Log "Executable not found: $exe" -Level "SKIP" continue } $existing = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue if ($existing) { Write-Log "Rule already exists: $exeName" -Level "SKIP" continue } if ($DryRun) { Write-Log "[DRY RUN] Would create block rule for: $exeName" -Level "WARN" } else { try { New-NetFirewallRule ` -DisplayName $ruleName ` -Description "Blocks outbound for SolidWorks update/telemetry" ` -Direction Outbound ` -Action Block ` -Program $fullPath ` -Enabled True ` -Profile Any ` -ErrorAction Stop | Out-Null Write-Log "Created block rule: $exeName" -Level "CHANGE" -Category "Firewall" } catch { Write-Log "Failed to create rule for $exeName : $_" -Level "ERROR" } } } } function Manage-Registry { param([switch]$Restore) $title = if ($Restore) { "REGISTRY RESTORATION" } else { "REGISTRY MODIFICATIONS" } Add-ReportSection -Title $title -Content "" $swBasePath = "HKCU:\Software\SolidWorks" if (-not (Test-Path $swBasePath)) { Write-Log "SolidWorks registry keys not found" -Level "WARN" Write-Log "Run this script again after launching SolidWorks" -Level "INFO" return } $versions = Get-ChildItem -Path $swBasePath -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -like "SOLIDWORKS *" } if (-not $versions) { Write-Log "No SolidWorks version keys found" -Level "WARN" return } $settings = @( @{ Path = "Performance"; Name = "OptInStatus"; DisableValue = 0; Desc = "CEIP" }, @{ Path = "General"; Name = "Auto Check for Updates"; DisableValue = 0; Desc = "Auto Updates" }, @{ Path = "Performance"; Name = "EnableAnalytics"; DisableValue = 0; Desc = "Analytics" }, @{ Path = "Performance"; Name = "EnableTelemetry"; DisableValue = 0; Desc = "Telemetry" } ) foreach ($version in $versions) { Write-Log "Processing: $($version.PSChildName)" -Level "INFO" foreach ($setting in $settings) { $regPath = Join-Path $version.PSPath $setting.Path if (-not (Test-Path $regPath)) { Write-Log " Path not found: $($setting.Path)" -Level "SKIP" continue } $props = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue $currentValue = $props.PSObject.Properties[$setting.Name].Value if ($Restore) { # Restore to enabled (1) if ($DryRun) { Write-Log "[DRY RUN] Would restore $($setting.Desc) to enabled" -Level "WARN" } else { try { Set-ItemProperty -Path $regPath -Name $setting.Name -Value 1 -Type DWord -Force Write-Log "Restored: $($setting.Desc) = 1 (enabled)" -Level "CHANGE" -Category "Registry" } catch { Write-Log "Failed to restore $($setting.Desc): $_" -Level "ERROR" } } } else { # Disable if ($null -ne $currentValue -and $currentValue -eq $setting.DisableValue) { Write-Log " $($setting.Desc): Already disabled" -Level "SKIP" continue } if ($DryRun) { Write-Log "[DRY RUN] Would disable $($setting.Desc) (current: $currentValue)" -Level "WARN" } else { try { Set-ItemProperty -Path $regPath -Name $setting.Name -Value $setting.DisableValue -Type DWord -Force Write-Log "Disabled: $($setting.Desc) (was: $currentValue -> now: $($setting.DisableValue))" -Level "CHANGE" -Category "Registry" } catch { Write-Log "Failed to disable $($setting.Desc): $_" -Level "ERROR" } } } } } } # Main execution if (-not (Test-IsAdmin)) { Write-Host "[ERROR] This script requires Administrator privileges." -ForegroundColor Red Write-Host "Please right-click PowerShell and select 'Run as Administrator'" -ForegroundColor Yellow exit 1 } Initialize-Report if ($DryRun) { Write-Host "" Write-Host "*** DRY RUN MODE - No changes will be made ***" -ForegroundColor Yellow Write-Host "" } # Capture initial state Get-SystemState if ($Undo) { Add-ReportSection -Title "REMOVING PRIVACY LOCKDOWN" -Content "" if (-not $DryRun) { Write-Host "" $confirm = Read-Host "Are you sure you want to remove privacy protections? (y/N)" if ($confirm -ne "y" -and $confirm -ne "Y") { Write-Log "Operation cancelled by user" -Level "WARN" Save-Report exit 0 } } Manage-Registry -Restore Manage-Firewall -Remove Manage-Services -Restore Remove-HostsFileBlock } else { Add-ReportSection -Title "APPLYING PRIVACY LOCKDOWN" -Content "" if (-not $DryRun) { Write-Host "" Write-Host "This will configure:" -ForegroundColor White Write-Host " - Block telemetry domains in hosts file" -ForegroundColor Gray Write-Host " - Disable update/background services" -ForegroundColor Gray Write-Host " - Create firewall block rules" -ForegroundColor Gray Write-Host " - Disable telemetry in registry" -ForegroundColor Gray Write-Host "" Write-Host "Licensing will remain functional." -ForegroundColor Green Write-Host "" $confirm = Read-Host "Proceed? (Y/n)" if ($confirm -eq "n" -or $confirm -eq "N") { Write-Log "Operation cancelled by user" -Level "WARN" Save-Report exit 0 } } Apply-HostsFileBlock Manage-Services Manage-Firewall Manage-Registry } Save-Report if ($DryRun) { Write-Host "" Write-Host "*** DRY RUN COMPLETE - No changes were made ***" -ForegroundColor Yellow Write-Host "Review the report above. Run without -DryRun to apply changes." -ForegroundColor Yellow }