【问题标题】:Process hangs pinvoking AmsiScanBuffer from managed Code进程挂起从托管代码中调用 AmsiScanBuffer
【发布时间】:2018-01-12 18:09:48
【问题描述】:

我正在尝试从托管代码(特别是 C#)中使用 Windows 反恶意软件服务接口的 AmsiScanBuffer 函数。当尝试调用该方法时,只要提供非零缓冲区长度,程序就会挂起调用。如果提供的缓冲区长度为零,则该方法立即返回 HResult E_INVALIDARG。 AMSI 公开的其他方法按预期工作,所以我希望我相信这个函数的 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);
}

【问题讨论】:

    标签: c# pinvoke dllimport


    【解决方案1】:

    主要问题是AmsiScanBufferlength 参数。 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
            );
        }
    }
    

    【讨论】:

    • 谢谢您,先生,这绝对是加倍努力。文体 cmets 是有道理的。在代码的非缩写版本中,我倾向于将包含的类名作为库名,这会导致 Amsi.AmsiInitialize 的冗余。因此,在搜索文档时要在可读性和复制/粘贴能力之间进行权衡。正如你所说,纯粹作为风格选择。
    • 你不知道吗,我自己也犯了同样的错误,ulong vs.uint。我决定对来自AmsiInitializeAmsiScanBufferHRESULT 返回值添加错误检查,并且在我知道正在运行的调用中得到非零结果。我注意到这些结果是 64 位值,高 32 位是垃圾,低 32 位是全零。果然,我错误地将返回值声明为ulong。现已修复!
    • 我没有在 Visual Studio 中运行,所以我没有调试器,但我很惊讶没有抛出异常。我曾预料到,如果超出缓冲区,我会收到某种形式的内存访问错误。不管怎样,这表明距离我编写任何 C 语言已经太久了。
    猜你喜欢
    • 2013-08-24
    • 2010-09-18
    • 1970-01-01
    • 2015-02-24
    • 1970-01-01
    • 2011-04-29
    • 2013-12-29
    • 1970-01-01
    • 2016-03-28
    相关资源
    最近更新 更多