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






4.88/5 (11投票s)
在您的应用程序内部集成恶意软件扫描
目录
示例代码托管在 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.Zero
。result
是这些函数的输出。必须在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日**:首次发布