2019-08-20 02:37:45 -04:00
################################################################################
#
# Microsoft WindowsDefender exclusions handler
# Espressif Systems, 2019
#
################################################################################
#
2020-11-10 02:40:01 -05:00
# PS utility to add/remove PROCESS exceptions to/from MS WD real-time
2019-11-22 05:55:42 -05:00
# scanning. Files (referenced by 'path' or 'path\filemask') are expected
# to be Windows process executables, for obvious reasons.
#
# The script requires Administrator privileges to succeed -> self-elevation procedure is involved
2019-08-20 02:37:45 -04:00
#
# Usage:
#
2019-11-22 05:55:42 -05:00
# PowerShell -ExecutionPolicy ByPass -File tools_WD_excl.ps1 <ARGUMENTS>
#
# ARGUMENTS:
# -AddExclPath <path | path\*.filemask>
# add all matching files in the path (recursive) to the WD exception list
2019-08-20 02:37:45 -04:00
#
2019-11-22 05:55:42 -05:00
# -AddExclFile <filepath>
# adds file to the WD exception list exactly as specified by 'filepath'
2019-08-20 02:37:45 -04:00
#
2019-11-22 05:55:42 -05:00
# -RmExclPath <path | path\*.filemask>
# remove all matching files in the path (recursive) from WD exclusions
2019-08-20 02:37:45 -04:00
#
2019-11-22 05:55:42 -05:00
# -RmExclFile <filepath>
# adds file to the WD exception list exactly as specified by 'filepath'
2019-08-20 02:37:45 -04:00
#
2019-11-22 05:55:42 -05:00
# -logFile <filepath>
2020-11-10 02:40:01 -05:00
# stdout/stderr redirection file. Used internally for elevated process (generated in tempdir, deleted after the script finishing)
2019-11-22 05:55:42 -05:00
# use manually at your own risk
2019-08-20 02:37:45 -04:00
#
2019-11-22 05:55:42 -05:00
# Returns 0 on success or -1 on failure
2019-08-20 02:37:45 -04:00
#
#
2019-11-22 05:55:42 -05:00
# Example:
# PowerShell -ExecutionPolicy ByPass -File tools_WD_excl.ps1 -AddExclPath "C:\Program Files\Espressif\ESP-IDF Tools\*.exe"
2019-08-20 02:37:45 -04:00
#
2019-11-22 05:55:42 -05:00
# Notes:
2020-11-10 02:40:01 -05:00
# - default scenario is set to the following
2019-11-22 05:55:42 -05:00
# -AddExclPath "$Env:ProgramFiles\Espressif\ESP-IDF Tools\*.exe"
# (eg when called with no params)
# - 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
# - minimum requirements: Windows XP SP3, PowerShell 2.0, Windows Defender with relevant PS cmdlets
2019-08-20 02:37:45 -04:00
#
################################################################################
Param
(
2019-11-22 05:55:42 -05:00
[ String ] $AddExclPath ,
[ String ] $AddExclFile ,
[ String ] $RmExclPath ,
[ String ] $RmExclFile ,
[ String ] $logFile
2019-08-20 02:37:45 -04:00
)
function Check-Command($cmdname )
{
2019-11-22 05:55:42 -05:00
return [ bool ] ( Get-Command -Name $cmdname -ErrorAction SilentlyContinue )
2019-08-20 02:37:45 -04:00
}
function Log-Msg($msg , $logF = $null )
{
2020-11-10 02:40:01 -05:00
if ( ! [ string ] :: IsNullOrEmpty ( $logF ) ) { Write-Output $msg * > > $logF }
2019-11-22 05:55:42 -05:00
else { Write-Output $msg }
[ Console ] :: Out . Flush ( )
2019-08-20 02:37:45 -04:00
}
2019-11-22 05:55:42 -05:00
$retVal = 1
2019-08-20 02:37:45 -04:00
Try
2020-11-10 02:40:01 -05:00
{
2019-11-22 05:55:42 -05:00
$bDebug = $false
2020-11-10 02:40:01 -05:00
2019-11-22 05:55:42 -05:00
#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
}
}
Import-Module Defender
#self-elevation support
$myWindowsID = [ System.Security.Principal.WindowsIdentity ] :: GetCurrent ( )
$myWindowsPrincipal = new-object System . Security . Principal . WindowsPrincipal ( $myWindowsID )
$adminRole = [ System.Security.Principal.WindowsBuiltInRole ] :: Administrator
2020-11-10 02:40:01 -05:00
if ( -not $myWindowsPrincipal . IsInRole ( $adminRole ) ) {
2019-11-22 05:55:42 -05:00
$params = " "
foreach ( $key in $PSBoundParameters . keys ) {
$params = -join ( $params , " - " , $key , " `" " , $PSBoundParameters [ $key ] , " `" " )
}
2020-11-10 02:40:01 -05:00
2019-11-22 05:55:42 -05:00
$arguments = " "
foreach ( $a in $Args ) {
$arguments = -join ( $arguments , " - " , $a )
}
#running elevated and logFile not set
$bOwnLogFile = [ string ] :: IsNullOrEmpty ( $logFile )
if ( $bOwnLogFile ) {
$tempFileName = Get-Date -UFormat " %Y%m%d%H%M%s "
$lf = Join-Path -Path $env:TEMP -ChildPath " WDEspLog $tempFileName .log "
}
else { $lf = $logFile }
$newProcess = new-object System . Diagnostics . ProcessStartInfo " PowerShell "
$newProcess . Arguments = " -ExecutionPolicy ByPass -File " + $script:MyInvocation . MyCommand . Definition + " " + $params + " -logFile $lf " + $arguments
$newProcess . Verb = " RunAs "
#show the process window for -Debug
if ( ! $bDebug ) { $newProcess . WindowStyle = [ System.Diagnostics.ProcessWindowStyle ] :: Hidden }
2020-11-10 02:40:01 -05:00
2019-11-22 05:55:42 -05:00
$proc = [ System.Diagnostics.Process ] :: Start ( $newProcess )
$proc . WaitForExit ( )
if ( Test-Path -Path $lf ) {
foreach ( $line in Get-Content $lf ) {
Log-Msg -msg $line
}
}
2020-11-10 02:40:01 -05:00
2019-11-22 05:55:42 -05:00
if ( $bDebug ) { Log-Msg -msg " Process finished with code " + $proc . ExitCode -logF $lf }
if ( $bOwnLogFile -And ! $bDebug ) { Remove-Item $lf }
2020-11-10 02:40:01 -05:00
2019-11-22 05:55:42 -05:00
[ Environment ] :: Exit ( $proc . ExitCode )
}
$pathsToExclude = New-Object 'System.Collections.Generic.List[String]'
$filesToExclude = New-Object 'System.Collections.Generic.List[String]'
$pathsToInclude = New-Object 'System.Collections.Generic.List[String]'
$filesToRemove = New-Object 'System.Collections.Generic.List[String]'
if ( $PSBoundParameters . Count -gt 0 ) {
$bAddPath = ! [ string ] :: IsNullOrEmpty ( $AddExclPath )
$bAddFile = ! [ string ] :: IsNullOrEmpty ( $AddExclFile )
$bRmPath = ! [ string ] :: IsNullOrEmpty ( $RmExclPath )
$bRmFile = ! [ string ] :: IsNullOrEmpty ( $RmExclFile )
2020-11-10 02:40:01 -05:00
2019-11-22 05:55:42 -05:00
if ( ! $bAddPath -And ! $bAddFile -And ! $bRmPath -And ! $bRmFile ) {
throw ( New-Object -TypeName System . ArgumentException -ArgumentList " Invalid parameter(s) " )
}
#ADD exclusion paths
if ( $bAddPath ) {
$pathsToExclude . Add ( $AddExclPath )
}
#ADD exclusion files
if ( $bAddFile ) {
$filesToExclude . Add ( $AddExclFile )
}
#REMOVE exclusion paths
if ( $bRmPath ) {
$pathsToInclude . Add ( $RmExclPath )
}
#ADD exclusion file
if ( $bAddFile ) {
$filesToRemove . Add ( $RmExclFile )
}
}
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 )
2019-08-20 02:37:45 -04:00
}
Catch
{
2019-11-22 05:55:42 -05:00
if ( ! [ string ] :: IsNullOrEmpty ( $logFile ) ) { Write-Error -Exception $_ . Exception * > > $logFile }
Write-Error -Exception $_ . Exception -ErrorAction Stop
[ Environment ] :: Exit ( $retVal )
}
Finally
{
[ Environment ] :: Exit ( $retVal )
2019-08-20 02:37:45 -04:00
}