diff --git a/install.ps1 b/install.ps1 index 705028f..64a6298 100644 --- a/install.ps1 +++ b/install.ps1 @@ -115,39 +115,6 @@ function Save-FileFromUrl { } } -# Function to test the network stack. Ping/GET requests to the resource to ensure that network stack looks good for installation -function Test-WebConnection { - param ( - [string]$url - ) - - Write-Host "[+] Checking for Internet connectivity ($url)..." - - if (-not (Test-Connection $url -Quiet)) { - Write-Host "`t[!] It looks like you cannot ping $url. Check your network settings." -ForegroundColor Red - Start-Sleep 3 - exit 1 - } - - $response = $null - try { - $response = Invoke-WebRequest -Uri "https://$url" -UseBasicParsing -DisableKeepAlive - } - catch { - Write-Host "`t[!] Error accessing $url. Exception: $($_.Exception.Message)`n`t[!] Check your network settings." -ForegroundColor Red - Start-Sleep 3 - exit 1 - } - - if ($response -and $response.StatusCode -ne 200) { - Write-Host "`t[!] Unable to access $url. Status code: $($response.StatusCode)`n`t[!] Check your network settings." -ForegroundColor Red - Start-Sleep 3 - exit 1 - } - - Write-Host "`t[+] Internet connectivity check for $url passed" -ForegroundColor Green -} - # Function used for getting configuration files (such as config.xml and LayoutModification.xml) function Get-ConfigFile { param ( @@ -169,305 +136,283 @@ function Get-ConfigFile { $desktopPath = [Environment]::GetFolderPath("Desktop") Set-Location -Path $desktopPath -PassThru | Out-Null -if (-not $noChecks.IsPresent) { - # Check PowerShell version - Write-Host "[+] Checking if PowerShell version is compatible..." - $psVersion = $PSVersionTable.PSVersion - if ($psVersion -lt [System.Version]"5.0.0") { - Write-Host "`t[!] You are using PowerShell version $psVersion. This is an old version and it is not supported" -ForegroundColor Red - Read-Host "Press any key to exit..." - exit 1 - } else { - Write-Host "`t[+] Installing with PowerShell version $psVersion" -ForegroundColor Green - } - - # Ensure script is ran as administrator - Write-Host "[+] Checking if script is running as administrator..." - $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) - if (-Not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { - Write-Host "`t[!] Please run this script as administrator" -ForegroundColor Red - Read-Host "Press any key to exit..." - exit 1 - } else { - Write-Host "`t[+] Running as administrator" -ForegroundColor Green - Start-Sleep -Milliseconds 500 - } +# Setting global variables +$script:checksPassed = $true +$mandatoryChecksPassed = $true +$exit_message = "Installation cannot continue." - # Ensure execution policy is unrestricted - Write-Host "[+] Checking if execution policy is unrestricted..." - if ((Get-ExecutionPolicy).ToString() -ne "Unrestricted") { - Write-Host "`t[!] Please run this script after updating your execution policy to unrestricted" -ForegroundColor Red - Write-Host "`t[-] Hint: Set-ExecutionPolicy Unrestricted" -ForegroundColor Yellow - Read-Host "Press any key to exit..." - exit 1 - } else { - Write-Host "`t[+] Execution policy is unrestricted" -ForegroundColor Green - Start-Sleep -Milliseconds 500 - } +################################# Functions that conduct Pre-Install Checks ################################# +# Function to test the network stack. Ping/GET requests to the resource to ensure that network stack looks good for installation +function Test-WebConnection { + param ( + [string]$url + ) - # Check if Windows < 10 - $os = Get-CimInstance -Class Win32_OperatingSystem - $osMajorVersion = $os.Version.Split('.')[0] # Version examples: "6.1.7601", "10.0.19045" - Write-Host "[+] Checking Operating System version compatibility..." - if ($osMajorVersion -lt 10) { - Write-Host "`t[!] Only Windows >= 10 is supported" -ForegroundColor Yellow - Write-Host "[-] Do you still wish to proceed? (Y/N): " -ForegroundColor Yellow -NoNewline - $response = Read-Host - if ($response -notin @("y","Y")) { - exit 1 - } - } + Write-Host "[+] Checking for Internet connectivity ($url)... (mandatory)" - # Check if host has been tested - # 17763: the version used by windows-2019 in GH actions - # 19045: https://www.microsoft.com/en-us/software-download/windows10ISO downloaded on April 25 2023. - # 20348: the version used by windows-2022 in GH actions - $testedVersions = @(17763, 19045, 20348) - if ($os.BuildNumber -notin $testedVersions) { - Write-Host "`t[!] Windows version $osVersion has not been tested. Tested versions: $($testedVersions -join ', ')" -ForegroundColor Yellow - Write-Host "`t[+] You are welcome to continue, but may experience errors downloading or installing packages" -ForegroundColor Yellow - Write-Host "[-] Do you still wish to proceed? (Y/N): " -ForegroundColor Yellow -NoNewline - $response = Read-Host - if ($response -notin @("y","Y")) { - exit 1 - } - } else { - Write-Host "`t[+] Installing on Windows version $osVersion" -ForegroundColor Green + if (-not (Test-Connection $url -Quiet)) { + return "It looks like you cannot ping $url. Check your network settings." } - # Check if system is a virtual machine - $virtualModels = @('VirtualBox', 'VMware', 'Virtual Machine', 'Hyper-V') - $computerSystemModel = (Get-CimInstance -Class Win32_ComputerSystem).Model - $isVirtualModel = $false - - foreach ($model in $virtualModels) { - if ($computerSystemModel.Contains($model)) { - $isVirtualModel = $true - break - } + $response = $null + try { + $response = Invoke-WebRequest -Uri "https://$url" -UseBasicParsing -DisableKeepAlive } - - if (!$isVirtualModel) { - Write-Host "`t[!] You are not on a virual machine or have hardened your machine to not appear as a virtual machine" -ForegroundColor Red - Write-Host "`t[!] Please do NOT install this on your host system as it can't be uninstalled completely" -ForegroundColor Red - Write-Host "`t[!] ** Please only install on a virtual machine **" -ForegroundColor Red - Write-Host "`t[!] ** Only continue if you know what you are doing! **" -ForegroundColor Red - Write-Host "[-] Do you still wish to proceed? (Y/N): " -ForegroundColor Yellow -NoNewline - $response = Read-Host - if ($response -notin @("y","Y")) { - exit 1 - } + catch { + return "Error accessing $url. Exception: $($_.Exception.Message)`n`t[!] Check your network settings." } - # Check for spaces in the username, exit if identified - Write-Host "[+] Checking for spaces in the username..." - if (${Env:UserName} -match '\s') { - Write-Host "`t[!] Username '${Env:UserName}' contains a space and will break installation." -ForegroundColor Red - Write-Host "`t[!] Exiting..." -ForegroundColor Red - Start-Sleep 3 - exit 1 - } else { - Write-Host "`t[+] Username '${Env:UserName}' does not contain any spaces." -ForegroundColor Green + if ($response -and $response.StatusCode -ne 200) { + return "Unable to access $url. Status code: $($response.StatusCode)`n`t[!] Check your network settings." } - # Check if host has enough disk space - Write-Host "[+] Checking if host has enough disk space..." - $disk = Get-PSDrive (Get-Location).Drive.Name - Start-Sleep -Seconds 1 - if (-Not (($disk.used + $disk.free)/1GB -gt 58.8)) { - Write-Host "`t[!] A minimum of 60 GB hard drive space is preferred. Please increase hard drive space of the VM, reboot, and retry install" -ForegroundColor Red - Write-Host "`t[+] If you have multiple drives, you may change the tool installation location via the envrionment variable %RAW_TOOLS_DIR% in config.xml or GUI" -ForegroundColor Yellow - Write-Host "`t[+] However, packages provided from the Chocolatey community repository will install to their default location" -ForegroundColor Yellow - Write-Host "`t[+] See: https://stackoverflow.com/questions/19752533/how-do-i-set-chocolatey-to-install-applications-onto-another-drive" -ForegroundColor Yellow - Write-Host "[-] Do you still wish to proceed? (Y/N): " -ForegroundColor Yellow -NoNewline - $response = Read-Host - if ($response -notin @("y","Y")) { - exit 1 - } - } else { - Write-Host "`t[+] Disk is larger than 60 GB" -ForegroundColor Green - } +} - # Internet connectivity checks - Test-WebConnection 'google.com' - Test-WebConnection 'github.com' - Test-WebConnection 'raw.githubusercontent.com' - Write-Host "`t[+] Network connectivity looks good" -ForegroundColor Green +function Test-PSVersion{ + try { + $psVersion = $PSVersionTable.PSVersion + if ($psVersion -lt [System.Version]"5.0.0") { + return "Your PowerShell version ($psVersion) is not supported" + } + } catch { + return "Unable to determine Powershell version" + } +} - # Check if Tamper Protection is disabled - Write-Host "[+] Checking if Windows Defender Tamper Protection is disabled..." +function Test-Admin { try { + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) + if (-not ($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))){ + return "The script is not running as Administrator" + } + } catch { + return "Unable to determine if the script is running as Administrator" + } +} +function Test-ExecutionPolicy { + try { + if (-not((Get-ExecutionPolicy).ToString() -eq "Unrestricted")){ + return "You need to enable script execution with 'Set-ExecutionPolicy Unrestricted -Force'" + } + } catch { + return "Unable to determine Powershell execution policy" + } +} +function Test-DefenderAndTamperProtection { + try { + $defender = Get-Service -Name WinDefend -ea 0 + if ($null -ne $defender) { + if ($defender.Status -eq "Running") { + return "Disable Windows Defender through Group Policy, reboot, and rerun installer" + } + } $tpEnabled = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Features" -Name "TamperProtection" -ErrorAction Stop if ($tpEnabled.TamperProtection -eq 5) { - Write-Host "`t[!] Please disable Tamper Protection, reboot, and rerun installer" -ForegroundColor Red - Write-Host "`t[+] Hint: https://support.microsoft.com/en-us/windows/prevent-changes-to-security-settings-with-tamper-protection-31d51aaa-645d-408e-6ce7-8d7f8e593f87" -ForegroundColor Yellow - Write-Host "`t[+] Hint: https://www.tenforums.com/tutorials/123792-turn-off-tamper-protection-windows-defender-antivirus.html" -ForegroundColor Yellow - Write-Host "`t[+] Hint: https://github.com/jeremybeaume/tools/blob/master/disable-defender.ps1" -ForegroundColor Yellow - Write-Host "`t[+] Hint: https://lazyadmin.nl/win-11/turn-off-windows-defender-windows-11-permanently/" -ForegroundColor Yellow - Write-Host "`t[+] You are welcome to continue, but may experience errors downloading or installing packages" -ForegroundColor Yellow - Write-Host "`t[-] Do you still wish to proceed? (Y/N): " -ForegroundColor Yellow -NoNewline - $response = Read-Host - if ($response -notin @("y","Y")) { - exit 1 - } - } else { - Write-Host "`t[+] Tamper Protection is disabled" -ForegroundColor Green - Start-Sleep -Milliseconds 500 + return "Disable Tamper Protection, reboot, and rerun installer" } } catch { - Write-Host "`t[+] Tamper Protection is either not enabled or not detected" -ForegroundColor Yellow - Write-Host "`t[-] Do you still wish to proceed? (Y/N): " -ForegroundColor Yellow -NoNewline - $response = Read-Host - if ($response -notin @("y","Y")) { - exit 1 - } - Start-Sleep -Milliseconds 500 - } - - # Check if Defender is disabled - Write-Host "[+] Checking if Windows Defender service is disabled..." - $defender = Get-Service -Name WinDefend -ea 0 - if ($null -ne $defender) { - if ($defender.Status -eq "Running") { - Write-Host "`t[!] Please disable Windows Defender through Group Policy, reboot, and rerun installer" -ForegroundColor Red - Write-Host "`t[+] Hint: https://stackoverflow.com/questions/62174426/how-to-permanently-disable-windows-defender-real-time-protection-with-gpo" -ForegroundColor Yellow - Write-Host "`t[+] Hint: https://www.windowscentral.com/how-permanently-disable-windows-defender-windows-10" -ForegroundColor Yellow - Write-Host "`t[+] Hint: https://github.com/jeremybeaume/tools/blob/master/disable-defender.ps1" -ForegroundColor Yellow - Write-Host "`t[+] You are welcome to continue, but may experience errors downloading or installing packages" -ForegroundColor Yellow - Write-Host "`t[-] Do you still wish to proceed? (Y/N): " -ForegroundColor Yellow -NoNewline - $response = Read-Host - if ($response -notin @("y","Y")) { - exit 1 - } - } else { - Write-Host "`t[+] Defender is disabled" -ForegroundColor Green - Start-Sleep -Milliseconds 500 - } + return "Unable to determine if TamperProtection and Defender are enabled" } +} - Write-Host "[+] Setting password to never expire to avoid that a password expiration blocks the installation..." - $UserNoPasswd = Get-CimInstance Win32_UserAccount -Filter "Name='${Env:UserName}'" - $UserNoPasswd | Set-CimInstance -Property @{ PasswordExpires = $false } +function Test-WindowsVersion { + try { + $os = Get-CimInstance -Class Win32_OperatingSystem + $osMajorVersion = $os.Version.Split('.')[0] # Version examples: "6.1.7601", "10.0.19045" + if ($osMajorVersion -lt 10) { + return "Only Windows >= 10 is supported" + } + } catch { + return "Unable to determine Windows Version" + } +} - # Prompt user to remind them to take a snapshot - Write-Host "[-] Have you taken a VM snapshot to ensure you can revert to pre-installation state? (Y/N): " -ForegroundColor Yellow -NoNewline - $response = Read-Host - if ($response -notin @("y","Y")) { - exit 1 - } +# 17763: the version used by windows-2019 in GH actions +# 19045: https://www.microsoft.com/en-us/software-download/windows10ISO downloaded on April 25 2023. +# 20348: the version used by windows-2022 in GH actions +function Test-TestedOS { + $testedVersions = @(17763, 19045, 20348) + try { + $osVersion = (Get-CimInstance -class Win32_OperatingSystem).BuildNumber + if (-not ($osVersion -in $testedVersions)){ + return "Windows version $osVersion has not been tested. Tested versions: $($testedVersions -join ', ')" + } + } catch { + return "Windows version may not have been tested. Tested versions: $($testedVersions -join ', ')" + } } +function Test-VM { + $virtualModels = @('VirtualBox', 'VMware', 'Virtual Machine', 'Hyper-V') + try { + $computerSystemModel = (Get-CimInstance win32_computersystem).model + $isVirtualModel = $false + + foreach ($model in $virtualModels) { + if ($computerSystemModel.Contains($model)) { + $isVirtualModel = $true + break + } + } -if (-not $noPassword.IsPresent) { - # Get user credentials for autologin during reboots - if ([string]::IsNullOrEmpty($password)) { - Write-Host "[+] Getting user credentials ..." - Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds" -Name "ConsolePrompting" -Value $True - Start-Sleep -Milliseconds 500 - $credentials = Get-Credential ${Env:UserName} - } else { - $securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force - $credentials = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList ${Env:UserName}, $securePassword - } + if (-not ($isVirtualModel)) { + return "You are not on a VM or have hardened your machine to not appear as such" + } + } catch { + return "Unable to determine if you are on a VM" + } } -# Check Boxstarter version -$boxstarterVersionGood = $false -if (${Env:ChocolateyInstall} -and (Test-Path "${Env:ChocolateyInstall}\bin\choco.exe")) { - choco info -l -r "boxstarter" | ForEach-Object { $name, $version = $_ -split '\|' } - $boxstarterVersionGood = [System.Version]$version -ge [System.Version]"3.0.2" +function Test-SpaceUserName { + try { + if (${Env:UserName} -match '\s') { + return "Username '${Env:UserName}' contains a space and will break installation" + } + } catch { + return "Unable to determine if the username contains a space" + } +} +function Test-Storage { + try { + $disk = Get-PSDrive (Get-Location).Drive.Name + Start-Sleep -Seconds 1 + if (-not (($disk.used + $disk.free)/1GB -gt 58.8)) { + return "A minimum of 60 GB hard drive space is preferred, increase hard drive space" + } + } catch { + return "Unable to determine hard drive space" + } } -# Install Boxstarter if needed -if (-not $boxstarterVersionGood) { - Write-Host "[+] Installing Boxstarter..." -ForegroundColor Cyan - [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 - Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://boxstarter.org/bootstrapper.ps1')) - Get-Boxstarter -Force - Start-Sleep -Milliseconds 500 -} -Import-Module "${Env:ProgramData}\boxstarter\boxstarter.chocolatey\boxstarter.chocolatey.psd1" -Force -# Check Chocolatey version -$version = choco --version -$chocolateyVersionGood = [System.Version]$version -ge [System.Version]"2.0.0" +if ($noGui.IsPresent) { + if (-not $noChecks.IsPresent) { + # Check PowerShell version + Write-Host "[+] Checking if PowerShell version is compatible (mandatory)..." + $error_info = Test-PSVersion + if ($error_info){ + Write-Host "`t[!]$error_info" -ForegroundColor Red + $mandatoryChecksPassed = $false + } -# Update Chocolatey if needed -if (-not ($chocolateyVersionGood)) { choco upgrade chocolatey } + # Ensure script is ran as administrator + Write-Host "[+] Checking if script is running as administrator (mandatory)..." + $error_info = Test-Admin + if ($error_info) { + Write-Host "`t[!]$error_info" -ForegroundColor Red + $mandatoryChecksPassed = $false + } -# Attempt to disable updates (i.e., windows updates and store updates) -Write-Host "[+] Attempting to disable updates..." -Disable-MicrosoftUpdate -try { - New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\WindowsStore" -Name "AutoDownload" -PropertyType DWord -Value 2 -ErrorAction Stop -Force | Out-Null -} catch { - Write-Host "`t[!] Failed to disable Microsoft Store updates" -ForegroundColor Yellow -} + # Ensure execution policy is unrestricted + Write-Host "[+] Checking if execution policy is unrestricted.. (mandatory)." + $error_info = Test-ExecutionPolicy + if ($error_info) { + Write-Host "`t[!]$error_info" -ForegroundColor Red + $mandatoryChecksPassed = $false + } -# Set Boxstarter options -$Boxstarter.RebootOk = (-not $noReboots.IsPresent) -$Boxstarter.NoPassword = $noPassword.IsPresent -$Boxstarter.AutoLogin = $true -$Boxstarter.SuppressLogging = $True -$global:VerbosePreference = "SilentlyContinue" -Set-BoxstarterConfig -NugetSources "$desktopPath;.;https://www.myget.org/F/vm-packages/api/v2;https://myget.org/F/vm-packages/api/v2;https://chocolatey.org/api/v2" -Set-WindowsExplorerOptions -EnableShowHiddenFilesFoldersDrives -EnableShowProtectedOSFiles -EnableShowFileExtensions -EnableShowFullPathInTitleBar + # Check if Windows < 10 + Write-Host "[+] Checking Operating System version compatibility..." + $error_info = Test-WindowsVersion + if ($error_info) { + Write-Host "`t[!]$error_info" -ForegroundColor Yellow + $script:checksPassed = $false + } -# Set Chocolatey options -Write-Host "[+] Updating Chocolatey settings..." -choco sources add -n="vm-packages" -s "$desktopPath;.;https://www.myget.org/F/vm-packages/api/v2;https://myget.org/F/vm-packages/api/v2" --priority 1 -choco feature enable -n allowGlobalConfirmation -choco feature enable -n allowEmptyChecksums -$cache = "${Env:LocalAppData}\ChocoCache" -New-Item -Path $cache -ItemType directory -Force | Out-Null -choco config set cacheLocation $cache + # Check if host has been tested + Write-Host "[+] Checking if the Operating System has been tested..." + $error_info= Test-TestedOS + if ($error_info) { + Write-Host "`t[!]$error_info" -ForegroundColor Yellow + $script:checksPassed = $false + } -# Set power options to prevent installs from timing out -powercfg -change -monitor-timeout-ac 0 | Out-Null -powercfg -change -monitor-timeout-dc 0 | Out-Null -powercfg -change -disk-timeout-ac 0 | Out-Null -powercfg -change -disk-timeout-dc 0 | Out-Null -powercfg -change -standby-timeout-ac 0 | Out-Null -powercfg -change -standby-timeout-dc 0 | Out-Null -powercfg -change -hibernate-timeout-ac 0 | Out-Null -powercfg -change -hibernate-timeout-dc 0 | Out-Null + # Check if system is a virtual machine + Write-Host "[+] Checking if the system runs on a Virtual Machine..." + $error_info = Test-VM + if ($error_info) { + Write-Host "`t[!]$error_info" -ForegroundColor Yellow + $script:checksPassed = $false + } -Write-Host "[+] Checking for configuration file..." -$configPath = Join-Path $desktopPath "config.xml" -if ([string]::IsNullOrEmpty($customConfig)) { - Write-Host "[+] Using github configuration file..." - $configSource = 'https://raw.githubusercontent.com/mandiant/flare-vm/main/config.xml' -} else { - Write-Host "[+] Using custom configuration file..." - $configSource = $customConfig -} + # Check for spaces in the username, exit if identified + Write-Host "[+] Checking for spaces in the username... (mandatory)" + $error_info = Test-SpaceUserName + if ($error_info) { + Write-Host "`t[!]$error_info" -ForegroundColor Red + $mandatoryChecksPassed = $false + } -Get-ConfigFile $configPath $configSource + # Check if host has enough disk space + Write-Host "[+] Checking if host has enough disk space..." + $error_info = Test-Storage + if ($error_info) { + Write-Host "`t[!]$error_info" -ForegroundColor Yellow + $script:checksPassed = $false + } -Write-Host "Configuration file path: $configPath" + # Internet connectivity checks + $error_info = Test-WebConnection 'google.com' + if ($error_info){ + Write-Host "`t[+] $error_info" -ForegroundColor Red + $mandatoryChecksPassed = $false + }else { + $error_info = Test-WebConnection 'github.com' + if ($error_info){ + Write-Host "`t[+] $error_info" -ForegroundColor Red + $mandatoryChecksPassed = $false + }else { + $error_info = Test-WebConnection 'raw.githubusercontent.com' + if ($error_info){ + Write-Host "`t[+] $error_info" -ForegroundColor Red + $mandatoryChecksPassed = $false + } + } + } -# Check the configuration file exists -if (-Not (Test-Path $configPath)) { - Write-Host "`t[!] Configuration file missing: " $configPath -ForegroundColor Red - Write-Host "`t[-] Please download config.xml from $configPathUrl to your desktop" -ForegroundColor Yellow - Write-Host "`t[-] Is the file on your desktop? (Y/N): " -ForegroundColor Yellow -NoNewline - $response = Read-Host - if ($response -notin @("y","Y")) { - exit 1 - } - if (-Not (Test-Path $configPath)) { - Write-Host "`t[!] Configuration file still missing: " $configPath -ForegroundColor Red - Write-Host "`t[!] Exiting..." -ForegroundColor Red - Start-Sleep 3 - exit 1 - } -} + # Check if Tamper Protection is disabled + Write-Host "[+] Checking if Windows Defender Tamper Protection is disabled..." + $error_info = Test-DefenderAndTamperProtection + if ($error_info) { + Write-Host "`t[!]$errorinfo" -ForegroundColor Red + $script:checksPassed = $false + } -# Get config contents -Start-Sleep 1 -$configXml = [xml](Get-Content $configPath) + if (-not $mandatoryChecksPassed){ + Write-Host "[!] $exit_message" -ForegroundColor Red + Start-Sleep 3 + exit 1 + } + + if (-not $script:checksPassed){ + Write-Host "[-] Do you still wish to proceed? (Y/N): " -ForegroundColor Yellow -NoNewline + $response = Read-Host + if ($response -notin @("y","Y")) { + exit 1 + } + } + Write-Host "[+] Setting password to never expire to avoid that a password expiration blocks the installation..." + $UserNoPasswd = Get-CimInstance Win32_UserAccount -Filter "Name='${Env:UserName}'" + $UserNoPasswd | Set-CimInstance -Property @{ PasswordExpires = $false } + + # Prompt user to remind them to take a snapshot + Write-Host "[-] Have you taken a VM snapshot to ensure you can revert to pre-installation state? (Y/N): " -ForegroundColor Yellow -NoNewline + $response = Read-Host + if ($response -notin @("y","Y")) { + exit 1 + } + } + +} + +function Open-CheckManager { + if ($formChecksManager.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { + exit + } +} +# Init Window Install checks if (-not $noGui.IsPresent) { Write-Host "[+] Starting GUI to allow user to edit configuration file..." @@ -479,102 +424,489 @@ if (-not $noGui.IsPresent) { $errorColor = [System.Drawing.ColorTranslator]::FromHtml("#c80505") $successColor = [System.Drawing.ColorTranslator]::FromHtml("#417505") - - - function Get-Folder($textBox, $envVar) { - $folderBrowserDialog = New-Object System.Windows.Forms.FolderBrowserDialog - $folderBrowserDialog.RootFolder = 'MyComputer' - if ($folderBrowserDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { - $textbox.text = (Join-Path $folderBrowserDialog.SelectedPath (Split-Path $envs[$envVar] -Leaf)) + $grayedColor = [System.Drawing.ColorTranslator]::FromHtml("#6e6964") + $orangeColor = [System.Drawing.ColorTranslator]::FromHtml("#bf8334") + + if (-not $noChecks.IsPresent) { + + ################################################################################################# + ################################ Installer Checks Form Controls ################################# + ################################################################################################# + + $formChecksManager = New-Object system.Windows.Forms.Form + $formChecksManager.ClientSize = New-Object System.Drawing.Point(700,640) + $formChecksManager.text = "FLAREVM Pre-Install Checks" + $formChecksManager.TopMost = $true + $formChecksManager.StartPosition = 'CenterScreen' + + $ChecksPanel = New-Object system.Windows.Forms.Panel + $ChecksPanel.height = 460 + $ChecksPanel.width = 89 + $ChecksPanel.location = New-Object System.Drawing.Point(570,8) + + $InstallChecksGroup = New-Object system.Windows.Forms.Groupbox + $InstallChecksGroup.height = 490 + $InstallChecksGroup.width = 665 + $InstallChecksGroup.text = "Installation Checks" + $InstallChecksGroup.location = New-Object System.Drawing.Point(23,14) + + ################################# Check Labels ################################# + + $PSVersionLabel = New-Object system.Windows.Forms.Label + $PSVersionLabel.text = "Valid Powershell version" + $PSVersionLabel.AutoSize = $true + $PSVersionLabel.width = 25 + $PSVersionLabel.height = 10 + $PSVersionLabel.location = New-Object System.Drawing.Point(15,18) + $PSVersionLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $RunningAsAdminLabel = New-Object system.Windows.Forms.Label + $RunningAsAdminLabel.text = "Running as Administrator" + $RunningAsAdminLabel.AutoSize = $true + $RunningAsAdminLabel.width = 25 + $RunningAsAdminLabel.height = 10 + $RunningAsAdminLabel.location = New-Object System.Drawing.Point(15,59) + $RunningAsAdminLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $ExecutionPolicyLabel = New-Object system.Windows.Forms.Label + $ExecutionPolicyLabel.text = "Execution Policy Unrestricted" + $ExecutionPolicyLabel.AutoSize = $true + $ExecutionPolicyLabel.width = 25 + $ExecutionPolicyLabel.height = 10 + $ExecutionPolicyLabel.location = New-Object System.Drawing.Point(15,104) + $ExecutionPolicyLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $validWindowsVersionLabel = New-Object system.Windows.Forms.Label + $validWindowsVersionLabel.text = "Valid Windows Version" + $validWindowsVersionLabel.AutoSize = $true + $validWindowsVersionLabel.location = New-Object System.Drawing.Point(15,149) + $validWindowsVersionLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $WindowsReleaseLabel = New-Object system.Windows.Forms.Label + $WindowsReleaseLabel.text = "Tested Windows Version" + $WindowsReleaseLabel.AutoSize = $true + $WindowsReleaseLabel.width = 25 + $WindowsReleaseLabel.height = 10 + $WindowsReleaseLabel.location = New-Object System.Drawing.Point(15,193) + $WindowsReleaseLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $RunningVMLabel = New-Object system.Windows.Forms.Label + $RunningVMLabel.text = "Running in a Virtual Machine" + $RunningVMLabel.AutoSize = $true + $RunningVMLabel.width = 25 + $RunningVMLabel.height = 10 + $RunningVMLabel.location = New-Object System.Drawing.Point(15,239) + $RunningVMLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $usernameContainsSpacesLabel = New-Object system.Windows.Forms.Label + $usernameContainsSpacesLabel.text = "Valid username" + $usernameContainsSpacesLabel.AutoSize = $true + $usernameContainsSpacesLabel.location = New-Object System.Drawing.Point(15,285) + $usernameContainsSpacesLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $EnoughHardStorageLabel = New-Object system.Windows.Forms.Label + $EnoughHardStorageLabel.text = "Enough Hard Drive Space" + $EnoughHardStorageLabel.AutoSize = $true + $EnoughHardStorageLabel.width = 25 + $EnoughHardStorageLabel.height = 10 + $EnoughHardStorageLabel.location = New-Object System.Drawing.Point(15,325) + $EnoughHardStorageLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $internetConnectivityLabel = New-Object system.Windows.Forms.Label + $internetConnectivityLabel.text = "Internet connectivity" + $internetConnectivityLabel.AutoSize = $true + $internetConnectivityLabel.location = New-Object System.Drawing.Point(15,369) + $internetConnectivityLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $WindowsDefenderLabel = New-Object system.Windows.Forms.Label + $WindowsDefenderLabel.text = "Windows Defender Disabled" + $WindowsDefenderLabel.AutoSize = $true + $WindowsDefenderLabel.width = 25 + $WindowsDefenderLabel.height = 10 + $WindowsDefenderLabel.location = New-Object System.Drawing.Point(15,411) + $WindowsDefenderLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + ################################# Check Boolean Controls ################################# + + $PSVersion = New-Object system.Windows.Forms.Label + $PSVersion.text = "False" + $PSVersion.AutoSize = $true + $PSVersion.width = 25 + $PSVersion.height = 10 + $PSVersion.location = New-Object System.Drawing.Point(24,18) + $PSVersion.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $PSVersion.ForeColor = $errorColor + + $RunningAsAdmin = New-Object system.Windows.Forms.Label + $RunningAsAdmin.text = "False" + $RunningAsAdmin.AutoSize = $true + $RunningAsAdmin.width = 25 + $RunningAsAdmin.height = 10 + $RunningAsAdmin.location = New-Object System.Drawing.Point(24,63) + $RunningAsAdmin.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $RunningAsAdmin.ForeColor = $errorColor + + $ExecutionPolicy = New-Object system.Windows.Forms.Label + $ExecutionPolicy.text = "False" + $ExecutionPolicy.AutoSize = $true + $ExecutionPolicy.width = 25 + $ExecutionPolicy.height = 10 + $ExecutionPolicy.location = New-Object System.Drawing.Point(24,108) + $ExecutionPolicy.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $ExecutionPolicy.ForeColor = $errorColor + + $validWindowsVersion = New-Object system.Windows.Forms.Label + $validWindowsVersion.text = "False" + $validWindowsVersion.AutoSize = $true + $validWindowsVersion.width = 25 + $validWindowsVersion.height = 10 + $validWindowsVersion.location = New-Object System.Drawing.Point(24,150) + $validWindowsVersion.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $validWindowsVersion.ForeColor = $errorColor + + $WindowsRelease = New-Object system.Windows.Forms.Label + $WindowsRelease.text = "False" + $WindowsRelease.AutoSize = $true + $WindowsRelease.width = 25 + $WindowsRelease.height = 10 + $WindowsRelease.location = New-Object System.Drawing.Point(24,195) + $WindowsRelease.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $WindowsRelease.ForeColor = $orangeColor + + $RunningVM = New-Object system.Windows.Forms.Label + $RunningVM.text = "False" + $RunningVM.AutoSize = $true + $RunningVM.width = 25 + $RunningVM.height = 10 + $RunningVM.location = New-Object System.Drawing.Point(24,240) + $RunningVM.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $RunningVM.ForeColor = $orangeColor + + $usernameContainsSpaces = New-Object system.Windows.Forms.Label + $usernameContainsSpaces.text = "False" + $usernameContainsSpaces.AutoSize = $true + $usernameContainsSpaces.width = 25 + $usernameContainsSpaces.height = 10 + $usernameContainsSpaces.location = New-Object System.Drawing.Point(24,285) + $usernameContainsSpaces.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $usernameContainsSpaces.ForeColor = $errorColor + + $EnoughHardStorage = New-Object system.Windows.Forms.Label + $EnoughHardStorage.text = "False" + $EnoughHardStorage.AutoSize = $true + $EnoughHardStorage.width = 25 + $EnoughHardStorage.height = 10 + $EnoughHardStorage.location = New-Object System.Drawing.Point(24,322) + $EnoughHardStorage.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $EnoughHardStorage.ForeColor = $orangeColor + + $internetConnectivity = New-Object system.Windows.Forms.Label + $internetConnectivity.text = "False" + $internetConnectivity.AutoSize = $true + $internetConnectivity.width = 25 + $internetConnectivity.height = 10 + $internetConnectivity.location = New-Object System.Drawing.Point(24,368) + $internetConnectivity.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $internetConnectivity.ForeColor = $errorColor + + $WindowsDefender = New-Object system.Windows.Forms.Label + $WindowsDefender.text = "False" + $WindowsDefender.AutoSize = $true + $WindowsDefender.width = 25 + $WindowsDefender.height = 10 + $WindowsDefender.location = New-Object System.Drawing.Point(24,409) + $WindowsDefender.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $WindowsDefender.ForeColor = $orangeColor + + ################################# Check Tooltip Controls ################################# + $verticalPosition = 41 + + # $PSVersionTooltip + $PSVersionTooltip = New-Object system.Windows.Forms.Label + $PSVersionTooltip.text = "Powershell version must be >= 5 (mandatory)" + $PSVersionTooltip.AutoSize = $true + $PSVersionTooltip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $PSVersionTooltip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $PSVersionTooltip.ForeColor = $grayedColor + $verticalPosition += 44 + + # $RunningAsAdminTooltip + $RunningAsAdminTooltip = New-Object system.Windows.Forms.Label + $RunningAsAdminTooltip.text = "You must run the script as Administrator (mandatory)" + $RunningAsAdminTooltip.AutoSize = $true + $RunningAsAdminTooltip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $RunningAsAdminTooltip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $RunningAsAdminTooltip.ForeColor = $grayedColor + $verticalPosition += 44 + + # $ExecutionPolicyTooltip + $ExecutionPolicyTooltip = New-Object system.Windows.Forms.Label + $ExecutionPolicyTooltip.text = "You must enable script execution (mandatory)" + $ExecutionPolicyTooltip.AutoSize = $true + $ExecutionPolicyTooltip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $ExecutionPolicyTooltip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $ExecutionPolicyTooltip.ForeColor = $grayedColor + $verticalPosition += 44 + + # $validWindowsVersionToolTip + $validWindowsVersionToolTip = New-Object system.Windows.Forms.Label + $validWindowsVersionToolTip.text = "Only Windows Version >= 10 is supported (mandatory)" + $validWindowsVersionToolTip.AutoSize = $true + $validWindowsVersionToolTip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $validWindowsVersionToolTip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $validWindowsVersionToolTip.ForeColor = $grayedColor + $verticalPosition += 44 + + # $WindowsReleaseTooltip + $WindowsReleaseTooltip = New-Object system.Windows.Forms.Label + $WindowsReleaseTooltip.text = "You might run into issues when using a non tested version" + $WindowsReleaseTooltip.AutoSize = $true + $WindowsReleaseTooltip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $WindowsReleaseTooltip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $WindowsReleaseTooltip.ForeColor = $grayedColor + $verticalPosition += 44 + + # $RunningVMTooltip + $RunningVMTooltip = New-Object system.Windows.Forms.Label + $RunningVMTooltip.text = "Only run this script inside a Virtual Machine (VM)" + $RunningVMTooltip.AutoSize = $true + $RunningVMTooltip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $RunningVMTooltip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $RunningVMTooltip.ForeColor = $grayedColor + $verticalPosition += 44 + + # $usernameContainsSpacesToolTip + $usernameContainsSpacesToolTip = New-Object system.Windows.Forms.Label + $usernameContainsSpacesToolTip.text = "Username cannot contain spaces (mandatory)" + $usernameContainsSpacesToolTip.AutoSize = $true + $usernameContainsSpacesToolTip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $usernameContainsSpacesToolTip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $usernameContainsSpacesToolTip.ForeColor = $grayedColor + $verticalPosition += 44 + + # $EnoughHardStorageTooltip + $EnoughHardStorageTooltip = New-Object system.Windows.Forms.Label + $EnoughHardStorageTooltip.text = "A minimum of 60 GB hard drive space is preferred" + $EnoughHardStorageTooltip.AutoSize = $true + $EnoughHardStorageTooltip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $EnoughHardStorageTooltip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $EnoughHardStorageTooltip.ForeColor = $grayedColor + $verticalPosition += 44 + + # $internetConnectivityTooltip + $internetConnectivityTooltip = New-Object system.Windows.Forms.Label + $internetConnectivityTooltip.text = "You must have internet connection (mandatory)" + $internetConnectivityTooltip.AutoSize = $true + $internetConnectivityTooltip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $internetConnectivityTooltip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $internetConnectivityTooltip.ForeColor = $grayedColor + $verticalPosition += 44 + + # $WindowsDefenderTooltip + $WindowsDefenderTooltip = New-Object system.Windows.Forms.Label + $WindowsDefenderTooltip.text = "Disable Windows Defender and Tamper Protection" + $WindowsDefenderTooltip.AutoSize = $true + $WindowsDefenderTooltip.location = New-Object System.Drawing.Point(15,$verticalPosition) + $WindowsDefenderTooltip.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $WindowsDefenderTooltip.ForeColor = $grayedColor + + + ################################# Check Completion Controls ################################# + + $breakInstallationLabel = New-Object system.Windows.Forms.Label + $breakInstallationLabel.Text = $exit_message + $breakInstallationLabel.AutoSize = $true + $breakInstallationLabel.location = New-Object System.Drawing.Point(40,530) + $breakInstallationLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12) + $breakInstallationLabel.ForeColor = $errorColor + $breakInstallationLabel.Visible = $false + + $BreakMyInstallCheckbox = New-Object system.Windows.Forms.CheckBox + $BreakMyInstallCheckbox.Visible = $false + $BreakMyInstallCheckbox.text = "I understand that continuing without satisfying all pre-install checks might cause install issues" + $BreakMyInstallCheckbox.AutoSize = $true + $BreakMyInstallCheckbox.width = 324 + $BreakMyInstallCheckbox.height = 21 + $BreakMyInstallCheckbox.location = New-Object System.Drawing.Point(30,510) + $BreakMyInstallCheckbox.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + + $snapshotCheckBox = New-Object system.Windows.Forms.CheckBox + $snapshotCheckBox.Text = "I have taken a VM snapshot to ensure I can revert to pre-installation state" + $snapshotCheckBox.AutoSize = $true + $snapshotCheckBox.location = New-Object System.Drawing.Point(30,532) + $snapshotCheckBox.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $snapshotCheckBox.Visible = $false + + $ChecksCompleteButton = New-Object system.Windows.Forms.Button + $ChecksCompleteButton.text = "Continue" + $ChecksCompleteButton.width = 97 + $ChecksCompleteButton.height = 37 + $ChecksCompleteButton.enabled = $false + $ChecksCompleteButton.DialogResult = [System.Windows.Forms.DialogResult]::OK + $ChecksCompleteButton.location = New-Object System.Drawing.Point(420,565) + $ChecksCompleteButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12) + $ChecksCompleteButton.Add_Click({ + $script:checksPassed = $true + [void]$formChecksManager.Close() + }) + + $checksCancelButton = New-Object system.Windows.Forms.Button + $checksCancelButton.Text = "Cancel" + $checksCancelButton.width = 97 + $checksCancelButton.height = 37 + $checksCancelButton.location = New-Object System.Drawing.Point(519,565) + $checksCancelButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12) + $checksCancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel + + $InstallChecksGroup.controls.AddRange(@($ChecksPanel,$RunningAsAdminLabel,$ExecutionPolicyLabel,$WindowsDefenderLabel,$WindowsReleaseLabel,$RunningVMLabel,$PSVersionLabel,$internetConnectivityLabel,$validWindowsVersionLabel,$validWindowsVersionToolTip,$RunningAsAdminTooltip,$ExecutionPolicyTooltip,$WindowsDefenderTooltip,$WindowsReleaseTooltip,$RunningVMTooltip,$EnoughHardStorageLabel, $EnoughHardStorageTooltip,$PSVersionTooltip,$internetConnectivityTooltip,$usernameContainsSpacesLabel,$usernameContainsSpacesToolTip,$RunningAsAdmin,$EnoughHardStorage)) + $formChecksManager.controls.AddRange(@($InstallChecksGroup,$ChecksCompleteButton,$checksCancelButton,$BreakMyInstallCheckbox,$snapshotCheckBox,$breakInstallationLabel)) + $ChecksPanel.controls.AddRange(@($RunningAsAdmin, $ExecutionPolicy,$WindowsDefender,$WindowsRelease,$RunningVM, $EnoughHardStorage, $PSVersion, $internetConnectivity, $validWindowsVersion,$usernameContainsSpaces )) + + # Make sure that the user completed all pre-install steps + $error_info = Test-Admin + if ($error_info){ + $RunningAsAdmin.Text = $error_info + $RunningAsAdmin.Forecolor = $errorColor + $mandatoryChecksPassed = $false + } else { + $RunningAsAdmin.Text = "True" + $RunningAsAdmin.ForeColor = $successColor + } + $error_info = Test-ExecutionPolicy + if ($error_info){ + $ExecutionPolicyTooltip.Text = $error_info + $ExecutionPolicyTooltip.Forecolor = $errorColor + $mandatoryChecksPassed = $false + } else { + $ExecutionPolicy.Text = "True" + $ExecutionPolicy.ForeColor = $successColor + } + $error_info = Test-DefenderAndTamperProtection + if ($error_info){ + $WindowsDefenderTooltip.Text = $error_info + $WindowsDefenderTooltip.Forecolor = $orangeColor + $script:checksPassed = $false + } else { + $WindowsDefender.Text = "True" + $WindowsDefender.ForeColor = $successColor } - } - - # Function that accesses MyGet vm-packages API URL to process packages that are the latest version and have a category - # Saves vm-packages.xml into disk and follows the link after the tag to retrieve a new version of the XML file - # Returns $packagesByCategory, a hashtable of arrays, where each entry is a PSCustomObject - function Get-Packages-Categories { - # MyGet API URL that contains a filter to display only the latest packages - # This URL displays the last two versions of a package - # Minimize the number of HTTP requests to display all the packages due to the number of versions a package might have - $vmPackagesUrl = "https://www.myget.org/F/vm-packages/api/v2/Packages?$filter=IsLatestVersion%20eq%20true" - $vmPackagesFile = "${Env:VM_COMMON_DIR}\vm-packages.xml" - $packagesByCategory=@{} - do { - # Download the XML from MyGet API - Save-FileFromUrl -fileSource $vmPackagesUrl -fileDestination $vmPackagesFile --exitOnError - - # Load the XML content - [xml]$vm_packages = Get-Content $vmPackagesFile - - # Define the namespaces defined in vm-packages.xml to access nodes - # Each package resides in the entry node that is defined in the dataservices namespace - # Each node has properties that are defined in the metadata namespace - $ns = New-Object System.Xml.XmlNamespaceManager($vm_packages.NameTable) - $ns.AddNamespace("atom", "http://www.w3.org/2005/Atom") - $ns.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices") - $ns.AddNamespace("m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata") - - # Extract package information from the XML - $vm_packages.feed.entry | ForEach-Object { - $isLatestVersion = $_.SelectSingleNode("m:properties/d:IsLatestVersion", $ns).InnerText - $category = $_.SelectSingleNode("m:properties/d:Tags", $ns).InnerText - # Select only packages that have the latest version, contain a category and the category is not excluded - if (($isLatestVersion -eq "true") -and ($category -ne "") -and ($excludedCategories -notcontains $category)) { - $packageName = $_.properties.Id - $description = $_.properties.Description - $projectUrl = $_.properties.projectUrl - - # Initialize category as an empty array - if (-not ($packagesByCategory.ContainsKey($category))) { - $packagesByCategory[$category] = @() - } - $packageObject = [PSCustomObject]@{ - PackageName = $packageName - PackageDescription = $description - } - # Check if $projectUrl contains a valid URL - if ($projectUrl -match "^http") { - Add-Member -InputObject $packageObject -MemberType NoteProperty -Name "PackageUrl" -Value $projectURl - } - # Add the PackageName and PackageDescription (and PackageUrl if present) to each entry in the array - $packagesByCategory[$category] += $packageObject - } - } - # Check if there is a next link in the XML and set the API URL to that link if it exists - $nextLink = $vm_packages.SelectSingleNode("//atom:link[@rel='next']/@href", $ns) - $vmPackagesUrl = $nextLink."#text" - - } while ($vmPackagesUrl) - - return $packagesByCategory - } - - # Function that returns an array of all the packages that are displayed sorted by category from $packagesByCategory - function Get-AllPackages{ - $listedPackages = $packagesByCategory.Values | ForEach-Object { $_ } | Select-Object -ExpandProperty PackageName - return $listedPackages - } - # Function that returns additional packages from the config that are not displayed in the textboxes - # which includes both Choco packages and packages from excluded categories - function Get-AdditionalPackages{ - $additionalPackages=@() + $error_info = Test-TestedOS + if ($error_info){ + $WindowsReleaseTooltip.Text = $error_info + $WindowsReleaseTooltip.Forecolor = $orangeColor + $script:checksPassed = $false + } else { + $WindowsRelease.Text = "True" + $WindowsRelease.ForeColor = $successColor + } + $error_info = Test-VM + if ($error_info){ + $RunningAsAdminTooltip.Text = $error_info + $RunningAsAdminTooltip.Forecolor = $orangeColor + $script:checksPassed = $false + } else { + $RunningVM.Text = "True" + $RunningVM.ForeColor = $successColor + } + $error_info = Test-Storage + if ($error_info){ + $EnoughHardStorageTooltip.Forecolor = $orangeColor + $EnoughHardStorageTooltip.Text = $error_info + $script:checksPassed = $false + } else { + $EnoughHardStorage.Text = "True" + $EnoughHardStorage.ForeColor = $successColor + } + $error_info = Test-PSVersion + if ($error_info){ + $PSVersionTooltip.Text = $error_info + $PSVersionTooltip.Forecolor = $errorColor + $MandatoryChecksPassed = $false + } else { + $PSVersion.Text = "True" + $PSVersion.ForeColor = $successColor + } - # Packages from the config that are not displayed - $additionalPackages = $packagesToInstall | where-Object { $listedPackages -notcontains $_} - return $additionalPackages - } + $error_info = Test-WebConnection 'google.com' + if ($error_info){ + $internetConnectivityTooltip.Text = $error_info + $internetConnectivityTooltip.Forecolor = $errorColor + $mandatoryChecksPassed = $false + }else { + $error_info = Test-WebConnection 'github.com' + if ($error_info){ + $internetConnectivityTooltip.Text = $error_info + $internetConnectivityTooltip.Forecolor = $errorColor + $mandatoryChecksPassed = $false + }else { + $error_info = Test-WebConnection 'raw.githubusercontent.com' + if ($error_info){ + $internetConnectivityTooltip.Text = $error_info + $internetConnectivityTooltip.Forecolor = $errorColor + $mandatoryChecksPassed = $false + } else { + $internetConnectivity.Text = "True" + $internetConnectivity.ForeColor = $successColor + } + } + } + $error_info = Test-WindowsVersion + if ($error_info){ + $validWindowsVersionToolTip.Text = $error_info + $validWindowsVersionToolTip.Forecolor = $errorColor + $mandatoryChecksPassed = $false + } else { + $validWindowsVersion.Text = "True" + $validWindowsVersion.ForeColor = $successColor + } + $error_info = Test-SpaceUserName + if ($error_info){ + $usernameContainsSpacesToolTip.Text = $error_info + $usernameContainsSpacesToolTip.Forecolor = $errorColor + $mandatoryChecksPassed = $false + }else { + $usernameContainsSpaces.Text = "True" + $usernameContainsSpaces.ForeColor = $successColor + } - # Gather lists of packages - $envs = [ordered]@{} - $configXml.config.envs.env.ForEach({ $envs[$_.name] = $_.value }) - $excludedCategories=@('Command and Control','Credential Access','Exploitation','Forensic','Lateral Movement', 'Payload Development','Privilege Escalation','Reconnaissance','Wordlists','Web Application') - # Read packages to install from the config - $packagesToInstall = $configXml.config.packages.package.name - $packagesByCategory = Get-Packages-Categories - $listedPackages = Get-AllPackages - $additionalPackages = Get-AdditionalPackages + #only display the checkbox if some checks did not pass + if ($mandatoryChecksPassed){ + if ($script:checksPassed){ + $BreakMyInstallCheckbox.Visible = $false + $snapshotCheckBox.Visible = $true + }else{ + $BreakMyInstallCheckbox.Visible = $true + $snapshotCheckBox.Visible = $true + } + }else{ + $breakInstallationLabel.visible = $true + } + $snapshotCheckBox.Add_CheckStateChanged({ + if (($snapshotCheckBox.Checked) -and ($script:checksPassed)){ + $ChecksCompleteButton.enabled = $true + } else { + if (($snapshotCheckBox.Checked) -and (-not $script:checksPassed)){ + $ChecksCompleteButton.enabled = $BreakMyInstallCheckbox.Checked + } else{ + if (-not ($snapshotCheckBox.Checked)){ + $ChecksCompleteButton.enabled = $false + } + } + } + }) + + $BreakMyInstallCheckbox.Add_CheckStateChanged({ + if ($BreakMyInstallCheckbox.Checked){ + $ChecksCompleteButton.enabled = $snapshotCheckBox.Checked + } else{ + $ChecksCompleteButton.enabled = $false + } + }) + Open-CheckManager + } + # init GUI controls of the install customization Window $formEnv = New-Object system.Windows.Forms.Form $formEnv.ClientSize = New-Object System.Drawing.Point(750,350) $formEnv.text = "FLARE VM Install Customization" @@ -603,7 +935,6 @@ if (-not $noGui.IsPresent) { $vmCommonDirText.height = 20 $vmCommonDirText.location = New-Object System.Drawing.Point(190,21) $vmCommonDirText.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $vmCommonDirText.text = $envs['VM_COMMON_DIR'] $vmCommonDirSelect = New-Object system.Windows.Forms.Button $vmCommonDirSelect.text = "Select Folder" @@ -636,7 +967,6 @@ if (-not $noGui.IsPresent) { $toolListDirText.height = 20 $toolListDirText.location = New-Object System.Drawing.Point(190,68) $toolListDirText.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $toolListDirText.text = $envs['TOOL_LIST_DIR'] $toolListDirSelect = New-Object system.Windows.Forms.Button $toolListDirSelect.text = "Select Folder" @@ -669,7 +999,6 @@ if (-not $noGui.IsPresent) { $rawToolsDirText.height = 20 $rawToolsDirText.location = New-Object System.Drawing.Point(190,113) $rawToolsDirText.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $rawToolsDirText.text = $envs['RAW_TOOLS_DIR'] $rawToolsDirSelect = New-Object system.Windows.Forms.Button $rawToolsDirSelect.text = "Select Folder" @@ -718,485 +1047,703 @@ if (-not $noGui.IsPresent) { $envVarGroup.controls.AddRange(@($vmCommonDirText,$vmCommonDirSelect,$vmCommonDirLabel,$toolListDirText,$toolListDirSelect,$toolListDirLabel,$toolListShortCutText,$toolListShortcutSelect,$toolListShortcutLabel,$vmCommonDirNote,$toolListDirNote,$toolListShortcutNote,$rawToolsDirText,$rawToolsDirSelect,$rawToolsDirLabel,$rawToolsDirNote)) - $formEnv.Topmost = $true - $Result = $formEnv.ShowDialog() +} +if (-not $noPassword.IsPresent) { + # Get user credentials for autologin during reboots + if ([string]::IsNullOrEmpty($password)) { + Write-Host "[+] Getting user credentials ..." + Set-ItemProperty "HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds" -Name "ConsolePrompting" -Value $True + Start-Sleep -Milliseconds 500 + $credentials = Get-Credential ${Env:UserName} + } else { + $securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force + $credentials = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList ${Env:UserName}, $securePassword + } +} - if ($Result -eq [System.Windows.Forms.DialogResult]::OK) { - # Remove default environment variables - $nodes = $configXml.SelectNodes('//config/envs/env') - foreach($node in $nodes) { - $node.ParentNode.RemoveChild($node) | Out-Null - } +# Check Boxstarter version +$boxstarterVersionGood = $false +if (${Env:ChocolateyInstall} -and (Test-Path "${Env:ChocolateyInstall}\bin\choco.exe")) { + choco info -l -r "boxstarter" | ForEach-Object { $name, $version = $_ -split '\|' } + $boxstarterVersionGood = [System.Version]$version -ge [System.Version]"3.0.2" +} - # Add environment variables - $envs = $configXml.SelectSingleNode('//envs') - $newXmlNode = $envs.AppendChild($configXml.CreateElement("env")) - $newXmlNode.SetAttribute("name", "VM_COMMON_DIR") - $newXmlNode.SetAttribute("value", $vmCommonDirText.text); - $newXmlNode = $envs.AppendChild($configXml.CreateElement("env")) - $newXmlNode.SetAttribute("name", "TOOL_LIST_DIR") - $newXmlNode.SetAttribute("value", $toolListDirText.text); - $newXmlNode = $envs.AppendChild($configXml.CreateElement("env")) - $newXmlNode.SetAttribute("name", "RAW_TOOLS_DIR") - $newXmlNode.SetAttribute("value", $rawToolsDirText.text) +# Install Boxstarter if needed +if (-not $boxstarterVersionGood) { + Write-Host "[+] Installing Boxstarter..." -ForegroundColor Cyan + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 + Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://boxstarter.org/bootstrapper.ps1')) + Get-Boxstarter -Force - [void]$formEnv.Close() + Start-Sleep -Milliseconds 500 +} +Import-Module "${Env:ProgramData}\boxstarter\boxstarter.chocolatey\boxstarter.chocolatey.psd1" -Force - } else { - Write-Host "[+] Cancel pressed, stopping installation..." +# Check Chocolatey version +$version = choco --version +$chocolateyVersionGood = [System.Version]$version -ge [System.Version]"2.0.0" + +# Update Chocolatey if needed +if (-not ($chocolateyVersionGood)) { choco upgrade chocolatey } + +# Attempt to disable updates (i.e., windows updates and store updates) +Write-Host "[+] Attempting to disable updates..." +Disable-MicrosoftUpdate +try { + New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\WindowsStore" -Name "AutoDownload" -PropertyType DWord -Value 2 -ErrorAction Stop -Force | Out-Null +} catch { + Write-Host "`t[!] Failed to disable Microsoft Store updates" -ForegroundColor Yellow +} + +# Set Boxstarter options +$Boxstarter.RebootOk = (-not $noReboots.IsPresent) +$Boxstarter.NoPassword = $noPassword.IsPresent +$Boxstarter.AutoLogin = $true +$Boxstarter.SuppressLogging = $True +$VerbosePreference = "SilentlyContinue" +Set-BoxstarterConfig -NugetSources "$desktopPath;.;https://www.myget.org/F/vm-packages/api/v2;https://myget.org/F/vm-packages/api/v2;https://chocolatey.org/api/v2" +Set-WindowsExplorerOptions -EnableShowHiddenFilesFoldersDrives -EnableShowProtectedOSFiles -EnableShowFileExtensions -EnableShowFullPathInTitleBar + +# Set Chocolatey options +Write-Host "[+] Updating Chocolatey settings..." +choco sources add -n="vm-packages" -s "$desktopPath;.;https://www.myget.org/F/vm-packages/api/v2;https://myget.org/F/vm-packages/api/v2" --priority 1 +choco feature enable -n allowGlobalConfirmation +choco feature enable -n allowEmptyChecksums +$cache = "${Env:LocalAppData}\ChocoCache" +New-Item -Path $cache -ItemType directory -Force | Out-Null +choco config set cacheLocation $cache + +# Set power options to prevent installs from timing out +powercfg -change -monitor-timeout-ac 0 | Out-Null +powercfg -change -monitor-timeout-dc 0 | Out-Null +powercfg -change -disk-timeout-ac 0 | Out-Null +powercfg -change -disk-timeout-dc 0 | Out-Null +powercfg -change -standby-timeout-ac 0 | Out-Null +powercfg -change -standby-timeout-dc 0 | Out-Null +powercfg -change -hibernate-timeout-ac 0 | Out-Null +powercfg -change -hibernate-timeout-dc 0 | Out-Null + +Write-Host "[+] Checking for configuration file..." +$configPath = Join-Path $desktopPath "config.xml" +if ([string]::IsNullOrEmpty($customConfig)) { + Write-Host "[+] Using github configuration file..." + $configSource = 'https://raw.githubusercontent.com/mandiant/flare-vm/main/config.xml' +} else { + Write-Host "[+] Using custom configuration file..." + $configSource = $customConfig +} + +Get-ConfigFile $configPath $configSource + +Write-Host "Configuration file path: $configPath" + +# Check the configuration file exists +if (-Not (Test-Path $configPath)) { + Write-Host "`t[!] Configuration file missing: " $configPath -ForegroundColor Red + Write-Host "`t[-] Please download config.xml from $configPathUrl to your desktop" -ForegroundColor Yellow + Write-Host "`t[-] Is the file on your desktop? (Y/N): " -ForegroundColor Yellow -NoNewline + $response = Read-Host + if ($response -notin @("y","Y")) { + exit 1 + } + if (-Not (Test-Path $configPath)) { + Write-Host "`t[!] Configuration file still missing: " $configPath -ForegroundColor Red + Write-Host "`t[!] Exiting..." -ForegroundColor Red Start-Sleep 3 exit 1 } +} - ################################################################################ - ## PACKAGE SELECTION BY CATEGORY - ################################################################################ +# Get config contents +Start-Sleep 1 +$configXml = [xml](Get-Content $configPath) - # Function that adds the selected packages to the config.xml for the installation - function Install-Selected-Packages{ - $selectedPackages = @() - $packages = $configXml.SelectSingleNode('//packages') - # Remove all child nodes inside - while ($packages.HasChildNodes) { - $packages.RemoveChild($packages.FirstChild) - } +######################################################################### +# GUI Functions +######################################################################### - foreach ($checkBox in $checkboxesPackages){ - if ($checkBox.Checked){ - $package = $checkbox.Text.split(":")[0] - $selectedPackages += $package - } - } - - foreach ($package in $additionalPackagesBox.Items){ - $selectedPackages += $package - } - # Add selected packages - foreach($package in $selectedPackages) { - $newXmlNode = $packages.AppendChild($configXml.CreateElement("package")) - $newXmlNode.SetAttribute("name", $package) - } - } +function Get-Folder($textBox, $envVar) { + $folderBrowserDialog = New-Object System.Windows.Forms.FolderBrowserDialog + $folderBrowserDialog.RootFolder = 'MyComputer' + if ($folderBrowserDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { + $textbox.text = (Join-Path $folderBrowserDialog.SelectedPath (Split-Path $envs[$envVar] -Leaf)) + } +} - # Function that resets the checkboxes to match the config.xml - function Set-InitialPackages { - foreach ($checkBox in $checkboxesPackages){ - $package =$checkbox.Text.split(":")[0] - if (($checkbox.Checked) -and ($package -notin $packagesToInstall)){ - $checkBox.Checked = $false - }else{ - if ((-not $checkbox.Checked ) -and ($package -in $packagesToInstall)){ - $checkBox.Checked = $true - } +# Function that accesses MyGet vm-packages API URL to process packages that are the latest version and have a category +# Saves vm-packages.xml into disk and follows the link after the tag to retrieve a new version of the XML file +# Returns $packagesByCategory, a hashtable of arrays, where each entry is a PSCustomObject +function Get-Packages-Categories { + # MyGet API URL that contains a filter to display only the latest packages + # This URL displays the last two versions of a package + # Minimize the number of HTTP requests to display all the packages due to the number of versions a package might have + $vmPackagesUrl = "https://www.myget.org/F/vm-packages/api/v2/Packages?$filter=IsLatestVersion%20eq%20true" + $vmPackagesFile = "${Env:VM_COMMON_DIR}\vm-packages.xml" + $packagesByCategory=@{} + do { + # Download the XML from MyGet API + Save-FileFromUrl -fileSource $vmPackagesUrl -fileDestination $vmPackagesFile --exitOnError + + # Load the XML content + [xml]$vm_packages = Get-Content $vmPackagesFile + + # Define the namespaces defined in vm-packages.xml to access nodes + # Each package resides in the entry node that is defined in the dataservices namespace + # Each node has properties that are defined in the metadata namespace + $ns = New-Object System.Xml.XmlNamespaceManager($vm_packages.NameTable) + $ns.AddNamespace("atom", "http://www.w3.org/2005/Atom") + $ns.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices") + $ns.AddNamespace("m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata") + + # Extract package information from the XML + $vm_packages.feed.entry | ForEach-Object { + $isLatestVersion = $_.SelectSingleNode("m:properties/d:IsLatestVersion", $ns).InnerText + $category = $_.SelectSingleNode("m:properties/d:Tags", $ns).InnerText + # Select only packages that have the latest version, contain a category and the category is not excluded + if (($isLatestVersion -eq "true") -and ($category -ne "") -and ($excludedCategories -notcontains $category)) { + $packageName = $_.properties.Id + $description = $_.properties.Description + $projectUrl = $_.properties.projectUrl + + # Initialize category as an empty array + if (-not ($packagesByCategory.ContainsKey($category))) { + $packagesByCategory[$category] = @() + } + $packageObject = [PSCustomObject]@{ + PackageName = $packageName + PackageDescription = $description + } + # Check if $projectUrl contains a valid URL + if ($projectUrl -match "^http") { + Add-Member -InputObject $packageObject -MemberType NoteProperty -Name "PackageUrl" -Value $projectURl + } + # Add the PackageName and PackageDescription (and PackageUrl if present) to each entry in the array + $packagesByCategory[$category] += $packageObject } - } - } - # Function that returns an array of packages that belong to a specific category - function Get-PackagesByCategory{ - param ( - [string]$category - ) - return $packagesByCategory[$category] - } + } + # Check if there is a next link in the XML and set the API URL to that link if it exists + $nextLink = $vm_packages.SelectSingleNode("//atom:link[@rel='next']/@href", $ns) + $vmPackagesUrl = $nextLink."#text" - # Function that returns additional packages from the config that are not displayed in the textboxes - # which includes both Choco packages and packages from excluded categories - function Get-AdditionalPackages{ - $additionalPackages=@() + } while ($vmPackagesUrl) - # Packages from the config that are not displayed - $additionalPackages = $packagesToInstall | where-Object { $listedPackages -notcontains $_} - return $additionalPackages - } + return $packagesByCategory +} - # Function that checks all the checkboxes - function Select-AllPackages { - foreach ($checkBox in $checkboxesPackages){ - $checkBox.Checked = $true - } - } +# Function that returns an array of all the packages that are displayed sorted by category from $packagesByCategory +function Get-AllPackages{ + $listedPackages = $packagesByCategory.Values | ForEach-Object { $_ } | Select-Object -ExpandProperty PackageName + return $listedPackages +} - # Function that unchecks all the checkboxes - function Clear-AllPackages { - foreach ($checkBox in $checkboxesPackages){ - $checkBox.Checked = $false - } - $additionalPackagesBox.Items.clear() - } +# Function that returns additional packages from the config that are not displayed in the textboxes +# which includes both Choco packages and packages from excluded categories +function Get-AdditionalPackages{ + $additionalPackages=@() - # Function that adds a new package to the listBox of additional packages - # If the package already exists it returns $false - function Add-NewPackage { - param ( - [Parameter(Mandatory=$true)] - [string]$packageName - ) - #$packageName = $packageName.Trim() - $packageName = $packageName -replace '^\s+|\s+$', '' - if ($packageName -notin $additionalPackagesBox.Items){ - $additionalPackagesBox.Items.Add($packageName) | Out-Null - return $true - } - else{ - return $false - } + # Packages from the config that are not displayed + $additionalPackages = $packagesToInstall | where-Object { $listedPackages -notcontains $_} + return $additionalPackages +} - } +if (-not $noGui.IsPresent) { - function Get-ChocoPackage { - param ( - [Parameter(Mandatory=$true)] - [string]$PackageName - ) - - choco search $PackageName -e -r | ForEach-Object { - $Name, $Version = $_ -split '\|' - New-Object -TypeName psobject -Property @{ - 'Name' = $Name - 'Version' = $Version - } - } - } + if ($script:checksPassed -or $noChecks.IsPresent) { + Write-Host "[+] Beginning graphical install" + + # Gather lists of packages + $envs = [ordered]@{} + $configXml.config.envs.env.ForEach({ $envs[$_.name] = $_.value }) + $excludedCategories=@('Command and Control','Credential Access','Exploitation','Forensic','Lateral Movement', 'Payload Development','Privilege Escalation','Reconnaissance','Wordlists','Web Application') + # Read packages to install from the config + $packagesToInstall = $configXml.config.packages.package.name + $packagesByCategory = Get-Packages-Categories + $listedPackages = Get-AllPackages + $additionalPackages = Get-AdditionalPackages + + $vmCommonDirText.text = $envs['VM_COMMON_DIR'] + $rawToolsDirText.text = $envs['RAW_TOOLS_DIR'] + $toolListDirText.text = $envs['TOOL_LIST_DIR'] + + $Result = $formEnv.ShowDialog() + + if ($Result -eq [System.Windows.Forms.DialogResult]::OK) { + # Remove default environment variables + $nodes = $configXml.SelectNodes('//config/envs/env') + foreach($node in $nodes) { + $node.ParentNode.RemoveChild($node) | Out-Null + } + + # Add environment variables + $envs = $configXml.SelectSingleNode('//envs') + $newXmlNode = $envs.AppendChild($configXml.CreateElement("env")) + $newXmlNode.SetAttribute("name", "VM_COMMON_DIR") + $newXmlNode.SetAttribute("value", $vmCommonDirText.text); + $newXmlNode = $envs.AppendChild($configXml.CreateElement("env")) + $newXmlNode.SetAttribute("name", "TOOL_LIST_DIR") + $newXmlNode.SetAttribute("value", $toolListDirText.text); + $newXmlNode = $envs.AppendChild($configXml.CreateElement("env")) + $newXmlNode.SetAttribute("name", "RAW_TOOLS_DIR") + $newXmlNode.SetAttribute("value", $rawToolsDirText.text) + + [void]$formEnv.Close() + + } else { + Write-Host "[+] Cancel pressed, stopping installation..." + Start-Sleep 3 + exit 1 + } - function Get-VMPackage { - param ( - [Parameter(Mandatory=$true)] - [string]$PackageName - ) - if ($PackageName -notlike "*.vm") { - $PackageName = $PackageName + ".vm" - } - choco search $PackageName --exact -r -s "https://www.myget.org/F/vm-packages/api/v2" | ForEach-Object { - $Name, $Version = $_ -split '\|' - New-Object -TypeName psobject -Property @{ - 'Name' = $Name - 'Version' = $Version - } - } - } + ################################################################################ + ## PACKAGE SELECTION BY CATEGORY + ################################################################################ - function Set-AdditionalPackages { - $additionalPackagesBox.Items.Clear() - foreach($package in $additionalPackages) - { - $additionalPackagesBox.Items.Add($package) | Out-Null - } - } + # Function that adds the selected packages to the config.xml for the installation + function Install-Selected-Packages{ + $selectedPackages = @() + $packages = $configXml.SelectSingleNode('//packages') - function Remove-SelectedPackages { - $additionalPackagesBox.BeginUpdate() - while ($additionalPackagesBox.SelectedItems.count -gt 0) { - $additionalPackagesBox.Items.RemoveAt($additionalPackagesBox.SelectedIndex) - } - $additionalPackagesBox.EndUpdate() - } + # Remove all child nodes inside + while ($packages.HasChildNodes) { + $packages.RemoveChild($packages.FirstChild) + } - Add-Type -AssemblyName System.Windows.Forms - [System.Windows.Forms.Application]::EnableVisualStyles() + foreach ($checkBox in $checkboxesPackages){ + if ($checkBox.Checked){ + $package = $checkbox.Text.split(":")[0] + $selectedPackages += $package + } + } - $formCategories = New-Object system.Windows.Forms.Form - $formCategories.ClientSize = New-Object System.Drawing.Point(1015,850) - $formCategories.text = "FLARE-VM Package selection" - $formCategories.StartPosition = 'CenterScreen' - $formCategories.TopMost = $true + foreach ($package in $additionalPackagesBox.Items){ + $selectedPackages += $package + } + # Add selected packages + foreach($package in $selectedPackages) { + $newXmlNode = $packages.AppendChild($configXml.CreateElement("package")) + $newXmlNode.SetAttribute("name", $package) + } + } - if ([string]::IsNullOrEmpty($customConfig)) { - $textLabel = "The default configuration (recommended) is pre-selected. Click on the reset button to restore the default configuration." - } else { - $textLabel = "The provided custom configuration is pre-selected. Click on the reset button to restore the custom configuration." - } + # Function that resets the checkboxes to match the config.xml + function Set-InitialPackages { + foreach ($checkBox in $checkboxesPackages){ + $package =$checkbox.Text.split(":")[0] + if (($checkbox.Checked) -and ($package -notin $packagesToInstall)){ + $checkBox.Checked = $false + }else{ + if ((-not $checkbox.Checked ) -and ($package -in $packagesToInstall)){ + $checkBox.Checked = $true + } + } + } + } + # Function that returns an array of packages that belong to a specific category + function Get-PackagesByCategory{ + param ( + [string]$category + ) + return $packagesByCategory[$category] + } - $labelCategories = New-Object system.Windows.Forms.Label - $labelCategories.text = "Select packages to install" - $labelCategories.AutoSize = $true - $labelCategories.width = 25 - $labelCategories.height = 10 - $labelCategories.location = New-Object System.Drawing.Point(30,20) - $labelCategories.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) - - - $labelCategories2 = New-Object system.Windows.Forms.Label - $labelCategories2.text = $textLabel - $labelCategories2.AutoSize = $true - $labelCategories2.location = New-Object System.Drawing.Point(30,40) - $labelCategories2.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - - $panelCategories = New-Object system.Windows.Forms.Panel - $panelCategories.height = 530 - $panelCategories.width = 970 - $panelCategories.location = New-Object System.Drawing.Point(30,60) - $panelCategories.AutoScroll = $true - - $resetButton = New-Object system.Windows.Forms.Button - $resetButton.text = "Reset" - $resetButton.AutoSize = $true - $resetButton.location = New-Object System.Drawing.Point(50,800) - $resetButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $resetButton.Add_Click({ - Set-InitialPackages - Set-AdditionalPackages - }) - - $allPackagesButton = New-Object system.Windows.Forms.Button - $allPackagesButton.text = "Select All" - $allPackagesButton.AutoSize = $true - $allPackagesButton.location = New-Object System.Drawing.Point(130,800) - $allPackagesButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $allPackagesButton.Add_Click({ - [System.Windows.Forms.MessageBox]::Show('Selecting all packages considerable increases installation time and it is not desirable for most use cases','Warning') - Select-AllPackages - }) - - $clearPackagesButton = New-Object system.Windows.Forms.Button - $clearPackagesButton.text = "Clear" - $clearPackagesButton.AutoSize = $true - $clearPackagesButton.location = New-Object System.Drawing.Point(210,800) - $clearPackagesButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $clearPackagesButton.Add_Click({Clear-AllPackages}) - - $installButton = New-Object system.Windows.Forms.Button - $installButton.text = "Install" - $installButton.width = 97 - $installButton.height = 37 - $installButton.DialogResult = [System.Windows.Forms.DialogResult]::OK - $installButton.location = New-Object System.Drawing.Point(750,800) - $installButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12) - - $cancelButton = New-Object system.Windows.Forms.Button - $cancelButton.text = "Cancel" - $cancelButton.width = 97 - $cancelButton.height = 37 - $cancelButton.location = New-Object System.Drawing.Point(850,800) - $cancelButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12) - $cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel + # Function that returns additional packages from the config that are not displayed in the textboxes + # which includes both Choco packages and packages from excluded categories + function Get-AdditionalPackages{ + $additionalPackages=@() - $formCategories.AcceptButton = $installButton - $formCategories.CancelButton = $cancelButton - - # Create checkboxes for each package - $checkboxesPackages = New-Object System.Collections.Generic.List[System.Object] - # Initial vertical position for checkboxes - $verticalPosition = 25 - $numCheckBoxPackages = 1 - $packages = @() - foreach ($category in $packagesByCategory.Keys |Sort-Object) { - # Create Labels for categories - $labelCategory = New-Object System.Windows.Forms.Label - $labelCategory.Text = $category - $labelCategory.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',11,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) - $labelCategory.AutoSize = $true - $labelCategory.Location = New-Object System.Drawing.Point(10, $verticalPosition) - $panelCategories.Controls.Add($labelCategory) - - $NumPackages = 0 - $verticalPosition2 = $verticalPosition + 20 - $packages= Get-PackagesByCategory -category $category - foreach ($package in $packages) - { - $NumPackages++ - $checkBox = New-Object System.Windows.Forms.CheckBox - $checkBox.Text = $package.PackageName + ": " + $package.PackageDescription - $checkBox.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $checkBox.AutoSize = $true - $checkBox.Location = New-Object System.Drawing.Point(10, $verticalPosition2) - $checkBox.Name = "checkBox$numCheckBoxPackages" - $checkboxesPackages.Add($checkBox) - $panelCategories.Controls.Add($checkBox) - $url = $package.PackageUrl - if ($url){ - $linkProjectUrl = New-Object System.Windows.Forms.linkLabel - $linkProjectUrl.Top = $checkbox.Top + 2 - $linkProjectUrl.Left = $checkbox.Right - 3 - $linkProjectUrl.AutoSize = $true - $linkProjectUrl.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $linkProjectUrl.LinkColor = "BLUE"; - $linkProjectUrl.ActiveLinkColor = "RED" - $linkProjectUrl.Text = "Link" - $linkProjectUrl.Links.Add(0, 4, $url)| Out-Null - $linkProjectUrl.add_Click({ Start-Process $this.Links.LinkData }) - $panelCategories.Controls.Add($linkProjectUrl) - } - $verticalPosition2 += 20 - $numCheckBoxPackages ++ + # Packages from the config that are not displayed + $additionalPackages = $packagesToInstall | where-Object { $listedPackages -notcontains $_} + return $additionalPackages } - # Increment to space checkboxes vertically - $verticalPosition += 20 * ($NumPackages ) + 30 - $numCategories ++ - } - # Create empty label and add it to the form categories to add some space - $posEnd = $verticalPosition2 +10 - $emptyLabel = New-Object system.Windows.Forms.Label - $emptyLabel.Width = 20 - $emptyLabel.Height = 10 - $emptyLabel.location = New-Object System.Drawing.Point(10,$posEnd) - $panelCategories.Controls.Add($emptyLabel) - - # Select packages that are in the config.xml - Set-InitialPackages - - $additionalPackagesLabel = New-Object system.Windows.Forms.Label - $additionalPackagesLabel.text = "Additional packages to install" - $additionalPackagesLabel.AutoSize = $true - $additionalPackagesLabel.width = 25 - $additionalPackagesLabel.height = 10 - $additionalPackagesLabel.location = New-Object System.Drawing.Point(30,615) - $additionalPackagesLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) - - $additionalPackagesBox = New-Object system.Windows.Forms.ListBox - $additionalPackagesBox.text = "listBox" - $additionalPackagesBox.SelectionMode = 'MultiSimple' - $additionalPackagesBox.Sorted = $true - $additionalPackagesBox.width = 130 - $additionalPackagesBox.height = 140 - $additionalPackagesBox.location = New-Object System.Drawing.Point(50,640) - - $deletePackageButton = New-Object system.Windows.Forms.Button - $deletePackageButton.text = "-" - $deletePackageButton.width = 24 - $deletePackageButton.height = 22 - $deletePackageButton.enabled = $true - $deletePackageButton.location = New-Object System.Drawing.Point(190,670) - $deletePackageButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]::Bold) - $deletePackageButton.Add_Click({Remove-SelectedPackages}) - - $packageLabel = New-Object system.Windows.Forms.Label - $packageLabel.text = "FLARE-VM uses Chocolatey packages. You can add additional packages from:" - $packageLabel.width = 260 - $packageLabel.height = 35 - $packageLabel.AutoSize = $true - $packageLabel.location = New-Object System.Drawing.Point(300,640) - $packageLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - - $labelChoco = New-Object System.Windows.Forms.Label - $labelChoco.Location = New-Object System.Drawing.Point(300,660) - $labelChoco.Size = New-Object System.Drawing.Size(280,20) - $labelChoco.AutoSize = $true - $labelChoco.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $labelChoco.Text = "Community Packages" - - $linkLabelChoco = New-Object System.Windows.Forms.linkLabel - $linkLabelChoco.Location = New-Object System.Drawing.Point(440,660) - $linkLabelChoco.AutoSize = $true - $linkLabelChoco.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $linkLabelChoco.LinkColor = "BLUE" - $linkLabelChoco.ActiveLinkColor = "RED" - $linkLabelChoco.Text = "https://community.chocolatey.org/packages" - $linkLabelChoco.add_Click({Start-Process "https://community.chocolatey.org/packages"}) - - $labelFlarevm = New-Object System.Windows.Forms.Label - $labelFlarevm.Location = New-Object System.Drawing.Point(300,680) - $labelFlarevm.Size = New-Object System.Drawing.Size(280,20) - $labelFlarevm.AutoSize = $true - $labelFlarevm.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $labelFlarevm.Text = "FLARE-VM Packages" - - $linkLabelFlarevm = New-Object System.Windows.Forms.linkLabel - $linkLabelFlarevm.Location = New-Object System.Drawing.Point(440,680) - $linkLabelFlarevm.AutoSize = $true - $linkLabelFlarevm.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $linkLabelFlarevm.LinkColor = "BLUE" - $linkLabelFlarevm.ActiveLinkColor = "RED" - $linkLabelFlarevm.Text = "https://github.com/mandiant/VM-Packages/wiki/Packages" - $linkLabelFlarevm.add_Click({Start-Process "https://github.com/mandiant/VM-Packages/wiki/Packages"}) - - Set-AdditionalPackages - - $chocoPackageLabel = New-Object system.Windows.Forms.Label - $chocoPackageLabel.text = "Enter package name:" - $chocoPackageLabel.AutoSize = $true - $chocoPackageLabel.width = 25 - $chocoPackageLabel.height = 10 - $chocoPackageLabel.location = New-Object System.Drawing.Point(300,715) - $chocoPackageLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - - $packageTextBox = New-Object system.Windows.Forms.TextBox - $packageTextBox.multiline = $false - $packageTextBox.width = 210 - $packageTextBox.height = 20 - $packageTextBox.location = New-Object System.Drawing.Point(300,735) - $packageTextBox.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $packageTextBox.Add_TextChanged({ - if ($addPackageButton.Enabled -eq $true){ - $addPackageButton.Enabled = $false - } - }) - - $chocoPackageErrorLabel = New-Object system.Windows.Forms.Label - $chocoPackageErrorLabel.text = "" - $chocoPackageErrorLabel.AutoSize = $true - $chocoPackageErrorLabel.visible = $false - $chocoPackageErrorLabel.width = 25 - $chocoPackageErrorLabel.height = 10 - $chocoPackageErrorLabel.location = New-Object System.Drawing.Point(300,765) - $chocoPackageErrorLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) - - $findPackageButton = New-Object system.Windows.Forms.Button - $findPackageButton.text = "Find Package" - $findPackageButton.width = 118 - $findPackageButton.height = 30 - $findPackageButton.enabled = $true - $findPackageButton.location = New-Object System.Drawing.Point(520,730) - $findPackageButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $findPackageButton.Add_Click({ - $chocoPackageErrorLabel.Visible = $true - $chocoPackageErrorLabel.text = "Finding package ..." - $vmPackage = Get-VMPackage -PackageName $packageTextBox.Text.Trim() - if ($vmPackage){ - $packageName = $vmPackage | Select-Object -ExpandProperty Name - $chocoPackageErrorLabel.text = "Found VM package" - $chocoPackageErrorLabel.ForeColor = $successColor - $packageTextBox.Text = $packageName - $addPackageButton.enabled = $true - } else { - $chocoPackage = Get-ChocoPackage -PackageName $packageTextBox.Text - if ($chocoPackage) { - $chocoPackageErrorLabel.text = "Found Choco package" - $chocoPackageErrorLabel.ForeColor = $successColor - $addPackageButton.enabled = $true - } else { - $chocoPackageErrorLabel.text = "Package not found" - $chocoPackageErrorLabel.ForeColor = $errorColor - $addPackageButton.enabled = $false - } - } - }) - - $addPackageButton = New-Object system.Windows.Forms.Button - $addPackageButton.text = "Add Package" - $addPackageButton.width = 118 - $addPackageButton.height = 30 - $addPackageButton.enabled = $false - $addPackageButton.location = New-Object System.Drawing.Point(650,730) - $addPackageButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) - $addPackageButton.Add_Click({ - if (Add-NewPackage -PackageName $packageTextBox.Text){ - $chocoPackageErrorLabel.ForeColor = $successColor - $chocoPackageErrorLabel.text = "Package added" - }else { - $chocoPackageErrorLabel.ForeColor = $errorColor - $chocoPackageErrorLabel.text = "Error to add the package: duplicated" - } - $addPackageButton.enabled = $false - }) - - $formCategories.controls.AddRange(@($additionalPackagesLabel,$packageLabel,$labelChoco,$labelFlarevm,$linkLabelChoco,$linkLabelFlarevm,$linkLabelFlarevm,$additionalPackagesBox,$deletePackageButton,$chocoPackageButton,$chocoPackageLabel,$packageTextBox,$chocoPackageErrorLabel,$findPackageButton,$addPackageButton)) - $formCategories.controls.AddRange(@($labelCategories,$labelCategories2,$panelCategories,$installButton,$resetButton,$allPackagesButton,$cancelButton,$clearPackagesButton)) - $formCategories.Add_Shown({$formCategories.Activate()}) - $resultCategories = $formCategories.ShowDialog() - if ($resultCategories -eq [System.Windows.Forms.DialogResult]::OK){ - Install-Selected-Packages - } else { - Write-Host "[+] Cancel pressed, stopping installation..." - Start-Sleep 3 - exit 1 - } + # Function that checks all the checkboxes + function Select-AllPackages { + foreach ($checkBox in $checkboxesPackages){ + $checkBox.Checked = $true + } + } - ################################################################################ - ## END GUI - ################################################################################ + # Function that unchecks all the checkboxes + function Clear-AllPackages { + foreach ($checkBox in $checkboxesPackages){ + $checkBox.Checked = $false + } + $additionalPackagesBox.Items.clear() + } + + # Function that adds a new package to the listBox of additional packages + # If the package already exists it returns $false + function Add-NewPackage { + param ( + [Parameter(Mandatory=$true)] + [string]$packageName + ) + #$packageName = $packageName.Trim() + $packageName = $packageName -replace '^\s+|\s+$', '' + if ($packageName -notin $additionalPackagesBox.Items){ + $additionalPackagesBox.Items.Add($packageName) | Out-Null + return $true + } + else{ + return $false + } + + } + + function Get-ChocoPackage { + param ( + [Parameter(Mandatory=$true)] + [string]$PackageName + ) + + choco search $PackageName -e -r | ForEach-Object { + $Name, $Version = $_ -split '\|' + New-Object -TypeName psobject -Property @{ + 'Name' = $Name + 'Version' = $Version + } + } + } + + function Get-VMPackage { + param ( + [Parameter(Mandatory=$true)] + [string]$PackageName + ) + if ($PackageName -notlike "*.vm") { + $PackageName = $PackageName + ".vm" + } + choco search $PackageName --exact -r -s "https://www.myget.org/F/vm-packages/api/v2" | ForEach-Object { + $Name, $Version = $_ -split '\|' + New-Object -TypeName psobject -Property @{ + 'Name' = $Name + 'Version' = $Version + } + } + } + + function Set-AdditionalPackages { + $additionalPackagesBox.Items.Clear() + foreach($package in $additionalPackages) + { + $additionalPackagesBox.Items.Add($package) | Out-Null + } + } + + function Remove-SelectedPackages { + $additionalPackagesBox.BeginUpdate() + while ($additionalPackagesBox.SelectedItems.count -gt 0) { + $additionalPackagesBox.Items.RemoveAt($additionalPackagesBox.SelectedIndex) + } + $additionalPackagesBox.EndUpdate() + } + + Add-Type -AssemblyName System.Windows.Forms + [System.Windows.Forms.Application]::EnableVisualStyles() + + $formCategories = New-Object system.Windows.Forms.Form + $formCategories.ClientSize = New-Object System.Drawing.Point(1015,850) + $formCategories.text = "FLARE-VM Package selection" + $formCategories.StartPosition = 'CenterScreen' + $formCategories.TopMost = $true + + if ([string]::IsNullOrEmpty($customConfig)) { + $textLabel = "The default configuration (recommended) is pre-selected. Click on the reset button to restore the default configuration." + } else { + $textLabel = "The provided custom configuration is pre-selected. Click on the reset button to restore the custom configuration." + } + + $labelCategories = New-Object system.Windows.Forms.Label + $labelCategories.text = "Select packages to install" + $labelCategories.AutoSize = $true + $labelCategories.width = 25 + $labelCategories.height = 10 + $labelCategories.location = New-Object System.Drawing.Point(30,20) + $labelCategories.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + + $labelCategories2 = New-Object system.Windows.Forms.Label + $labelCategories2.text = $textLabel + $labelCategories2.AutoSize = $true + $labelCategories2.location = New-Object System.Drawing.Point(30,40) + $labelCategories2.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + + $panelCategories = New-Object system.Windows.Forms.Panel + $panelCategories.height = 530 + $panelCategories.width = 970 + $panelCategories.location = New-Object System.Drawing.Point(30,60) + $panelCategories.AutoScroll = $true + + $resetButton = New-Object system.Windows.Forms.Button + $resetButton.text = "Reset" + $resetButton.AutoSize = $true + $resetButton.location = New-Object System.Drawing.Point(50,800) + $resetButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $resetButton.Add_Click({ + Set-InitialPackages + Set-AdditionalPackages + }) + + $allPackagesButton = New-Object system.Windows.Forms.Button + $allPackagesButton.text = "Select All" + $allPackagesButton.AutoSize = $true + $allPackagesButton.location = New-Object System.Drawing.Point(130,800) + $allPackagesButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $allPackagesButton.Add_Click({ + [System.Windows.Forms.MessageBox]::Show('Selecting all packages considerable increases installation time and it is not desirable for most use cases','Warning') + Select-AllPackages + }) + + $clearPackagesButton = New-Object system.Windows.Forms.Button + $clearPackagesButton.text = "Clear" + $clearPackagesButton.AutoSize = $true + $clearPackagesButton.location = New-Object System.Drawing.Point(210,800) + $clearPackagesButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $clearPackagesButton.Add_Click({Clear-AllPackages}) + + $installButton = New-Object system.Windows.Forms.Button + $installButton.text = "Install" + $installButton.width = 97 + $installButton.height = 37 + $installButton.DialogResult = [System.Windows.Forms.DialogResult]::OK + $installButton.location = New-Object System.Drawing.Point(750,800) + $installButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12) + + $cancelButton = New-Object system.Windows.Forms.Button + $cancelButton.text = "Cancel" + $cancelButton.width = 97 + $cancelButton.height = 37 + $cancelButton.location = New-Object System.Drawing.Point(850,800) + $cancelButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12) + $cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel + + $formCategories.AcceptButton = $installButton + $formCategories.CancelButton = $cancelButton + + # Create checkboxes for each package + $checkboxesPackages = New-Object System.Collections.Generic.List[System.Object] + # Initial vertical position for checkboxes + $verticalPosition = 25 + $numCheckBoxPackages = 1 + $packages = @() + foreach ($category in $packagesByCategory.Keys |Sort-Object) { + # Create Labels for categories + $labelCategory = New-Object System.Windows.Forms.Label + $labelCategory.Text = $category + $labelCategory.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',11,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + $labelCategory.AutoSize = $true + $labelCategory.Location = New-Object System.Drawing.Point(10, $verticalPosition) + $panelCategories.Controls.Add($labelCategory) + + $NumPackages = 0 + $verticalPosition2 = $verticalPosition + 20 + $packages= Get-PackagesByCategory -category $category + foreach ($package in $packages) + { + $NumPackages++ + $checkBox = New-Object System.Windows.Forms.CheckBox + $checkBox.Text = $package.PackageName + ": " + $package.PackageDescription + $checkBox.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $checkBox.AutoSize = $true + $checkBox.Location = New-Object System.Drawing.Point(10, $verticalPosition2) + $checkBox.Name = "checkBox$numCheckBoxPackages" + $checkboxesPackages.Add($checkBox) + $panelCategories.Controls.Add($checkBox) + $url = $package.PackageUrl + if ($url){ + $linkProjectUrl = New-Object System.Windows.Forms.linkLabel + $linkProjectUrl.Top = $checkbox.Top + 2 + $linkProjectUrl.Left = $checkbox.Right - 3 + $linkProjectUrl.AutoSize = $true + $linkProjectUrl.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $linkProjectUrl.LinkColor = "BLUE"; + $linkProjectUrl.ActiveLinkColor = "RED" + $linkProjectUrl.Text = "Link" + $linkProjectUrl.Links.Add(0, 4, $url)| Out-Null + $linkProjectUrl.add_Click({ Start-Process $this.Links.LinkData }) + $panelCategories.Controls.Add($linkProjectUrl) + } + $verticalPosition2 += 20 + $numCheckBoxPackages ++ + } + # Increment to space checkboxes vertically + $verticalPosition += 20 * ($NumPackages ) + 30 + $numCategories ++ + } + + # Create empty label and add it to the form categories to add some space + $posEnd = $verticalPosition2 +10 + $emptyLabel = New-Object system.Windows.Forms.Label + $emptyLabel.Width = 20 + $emptyLabel.Height = 10 + $emptyLabel.location = New-Object System.Drawing.Point(10,$posEnd) + $panelCategories.Controls.Add($emptyLabel) + + # Select packages that are in the config.xml + Set-InitialPackages + + $additionalPackagesLabel = New-Object system.Windows.Forms.Label + $additionalPackagesLabel.text = "Additional packages to install" + $additionalPackagesLabel.AutoSize = $true + $additionalPackagesLabel.width = 25 + $additionalPackagesLabel.height = 10 + $additionalPackagesLabel.location = New-Object System.Drawing.Point(30,615) + $additionalPackagesLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $additionalPackagesBox = New-Object system.Windows.Forms.ListBox + $additionalPackagesBox.text = "listBox" + $additionalPackagesBox.SelectionMode = 'MultiSimple' + $additionalPackagesBox.Sorted = $true + $additionalPackagesBox.width = 130 + $additionalPackagesBox.height = 140 + $additionalPackagesBox.location = New-Object System.Drawing.Point(50,640) + + $deletePackageButton = New-Object system.Windows.Forms.Button + $deletePackageButton.text = "-" + $deletePackageButton.width = 24 + $deletePackageButton.height = 22 + $deletePackageButton.enabled = $true + $deletePackageButton.location = New-Object System.Drawing.Point(190,670) + $deletePackageButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',12,[System.Drawing.FontStyle]::Bold) + $deletePackageButton.Add_Click({Remove-SelectedPackages}) + + $packageLabel = New-Object system.Windows.Forms.Label + $packageLabel.text = "FLARE-VM uses Chocolatey packages. You can add additional packages from:" + $packageLabel.width = 260 + $packageLabel.height = 35 + $packageLabel.AutoSize = $true + $packageLabel.location = New-Object System.Drawing.Point(300,640) + $packageLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + + $labelChoco = New-Object System.Windows.Forms.Label + $labelChoco.Location = New-Object System.Drawing.Point(300,660) + $labelChoco.Size = New-Object System.Drawing.Size(280,20) + $labelChoco.AutoSize = $true + $labelChoco.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $labelChoco.Text = "Community Packages" + + $linkLabelChoco = New-Object System.Windows.Forms.linkLabel + $linkLabelChoco.Location = New-Object System.Drawing.Point(440,660) + $linkLabelChoco.AutoSize = $true + $linkLabelChoco.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $linkLabelChoco.LinkColor = "BLUE" + $linkLabelChoco.ActiveLinkColor = "RED" + $linkLabelChoco.Text = "https://community.chocolatey.org/packages" + $linkLabelChoco.add_Click({Start-Process "https://community.chocolatey.org/packages"}) + + $labelFlarevm = New-Object System.Windows.Forms.Label + $labelFlarevm.Location = New-Object System.Drawing.Point(300,680) + $labelFlarevm.Size = New-Object System.Drawing.Size(280,20) + $labelFlarevm.AutoSize = $true + $labelFlarevm.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $labelFlarevm.Text = "FLARE-VM Packages" + + $linkLabelFlarevm = New-Object System.Windows.Forms.linkLabel + $linkLabelFlarevm.Location = New-Object System.Drawing.Point(440,680) + $linkLabelFlarevm.AutoSize = $true + $linkLabelFlarevm.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $linkLabelFlarevm.LinkColor = "BLUE" + $linkLabelFlarevm.ActiveLinkColor = "RED" + $linkLabelFlarevm.Text = "https://github.com/mandiant/VM-Packages/wiki/Packages" + $linkLabelFlarevm.add_Click({Start-Process "https://github.com/mandiant/VM-Packages/wiki/Packages"}) + + Set-AdditionalPackages + + $chocoPackageLabel = New-Object system.Windows.Forms.Label + $chocoPackageLabel.text = "Enter package name:" + $chocoPackageLabel.AutoSize = $true + $chocoPackageLabel.width = 25 + $chocoPackageLabel.height = 10 + $chocoPackageLabel.location = New-Object System.Drawing.Point(300,715) + $chocoPackageLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + + $packageTextBox = New-Object system.Windows.Forms.TextBox + $packageTextBox.multiline = $false + $packageTextBox.width = 210 + $packageTextBox.height = 20 + $packageTextBox.location = New-Object System.Drawing.Point(300,735) + $packageTextBox.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $packageTextBox.Add_TextChanged({ + if ($addPackageButton.Enabled -eq $true){ + $addPackageButton.Enabled = $false + } + }) + + $chocoPackageErrorLabel = New-Object system.Windows.Forms.Label + $chocoPackageErrorLabel.text = "" + $chocoPackageErrorLabel.AutoSize = $true + $chocoPackageErrorLabel.visible = $false + $chocoPackageErrorLabel.width = 25 + $chocoPackageErrorLabel.height = 10 + $chocoPackageErrorLabel.location = New-Object System.Drawing.Point(300,765) + $chocoPackageErrorLabel.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10,[System.Drawing.FontStyle]([System.Drawing.FontStyle]::Bold)) + + $findPackageButton = New-Object system.Windows.Forms.Button + $findPackageButton.text = "Find Package" + $findPackageButton.width = 118 + $findPackageButton.height = 30 + $findPackageButton.enabled = $true + $findPackageButton.location = New-Object System.Drawing.Point(520,730) + $findPackageButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $findPackageButton.Add_Click({ + $chocoPackageErrorLabel.Visible = $true + $chocoPackageErrorLabel.text = "Finding package ..." + $vmPackage = Get-VMPackage -PackageName $packageTextBox.Text.Trim() + if ($vmPackage){ + $packageName = $vmPackage | Select-Object -ExpandProperty Name + $chocoPackageErrorLabel.text = "Found VM package" + $chocoPackageErrorLabel.ForeColor = $successColor + $packageTextBox.Text = $packageName + $addPackageButton.enabled = $true + } else { + $chocoPackage = Get-ChocoPackage -PackageName $packageTextBox.Text + if ($chocoPackage) { + $chocoPackageErrorLabel.text = "Found Choco package" + $chocoPackageErrorLabel.ForeColor = $successColor + $addPackageButton.enabled = $true + } else { + $chocoPackageErrorLabel.text = "Package not found" + $chocoPackageErrorLabel.ForeColor = $errorColor + $addPackageButton.enabled = $false + } + } + }) + + $addPackageButton = New-Object system.Windows.Forms.Button + $addPackageButton.text = "Add Package" + $addPackageButton.width = 118 + $addPackageButton.height = 30 + $addPackageButton.enabled = $false + $addPackageButton.location = New-Object System.Drawing.Point(650,730) + $addPackageButton.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) + $addPackageButton.Add_Click({ + if (Add-NewPackage -PackageName $packageTextBox.Text){ + $chocoPackageErrorLabel.ForeColor = $successColor + $chocoPackageErrorLabel.text = "Package added" + }else { + $chocoPackageErrorLabel.ForeColor = $errorColor + $chocoPackageErrorLabel.text = "Error to add the package: duplicated" + } + $addPackageButton.enabled = $false + }) + + $formCategories.controls.AddRange(@($additionalPackagesLabel,$packageLabel,$labelChoco,$labelFlarevm,$linkLabelChoco,$linkLabelFlarevm,$linkLabelFlarevm,$additionalPackagesBox,$deletePackageButton,$chocoPackageButton,$chocoPackageLabel,$packageTextBox,$chocoPackageErrorLabel,$findPackageButton,$addPackageButton)) + $formCategories.controls.AddRange(@($labelCategories,$labelCategories2,$panelCategories,$installButton,$resetButton,$allPackagesButton,$cancelButton,$clearPackagesButton)) + $formCategories.Add_Shown({$formCategories.Activate()}) + $resultCategories = $formCategories.ShowDialog() + if ($resultCategories -eq [System.Windows.Forms.DialogResult]::OK){ + Install-Selected-Packages + } else { + Write-Host "[+] Cancel pressed, stopping installation..." + Start-Sleep 3 + exit 1 + } + } + ################################################################################ + ## END GUI + ################################################################################ } # Save the config file