Merge branch 'bugfix/IDFGH-2036' into 'master'

Fix for Windows Defender PS task hanging (IDF tools installation)

Closes IDFGH-2036

See merge request espressif/esp-idf!6796
This commit is contained in:
Ivan Grokhotkov
2019-12-04 03:00:23 +08:00
5 changed files with 389 additions and 304 deletions

View File

@@ -229,14 +229,14 @@ end;
{ ------------------------------ Start menu shortcut ------------------------------ } { ------------------------------ Start menu shortcut ------------------------------ }
procedure CreateIDFCommandPromptShortcut(); procedure CreateIDFCommandPromptShortcut(LnkString: String);
var var
Destination: String; Destination: String;
Description: String; Description: String;
Command: String; Command: String;
begin begin
ForceDirectories(ExpandConstant('{group}')); ForceDirectories(ExpandConstant(LnkString));
Destination := ExpandConstant('{group}\{#IDFCmdExeShortcutFile}'); Destination := ExpandConstant(LnkString + '\{#IDFCmdExeShortcutFile}');
Description := '{#IDFCmdExeShortcutDescription}'; Description := '{#IDFCmdExeShortcutDescription}';
{ If cmd.exe command argument starts with a quote, the first and last quote chars in the command { If cmd.exe command argument starts with a quote, the first and last quote chars in the command
will be removed by cmd.exe; each argument needs to be surrounded by quotes as well. } will be removed by cmd.exe; each argument needs to be surrounded by quotes as well. }
@@ -267,3 +267,42 @@ begin
Log('Registering IDF Tools executables in Windows Defender: ' + CmdLine); Log('Registering IDF Tools executables in Windows Defender: ' + CmdLine);
DoCmdlineInstall('Finishing ESP-IDF installation', 'Registering IDF Tools executables in Windows Defender', CmdLine); DoCmdlineInstall('Finishing ESP-IDF installation', 'Registering IDF Tools executables in Windows Defender', CmdLine);
end; end;
<event('CurPageChanged')>
procedure CheckWinDefenderAvailable(CurPageID: Integer);
var
bHasWD: Boolean;
szHasWD: String;
szWDPath: String;
listPSModulePath: TStringList;
x: Integer;
begin
if CurPageID = wpSelectTasks then
begin
listPSModulePath := TStringList.Create;
listPSModulePath.Delimiter := ';';
listPSModulePath.StrictDelimiter := True;
listPSModulePath.DelimitedText := GetEnv('PsModulePath');
Log('Checking PSMODULEPATH for Windows Defender module...');
for x:=0 to (listPSModulePath.Count-1) do
begin
szWDPath := listPSModulePath[x] + '\Defender'
bHasWD := DirExists(szWDPath);
if bHasWD then
begin
szHasWD := 'YES (' + szWDPath + ')';
Break;
end
else
szHasWD := 'NO';
end;
Log('CheckWinDefenderAvailable: ' + szHasWD);
WizardForm.TasksList.ItemEnabled[1] := bHasWD;
WizardForm.TasksList.Checked[1] := bHasWD;
end;
end;

View File

@@ -73,16 +73,18 @@ Type: filesandordirs; Name: "{app}\dist"
Type: filesandordirs; Name: "{app}\releases" Type: filesandordirs; Name: "{app}\releases"
Type: filesandordirs; Name: "{app}\tools" Type: filesandordirs; Name: "{app}\tools"
Type: filesandordirs; Name: "{app}\python_env" Type: filesandordirs; Name: "{app}\python_env"
Type: files; Name: "{autostartmenu}\Programs\ESP-IDF\{#IDFCmdExeShortcutFile}"
Type: files; Name: "{autodesktop}\{#IDFCmdExeShortcutFile}"
[Tasks] [Tasks]
Name: createlnk; Description: "Create Desktop shortcut for ESP-IDF Tools Command Line"; Name: createlnk; Description: "Create Start Menu shortcut for the ESP-IDF Tools Command Prompt";
Name: wdexcl; Description: "Register ESP-IDF Tools executables as Windows Defender exclusions (improves compilation speed, requires elevation)"; Name: createdsk; Description: "Create Desktop shortcut for the ESP-IDF Tools Command Prompt";
Name: wdexcl; Description: "Register the ESP-IDF Tools executables as Windows Defender exclusions (improves compilation speed, requires elevation)";
[Run] [Run]
Filename: "{app}\dist\{#PythonInstallerName}"; Parameters: "/passive PrependPath=1 InstallLauncherAllUsers=0 Include_dev=0 Include_tcltk=0 Include_launcher=0 Include_test=0 Include_doc=0"; Description: "Installing Python"; Check: PythonInstallRequired Filename: "{app}\dist\{#PythonInstallerName}"; Parameters: "/passive PrependPath=1 InstallLauncherAllUsers=0 Include_dev=0 Include_tcltk=0 Include_launcher=0 Include_test=0 Include_doc=0"; Description: "Installing Python"; Check: PythonInstallRequired
Filename: "{app}\dist\{#GitInstallerName}"; Parameters: "/silent /tasks="""" /norestart"; Description: "Installing Git"; Check: GitInstallRequired Filename: "{app}\dist\{#GitInstallerName}"; Parameters: "/silent /tasks="""" /norestart"; Description: "Installing Git"; Check: GitInstallRequired
Filename: "{group}\{#IDFCmdExeShortcutFile}"; Flags: postinstall shellexec; Description: "Run ESP-IDF Command Prompt (cmd.exe)"; Check: InstallationSuccessful Filename: "{autostartmenu}\Programs\ESP-IDF\{#IDFCmdExeShortcutFile}"; Flags: postinstall shellexec; Description: "Run ESP-IDF Command Prompt (cmd.exe)"; Check: InstallationSuccessful
[UninstallRun] [UninstallRun]
Filename: "powershell.exe"; \ Filename: "powershell.exe"; \

View File

@@ -122,7 +122,12 @@ begin
if WizardIsTaskSelected('createlnk') then if WizardIsTaskSelected('createlnk') then
begin begin
CreateIDFCommandPromptShortcut(); CreateIDFCommandPromptShortcut('{autostartmenu}\Programs\ESP-IDF');
end;
if WizardIsTaskSelected('createdsk') then
begin
CreateIDFCommandPromptShortcut('{autodesktop}');
end; end;
if WizardIsTaskSelected('wdexcl') then if WizardIsTaskSelected('wdexcl') then

View File

@@ -15,152 +15,165 @@
Param Param
( (
[String]$RmExclPath, [String]$RmExclPath,
[String]$logFile [String]$logFile
) )
Import-Module Defender
function Check-Command($cmdname) function Check-Command($cmdname)
{ {
return [bool](Get-Command -Name $cmdname -ErrorAction SilentlyContinue) return [bool](Get-Command -Name $cmdname -ErrorAction SilentlyContinue)
} }
function Log-Msg($msg, $logF = $null) function Log-Msg($msg, $logF = $null)
{ {
if( ![string]::IsNullOrEmpty($logF) ) { Write-Output $msg *>> $logF } if( ![string]::IsNullOrEmpty($logF) ) { Write-Output $msg *>> $logF }
else { Write-Output $msg } else { Write-Output $msg }
[Console]::Out.Flush() [Console]::Out.Flush()
} }
$retVal = 1
Try Try
{ {
#self-elevation support #check the Defender module availability
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent() if (!(Get-Module "Defender")) {
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID) Write-Output "Windows Defender module not available, aborting"
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator [Environment]::Exit(0)
}
if( -not $myWindowsPrincipal.IsInRole($adminRole) ) { Import-Module Defender
$params = "" #self-elevation support
foreach($key in $PSBoundParameters.keys) { $myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$params = -join( $params, "-", $key, " `"", $PSBoundParameters[$key], "`"" ) $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
} $adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
#running elevated and logFile not set if( -not $myWindowsPrincipal.IsInRole($adminRole) ) {
if( [string]::IsNullOrEmpty($logFile) ) {
$tempFileName = Get-Date -UFormat "%Y%m%d%H%M%s"
$lf = Join-Path -Path $env:TEMP -ChildPath "WDEspLog$tempFileName.log"
#Write-Output "Logfile: $lf"
}
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell" $params = ""
$newProcess.Arguments = "-ExecutionPolicy ByPass -File " + $script:MyInvocation.MyCommand.Definition + " " + $params + " -logFile $lf" foreach($key in $PSBoundParameters.keys) {
$newProcess.Verb = "RunAs" $params = -join( $params, "-", $key, " `"", $PSBoundParameters[$key], "`"" )
$newProcess.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden }
$proc = [System.Diagnostics.Process]::Start($newProcess) #running elevated and logFile not set
$proc.WaitForExit() if( [string]::IsNullOrEmpty($logFile) ) {
$tempFileName = Get-Date -UFormat "%Y%m%d%H%M%s"
$lf = Join-Path -Path $env:TEMP -ChildPath "WDEspLog$tempFileName.log"
#Write-Output "Logfile: $lf"
}
if (Test-Path -Path $lf ) { $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"
foreach($line in Get-Content $lf) { $newProcess.Arguments = "-ExecutionPolicy ByPass -File " + $script:MyInvocation.MyCommand.Definition + " " + $params + " -logFile $lf"
Log-Msg -msg $line $newProcess.Verb = "RunAs"
} $newProcess.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
Remove-Item $lf
}
#Write-Output "Process finished with code " $proc.ExitCode $proc = [System.Diagnostics.Process]::Start($newProcess)
exit $proc.ExitCode $proc.WaitForExit()
}
Log-Msg -msg "Getting Windows Defender process exclusions..." -logF $logFile if (Test-Path -Path $lf ) {
foreach($line in Get-Content $lf) {
Log-Msg -msg $line
}
Remove-Item $lf
}
$Preferences = Get-MpPreference #Write-Output "Process finished with code " $proc.ExitCode
exit $proc.ExitCode
}
#ExclusionProcess Log-Msg -msg "Getting Windows Defender process exclusions..." -logF $logFile
$cnt = $Preferences.ExclusionProcess.Count
$cntRemoved = 0
$cntRemovedTotal = 0
$cntMissed = 0
$cntMissedTotal = 0
$bRmPath = ![string]::IsNullOrEmpty($RmExclPath) $Preferences = Get-MpPreference
if( $bRmPath ) { Log-Msg -msg "Exclusion path: $RmExclPath" -logF $logFile }
Log-Msg -msg " Found total $cnt of ExclusionProcess items" -logF $logFile #ExclusionProcess
$cnt = $Preferences.ExclusionProcess.Count
$cntRemoved = 0
$cntRemovedTotal = 0
$cntMissed = 0
$cntMissedTotal = 0
foreach( $pref in $Preferences.ExclusionProcess ) { $bRmPath = ![string]::IsNullOrEmpty($RmExclPath)
if( $bRmPath ) { Log-Msg -msg "Exclusion path: $RmExclPath" -logF $logFile }
if( $bRmPath ) { $bGoAhead = $pref.Contains($RmExclPath) } Log-Msg -msg " Found total $cnt of ExclusionProcess items" -logF $logFile
else { $bGoAhead = $true }
if( $bGoAhead ) { foreach( $pref in $Preferences.ExclusionProcess ) {
Log-Msg -msg " removing $pref" -logF $logFile
Try
{
Remove-MpPreference -ExclusionProcess $pref
$cntRemoved++
}
Catch
{
if( ![string]::IsNullOrEmpty($logFile) ) { Write-Error -Exception $_.Exception *>> $logFile }
Write-Error -Exception $_.Exception
$cntMissed++
}
}
}
if( $cntMissed -eq 0 ) { Log-Msg -msg " $cntRemoved relevant items removed from ExclusionProcess list" -logF $logFile } if( $bRmPath ) { $bGoAhead = $pref.Contains($RmExclPath) }
else { Log-Msg -msg " WARNING: Only $cntRemoved out of $(cntRemoved+cntMissed) relevant items removed from ExclusionProcess list" -logF $logFile } else { $bGoAhead = $true }
#ExclusionPath if( $bGoAhead ) {
$cnt = $Preferences.ExclusionPath.Count Log-Msg -msg " removing $pref" -logF $logFile
$cntRemovedTotal = $cntRemoved Try
$cntRemoved = 0 {
$cntMissedTotal = $cntMissed Remove-MpPreference -ExclusionProcess $pref
$cntMissed = 0 $cntRemoved++
}
Catch
{
if( ![string]::IsNullOrEmpty($logFile) ) { Write-Error -Exception $_.Exception *>> $logFile }
Write-Error -Exception $_.Exception
$cntMissed++
}
}
}
Log-Msg -msg " Found total $cnt of ExclusionPath items" -logF $logFile if( $cntMissed -eq 0 ) { Log-Msg -msg " $cntRemoved relevant items removed from ExclusionProcess list" -logF $logFile }
else { Log-Msg -msg " WARNING: Only $cntRemoved out of $(cntRemoved+cntMissed) relevant items removed from ExclusionProcess list" -logF $logFile }
foreach( $pref in $Preferences.ExclusionPath ) { #ExclusionPath
$cnt = $Preferences.ExclusionPath.Count
$cntRemovedTotal = $cntRemoved
$cntRemoved = 0
$cntMissedTotal = $cntMissed
$cntMissed = 0
if( $bRmPath ) { $bGoAhead = $pref.Contains($RmExclPath) } Log-Msg -msg " Found total $cnt of ExclusionPath items" -logF $logFile
else { $bGoAhead = $true }
if( $bGoAhead ) { foreach( $pref in $Preferences.ExclusionPath ) {
Log-Msg -msg " removing $pref" -logF $logFile
Try
{
Remove-MpPreference -ExclusionPath $pref
$cntRemoved++
}
Catch
{
if( ![string]::IsNullOrEmpty($logFile) ) { Write-Error -Exception $_.Exception *>> $logFile }
Write-Error -Exception $_.Exception
$cntMissed++
}
}
}
if( $cntMissed -eq 0 ) { Log-Msg -msg " $cntRemoved relevant items removed from ExclusionPath list" -logF $logFile } if( $bRmPath ) { $bGoAhead = $pref.Contains($RmExclPath) }
else { Log-Msg -msg " WARNING: Only $cntRemoved out of $(cntRemoved+cntMissed) relevant items removed from ExclusionPath list" -logF $logFile } else { $bGoAhead = $true }
#TOTAL if( $bGoAhead ) {
$cntRemovedTotal += $cntRemoved Log-Msg -msg " removing $pref" -logF $logFile
$cntMissedTotal += $cntMissed Try
{
Remove-MpPreference -ExclusionPath $pref
$cntRemoved++
}
Catch
{
if( ![string]::IsNullOrEmpty($logFile) ) { Write-Error -Exception $_.Exception *>> $logFile }
Write-Error -Exception $_.Exception
$cntMissed++
}
}
}
Log-Msg -msg "============================" -logF $logFile if( $cntMissed -eq 0 ) { Log-Msg -msg " $cntRemoved relevant items removed from ExclusionPath list" -logF $logFile }
if( $cntMissedTotal -eq 0 ) { Log-Msg -msg "OK: Processed all $cntRemovedTotal items" -logF $logFile } else { Log-Msg -msg " WARNING: Only $cntRemoved out of $(cntRemoved+cntMissed) relevant items removed from ExclusionPath list" -logF $logFile }
else { Log-Msg -msg "WARNING: Processed only $cntRemovedTotal out of $(cntRemovedTotal+cntMissedTotal) relevat items" -logF $logFile }
Log-Msg -msg "`nDone" -logF $logFile #TOTAL
$cntRemovedTotal += $cntRemoved
$cntMissedTotal += $cntMissed
Log-Msg -msg "============================" -logF $logFile
if( $cntMissedTotal -eq 0 ) { Log-Msg -msg "OK: Processed all $cntRemovedTotal items" -logF $logFile }
else { Log-Msg -msg "WARNING: Processed only $cntRemovedTotal out of $(cntRemovedTotal+cntMissedTotal) relevat items" -logF $logFile }
Log-Msg -msg "`nDone" -logF $logFile
$retVal = 0
} }
Catch Catch
{ {
if( ![string]::IsNullOrEmpty($logFile) ) { Write-Error -Exception $_.Exception *>> $logFile } if( ![string]::IsNullOrEmpty($logFile) ) { Write-Error -Exception $_.Exception *>> $logFile }
Write-Error -Exception $_.Exception Write-Error -Exception $_.Exception
exit -1 [Environment]::Exit($retVal)
}
Finally
{
[Environment]::Exit($retVal)
} }

View File

@@ -5,234 +5,260 @@
# #
################################################################################ ################################################################################
# #
# PS utility to add/remove PROCESS exceptions to/from MS WD real-time # PS utility to add/remove PROCESS exceptions to/from MS WD real-time
# scanning. Files (referenced by 'path' or 'path\filemask') are expected # scanning. Files (referenced by 'path' or 'path\filemask') are expected
# to be Windows process executables, for obvious reasons. # to be Windows process executables, for obvious reasons.
# #
# The script requires Administrator privileges to succeed -> self-elevation procedure is involved # The script requires Administrator privileges to succeed -> self-elevation procedure is involved
# #
# Usage: # Usage:
# #
# PowerShell -ExecutionPolicy ByPass -File tools_WD_excl.ps1 <ARGUMENTS> # PowerShell -ExecutionPolicy ByPass -File tools_WD_excl.ps1 <ARGUMENTS>
# #
# ARGUMENTS: # ARGUMENTS:
# -AddExclPath <path | path\*.filemask> # -AddExclPath <path | path\*.filemask>
# add all matching files in the path (recursive) to the WD exception list # add all matching files in the path (recursive) to the WD exception list
# #
# -AddExclFile <filepath> # -AddExclFile <filepath>
# adds file to the WD exception list exactly as specified by 'filepath' # adds file to the WD exception list exactly as specified by 'filepath'
# #
# -RmExclPath <path | path\*.filemask> # -RmExclPath <path | path\*.filemask>
# remove all matching files in the path (recursive) from WD exclusions # remove all matching files in the path (recursive) from WD exclusions
# #
# -RmExclFile <filepath> # -RmExclFile <filepath>
# adds file to the WD exception list exactly as specified by 'filepath' # adds file to the WD exception list exactly as specified by 'filepath'
# #
# -logFile <filepath> # -logFile <filepath>
# stdout/stderr redirection file. Used internally for elevated process (generated in tempdir, deleted after the script finishing) # stdout/stderr redirection file. Used internally for elevated process (generated in tempdir, deleted after the script finishing)
# use manually at your own risk # use manually at your own risk
# #
# Returns 0 on success or -1 on failure # Returns 0 on success or -1 on failure
# #
# #
# Example: # Example:
# PowerShell -ExecutionPolicy ByPass -File tools_WD_excl.ps1 -AddExclPath "C:\Program Files\Espressif\ESP-IDF Tools\*.exe" # PowerShell -ExecutionPolicy ByPass -File tools_WD_excl.ps1 -AddExclPath "C:\Program Files\Espressif\ESP-IDF Tools\*.exe"
# #
# Notes: # Notes:
# - default scenario is set to the following # - default scenario is set to the following
# -AddExclPath "$Env:ProgramFiles\Espressif\ESP-IDF Tools\*.exe" # -AddExclPath "$Env:ProgramFiles\Espressif\ESP-IDF Tools\*.exe"
# (eg when called with no params) # (eg when called with no params)
# - only named parameters are supported, any other use-cases redirect to the default # - only named parameters are supported, any other use-cases redirect to the default
# - multiple paths/files in 1 parameter are not supported by this version # - multiple paths/files in 1 parameter are not supported by this version
# - minimum requirements: Windows XP SP3, PowerShell 2.0, Windows Defender with relevant PS cmdlets # - minimum requirements: Windows XP SP3, PowerShell 2.0, Windows Defender with relevant PS cmdlets
# #
################################################################################ ################################################################################
Param Param
( (
[String]$AddExclPath, [String]$AddExclPath,
[String]$AddExclFile, [String]$AddExclFile,
[String]$RmExclPath, [String]$RmExclPath,
[String]$RmExclFile, [String]$RmExclFile,
[String]$logFile [String]$logFile
) )
Import-Module Defender
function Check-Command($cmdname) function Check-Command($cmdname)
{ {
return [bool](Get-Command -Name $cmdname -ErrorAction SilentlyContinue) return [bool](Get-Command -Name $cmdname -ErrorAction SilentlyContinue)
} }
function Log-Msg($msg, $logF = $null) function Log-Msg($msg, $logF = $null)
{ {
if( ![string]::IsNullOrEmpty($logF) ) { Write-Output $msg *>> $logF } if( ![string]::IsNullOrEmpty($logF) ) { Write-Output $msg *>> $logF }
else { Write-Output $msg } else { Write-Output $msg }
[Console]::Out.Flush() [Console]::Out.Flush()
} }
$retVal = 1
Try Try
{ {
#self-elevation support $bDebug = $false
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
if( -not $myWindowsPrincipal.IsInRole($adminRole) ) { #parameter sanity check
if( $Args.Count -gt 0 ) {
if( $Args.Count -eq 1 -And $Args[0] -eq "Debug" ) {
$bDebug = $true
}
else {
$Exception = [ArgumentException]::new("Invalid parameters: $Args")
throw $Exception
}
}
$params = "" #check the Defender module availability
foreach($key in $PSBoundParameters.keys) { $wdModuleDir = Join-Path -Path $env:SystemRoot -ChildPath "System32\WindowsPowerShell\v1.0\Modules\Defender"
$params = -join( $params, "-", $key, " `"", $PSBoundParameters[$key], "`"" ) if( -not (Test-Path -Path $wdModuleDir) ) {
} Write-Output "Windows Defender module not found, aborting"
[Environment]::Exit($retVal)
}
#running elevated and logFile not set Import-Module Defender
if( [string]::IsNullOrEmpty($logFile) ) {
$tempFileName = Get-Date -UFormat "%Y%m%d%H%M%s"
$lf = Join-Path -Path $env:TEMP -ChildPath "WDEspLog$tempFileName.log"
#Write-Output "Logfile: $lf"
}
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell" #self-elevation support
$newProcess.Arguments = "-ExecutionPolicy ByPass -File " + $script:MyInvocation.MyCommand.Definition + " " + $params + " -logFile $lf" $myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$newProcess.Verb = "RunAs" $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
$newProcess.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden $adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
$proc = [System.Diagnostics.Process]::Start($newProcess) if( -not $myWindowsPrincipal.IsInRole($adminRole) ) {
$proc.WaitForExit()
if (Test-Path -Path $lf ) { $params = ""
foreach($line in Get-Content $lf) { foreach($key in $PSBoundParameters.keys) {
Log-Msg -msg $line $params = -join( $params, "-", $key, " `"", $PSBoundParameters[$key], "`"" )
} }
Remove-Item $lf
}
#Write-Output "Process finished with code " $proc.ExitCode $arguments = ""
exit $proc.ExitCode foreach($a in $Args) {
} $arguments = -join( $arguments, "-", $a )
}
#parameter sanity check #running elevated and logFile not set
if( $Args.Count -gt 0 ) { $bOwnLogFile = [string]::IsNullOrEmpty($logFile)
$Exception = [ArgumentException]::new("Only named parameters are supported: $Args") if( $bOwnLogFile ) {
throw $Exception $tempFileName = Get-Date -UFormat "%Y%m%d%H%M%s"
} $lf = Join-Path -Path $env:TEMP -ChildPath "WDEspLog$tempFileName.log"
Write-Output "Logfile: $lf"
}
else { $lf = $logFile }
#check WinDefender cmdlets are available $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"
if (!(Check-Command -cmdname 'Add-MpPreference')) { $newProcess.Arguments = "-ExecutionPolicy ByPass -File " + $script:MyInvocation.MyCommand.Definition + " " + $params + " -logFile $lf " + $arguments
$Exception = [NotSupportedException ]::new("Windows Defender cmdlets not available") $newProcess.Verb = "RunAs"
throw $Exception
}
$pathsToExclude = New-Object 'System.Collections.Generic.List[String]' #show the process window for -Debug
$filesToExclude = New-Object 'System.Collections.Generic.List[String]' if( !$bDebug ) { $newProcess.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden }
$pathsToInclude = New-Object 'System.Collections.Generic.List[String]'
$filesToRemove = New-Object 'System.Collections.Generic.List[String]'
if( $PSBoundParameters.Count -gt 0 ) { $proc = [System.Diagnostics.Process]::Start($newProcess)
$proc.WaitForExit()
$bAddPath = ![string]::IsNullOrEmpty($AddExclPath) if (Test-Path -Path $lf ) {
$bAddFile = ![string]::IsNullOrEmpty($AddExclFile) foreach($line in Get-Content $lf) {
$bRmPath = ![string]::IsNullOrEmpty($RmExclPath) Log-Msg -msg $line
$bRmFile = ![string]::IsNullOrEmpty($RmExclFile) }
}
if( !$bAddPath -And !$bAddFile -And !$bRmPath -And !$bRmFile ) { if( $bDebug ) { Log-Msg -msg "Process finished with code " + $proc.ExitCode -logF $lf }
$Exception = [ArgumentException]::new("Invalid parameter(s): $Args") if( $bOwnLogFile -And !$bDebug) { Remove-Item $lf }
throw $Exception
}
#ADD exclusion paths [Environment]::Exit($proc.ExitCode)
if( $bAddPath ) { }
#foreach ($wdPath in $AddExclPath) {
# $pathsToExclude.Add( $wdPath )
#}
$pathsToExclude.Add( $AddExclPath )
}
#ADD exclusion files
if( $bAddFile ) {
#foreach ($wdFile in $AddExclFile) {
# $filesToExclude.Add( $wdFile )
#}
$filesToExclude.Add( $AddExclFile )
}
#REMOVE exclusion paths
if( $bRmPath ) {
#foreach ($wdPath in $RmExclPath) {
# $pathsToInclude.Add( $wdPath )
#}
$pathsToInclude.Add( $RmExclPath )
}
#ADD exclusion file
if( $bAddFile ) {
#foreach ($wdFile in $RmExclFile) {
# $filesToRemove.Add( $wdFile )
#}
$filesToRemove.Add( $RmExclFile )
}
}
#default: throw exception
else {
$Exception = [ArgumentException]::new("Mandatory parameter missing")
throw $Exception
}
#to exclude all files opened by a process including the process' binary, a record must be added to both Exclusions/Paths and Exclusions/Processes configurations, see $pathsToExclude = New-Object 'System.Collections.Generic.List[String]'
# https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/configure-process-opened-file-exclusions-windows-defender-antivirus : $filesToExclude = New-Object 'System.Collections.Generic.List[String]'
# "When you add a process to the process exclusion list, Windows Defender Antivirus won't scan files opened by that process, no matter where the files are located. The process itself, however, will be scanned unless it has also been added to the file exclusion list. $pathsToInclude = New-Object 'System.Collections.Generic.List[String]'
#The exclusions only apply to always-on real-time protection and monitoring. They don't apply to scheduled or on-demand scans." $filesToRemove = New-Object 'System.Collections.Generic.List[String]'
Log-Msg -msg "Updating Windows Defender real-time scan exclusions:" -logF $logFile if( $PSBoundParameters.Count -gt 0 ) {
$itemCount = 0 $bAddPath = ![string]::IsNullOrEmpty($AddExclPath)
$bAddFile = ![string]::IsNullOrEmpty($AddExclFile)
$bRmPath = ![string]::IsNullOrEmpty($RmExclPath)
$bRmFile = ![string]::IsNullOrEmpty($RmExclFile)
#exclusions if( !$bAddPath -And !$bAddFile -And !$bRmPath -And !$bRmFile ) {
foreach( $exclPath in $pathsToExclude ) { throw (New-Object -TypeName System.ArgumentException -ArgumentList "Invalid parameter(s)")
$exclFiles = Get-ChildItem -Recurse -File -Path $exclPath | % { $_.FullName } }
foreach ($exfile in $exclFiles) {
Log-Msg -msg " adding $exfile" -logF $logFile
Add-MpPreference -ExclusionProcess $exfile
Add-MpPreference -ExclusionPath $exfile
$itemCount++
}
}
### ! better run in separate, adding files to exclusion object array from above is very inefficient (forced reallocations) #ADD exclusion paths
foreach ($exfile1 in $filesToExclude) { if( $bAddPath ) {
Log-Msg -msg " adding $exfile1" -logF $logFile #foreach ($wdPath in $AddExclPath) {
Add-MpPreference -ExclusionProcess $exfile1 # $pathsToExclude.Add( $wdPath )
Add-MpPreference -ExclusionPath $exfile1 #}
$itemCount++ $pathsToExclude.Add( $AddExclPath )
} }
#inclusions #ADD exclusion files
foreach( $inclPath in $pathsToInclude ) { if( $bAddFile ) {
$inclFiles = Get-ChildItem -Recurse -File -Path $inclPath | % { $_.FullName } #foreach ($wdFile in $AddExclFile) {
foreach ($infile in $inclFiles) { # $filesToExclude.Add( $wdFile )
Log-Msg -msg " removing $infile" -logF $logFile #}
Remove-MpPreference -ExclusionProcess $infile $filesToExclude.Add( $AddExclFile )
Remove-MpPreference -ExclusionPath $infile }
$itemCount++
}
}
### ! see exclusions #REMOVE exclusion paths
foreach ($infile1 in $filesToExclude) { if( $bRmPath ) {
Log-Msg -msg " removing $infile1" -logF $logFile #foreach ($wdPath in $RmExclPath) {
Remove-MpPreference -ExclusionProcess $infile1 # $pathsToInclude.Add( $wdPath )
Remove-MpPreference -ExclusionPath $infile1 #}
$itemCount++ $pathsToInclude.Add( $RmExclPath )
} }
Log-Msg -msg "Done (processed $itemCount items)" -logF $logFile #ADD exclusion file
if( $bAddFile ) {
#foreach ($wdFile in $RmExclFile) {
# $filesToRemove.Add( $wdFile )
#}
$filesToRemove.Add( $RmExclFile )
}
}
#default: throw exception
else {
throw (New-Object -TypeName System.ArgumentException -ArgumentList "Mandatory parameter(s) missing")
}
#to exclude all files opened by a process including the process' binary, a record must be added to both Exclusions/Paths and Exclusions/Processes configurations, see
# https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/configure-process-opened-file-exclusions-windows-defender-antivirus :
# "When you add a process to the process exclusion list, Windows Defender Antivirus won't scan files opened by that process, no matter where the files are located. The process itself, however, will be scanned unless it has also been added to the file exclusion list.
#The exclusions only apply to always-on real-time protection and monitoring. They don't apply to scheduled or on-demand scans."
Log-Msg -msg "Updating Windows Defender real-time scan exclusions:" -logF $logFile
$itemCount = 0
#exclusions
foreach( $exclPath in $pathsToExclude ) {
$exclFiles = Get-ChildItem -Recurse -File -Path $exclPath | % { $_.FullName }
foreach ($exfile in $exclFiles) {
Log-Msg -msg " adding $exfile" -logF $logFile
Add-MpPreference -ExclusionProcess $exfile
Add-MpPreference -ExclusionPath $exfile
$itemCount++
}
}
### ! better run in separate, adding files to exclusion object array from above is very inefficient (forced reallocations)
foreach ($exfile1 in $filesToExclude) {
Log-Msg -msg " adding $exfile1" -logF $logFile
Add-MpPreference -ExclusionProcess $exfile1
Add-MpPreference -ExclusionPath $exfile1
$itemCount++
}
#inclusions
foreach( $inclPath in $pathsToInclude ) {
$inclFiles = Get-ChildItem -Recurse -File -Path $inclPath | % { $_.FullName }
foreach ($infile in $inclFiles) {
Log-Msg -msg " removing $infile" -logF $logFile
Remove-MpPreference -ExclusionProcess $infile
Remove-MpPreference -ExclusionPath $infile
$itemCount++
}
}
### ! see exclusions
foreach ($infile1 in $filesToExclude) {
Log-Msg -msg " removing $infile1" -logF $logFile
Remove-MpPreference -ExclusionProcess $infile1
Remove-MpPreference -ExclusionPath $infile1
$itemCount++
}
Log-Msg -msg "Done (processed $itemCount items)" -logF $logFile
$retVal = 0
[Environment]::Exit($retVal)
} }
Catch Catch
{ {
if( ![string]::IsNullOrEmpty($logFile) ) { Write-Error -Exception $_.Exception *>> $logFile } if( ![string]::IsNullOrEmpty($logFile) ) { Write-Error -Exception $_.Exception *>> $logFile }
Write-Error -Exception $_.Exception Write-Error -Exception $_.Exception -ErrorAction Stop
exit -1 [Environment]::Exit($retVal)
}
Finally
{
[Environment]::Exit($retVal)
} }