$ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $AppName = "evade" $BaseUrl = if ($env:EVADE_INSTALL_BASE_URL) { $env:EVADE_INSTALL_BASE_URL.TrimEnd("/") } else { "https://evade.fail/cdn" } $Version = if ($env:EVADE_VERSION) { $env:EVADE_VERSION } else { "latest" } $InstallDir = if ($env:EVADE_INSTALL_DIR) { $env:EVADE_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA "evade\bin" } function Write-Log { param([string]$Message) Write-Host "evade-install: $Message" } if ($env:OS -ne "Windows_NT") { throw "This installer targets Windows. Use install.sh on Linux or macOS." } $archRaw = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } if ([string]::IsNullOrWhiteSpace($archRaw)) { throw "Unable to detect processor architecture" } switch ($archRaw.ToUpperInvariant()) { "AMD64" { $Target = "x86_64-pc-windows-msvc" } "X86_64" { $Target = "x86_64-pc-windows-msvc" } "ARM64" { $Target = "aarch64-pc-windows-msvc" } "AARCH64" { $Target = "aarch64-pc-windows-msvc" } default { throw "Unsupported architecture: $archRaw" } } $AssetRoot = "$BaseUrl/$Version" $ChecksumsUrl = "$AssetRoot/checksums.txt" $TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("evade-install-" + [Guid]::NewGuid().ToString("N")) $ArchivePath = Join-Path $TempDir "$AppName.zip" $ChecksumsPath = Join-Path $TempDir "checksums.txt" New-Item -Path $TempDir -ItemType Directory -Force | Out-Null try { $assetCandidates = @("${AppName}_${Target}.zip") if ($Version -ne "latest") { $assetCandidates += "${AppName}_${Version}_${Target}.zip" } $AssetName = $null foreach ($candidate in $assetCandidates) { $ArchiveUrl = "$AssetRoot/$candidate" Write-Log "downloading $ArchiveUrl" try { Invoke-WebRequest -Uri $ArchiveUrl -OutFile $ArchivePath -UseBasicParsing $AssetName = $candidate break } catch { continue } } if (-not $AssetName) { throw "Failed to download installer asset for target $Target" } Invoke-WebRequest -Uri $ChecksumsUrl -OutFile $ChecksumsPath -UseBasicParsing $escapedAsset = [Regex]::Escape($AssetName) $pattern = "^[0-9a-fA-F]{64}\s+\*?$escapedAsset$" $checksumLine = Get-Content -Path $ChecksumsPath | Where-Object { $_ -match $pattern } | Select-Object -First 1 if (-not $checksumLine) { throw "Could not find checksum entry for $AssetName" } $expectedChecksum = ($checksumLine -split '\s+')[0].ToLowerInvariant() $actualChecksum = (Get-FileHash -Path $ArchivePath -Algorithm SHA256).Hash.ToLowerInvariant() if ($expectedChecksum -ne $actualChecksum) { throw "Checksum mismatch for $AssetName" } Expand-Archive -Path $ArchivePath -DestinationPath $TempDir -Force $binaryPath = Join-Path $TempDir "$AppName.exe" if (-not (Test-Path -Path $binaryPath -PathType Leaf)) { $binaryMatch = Get-ChildItem -Path $TempDir -Filter "$AppName.exe" -Recurse -File | Select-Object -First 1 if (-not $binaryMatch) { throw "Archive did not contain $AppName.exe" } $binaryPath = $binaryMatch.FullName } New-Item -Path $InstallDir -ItemType Directory -Force | Out-Null $targetPath = Join-Path $InstallDir "$AppName.exe" Copy-Item -Path $binaryPath -Destination $targetPath -Force $userPath = [Environment]::GetEnvironmentVariable("Path", "User") if ($null -eq $userPath) { $userPath = "" } $needle = $InstallDir.TrimEnd("\\") $alreadyInPath = $false foreach ($entry in $userPath.Split(";", [System.StringSplitOptions]::RemoveEmptyEntries)) { if ($entry.TrimEnd("\\") -ieq $needle) { $alreadyInPath = $true break } } $pathUpdated = $false if (-not $alreadyInPath) { $newPath = if ([string]::IsNullOrWhiteSpace($userPath)) { $InstallDir } else { "$userPath;$InstallDir" } [Environment]::SetEnvironmentVariable("Path", $newPath, "User") $pathUpdated = $true } Write-Log "installed to $targetPath" if ($pathUpdated) { Write-Log "added $InstallDir to user PATH (open a new terminal)" } Write-Log "run '$AppName --version' to verify" } finally { if (Test-Path -LiteralPath $TempDir) { Remove-Item -LiteralPath $TempDir -Recurse -Force -ErrorAction SilentlyContinue } }