5

Bring Antimalware Scanning into your .NET 6 Application

 2 years ago
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.
neoserver,ios ssh client

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.

Copy Code
uint AmsiInitialize(String appName, out IntPtr amsiContext);

After use, the context must be uninitialized with a call to AmsiUninitialize().

Copy Code
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().

Copy Code
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().

Copy Code
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.

Copy Code
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.

Copy Code
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.

Copy Code
uint AmsiNotifyOperation(IntPtr amsiContext, IntPtr buffer, 
                         uint length, String contentName, out uint result);

IsMalware is defined as below:

Copy Code
bool IsMalware(uint result)
{
    return (result >= 32768);
}

Raw Win32 Functions Example

Below is an example of using the raw Win32 AMSI functions.

Shrink ▲   Copy Code
// 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:

Copy Code
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.

Copy Code
// 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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK