From 84571c241b7b69fca3a96bdccba350a5b916e1ac Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 11 Jun 2019 19:20:32 +0200 Subject: [PATCH] Check for Windows Defender process exclusions GitOrigin-RevId: 07f9294290b57ee9157177c9d61511eeb7c59db2 --- .../diagnostic/WindowsDefenderChecker.java | 55 ++++++++++++------- .../src/com/intellij/util/Restarter.java | 25 ++++++--- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/diagnostic/WindowsDefenderChecker.java b/platform/platform-impl/src/com/intellij/diagnostic/WindowsDefenderChecker.java index 9e6651e42818..5bb0922f9d89 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/WindowsDefenderChecker.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/WindowsDefenderChecker.java @@ -10,6 +10,8 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Restarter; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,7 +21,6 @@ import java.nio.file.Paths; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; public class WindowsDefenderChecker { private static final Logger LOG = Logger.getInstance(WindowsDefenderChecker.class); @@ -41,6 +42,7 @@ public class WindowsDefenderChecker { public static class CheckResult { public final RealtimeScanningStatus status; + // Value in the map is true if the path is excluded, false otherwise public final Map pathStatus; @@ -53,6 +55,14 @@ public class WindowsDefenderChecker { public CheckResult checkWindowsDefender(@NotNull Project project) { RealtimeScanningStatus scanningStatus = getRealtimeScanningEnabled(); if (scanningStatus == RealtimeScanningStatus.SCANNING_ENABLED) { + final Collection processes = getExcludedProcesses(); + final String binaryName = Restarter.getCurrentProcessExecutableName(); + if (binaryName != null && processes != null && + processes.contains(StringUtil.substringAfterLast(binaryName.toLowerCase(), "\\")) && + processes.contains("java.exe")) { + return new CheckResult(RealtimeScanningStatus.SCANNING_DISABLED, Collections.emptyMap()); + } + List excludedPatterns = getExcludedPatterns(); if (excludedPatterns != null) { Map pathStatuses = checkPathsExcluded(getImportantPaths(project), excludedPatterns); @@ -63,6 +73,7 @@ public class WindowsDefenderChecker { } /** Runs a powershell command to list the paths that are excluded from realtime scanning by Windows Defender. These + * * paths can contain environment variable references, as well as wildcards ('?', which matches a single character, and * '*', which matches any sequence of characters (but cannot match multiple nested directories; i.e., "foo\*\bar" would * match foo\baz\bar but not foo\baz\quux\bar)). The behavior of wildcards with respect to case-sensitivity is undocumented. @@ -70,39 +81,43 @@ public class WindowsDefenderChecker { */ @Nullable private static List getExcludedPatterns() { - try { - ProcessOutput output = ExecUtil.execAndGetOutput(new GeneralCommandLine( - "powershell", "-inputformat", "none", "-outputformat", "text", "-NonInteractive", "-Command", "Get-MpPreference | select -ExpandProperty \"ExclusionPath\""), POWERSHELL_COMMAND_TIMEOUT_MS); - if (output.getExitCode() == 0) { - return output.getStdoutLines(true).stream().map(path -> wildcardsToRegex(expandEnvVars(path))).collect(Collectors.toList()); - } else { - LOG.warn("Windows Defender exclusion path check exited with status " + output.getExitCode() + ": " + - StringUtil.first(output.getStderr(), MAX_POWERSHELL_STDERR_LENGTH, false)); - } - } catch (ExecutionException e) { - LOG.warn("Windows Defender exclusion path check failed", e); - } - return null; + final Collection paths = getWindowsDefenderProperty("ExclusionPath"); + if (paths == null) return null; + return ContainerUtil.map(paths, path -> wildcardsToRegex(expandEnvVars(path))); } + @Nullable + private static Collection getExcludedProcesses() { + final Collection processes = getWindowsDefenderProperty("ExclusionProcess"); + if (processes == null) return null; + return ContainerUtil.map(processes, process -> process.toLowerCase()); + } /** Runs a powershell command to determine whether realtime scanning is enabled or not. */ @NotNull private static RealtimeScanningStatus getRealtimeScanningEnabled() { + final Collection output = getWindowsDefenderProperty("DisableRealtimeMonitoring"); + if (output == null) return RealtimeScanningStatus.ERROR; + if (output.size() > 0 && output.iterator().next().startsWith("False")) return RealtimeScanningStatus.SCANNING_ENABLED; + return RealtimeScanningStatus.SCANNING_DISABLED; + } + + @Nullable + private static Collection getWindowsDefenderProperty(final String propertyName) { try { ProcessOutput output = ExecUtil.execAndGetOutput(new GeneralCommandLine( - "powershell", "-inputformat", "none", "-outputformat", "text", "-NonInteractive", "-Command", "Get-MpPreference | select -ExpandProperty \"DisableRealtimeMonitoring\""), POWERSHELL_COMMAND_TIMEOUT_MS); + "powershell", "-inputformat", "none", "-outputformat", "text", "-NonInteractive", "-Command", + "Get-MpPreference | select -ExpandProperty \"" + propertyName + "\""), POWERSHELL_COMMAND_TIMEOUT_MS); if (output.getExitCode() == 0) { - if (output.getStdout().startsWith("False")) return RealtimeScanningStatus.SCANNING_ENABLED; - return RealtimeScanningStatus.SCANNING_DISABLED; + return output.getStdoutLines(); } else { - LOG.warn("Windows Defender realtime scanning status check exited with status " + output.getExitCode() + ": " + + LOG.warn("Windows Defender " + propertyName + " check exited with status " + output.getExitCode() + ": " + StringUtil.first(output.getStderr(), MAX_POWERSHELL_STDERR_LENGTH, false)); } } catch (ExecutionException e) { - LOG.warn("Windows Defender realtime scanning status check failed", e); + LOG.warn("Windows Defender " + propertyName + " check failed", e); } - return RealtimeScanningStatus.ERROR; + return null; } /** Returns a list of paths that might impact build performance if Windows Defender were configured to scan them. */ diff --git a/platform/platform-impl/src/com/intellij/util/Restarter.java b/platform/platform-impl/src/com/intellij/util/Restarter.java index 0bee8ec389ed..3a6b64b9b7e8 100644 --- a/platform/platform-impl/src/com/intellij/util/Restarter.java +++ b/platform/platform-impl/src/com/intellij/util/Restarter.java @@ -120,14 +120,9 @@ public class Restarter { // See https://blogs.msdn.microsoft.com/oldnewthing/20060515-07/?p=31203 // argv[0] as the program name is only a convention, i.e. there is no guarantee // the name is the full path to the executable. - // - // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683197(v=vs.85).aspx - // To retrieve the full path to the executable, use "GetModuleFileName(NULL, ...)". - // - // Note: We use 32,767 as buffer size to avoid limiting ourselves to MAX_PATH (260). - char[] buffer = new char[32767]; - if (kernel32.GetModuleFileNameW(null, buffer, new WinDef.DWORD(buffer.length)).intValue() > 0) { - argv[0] = Native.toString(buffer); + final String binaryName = getCurrentProcessExecutableName(); + if (binaryName != null) { + argv[0] = binaryName; } List args = new ArrayList<>(); @@ -158,6 +153,20 @@ public class Restarter { TimeoutUtil.sleep(500); } + // + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683197(v=vs.85).aspx + // To retrieve the full path to the executable, use "GetModuleFileName(NULL, ...)". + // + // Note: We use 32,767 as buffer size to avoid limiting ourselves to MAX_PATH (260). + public static String getCurrentProcessExecutableName() { + Kernel32 kernel32 = Native.load("kernel32", Kernel32.class); + char[] buffer = new char[32767]; + if (kernel32.GetModuleFileNameW(null, buffer, new WinDef.DWORD(buffer.length)).intValue() > 0) { + return Native.toString(buffer); + } + return null; + } + private static String[] getRestartArgv(String[] argv) { String mainClass = System.getProperty("idea.main.class.name", "com.intellij.idea.Main");