65.9K
CodeProject 正在变化。 阅读更多。
Home

将恶意软件扫描集成到您的应用程序中

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (11投票s)

2022年5月3日

CPOL

4分钟阅读

viewsIcon

15076

downloadIcon

539

在您的应用程序内部集成恶意软件扫描

目录

示例代码托管在 Github

引言

对于那些期待一篇冗长复杂的文章的读者来说,事情其实很简单,这都要归功于自2015年起在Windows 10上可用的反恶意软件扫描接口 (AMSI),它允许对用户提供的内容进行反恶意软件扫描。AMSI与反恶意软件厂商无关。扫描工作通过用户计算机上安装的反恶意软件进行。即使这些信息对于联系反恶意软件厂商报告误报至关重要,但AMSI也不会提供反恶意软件厂商和版本信息。另一个缺失的信息是检测到的恶意软件名称和漏洞类型,这有助于进一步调查。AMSI被积极地用于微软产品,例如MS Office。

本文分为两个主要部分:原始API部分和包装类部分。另一种调用AMSI的方法是通过其COM API,但是由于原始API提供了与COM API相同的功能,因此最好避免在.NET上使用COM互操作的复杂性。这里只介绍原始Win32 AMSI API。

原始 Win32 函数

AMSI上下文

注意:本节中列出的函数定义已通过P/Invoke进行转换。例如,HRESULT返回类型已更改为uint,上下文和会话的句柄类型已更改为IntPtr。要使用AMSI,必须首先使用AmsiInitialize()创建上下文。在appName参数中提供您的应用程序名称。该函数返回一个HRESULT,其中S_OK (0) 表示成功。当禁用Microsoft Defender并且用户计算机上没有其他反恶意软件产品时,AMSI上下文可能无法初始化。

uint AmsiInitialize(String appName, out IntPtr amsiContext);
HRESULT AmsiInitialize(LPCWSTR appName, HAMSICONTEXT *amsiContext);

使用后,必须使用对AmsiUninitialize()的调用来取消初始化上下文。

void AmsiUninitialize(IntPtr amsiContext);
void AmsiUninitialize(HAMSICONTEXT amsiContext);

AMSI会话

要进行扫描,如果您不想将扫描分组到会话中,或者您只有一个内容要扫描,则不需要会话。要打开会话,请使用从AmsiInitialize()创建的上下文调用AmsiOpenSession()

uint AmsiOpenSession(IntPtr amsiContext, out IntPtr amsiSession);
HRESULT AmsiOpenSession(HAMSICONTEXT amsiContext, HAMSISESSION *amsiSession);

扫描完成后,使用传递给AmsiOpenSession()的相同上下文,使用对AmsiCloseSession()的调用关闭会话。

void AmsiCloseSession(IntPtr amsiContext, IntPtr amsiSession);
void AmsiCloseSession(HAMSICONTEXT amsiContext, HAMSISESSION amsiSession);

AMSI扫描函数

有两种类型的扫描:字符串和二进制缓冲区。使用AmsiScanString()扫描文本,使用AmsiScanBuffer()扫描缓冲区。contentName是此内容的来源文件名或URL。对于不提供会话,amsiSession可以是IntPtr.Zeroresult是这些函数的输出。必须在result上调用IsMalware()来检查它是否指示恶意软件。其他参数是不言自明的。该函数返回一个HRESULT,其中S_OK (0) 表示成功。AmsiScanString()用于调用以Powershell、JavaScript、VBScript或Office VBA宏编写的脚本。

uint AmsiScanString(IntPtr amsiContext, String text, String contentName,
            IntPtr amsiSession, out uint result);
HRESULT AmsiScanString(HAMSICONTEXT amsiContext, LPCWSTR string, 
            LPCWSTR contentName, HAMSISESSION amsiSession, 
            AMSI_RESULT *result);

AmsiScanBuffer()用于扫描可能包含恶意可执行程序的二进制缓冲区。一个示例场景是您的软件实现了一个插件系统,任何第三方都可以提供自己的插件来扩展您的软件,建议在加载和运行之前调用AmsiScanBuffer()来扫描DLL。

uint AmsiScanBuffer(IntPtr amsiContext, IntPtr buffer, uint length, String contentName,
            IntPtr amsiSession, out uint result);
HRESULT AmsiScanBuffer(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, 
            LPCWSTR contentName, HAMSISESSION amsiSession, 
            AMSI_RESULT *result);

AmsiNotifyOperation()用于在发现恶意软件的情况下通知反恶意软件。调用此函数不需要会话。最好不要假设扫描在AmsiNotifyOperation中完成,因为此函数可能未由第三方反恶意软件供应商实现,因此不要依赖result

uint AmsiNotifyOperation(IntPtr amsiContext, IntPtr buffer, 
                         uint length, String contentName, out uint result);
HRESULT AmsiNotifyOperation(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length,
                            LPCWSTR contentName, AMSI_RESULT  *result);

IsMalware定义如下

bool IsMalware(uint result)
{
    return (result >= 32768);
}
bool IsMalware(AMSI_RESULT result)
{
    return (result >= AMSI_RESULT_DETECTED);
}

原始 Win32 函数示例

以下是使用原始Win32 AMSI函数的示例。

// 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);
// Example of using the raw AMSI functions
HAMSICONTEXT amsiContext;
HRESULT hr = AmsiInitialize(L"ScanContentCpp", &amsiContext);
if (FAILED(hr))
{
    printf("AmsiInitialize failed!");
    return 1;
}

HAMSISESSION amsiSession;
hr = AmsiOpenSession(amsiContext, &amsiSession);
if (FAILED(hr))
{
    printf("AmsiOpenSession failed!");
    AmsiUninitialize(amsiContext);
    return 1;
}

AMSI_RESULT result;
hr = AmsiScanString(amsiContext, L"Hello World!", L"Testing.txt", amsiSession, &result);
if (FAILED(hr))
    printf("AmsiScanString failed!");
else
{
    if (result >= AMSI_RESULT_DETECTED)
        printf("Malware detected");
    else
        printf("No malware detected");
}

AmsiCloseSession(amsiContext, amsiSession);

AmsiUninitialize(amsiContext);

包装类:AmsiHelper

编写了一个名为AmsiHelper的包装器来简化AMSI的使用。上下文和会话在构造函数中创建,并在终结器中销毁。其公共方法如下所示

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
AmsiHelper();  // Constructor
~AmsiHelper(); // Destructor

bool IsValidAmsi() const; // Check if managed to get a ASMI context and session

bool ScanString(LPCWSTR text, LPCWSTR contentName, bool* isMalware); // Scan text
bool ScanBuffer(PVOID buffer, ULONG length, 
                LPCWSTR contentName, bool* isMalware); // Scan buffer
bool NotifyOperation(PVOID buffer, ULONG length, LPCWSTR contentName,
                     bool* isMalware); // Notify anti-malware of this buffer

包装类示例

以下是使用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");
    }
}
// Example of using the AMSI wrapper class: AmsiHelper
AmsiHelper amsi(L"ScanContentCpp");

if (!amsi.IsValidAmsi())
    printf("AmsiOpenSession failed!");

bool isMalware = false;
if (!amsi.ScanString(L"Hello World!", L"Testing.txt", &isMalware))
    printf("AmsiScanString failed!");
else
{
    if (isMalware)
        printf("Malware detected");
    else
        printf("No malware detected");
}

尽管AmsiHelper简化了用法和资源管理,但原始API允许灵活地不使用会话。.NET Framework、.NET 6和C++示例代码可供下载。

历史

  • **2022年6月8日**:通过C++选项卡向文章添加了C++源代码示例
  • **2022年5月3日**:首次发布

《将你的...》系列的其他文章

© . All rights reserved.