Antimalware scanning
editThe Antimalware Scan Interface (AMSI) lets an application ask the antimalware product installed on the machine, such as Microsoft Defender, whether some content is malicious before using it. Use it when the application runs scripts or macros, or accepts files from users. The Meziantou.Framework.Win32.Amsi package wraps it.
dotnet package add Meziantou.Framework.Win32.AmsiIt requires Windows 10 or Windows Server 2016, or later.
Scan a string or a buffer
Create a context once for the application, and scan the content with it:
using var context = AmsiContext.Create("MyApplication");
var isMalware = context.IsMalware(@"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*", "test.txt");
Assert.True(isMalware);The examples scan the EICAR test string, a harmless string that every antimalware product detects as malware. Use it to test your own integration.
Byte buffers are supported too, such as the content of an uploaded file:
var payload = Encoding.ASCII.GetBytes(@"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*");
Assert.True(context.IsMalware(payload, "document.txt"));
Assert.False(context.IsMalware([0, 0, 0, 0], "document.bin"));- The name passed to AmsiContext.Create identifies your application to the antimalware product.
- The content name, such as
test.txt, identifies the content: a file name, a URL or a script name. The antimalware product can use it in its reports. - Dispose the context when the application no longer scans content.
Block what the administrator blocks
IsMalware only reports malware. An administrator policy can also block content that is not malware. Scan returns the raw AmsiResult, and its extension methods interpret it:
using var context = AmsiContext.Create("MyApplication");
bool CanRun(string script)
{
var result = context.Scan(script, "script.ps1");
// ShouldBlock covers malware and the content that an administrator policy blocks
return !result.ShouldBlock();
}
Assert.True(CanRun("Write-Host 'Hello'"));
Assert.False(CanRun(@"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"));| Method | Returns true when |
|---|---|
| IsMalware() | The result is Detected or higher: the content is malware |
| IsBlockedByAdmin() | The result is between BlockedByAdminStart and BlockedByAdminEnd: an administrator policy blocked the content |
| ShouldBlock() | Either of the above: do not use the content |
Clean means the content is known to be safe, and NotDetected that nothing was detected with the current definitions. The antimalware product may return any value between NotDetected and 32767 as a risk level.
Scan the parts of the same content
When several scans belong together, such as the fragments of a script that is evaluated piece by piece or the parts of one upload, scan them in a session. The antimalware product can then correlate them:
string[] fragments = ["Write-Host 'Hello'", @"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"];
using var context = AmsiContext.Create("MyApplication");
using var session = context.CreateSession();
var detected = false;
foreach (var fragment in fragments)
{
if (session.IsMalware(fragment, "script.ps1"))
{
detected = true;
break;
}
}
Assert.True(detected);AmsiSession has the same IsMalware and Scan methods as the context. Dispose the session when the content is complete. A session cannot be used once its context is disposed: it throws an ObjectDisposedException.
Errors and limitations
- The methods throw a
COMExceptionwhen AMSI cannot be initialized or a scan fails, for instance when the antimalware product is not ready. - When no antimalware product is registered with AMSI, scans succeed and report the content as not detected. A negative answer is not a proof that the content is safe.
- Using a disposed context or session throws an
ObjectDisposedException.