using System; using System.Runtime.InteropServices; namespace Vanara.PInvoke { /// Items from the AMSI.dll. public static partial class AMSI { private const string Lib_Amsi = "amsi.dll"; /// The AMSI_ATTRIBUTE enumeration specifies the types of attributes that can be requested by IAmsiStream::GetAttribute. // https://docs.microsoft.com/en-us/windows/win32/api/amsi/ne-amsi-amsi_attribute typedef enum AMSI_ATTRIBUTE { // AMSI_ATTRIBUTE_APP_NAME, AMSI_ATTRIBUTE_CONTENT_NAME, AMSI_ATTRIBUTE_CONTENT_SIZE, AMSI_ATTRIBUTE_CONTENT_ADDRESS, // AMSI_ATTRIBUTE_SESSION, AMSI_ATTRIBUTE_REDIRECT_CHAIN_SIZE, AMSI_ATTRIBUTE_REDIRECT_CHAIN_ADDRESS, AMSI_ATTRIBUTE_ALL_SIZE, // AMSI_ATTRIBUTE_ALL_ADDRESS, AMSI_ATTRIBUTE_QUIET } ; [PInvokeData("amsi.h", MSDNShortId = "NE:amsi.AMSI_ATTRIBUTE")] public enum AMSI_ATTRIBUTE { /// /// Return the name, version, or GUID string of the calling application, copied from a LPWSTR. /// AMSI_ATTRIBUTE_APP_NAME = 0, /// /// Return the filename, URL, unique script ID, or similar of the content, copied from a LPWSTR. /// AMSI_ATTRIBUTE_CONTENT_NAME, /// /// Return the size of the input, as a ULONGLONG. /// AMSI_ATTRIBUTE_CONTENT_SIZE, /// Return the memory address if the content is fully loaded into memory. AMSI_ATTRIBUTE_CONTENT_ADDRESS, /// /// Session is used to associate different scan calls, such as if the contents to be scanned belong to the sample original /// script. Return a PVOID to the next portion of the content to be scanned. Return NULL if the content is self-contained. /// AMSI_ATTRIBUTE_SESSION, /// AMSI_ATTRIBUTE_REDIRECT_CHAIN_SIZE, /// AMSI_ATTRIBUTE_REDIRECT_CHAIN_ADDRESS, /// AMSI_ATTRIBUTE_ALL_SIZE, /// AMSI_ATTRIBUTE_ALL_ADDRESS, /// AMSI_ATTRIBUTE_QUIET, } /// The AMSI_RESULT enumeration specifies the types of results returned by scans. /// /// /// The antimalware provider may return a result between 1 and 32767, inclusive, as an estimated risk level. The larger the result, /// the riskier it is to continue with the content. These values are provider specific, and may indicate a malware family or ID. /// /// /// Results within the range of AMSI_RESULT_BLOCKED_BY_ADMIN_START and AMSI_RESULT_BLOCKED_BY_ADMIN_END values /// (inclusive) are officially blocked by the admin specified policy. In these cases, the script in question will be blocked from /// executing. The range is large to accommodate future additions in functionality. /// /// /// Any return result equal to or larger than 32768 is considered malware, and the content should be blocked. An app should use /// AmsiResultIsMalware to determine if this is the case. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/amsi/ne-amsi-amsi_result typedef enum AMSI_RESULT { AMSI_RESULT_CLEAN, // AMSI_RESULT_NOT_DETECTED, AMSI_RESULT_BLOCKED_BY_ADMIN_START, AMSI_RESULT_BLOCKED_BY_ADMIN_END, AMSI_RESULT_DETECTED } ; [PInvokeData("amsi.h", MSDNShortId = "NE:amsi.AMSI_RESULT")] public enum AMSI_RESULT : uint { /// Known good. No detection found, and the result is likely not going to change after a future definition update. AMSI_RESULT_CLEAN = 0, /// No detection found, but the result might change after a future definition update. AMSI_RESULT_NOT_DETECTED = 1, /// Administrator policy blocked this content on this machine (beginning of range). AMSI_RESULT_BLOCKED_BY_ADMIN_START = 0x4000, /// Administrator policy blocked this content on this machine (end of range). AMSI_RESULT_BLOCKED_BY_ADMIN_END = 0x4fff, /// Detection found. The content is considered malware and should be blocked. AMSI_RESULT_DETECTED = 32768, } /// Close a session that was opened by AmsiOpenSession. /// The handle of type HAMSICONTEXT that was initially received from AmsiInitialize. /// The handle of type HAMSISESSION that was initially received from AmsiOpenSession. /// None // https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiclosesession void AmsiCloseSession( [in] HAMSICONTEXT // amsiContext, [in] HAMSISESSION amsiSession ); [DllImport(Lib_Amsi, SetLastError = false, ExactSpelling = true)] [PInvokeData("amsi.h", MSDNShortId = "NF:amsi.AmsiCloseSession")] public static extern void AmsiCloseSession([In] HAMSICONTEXT amsiContext, [In] HAMSISESSION amsiSession); /// Initialize the AMSI API. /// The name, version, or GUID string of the app calling the AMSI API. /// A handle of type HAMSICONTEXT that must be passed to all subsequent calls to the AMSI API. /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. /// When the app is finished with the AMSI API it must call AmsiUninitialize. // https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiinitialize HRESULT AmsiInitialize( [in] LPCWSTR appName, [out] // HAMSICONTEXT *amsiContext ); [DllImport(Lib_Amsi, SetLastError = false, ExactSpelling = true)] [PInvokeData("amsi.h", MSDNShortId = "NF:amsi.AmsiInitialize")] public static extern HRESULT AmsiInitialize([MarshalAs(UnmanagedType.LPWStr)] string appName, out SafeHAMSICONTEXT amsiContext); /// /// Sends to the antimalware provider a notification of an arbitrary operation. The notification doesn't imply the request of an /// antivirus scan. Rather, IAntimalwareProvider2::Notify is designed to provide a quick and lightweight mechanism to /// communicate to the antimalware provider that an event has taken place. In general, the antimalware provider should process the /// notification, and return to the caller as quickly as possible. /// /// /// Type: _In_ HAMSICONTEXT /// The handle (of type HAMSICONTEXT) that was initially received from AmsiInitialize. /// /// /// Type: _In_reads_bytes_(length) PVOID /// The buffer that contains the notification data. /// /// /// Type: _In_ ULONG /// The length, in bytes, of the data to be read from buffer. /// /// /// Type: _In_opt_ LPCWSTR /// The filename, URL, unique script ID, or similar of the content being scanned. /// /// /// Type: _Out_ AMSI_RESULT* /// The result of the scan. /// You should use AmsiResultIsMalware to determine whether the content should be blocked. /// /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. // https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsinotifyoperation HRESULT AmsiNotifyOperation( HAMSICONTEXT // amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, AMSI_RESULT *result ); [DllImport(Lib_Amsi, SetLastError = false, ExactSpelling = true)] [PInvokeData("amsi.h", MSDNShortId = "NF:amsi.AmsiNotifyOperation")] public static extern HRESULT AmsiNotifyOperation([In] HAMSICONTEXT amsiContext, [In] IntPtr buffer, [In] uint length, [Optional, MarshalAs(UnmanagedType.LPWStr)] string contentName, out AMSI_RESULT result); /// Opens a session within which multiple scan requests can be correlated. /// The handle of type HAMSICONTEXT that was initially received from AmsiInitialize. /// /// A handle of type HAMSISESSION that must be passed to all subsequent calls to the AMSI API within the session. /// /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. /// When the app is finished with the session it must call AmsiCloseSession. // https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiopensession HRESULT AmsiOpenSession( [in] HAMSICONTEXT // amsiContext, [out] HAMSISESSION *amsiSession ); public static HRESULT AmsiOpenSession([In] HAMSICONTEXT amsiContext, out SafeHAMSISESSION amsiSession) { HRESULT hr = AmsiOpenSessionInternal(amsiContext, out HAMSISESSION h); amsiSession = hr.Succeeded ? new SafeHAMSISESSION((IntPtr)h, true) : new SafeHAMSISESSION(IntPtr.Zero, false); return hr; } /// Determines if the result of a scan indicates that the content should be blocked. /// The AMSI_RESULT returned by AmsiScanBuffer or AmsiScanString. /// None // https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiresultismalware void AmsiResultIsMalware( [in] r ); [PInvokeData("amsi.h", MSDNShortId = "NF:amsi.AmsiResultIsMalware")] public static bool AmsiResultIsMalware(AMSI_RESULT r) => r >= AMSI_RESULT.AMSI_RESULT_DETECTED; /// Scans a buffer-full of content for malware. /// The handle of type HAMSICONTEXT that was initially received from AmsiInitialize. /// The buffer from which to read the data to be scanned. /// The length, in bytes, of the data to be read from buffer. /// The filename, URL, unique script ID, or similar of the content being scanned. /// /// If multiple scan requests are to be correlated within a session, set session to the handle of type HAMSISESSION that was /// initially received from AmsiOpenSession. Otherwise, set session to nullptr. /// /// /// The result of the scan. See AMSI_RESULT. /// An app should use AmsiResultIsMalware to determine whether the content should be blocked. /// /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. // https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiscanbuffer HRESULT AmsiScanBuffer( [in] HAMSICONTEXT // amsiContext, [in] PVOID buffer, [in] ULONG length, [in] LPCWSTR contentName, [in, optional] HAMSISESSION amsiSession, [out] // AMSI_RESULT *result ); [DllImport(Lib_Amsi, SetLastError = false, ExactSpelling = true)] [PInvokeData("amsi.h", MSDNShortId = "NF:amsi.AmsiScanBuffer")] public static extern HRESULT AmsiScanBuffer([In] HAMSICONTEXT amsiContext, [In] IntPtr buffer, uint length, [Optional, MarshalAs(UnmanagedType.LPWStr)] string contentName, [In, Optional] HAMSISESSION amsiSession, out AMSI_RESULT result); /// Scans a string for malware. /// The handle of type HAMSICONTEXT that was initially received from AmsiInitialize. /// The string to be scanned. /// The filename, URL, unique script ID, or similar of the content being scanned. /// /// If multiple scan requests are to be correlated within a session, set session to the handle of type HAMSISESSION that was /// initially received from AmsiOpenSession. Otherwise, set session to nullptr. /// /// /// The result of the scan. See AMSI_RESULT. /// An app should use AmsiResultIsMalware to determine whether the content should be blocked. /// /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. // https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiscanstring HRESULT AmsiScanString( [in] HAMSICONTEXT // amsiContext, [in] LPCWSTR string, [in] LPCWSTR contentName, [in, optional] HAMSISESSION amsiSession, [out] AMSI_RESULT *result ); [DllImport(Lib_Amsi, SetLastError = false, ExactSpelling = true)] [PInvokeData("amsi.h", MSDNShortId = "NF:amsi.AmsiScanString")] public static extern HRESULT AmsiScanString(HAMSICONTEXT amsiContext, [MarshalAs(UnmanagedType.LPWStr)] string str, [Optional, MarshalAs(UnmanagedType.LPWStr)] string contentName, [In, Optional] HAMSISESSION amsiSession, out AMSI_RESULT result); /// Remove the instance of the AMSI API that was originally opened by AmsiInitialize. /// The handle of type HAMSICONTEXT that was initially received from AmsiInitialize. /// None // https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiuninitialize void AmsiUninitialize( [in] HAMSICONTEXT // amsiContext ); [DllImport(Lib_Amsi, SetLastError = false, ExactSpelling = true)] [PInvokeData("amsi.h", MSDNShortId = "NF:amsi.AmsiUninitialize")] public static extern void AmsiUninitialize(HAMSICONTEXT amsiContext); [DllImport(Lib_Amsi, SetLastError = false, EntryPoint = "AmsiOpenSession")] private static extern HRESULT AmsiOpenSessionInternal([In] HAMSICONTEXT amsiContext, out HAMSISESSION amsiSession); /// Provides a handle to an AMSI context. [StructLayout(LayoutKind.Sequential)] public struct HAMSICONTEXT : IHandle { private readonly IntPtr handle; /// Initializes a new instance of the struct. /// An object that represents the pre-existing handle to use. public HAMSICONTEXT(IntPtr preexistingHandle) => handle = preexistingHandle; /// Returns an invalid handle by instantiating a object with . public static HAMSICONTEXT NULL { get; } = default; /// Gets a value indicating whether this instance is a null handle. public bool IsNull => handle == IntPtr.Zero; /// Performs an explicit conversion from to . /// The handle. /// The result of the conversion. public static explicit operator IntPtr(HAMSICONTEXT h) => h.handle; /// Performs an implicit conversion from to . /// The pointer to a handle. /// The result of the conversion. public static implicit operator HAMSICONTEXT(IntPtr h) => new(h); /// Implements the operator !=. /// The first handle. /// The second handle. /// The result of the operator. public static bool operator !=(HAMSICONTEXT h1, HAMSICONTEXT h2) => h1.handle != h2.handle; /// Implements the operator ==. /// The first handle. /// The second handle. /// The result of the operator. public static bool operator ==(HAMSICONTEXT h1, HAMSICONTEXT h2) => h1.handle == h2.handle; /// public override bool Equals(object obj) => (obj is IHandle h && handle == h.DangerousGetHandle()) || (obj is IntPtr p && handle == p); /// public override int GetHashCode() => handle.GetHashCode(); /// public IntPtr DangerousGetHandle() => handle; } /// Provides a handle to an AMSI session. [StructLayout(LayoutKind.Sequential)] public struct HAMSISESSION : IHandle { private readonly IntPtr handle; /// Initializes a new instance of the struct. /// An object that represents the pre-existing handle to use. public HAMSISESSION(IntPtr preexistingHandle) => handle = preexistingHandle; /// Returns an invalid handle by instantiating a object with . public static HAMSISESSION NULL { get; } = default; /// Gets a value indicating whether this instance is a null handle. public bool IsNull => handle == IntPtr.Zero; /// Performs an explicit conversion from to . /// The handle. /// The result of the conversion. public static explicit operator IntPtr(HAMSISESSION h) => h.handle; /// Performs an implicit conversion from to . /// The pointer to a handle. /// The result of the conversion. public static implicit operator HAMSISESSION(IntPtr h) => new(h); /// Implements the operator !=. /// The first handle. /// The second handle. /// The result of the operator. public static bool operator !=(HAMSISESSION h1, HAMSISESSION h2) => h1.handle != h2.handle; /// Implements the operator ==. /// The first handle. /// The second handle. /// The result of the operator. public static bool operator ==(HAMSISESSION h1, HAMSISESSION h2) => h1.handle == h2.handle; /// public override bool Equals(object obj) => (obj is IHandle h && handle == h.DangerousGetHandle()) || (obj is IntPtr p && handle == p); /// public override int GetHashCode() => handle.GetHashCode(); /// public IntPtr DangerousGetHandle() => handle; } /// Provides a for that is disposed using . public class SafeHAMSICONTEXT : SafeHANDLE { /// Initializes a new instance of the class and assigns an existing handle. /// An object that represents the pre-existing handle to use. /// /// to reliably release the handle during the finalization phase; otherwise, (not recommended). /// public SafeHAMSICONTEXT(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { } /// Initializes a new instance of the class. private SafeHAMSICONTEXT() : base() { } /// Performs an implicit conversion from to . /// The safe handle instance. /// The result of the conversion. public static implicit operator HAMSICONTEXT(SafeHAMSICONTEXT h) => h.handle; /// protected override bool InternalReleaseHandle() { AmsiUninitialize(handle); return true; } } /// Provides a for that is disposed using . public class SafeHAMSISESSION : SafeHANDLE { private SafeHAMSICONTEXT ctx; /// Initializes a new instance of the class and assigns an existing handle. /// An object that represents the pre-existing handle to use. /// /// to reliably release the handle during the finalization phase; otherwise, (not recommended). /// public SafeHAMSISESSION(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { } /// Initializes a new instance of the class. /// The context. public SafeHAMSISESSION(HAMSICONTEXT context) : base() => Open(Context = context); /// Initializes a new instance of the class. /// The name, version, or GUID string of the app calling the AMSI API. public SafeHAMSISESSION(string appName) : base() { AmsiInitialize(appName, out SafeHAMSICONTEXT hc).ThrowIfFailed(); Open(ctx = hc); } /// Initializes a new instance of the class. private SafeHAMSISESSION() : base() { } /// Gets or sets the handle of type HAMSICONTEXT that was initially received from AmsiInitialize. /// The context handle. public HAMSICONTEXT Context { get => ctx; set => ctx = new SafeHAMSICONTEXT((IntPtr)value, false); } /// Performs an implicit conversion from to . /// The safe handle instance. /// The result of the conversion. public static implicit operator HAMSISESSION(SafeHAMSISESSION h) => h.handle; /// protected override bool InternalReleaseHandle() { AmsiCloseSession(Context, handle); ctx?.Dispose(); return true; } private void Open(HAMSICONTEXT context) { AmsiOpenSessionInternal(context, out HAMSISESSION h).ThrowIfFailed(); SetHandle((IntPtr)h); } } } }