从托管代码调用AmsiScanBuffer时进程挂起

4
我正在尝试从托管代码,特别是C#中使用Windows反恶意软件服务接口的AmsiScanBuffer函数。当尝试调用该方法时,只要提供非零缓冲区长度,程序就会在调用时挂起。如果提供0长度的缓冲区,则该方法立即返回HResult E_INVALIDARG。AMS​​I公开的其他方法都按预期工作,因此我认为我的dllimport对于此函数来说非常接近,但可能不完全正确。除了这里表示的数组复制方法之外,我还尝试过固定数组,结果行为相同。 C原型
HRESULT WINAPI AmsiScanBuffer(
  _In_     HAMSICONTEXT amsiContext,
  _In_     PVOID        buffer,
  _In_     ULONG        length,
  _In_     LPCWSTR      contentName,
  _In_opt_ HAMSISESSION session,
  _Out_    AMSI_RESULT  *result
);

托管代码

[DllImport("Amsi.dll", EntryPoint = "AmsiScanBuffer", CallingConvention = CallingConvention.StdCall)]
public static extern int ScanBuffer(IntPtr amsiContext, IntPtr ptr, ulong length, string contentName, IntPtr session, out int result);

var virus = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";
var bytes = Encoding.UTF8.GetBytes(virus);
int sizet = Marshal.SizeOf(typeof(byte)) * bytes.Length;
var ptr = Marshal.AllocHGlobal(sizet);

try
{
    Marshal.Copy(bytes, 0, ptr, bytes.Length);
    int hr = Amsi.ScanBuffer(context, ptr, (ulong)sizet, "Unknown Data", session, out result);
}
finally
{
    Marshal.FreeHGlobal(ptr);
}
1个回答

10
主要问题是AmsiScanBuffer中的length参数。在Windows上,C/C++中的ULONG为32位,而C#中的ulong为64位。因此,参数需要声明为uint。即使您传递了零长度的缓冲区,我也希望在调试器下运行时会收到“不平衡的堆栈”错误提示。您还可以将buffer声明为byte[],然后直接传入bytes
为进一步简化,您可以省略CallingConvention,因为StdCall是默认设置。我还将其更改为使用确切的函数名称,因此不需要在DllImport中指定它。通常,当我直接从C#使用C库时,我喜欢保留原始函数名称,例如AmsiScanBuffer,而不是将其更改为Amsi.ScanBuffer。这样,在有人处理代码时更容易查找文档,尽管这显然是品味的问题。
以下是作为控制台应用程序的工作版本。
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace AmsiTest {

    class Program {
        static void Main( string[] args ) {

            var virus = Encoding.UTF8.GetBytes(
                "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"
            );

            IntPtr context;
            var hrInit = AmsiInitialize( "AmsiTest", out context );
            if( hrInit != 0 ) {
                Console.WriteLine( $"AmsiInitialize failed, HRESULT {hrInit:X8}" );
                return;
            }

            AMSI_RESULT result;
            var hrScan = AmsiScanBuffer(
                context, virus, (uint)virus.Length,
                "EICAR Test File", IntPtr.Zero, out result
            );

            AmsiUninitialize( context );

            if( hrScan != 0 ) {
                Console.WriteLine( $"AmsiScanBuffer failed, HRESULT {hrScan:X8}" );
            } else if( result == AMSI_RESULT.AMSI_RESULT_DETECTED ) {
                Console.WriteLine( "Detected EICAR test" );
            } else {
                Console.WriteLine( $"Failed to detect EICAR test, result {result:X8}" );
            }

        }

        public enum AMSI_RESULT { 
            AMSI_RESULT_CLEAN = 0,
            AMSI_RESULT_NOT_DETECTED = 1,
            AMSI_RESULT_BLOCKED_BY_ADMIN_START = 16384,
            AMSI_RESULT_BLOCKED_BY_ADMIN_END = 20479,
            AMSI_RESULT_DETECTED = 32768
        }

        [DllImport( "Amsi.dll" )]
        public static extern uint AmsiInitialize(
            string appName,
            out IntPtr amsiContext
        );

        [DllImport( "Amsi.dll" )]
        public static extern uint AmsiScanBuffer(
            IntPtr amsiContext,
            byte[] buffer,
            uint length,
            string contentName,
            IntPtr session,
            out AMSI_RESULT result
        );

        [DllImport( "Amsi.dll" )]
        public static extern void AmsiUninitialize(
            IntPtr amsiContext
        );
    }
}

谢谢您,先生。这绝对是额外加了一些努力的地方。在代码的非缩写版本中,我倾向于将包含类名作为库名,这将导致 Amsi.AmsiInitialize 的重复。所以需要在可读性和搜索文档时的复制/粘贴能力之间进行权衡。正如您所说,这只是一个风格选择。 - Tedford
你知道吗,我也曾经因为 ulonguint 的问题犯过同样的错误。我决定在对 AmsiInitializeAmsiScanBuffer 返回值的 HRESULT 进行错误检查时,发现一些本应该正常工作的调用返回了非零结果。我注意到这些结果是 64 位值,高 32 位有垃圾值,低 32 位全是零。果然,我错误地将返回值声明为 ulong。现在已经修复了! - Michael Geary
我没有在Visual Studio中运行,所以我没有调试器,但是我很惊讶居然没有抛出异常。我曾期望如果我超出缓冲区,则会得到某种形式的内存访问错误。无论如何,这表明我已经太久没有编写C语言了。 - Tedford
如果由于某种原因,Windows Defender安全中心中的实时保护已关闭,则会收到HRESULT 80070015错误消息...请编辑为: if (hrScan == 2147942421) { Console.WriteLine( $"AmsiScanBuffer 失败,HRESULT {hrScan:X8} Windows Defender 安全中心已关闭!!! 您需要启用Windows Defender安全中心中的实时保护"); } else if (hrScan != 0) ... - Zvi Redler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接