tools_WD_excl.ps1 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. ################################################################################
  2. #
  3. # Microsoft WindowsDefender exclusions handler
  4. # Espressif Systems, 2019
  5. #
  6. ################################################################################
  7. #
  8. # PS utility to add/remove PROCESS exceptions to/from MS WD real-time
  9. # scanning. Files (referenced by 'path' or 'path\filemask') are expected
  10. # to be Windows process executables, for obvious reasons.
  11. #
  12. # The script requires Administrator privileges to succeed -> self-elevation procedure is involved
  13. #
  14. # Usage:
  15. #
  16. # PowerShell -ExecutionPolicy ByPass -File tools_WD_excl.ps1 <ARGUMENTS>
  17. #
  18. # ARGUMENTS:
  19. # -AddExclPath <path | path\*.filemask>
  20. # add all matching files in the path (recursive) to the WD exception list
  21. #
  22. # -AddExclFile <filepath>
  23. # adds file to the WD exception list exactly as specified by 'filepath'
  24. #
  25. # -RmExclPath <path | path\*.filemask>
  26. # remove all matching files in the path (recursive) from WD exclusions
  27. #
  28. # -RmExclFile <filepath>
  29. # adds file to the WD exception list exactly as specified by 'filepath'
  30. #
  31. # -logFile <filepath>
  32. # stdout/stderr redirection file. Used internally for elevated process (generated in tempdir, deleted after the script finishing)
  33. # use manually at your own risk
  34. #
  35. # Returns 0 on success or -1 on failure
  36. #
  37. #
  38. # Example:
  39. # PowerShell -ExecutionPolicy ByPass -File tools_WD_excl.ps1 -AddExclPath "C:\Program Files\Espressif\ESP-IDF Tools\*.exe"
  40. #
  41. # Notes:
  42. # - default scenario is set to the following
  43. # -AddExclPath "$Env:ProgramFiles\Espressif\ESP-IDF Tools\*.exe"
  44. # (eg when called with no params)
  45. # - only named parameters are supported, any other use-cases redirect to the default
  46. # - multiple paths/files in 1 parameter are not supported by this version
  47. # - minimum requirements: Windows XP SP3, PowerShell 2.0, Windows Defender with relevant PS cmdlets
  48. #
  49. ################################################################################
  50. Param
  51. (
  52. [String]$AddExclPath,
  53. [String]$AddExclFile,
  54. [String]$RmExclPath,
  55. [String]$RmExclFile,
  56. [String]$logFile
  57. )
  58. function Check-Command($cmdname)
  59. {
  60. return [bool](Get-Command -Name $cmdname -ErrorAction SilentlyContinue)
  61. }
  62. function Log-Msg($msg, $logF = $null)
  63. {
  64. if( ![string]::IsNullOrEmpty($logF) ) { Write-Output $msg *>> $logF }
  65. else { Write-Output $msg }
  66. [Console]::Out.Flush()
  67. }
  68. $retVal = 1
  69. Try
  70. {
  71. $bDebug = $false
  72. #parameter sanity check
  73. if( $Args.Count -gt 0 ) {
  74. if( $Args.Count -eq 1 -And $Args[0] -eq "Debug" ) {
  75. $bDebug = $true
  76. }
  77. else {
  78. $Exception = [ArgumentException]::new("Invalid parameters: $Args")
  79. throw $Exception
  80. }
  81. }
  82. Import-Module Defender
  83. #self-elevation support
  84. $myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
  85. $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
  86. $adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
  87. if( -not $myWindowsPrincipal.IsInRole($adminRole) ) {
  88. $params = ""
  89. foreach($key in $PSBoundParameters.keys) {
  90. $params = -join( $params, "-", $key, " `"", $PSBoundParameters[$key], "`"" )
  91. }
  92. $arguments = ""
  93. foreach($a in $Args) {
  94. $arguments = -join( $arguments, "-", $a )
  95. }
  96. #running elevated and logFile not set
  97. $bOwnLogFile = [string]::IsNullOrEmpty($logFile)
  98. if( $bOwnLogFile ) {
  99. $tempFileName = Get-Date -UFormat "%Y%m%d%H%M%s"
  100. $lf = Join-Path -Path $env:TEMP -ChildPath "WDEspLog$tempFileName.log"
  101. }
  102. else { $lf = $logFile }
  103. $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"
  104. $newProcess.Arguments = "-ExecutionPolicy ByPass -File " + $script:MyInvocation.MyCommand.Definition + " " + $params + " -logFile $lf " + $arguments
  105. $newProcess.Verb = "RunAs"
  106. #show the process window for -Debug
  107. if( !$bDebug ) { $newProcess.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden }
  108. $proc = [System.Diagnostics.Process]::Start($newProcess)
  109. $proc.WaitForExit()
  110. if (Test-Path -Path $lf ) {
  111. foreach($line in Get-Content $lf) {
  112. Log-Msg -msg $line
  113. }
  114. }
  115. if( $bDebug ) { Log-Msg -msg "Process finished with code " + $proc.ExitCode -logF $lf }
  116. if( $bOwnLogFile -And !$bDebug) { Remove-Item $lf }
  117. [Environment]::Exit($proc.ExitCode)
  118. }
  119. $pathsToExclude = New-Object 'System.Collections.Generic.List[String]'
  120. $filesToExclude = New-Object 'System.Collections.Generic.List[String]'
  121. $pathsToInclude = New-Object 'System.Collections.Generic.List[String]'
  122. $filesToRemove = New-Object 'System.Collections.Generic.List[String]'
  123. if( $PSBoundParameters.Count -gt 0 ) {
  124. $bAddPath = ![string]::IsNullOrEmpty($AddExclPath)
  125. $bAddFile = ![string]::IsNullOrEmpty($AddExclFile)
  126. $bRmPath = ![string]::IsNullOrEmpty($RmExclPath)
  127. $bRmFile = ![string]::IsNullOrEmpty($RmExclFile)
  128. if( !$bAddPath -And !$bAddFile -And !$bRmPath -And !$bRmFile ) {
  129. throw (New-Object -TypeName System.ArgumentException -ArgumentList "Invalid parameter(s)")
  130. }
  131. #ADD exclusion paths
  132. if( $bAddPath ) {
  133. $pathsToExclude.Add( $AddExclPath )
  134. }
  135. #ADD exclusion files
  136. if( $bAddFile ) {
  137. $filesToExclude.Add( $AddExclFile )
  138. }
  139. #REMOVE exclusion paths
  140. if( $bRmPath ) {
  141. $pathsToInclude.Add( $RmExclPath )
  142. }
  143. #ADD exclusion file
  144. if( $bAddFile ) {
  145. $filesToRemove.Add( $RmExclFile )
  146. }
  147. }
  148. else {
  149. throw (New-Object -TypeName System.ArgumentException -ArgumentList "Mandatory parameter(s) missing")
  150. }
  151. #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
  152. # https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-antivirus/configure-process-opened-file-exclusions-windows-defender-antivirus :
  153. # "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.
  154. #The exclusions only apply to always-on real-time protection and monitoring. They don't apply to scheduled or on-demand scans."
  155. Log-Msg -msg "Updating Windows Defender real-time scan exclusions:" -logF $logFile
  156. $itemCount = 0
  157. #exclusions
  158. foreach( $exclPath in $pathsToExclude ) {
  159. $exclFiles = Get-ChildItem -Recurse -File -Path $exclPath | % { $_.FullName }
  160. foreach ($exfile in $exclFiles) {
  161. Log-Msg -msg " adding $exfile" -logF $logFile
  162. Add-MpPreference -ExclusionProcess $exfile
  163. Add-MpPreference -ExclusionPath $exfile
  164. $itemCount++
  165. }
  166. }
  167. ### ! better run in separate, adding files to exclusion object array from above is very inefficient (forced reallocations)
  168. foreach ($exfile1 in $filesToExclude) {
  169. Log-Msg -msg " adding $exfile1" -logF $logFile
  170. Add-MpPreference -ExclusionProcess $exfile1
  171. Add-MpPreference -ExclusionPath $exfile1
  172. $itemCount++
  173. }
  174. #inclusions
  175. foreach( $inclPath in $pathsToInclude ) {
  176. $inclFiles = Get-ChildItem -Recurse -File -Path $inclPath | % { $_.FullName }
  177. foreach ($infile in $inclFiles) {
  178. Log-Msg -msg " removing $infile" -logF $logFile
  179. Remove-MpPreference -ExclusionProcess $infile
  180. Remove-MpPreference -ExclusionPath $infile
  181. $itemCount++
  182. }
  183. }
  184. ### ! see exclusions
  185. foreach ($infile1 in $filesToExclude) {
  186. Log-Msg -msg " removing $infile1" -logF $logFile
  187. Remove-MpPreference -ExclusionProcess $infile1
  188. Remove-MpPreference -ExclusionPath $infile1
  189. $itemCount++
  190. }
  191. Log-Msg -msg "Done (processed $itemCount items)" -logF $logFile
  192. $retVal = 0
  193. [Environment]::Exit($retVal)
  194. }
  195. Catch
  196. {
  197. if( ![string]::IsNullOrEmpty($logFile) ) { Write-Error -Exception $_.Exception *>> $logFile }
  198. Write-Error -Exception $_.Exception -ErrorAction Stop
  199. [Environment]::Exit($retVal)
  200. }
  201. Finally
  202. {
  203. [Environment]::Exit($retVal)
  204. }