Bring Antimalware Scanning into your .NET 6 Application
source link: https://www.codeproject.com/Articles/5331133/Bring-Antimalware-Scanning-into-your-NET-6-Applica
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Table of Contents
The example code is hosted at Github.
Introduction
For those of you who came in and half-expecting a long and complicated article, things couldn't be simpler through Antimalware Scan Interface (AMSI) available on Windows 10 since 2015 to allow the anti-malware scanning on user-supplied content. AMSI is agnostic of the antimalware vendor. The scanning is done through the anti-malware installed on the user's computer. Anti-malware vendor and version are not provided by AMSI even though this information is vital for contacting the anti-malware vendor to report false positive detection. Another missing information is the name of malware detected and the type of vulnerability to aid the further investigation. AMSI is actively used in Microsoft products such as MS Office.
This article is divided into two main sections: the raw API section and the wrapper class section. Another way to call AMSI is through its COM API but the complexity of COM interop on .NET is best avoided since the raw API provides the same functionality as the COM API. Only raw Win32 AMSI APIs are covered here.
Raw Win32 Functions
AMSI Context
Note: The function definitions listed in this section have been converted through P/Invoke. For instance, the HRESULT
return type has been changed to uint
and the handle types for the context and session are changed to IntPtr
. To use AMSI, a context must be first created with AmsiInitialize()
. Supply your application name in appName
parameter. The function returns a HRESULT
where S_OK
(0) means success. An AMSI context may fail to initialize when Microsoft Defender is disabled and no other anti-malware product is present on the user's computer.
uint AmsiInitialize(String appName, out IntPtr amsiContext);
After use, the context must be uninitialized with a call to AmsiUninitialize()
.
void AmsiUninitialize(IntPtr amsiContext);
AMSI Session
To do scanning, if you do not want to group the scannings under a session or when you only have one content to scan, a session is not required. To open a session, call AmsiOpenSession()
with the context created from AmsiInitialize().
uint AmsiOpenSession(IntPtr amsiContext, out IntPtr amsiSession);
After the scanning is done, close the session with a call to AmsiCloseSession()
with the same context supplied to AmsiOpenSession()
.
void AmsiCloseSession(IntPtr amsiContext, IntPtr amsiSession);
AMSI Scan Functions
There are two types of scanning: string and binary buffer. Use AmsiScanString()
to scan text and the AmsiScanBuffer()
to scan buffer. contentName
is either the filename or the URL where this content is from. amsiSession
can be IntPtr.Zero
for not providing a session. result
is the output of these functions. IsMalware()
must be called on result
to check if it indicates malware. Other parameters are self-explanatory. The function returns a HRESULT
where S_OK
(0) means success. AmsiScanString()
is called on the script written in Powershell, JavaScript, VBScript or Office VBA macros.
uint AmsiScanString(IntPtr amsiContext, String text, String contentName, IntPtr amsiSession, out uint result);
AmsiScanBuffer()
is for scanning a binary buffer that could contain a malicious executable program. An example scenario is your software implements a plugin system where any third party can supply their own plugins to extend your software, it is advisable to call AmsiScanBuffer()
to scan the DLLs prior to loading and running them.
uint AmsiScanBuffer(IntPtr amsiContext, IntPtr buffer, uint length, String contentName, IntPtr amsiSession, out uint result);
AmsiNotifyOperation()
is to notify the anti-malware in the case that malware is found. No session is required to call this function. It is best not to assume scanning is done in AmsiNotifyOperation
because this function may not be implemented by third-party anti-malware vendors, so do not rely on the result
.
uint AmsiNotifyOperation(IntPtr amsiContext, IntPtr buffer, uint length, String contentName, out uint result);
IsMalware
is defined as below:
bool IsMalware(uint result) { return (result >= 32768); }
Raw Win32 Functions Example
Below is an example of using the raw Win32 AMSI functions.
// Example of using the raw AMSI functions IntPtr amsiContext = IntPtr.Zero; uint hr = AmsiMethods.AmsiInitialize("ScanContentCSharp", out amsiContext); if (hr != 0) { Console.WriteLine("AmsiInitialize failed!"); return; } IntPtr amsiSession = IntPtr.Zero; hr = AmsiMethods.AmsiOpenSession(amsiContext, out amsiSession); if (hr != 0) { Console.WriteLine("AmsiOpenSession failed!"); AmsiMethods.AmsiUninitialize(amsiContext); return; } uint result = 0; hr = AmsiMethods.AmsiScanString (amsiContext, "Hello World!", "Testing.txt", amsiSession, out result); if (hr != 0) { Console.WriteLine("AmsiScanString failed!"); } else { if (AmsiMethods.IsMalware(result)) Console.WriteLine("Malware detected"); else Console.WriteLine("No malware detected"); } AmsiMethods.AmsiCloseSession(amsiContext, amsiSession); AmsiMethods.AmsiUninitialize(amsiContext);
Wrapper Class: AmsiHelper
A wrapper called AmsiHelper
is written to simplify the AMSI usage. A context and session are created in the constructor and destroyed in the finalizer. Its public
methods are listed below:
AmsiHelper(string appName); // Constructor ~AmsiHelper(string appName); // Finalizer bool IsValidAmsi(); // Check if managed to get a ASMI context and session bool ScanString(string text, string contentName, out bool isMalware); // Scan text bool ScanBuffer(IntPtr buffer, uint length, string contentName, out bool isMalware); // Scan buffer bool NotifyOperation(IntPtr buffer, uint length, string contentName, out bool isMalware); // Notify anti-malware of this buffer
Wrapper Class Example
Below is an example of using the AmsiHelper
.
// Example of using the AMSI wrapper class: AmsiHelper using (AmsiHelper amsi = new AmsiHelper("ScanContentCSharp")) { if (!amsi.IsValidAmsi()) Console.WriteLine("AmsiOpenSession failed!"); bool isMalware = false; if (!amsi.ScanString("Hello World!", "Testing.txt", out isMalware)) Console.WriteLine("AmsiScanString failed!"); else { if (isMalware) Console.WriteLine("Malware detected"); else Console.WriteLine("No malware detected"); } }
Though the AmsiHelper
simplifies the usage and resource management, the raw APIs that allow the flexibility of not using the session. .NET Framework, .NET 6 and C++ sample codes are made available for download.
History
- 3rd May, 2022: First release
Other Articles in the Bring Your... Series
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK