├── LICENSE ├── README.md └── WinDefender.cs /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jitbit (the company behind "Jitbit Helpdesk" software) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WinDefender - Windows Defender C# API 2 | 3 | Invokes Windows Defender scan from C# .NET Core 6 (on Windows). Works even in ASP.NET 4 | 5 | ## Usage 6 | 7 | Add [this file](/WinDefender.cs) to your project then call: 8 | 9 | ```csharp 10 | //check by filename 11 | bool isVirus = await WinDefender.IsVirus(@"c:\path\to\file"); 12 | 13 | //check by byte array 14 | byte[] fileContents = ReadFileFromSomewhere(); 15 | bool isVirus = await WinDefender.IsVirus(fileContents); 16 | 17 | //cancellation token support if you want ot abort 18 | bool isVirus = await WinDefender.IsVirus(fileContents, cancellationToken); 19 | 20 | ``` 21 | 22 | ## Background 23 | 24 | Windows defender comes with a CLI tool. It does not launch the actual antivirus process, it's just a CLI-API to the antivirus that is always running. Even if you have "real time protection" disabled (recommended for server environment) the CLI still works. 25 | -------------------------------------------------------------------------------- /WinDefender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Jitbit.Utils 8 | { 9 | public class WinDefender 10 | { 11 | private static bool _isDefenderAvailable; 12 | private static string _defenderPath; 13 | private static SemaphoreSlim _lock = new SemaphoreSlim(5); //limit to 5 concurrent checks at a time 14 | 15 | //static ctor 16 | static WinDefender() 17 | { 18 | if (OperatingSystem.IsWindows()) 19 | { 20 | _defenderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Windows Defender", "MpCmdRun.exe"); 21 | _isDefenderAvailable = File.Exists(_defenderPath); 22 | } 23 | else 24 | _isDefenderAvailable = false; 25 | } 26 | 27 | public static async Task IsVirus(byte[] file, CancellationToken cancellationToken = default) 28 | { 29 | if (!_isDefenderAvailable) return false; 30 | 31 | string path = Path.GetTempFileName(); 32 | await File.WriteAllBytesAsync(path, file, cancellationToken); //save temp file 33 | 34 | if (cancellationToken.IsCancellationRequested) return false; 35 | 36 | try 37 | { 38 | return await IsVirus(path, cancellationToken); 39 | } 40 | finally 41 | { 42 | File.Delete(path); //cleanup temp file 43 | } 44 | } 45 | 46 | public static async Task IsVirus(string path, CancellationToken cancellationToken = default) 47 | { 48 | await _lock.WaitAsync(cancellationToken); 49 | 50 | try 51 | { 52 | using (var process = Process.Start(_defenderPath, $"-Scan -ScanType 3 -File \"{path}\" -DisableRemediation")) 53 | { 54 | if (process == null) 55 | { 56 | _isDefenderAvailable = false; //disable future attempts 57 | throw new InvalidOperationException("Failed to start MpCmdRun.exe"); 58 | } 59 | 60 | try 61 | { 62 | await process.WaitForExitAsync().WaitAsync(TimeSpan.FromMilliseconds(2500), cancellationToken); 63 | } 64 | catch (TimeoutException ex) //timeout 65 | { 66 | throw new TimeoutException("Timeout waiting for MpCmdRun.exe to return", ex); 67 | } 68 | finally 69 | { 70 | process.Kill(); //always kill the process, it's fine if it's already exited, but if we were timed out or cancelled via token - let's kill it 71 | } 72 | 73 | return process.ExitCode == 2; 74 | } 75 | } 76 | finally 77 | { 78 | _lock.Release(); 79 | } 80 | } 81 | } 82 | } 83 | --------------------------------------------------------------------------------