Vanara/PInvoke/AMSI/amsi.Interfaces.cs

626 lines
28 KiB
C#

using System;
using System.IO;
using System.Runtime.InteropServices;
using Vanara.Extensions;
using Vanara.InteropServices;
namespace Vanara.PInvoke
{
public static partial class AMSI
{
/// <summary>Represents a stream to be scanned.</summary>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nn-amsi-iamsistream
[PInvokeData("amsi.h", MSDNShortId = "NN:amsi.IAmsiStream")]
[ComImport, Guid("3e47f2e5-81d4-4d3b-897f-545096770373"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAmsiStream
{
/// <summary>Returns a requested attribute from the stream.</summary>
/// <param name="attribute">Specifies the type of attribute to be returned. See Remarks.</param>
/// <param name="dataSize">The size of the output buffer, <c>data</c>, in bytes.</param>
/// <param name="data">Buffer to receive the requested attribute. <c>data</c> must be set to its size in bytes.</param>
/// <param name="retData">
/// The number of bytes returned in <c>data</c>. If this method returns <c>E_NOT_SUFFICIENT_BUFFER</c>, <c>retData</c> contains
/// the number of bytes required.
/// </param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term><c>S_OK</c></term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term><c>E_NOTIMPL</c></term>
/// <term>The attribute is not supported.</term>
/// </item>
/// <item>
/// <term><c>E_NOT_SUFFICIENT_BUFFER</c></term>
/// <term>
/// The size of the output buffer, as indicated by <c>data</c>, is not large enough. <c>retData</c> contains the number of bytes required.
/// </term>
/// </item>
/// <item>
/// <term><c>E_INVALIDARG</c></term>
/// <term>One or more argument is invalid.</term>
/// </item>
/// <item>
/// <term><c>E_NOT_VALID_STATE</c></term>
/// <term>The object is not initialized.</term>
/// </item>
/// </list>
/// </returns>
/// <remarks>
/// <para>Depending on the attribute requested in <c>attribute</c>, the following data should be copied to <c>data</c>:</para>
/// <list type="table">
/// <listheader>
/// <term><c>attribute</c></term>
/// <term><c>data</c></term>
/// </listheader>
/// <item>
/// <term><c>AMSI_ATTRIBUTE_APP_NAME</c></term>
/// <term>The name, version, or GUID string of the calling application, copied from a <c>LPWSTR</c>.</term>
/// </item>
/// <item>
/// <term><c>AMSI_ATTRIBUTE_CONTENT_NAME</c></term>
/// <term>The filename, URL, unique script ID, or similar of the content, copied from a <c>LPWSTR</c>.</term>
/// </item>
/// <item>
/// <term><c>AMSI_ATTRIBUTE_CONTENT_SIZE</c></term>
/// <term>The size of the input, as a <c>ULONGLONG</c>.</term>
/// </item>
/// <item>
/// <term><c>AMSI_ATTRIBUTE_CONTENT_ADDRESS</c></term>
/// <term>The memory address if the content is fully loaded into memory.</term>
/// </item>
/// <item>
/// <term><c>AMSI_ATTRIBUTE_SESSION</c></term>
/// <term>
/// Session is used to associate different scan calls, such as if the contents to be scanned belong to the same original script.
/// Return <c>nullptr</c> if the content is self-contained.
/// </term>
/// </item>
/// </list>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iamsistream-getattribute HRESULT GetAttribute( [in]
// AMSI_ATTRIBUTE attribute, [in] ULONG dataSize, [out] unsigned char *data, [out] ULONG *retData );
[PreserveSig]
HRESULT GetAttribute(AMSI_ATTRIBUTE attribute, uint dataSize, [Out] IntPtr data, out uint retData);
/// <summary>Requests a buffer-full of content to be read.</summary>
/// <param name="position">The zero-based index into the content at which the read is to begin.</param>
/// <param name="size">The number of bytes to read from the content.</param>
/// <param name="buffer">Buffer into which the content is to be read.</param>
/// <param name="readSize">The number of bytes read into <c>buffer</c>.</param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term><c>S_OK</c></term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term><c>E_INVALIDARG</c></term>
/// <term>One or more argument is invalid.</term>
/// </item>
/// <item>
/// <term><c>E_NOT_VALID_STATE</c></term>
/// <term>The object is not initialized.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iamsistream-read HRESULT Read( [in] ULONGLONG position, [in]
// ULONG size, [out] unsigned char *buffer, [out] ULONG *readSize );
[PreserveSig]
HRESULT Read(ulong position, uint size, [Out] IntPtr buffer, out uint readSize);
}
/// <summary>Represents the antimalware product.</summary>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nn-amsi-iantimalware
[PInvokeData("amsi.h", MSDNShortId = "NN:amsi.IAntimalware")]
[ComImport, Guid("82d29c2e-f062-44e6-b5c9-3d9a2f24a2df"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), CoClass(typeof(CAntimalware))]
public interface IAntimalware
{
/// <summary>Scan a stream of content.</summary>
/// <param name="stream">The IAmsiStream stream to be scanned.</param>
/// <param name="result">The result of the scan. See AMSI_RESULT.</param>
/// <param name="provider">The IAntimalwareProvider provider of the antimalware product.</param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term><c>S_OK</c></term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term><c>E_INVALIDARG</c></term>
/// <term>One or more argument is invalid.</term>
/// </item>
/// <item>
/// <term><c>E_NOT_VALID_STATE</c></term>
/// <term>The object is not initialized.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalware-scan HRESULT Scan( [in] IAmsiStream *stream,
// [out] AMSI_RESULT *result, [out] IAntimalwareProvider **provider );
[PreserveSig]
HRESULT Scan([In] IAmsiStream stream, out AMSI_RESULT result, out IAntimalwareProvider provider);
/// <summary>Closes the session.</summary>
/// <param name="session">
/// <para>Type: ULONGLONG</para>
/// <para>The id/handle of the session to close.</para>
/// </param>
/// <returns>None</returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalware-closesession void CloseSession( [in] ULONGLONG
// session );
[PreserveSig]
void CloseSession(ulong session);
}
/// <summary>Represents the antimalware product.</summary>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nn-amsi-iantimalware2
[PInvokeData("amsi.h", MSDNShortId = "NN:amsi.IAntimalware2")]
[ComImport, Guid("301035b5-2d42-4f56-8c65-2dcaa7fb3cdc"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), CoClass(typeof(CAntimalware))]
public interface IAntimalware2 : IAntimalware
{
/// <summary>Scan a stream of content.</summary>
/// <param name="stream">The IAmsiStream stream to be scanned.</param>
/// <param name="result">The result of the scan. See AMSI_RESULT.</param>
/// <param name="provider">The IAntimalwareProvider provider of the antimalware product.</param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term><c>S_OK</c></term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term><c>E_INVALIDARG</c></term>
/// <term>One or more argument is invalid.</term>
/// </item>
/// <item>
/// <term><c>E_NOT_VALID_STATE</c></term>
/// <term>The object is not initialized.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalware-scan HRESULT Scan( [in] IAmsiStream *stream,
// [out] AMSI_RESULT *result, [out] IAntimalwareProvider **provider );
[PreserveSig]
new HRESULT Scan([In] IAmsiStream stream, out AMSI_RESULT result, out IAntimalwareProvider provider);
/// <summary>Closes the session.</summary>
/// <param name="session">
/// <para>Type: ULONGLONG</para>
/// <para>The id/handle of the session to close.</para>
/// </param>
/// <returns>None</returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalware-closesession void CloseSession( [in] ULONGLONG
// session );
[PreserveSig]
new void CloseSession(ulong session);
/// <summary>
/// Sends to the antimalware product a notification of an arbitrary operation. The notification doesn't imply the request of an
/// antivirus scan. Rather, <c>IAntimalware2::Notify</c> is designed to provide a quick and lightweight mechanism to communicate
/// to the antimalware product that an event has taken place. In general, the antimalware product should process the
/// notification, and return to the caller as quickly as possible.
/// </summary>
/// <param name="buffer">
/// <para>Type: <c>PVOID</c></para>
/// <para>The buffer that contains the notification data.</para>
/// </param>
/// <param name="length">
/// <para>Type: <c>ULONG</c></para>
/// <para>The length, in bytes, of the data to be read from buffer.</para>
/// </param>
/// <param name="contentName">
/// <para>Type: <c>LPCWSTR</c></para>
/// <para>The filename, URL, unique script ID, or similar of the content being scanned.</para>
/// </param>
/// <param name="appName">
/// <para>Type: <c>LPCWSTR</c></para>
/// <para>The name of the application sending the AMSI notification.</para>
/// </param>
/// <param name="pResult">
/// <para>Type: <c>AMSI_RESULT*</c></para>
/// <para>The result of the scan.</para>
/// </param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term>S_OK</term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term>E_INVALIDARG</term>
/// <term>One or more arguments is invalid.</term>
/// </item>
/// <item>
/// <term>E_NOT_VALID_STATE</term>
/// <term>The object isn't initialized.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalware2-notify HRESULT Notify( PVOID buffer, ULONG
// length, LPCWSTR contentName, LPCWSTR appName, AMSI_RESULT *pResult );
[PreserveSig]
HRESULT Notify([In] IntPtr buffer, uint length, [MarshalAs(UnmanagedType.LPWStr)] string contentName,
[MarshalAs(UnmanagedType.LPWStr)] string appName, out AMSI_RESULT pResult);
}
/// <summary>Represents the provider of the antimalware product.</summary>
/// <remarks>
/// <para>
/// As of Windows 10, version 1903, Windows has added a way to enable Authenticode signing checks for providers. The feature is
/// disabled by default, for both 32-bit and 64-bit processes. If you are creating a provider for test purposes, then you can enable
/// or disable sign checks by setting the following Windows Registry value appropriately. The value is a DWORD.
/// </para>
/// <para>
/// <code>Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AMSI\FeatureBits</code>
/// </para>
/// <list type="table">
/// <listheader>
/// <term>Value</term>
/// <term>Behavior</term>
/// </listheader>
/// <item>
/// <term>0x1</term>
/// <term>The signing check is disabled. This is the default behavior. You can also use this value, temporarily, while testing.</term>
/// </item>
/// <item>
/// <term>0x2</term>
/// <term>The check for Authenticode signing is enabled.</term>
/// </item>
/// </list>
/// <para>Deleting the registry value altogether behaves as if the value 0x1 were present.</para>
/// <para>
/// <para>Note</para>
/// <para>As a provider, you must use the
/// <code>/ac</code>
/// switch (with the SignTool) to cross-sign with an Authenticode certificate. Once you've signed your binary, you can then verify it
/// by using the SignTool and the
/// <code>/kp</code>
/// option. If the SignTool returns no error, then your binary is properly signed.
/// </para>
/// </para>
/// <para>
/// <para>Important</para>
/// <para>
/// Even though the Windows Registry value is not protected by the operating system, your computer's antivirus provider might protect
/// the value, thus making it write-protected.
/// </para>
/// </para>
/// <para>
/// To check whether or not your provider is loading, you can view code integrity events. Be sure to enable verbose logging of code
/// integrity diagnostic events. The event IDs to look for are 3040 and 3041. Here are some examples.
/// </para>
/// <para>
/// <code>Log Name: Microsoft-Windows-CodeIntegrity/Verbose Source: Microsoft-Windows-CodeIntegrity Date: M/DD/YYYY H:MM:SS PM Event ID: 3040 Task Category: (14) Level: Verbose Keywords: User: [DOMAIN_NAME]\Administrator Computer: [COMPUTER_NAME] Description: Code Integrity started retrieving the cached data of [PATH_AND_FILENAME] file. Event Xml: &lt;Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"&gt; &lt;System&gt; &lt;Provider Name="Microsoft-Windows-CodeIntegrity" Guid="{4ee76bd8-3cf4-44a0-a0ac-3937643e37a3}" /&gt; &lt;EventID&gt;3040&lt;/EventID&gt; &lt;Version&gt;0&lt;/Version&gt; &lt;Level&gt;5&lt;/Level&gt; &lt;Task&gt;14&lt;/Task&gt; &lt;Opcode&gt;1&lt;/Opcode&gt; &lt;Keywords&gt;0x4000000000000000&lt;/Keywords&gt; &lt;TimeCreated SystemTime="YYYY-MM-DDT02:26:48.875954700Z" /&gt; &lt;EventRecordID&gt;7&lt;/EventRecordID&gt; &lt;Correlation /&gt; &lt;Execution ProcessID="4972" ThreadID="7752" ProcessorID="1" KernelTime="14" UserTime="2" /&gt; &lt;Channel&gt;Microsoft-Windows-CodeIntegrity/Verbose&lt;/Channel&gt; &lt;Computer&gt;[COMPUTER_NAME]&lt;/Computer&gt; &lt;Security UserID="[USER_SID]" /&gt; &lt;/System&gt; &lt;EventData&gt; &lt;Data Name="FileNameLength"&gt;40&lt;/Data&gt; &lt;Data Name="FileNameBuffer"&gt;[PATH_AND_FILENAME]&lt;/Data&gt; &lt;/EventData&gt; &lt;/Event&gt;</code>
/// </para>
/// <para>
/// <code>Log Name: Microsoft-Windows-CodeIntegrity/Verbose Source: Microsoft-Windows-CodeIntegrity Date: M/DD/YYYY H:MM:SS PM Event ID: 3041 Task Category: (14) Level: Verbose Keywords: User: [DOMAIN_NAME]\Administrator Computer: [COMPUTER_NAME] Description: Code Integrity completed retrieval of file cache. Status 0xC0000225. Event Xml: &lt;Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"&gt; &lt;System&gt; &lt;Provider Name="Microsoft-Windows-CodeIntegrity" Guid="{4ee76bd8-3cf4-44a0-a0ac-3937643e37a3}" /&gt; &lt;EventID&gt;3041&lt;/EventID&gt; &lt;Version&gt;2&lt;/Version&gt; &lt;Level&gt;5&lt;/Level&gt; &lt;Task&gt;14&lt;/Task&gt; &lt;Opcode&gt;2&lt;/Opcode&gt; &lt;Keywords&gt;0x4000000000000000&lt;/Keywords&gt; &lt;TimeCreated SystemTime="YYYY-MM-DDT02:26:48.875964700Z" /&gt; &lt;EventRecordID&gt;8&lt;/EventRecordID&gt; &lt;Correlation /&gt; &lt;Execution ProcessID="4972" ThreadID="7752" ProcessorID="1" KernelTime="14" UserTime="2" /&gt; &lt;Channel&gt;Microsoft-Windows-CodeIntegrity/Verbose&lt;/Channel&gt; &lt;Computer&gt;[COMPUTER_NAME]&lt;/Computer&gt; &lt;Security UserID="[USER_SID]" /&gt; &lt;/System&gt; &lt;EventData&gt; &lt;Data Name="Status"&gt;0xc0000225&lt;/Data&gt; &lt;Data Name="CachedFlags"&gt;0x0&lt;/Data&gt; &lt;Data Name="CacheSource"&gt;0&lt;/Data&gt; &lt;Data Name="CachedPolicy"&gt;0&lt;/Data&gt; &lt;/EventData&gt; &lt;/Event&gt;</code>
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nn-amsi-iantimalwareprovider
[PInvokeData("amsi.h", MSDNShortId = "NN:amsi.IAntimalwareProvider")]
[ComImport, Guid("b2cabfe3-fe04-42b1-a5df-08d483d4d125"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAntimalwareProvider
{
/// <summary>Scan a stream of content.</summary>
/// <param name="stream">The IAmsiStream stream to be scanned.</param>
/// <param name="result">The result of the scan. See AMSI_RESULT.</param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term><c>S_OK</c></term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term><c>E_INVALIDARG</c></term>
/// <term>One or more argument is invalid.</term>
/// </item>
/// <item>
/// <term><c>E_NOT_VALID_STATE</c></term>
/// <term>The object is not initialized.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalwareprovider-scan HRESULT Scan( [in] IAmsiStream
// *stream, [out] AMSI_RESULT *result );
[PreserveSig]
HRESULT Scan(IAmsiStream stream, out AMSI_RESULT result);
/// <summary>Closes the session.</summary>
/// <param name="session">
/// <para>Type: ULONGLONG</para>
/// <para>The id/handle of the session to close.</para>
/// </param>
/// <returns>None</returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalwareprovider-closesession void CloseSession( [in]
// ULONGLONG session );
[PreserveSig]
void CloseSession(ulong session);
/// <summary>The name of the antimalware provider to be displayed.</summary>
/// <param name="displayName">A pointer to a <c>LPWSTR</c> that contains the display name.</param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term><c>S_OK</c></term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term><c>E_INVALIDARG</c></term>
/// <term>The argument is invalid.</term>
/// </item>
/// <item>
/// <term><c>E_NOT_VALID_STATE</c></term>
/// <term>The object is not initialized.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalwareprovider-displayname HRESULT DisplayName( [out]
// LPWSTR *displayName );
[PreserveSig]
HRESULT DisplayName([MarshalAs(UnmanagedType.LPWStr)] out string displayName);
}
/// <summary>Represents the provider of the antimalware product.</summary>
/// <remarks>See <c>Remarks</c> in the IAntimalwareProvider interface topic.</remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nn-amsi-iantimalwareprovider2
[PInvokeData("amsi.h", MSDNShortId = "NN:amsi.IAntimalwareProvider2")]
[ComImport, Guid("7c1e6570-3f73-4e0f-8ad4-98b94cd3290f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAntimalwareProvider2 : IAntimalwareProvider
{
/// <summary>Scan a stream of content.</summary>
/// <param name="stream">The IAmsiStream stream to be scanned.</param>
/// <param name="result">The result of the scan. See AMSI_RESULT.</param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term><c>S_OK</c></term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term><c>E_INVALIDARG</c></term>
/// <term>One or more argument is invalid.</term>
/// </item>
/// <item>
/// <term><c>E_NOT_VALID_STATE</c></term>
/// <term>The object is not initialized.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalwareprovider-scan HRESULT Scan( [in] IAmsiStream
// *stream, [out] AMSI_RESULT *result );
[PreserveSig]
new HRESULT Scan(IAmsiStream stream, out AMSI_RESULT result);
/// <summary>Closes the session.</summary>
/// <param name="session">
/// <para>Type: ULONGLONG</para>
/// <para>The id/handle of the session to close.</para>
/// </param>
/// <returns>None</returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalwareprovider-closesession void CloseSession( [in]
// ULONGLONG session );
[PreserveSig]
new void CloseSession(ulong session);
/// <summary>The name of the antimalware provider to be displayed.</summary>
/// <param name="displayName">A pointer to a <c>LPWSTR</c> that contains the display name.</param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term><c>S_OK</c></term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term><c>E_INVALIDARG</c></term>
/// <term>The argument is invalid.</term>
/// </item>
/// <item>
/// <term><c>E_NOT_VALID_STATE</c></term>
/// <term>The object is not initialized.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalwareprovider-displayname HRESULT DisplayName( [out]
// LPWSTR *displayName );
[PreserveSig]
new HRESULT DisplayName([MarshalAs(UnmanagedType.LPWStr)] out string displayName);
/// <summary>
/// Sends to the antimalware provider a notification of an arbitrary operation. The notification doesn't imply the request of an
/// antivirus scan. Rather, <c>IAntimalwareProvider2::Notify</c> 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.
/// </summary>
/// <param name="buffer">
/// <para>Type: <c>PVOID</c></para>
/// <para>The buffer that contains the notification data.</para>
/// </param>
/// <param name="length">
/// <para>Type: <c>ULONG</c></para>
/// <para>The length, in bytes, of the data to be read from buffer.</para>
/// </param>
/// <param name="contentName">
/// <para>Type: <c>LPCWSTR</c></para>
/// <para>The filename, URL, unique script ID, or similar of the content being scanned.</para>
/// </param>
/// <param name="appName">
/// <para>Type: <c>LPCWSTR</c></para>
/// <para>The name of the application sending the AMSI notification.</para>
/// </param>
/// <param name="pResult">
/// <para>Type: <c>AMSI_RESULT*</c></para>
/// <para>The result of the scan.</para>
/// </param>
/// <returns>
/// <para>This method can return one of these values.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term>S_OK</term>
/// <term>Success.</term>
/// </item>
/// <item>
/// <term>E_INVALIDARG</term>
/// <term>One or more arguments is invalid.</term>
/// </item>
/// <item>
/// <term>E_NOT_VALID_STATE</term>
/// <term>The object isn't initialized.</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalwareprovider2-notify HRESULT Notify( PVOID buffer,
// ULONG length, LPCWSTR contentName, LPCWSTR appName, AMSI_RESULT *pResult );
[PreserveSig]
HRESULT Notify(IntPtr buffer, uint length, [MarshalAs(UnmanagedType.LPWStr)] string contentName,
[MarshalAs(UnmanagedType.LPWStr)] string appName, out AMSI_RESULT pResult);
}
/// <summary>Memory based stream that implements IAmsiStream.</summary>
/// <seealso cref="System.IO.MemoryStream"/>
/// <seealso cref="Vanara.PInvoke.AMSI.IAmsiStream"/>
public class AmsiStream : NativeMemoryStream, IAmsiStream
{
private static readonly HRESULT E_INSUFF_BUFFER = HRESULT.HRESULT_FROM_WIN32(Win32Error.ERROR_INSUFFICIENT_BUFFER);
/// <summary>Initializes a new instance of the <see cref="AmsiStream"/> class.</summary>
public AmsiStream() : base()
{
}
/// <summary>Initializes a new instance of the <see cref="AmsiStream"/> class with an initial capacity.</summary>
/// <param name="capacity">The capacity.</param>
public AmsiStream(int capacity) : base(capacity)
{
}
/// <summary>Initializes a new instance of the <see cref="AmsiStream"/> class and inserts the contents of a buffer.</summary>
/// <param name="buffer">The buffer to copy.</param>
/// <param name="writable">if set to <see langword="true"/>, the stream is read-write; if <see langword="false"/>, it is read-only.</param>
public AmsiStream(byte[] buffer, bool writable) : base(new SafeCoTaskMemHandle(buffer), access: writable ? FileAccess.ReadWrite : FileAccess.Read)
{
}
/// <summary>Initializes a new instance of the <see cref="AmsiStream"/> class with file information.</summary>
/// <param name="file">The file information.</param>
/// <param name="writable">if set to <see langword="true"/>, the stream is read-write; if <see langword="false"/>, it is read-only.</param>
public AmsiStream(FileInfo file, bool writable) : this(file is null ? null : File.ReadAllBytes(file.FullName), writable) => ContentName = file.FullName;
/// <summary>Initializes a new instance of the <see cref="AmsiStream"/> class.</summary>
/// <param name="mem">The memory allocator used to create and extend the native memory.</param>
/// <param name="writable">if set to <see langword="true"/>, the stream is read-write; if <see langword="false"/>, it is read-only.</param>
public AmsiStream(SafeAllocatedMemoryHandle mem, bool writable) : this(mem.GetBytes(), writable)
{
}
/// <summary>Gets or sets the name, version, or GUID string of the calling application.</summary>
public string AppName { get; set; }
/// <summary>Gets or sets the filename, URL, unique script ID, or similar of the content.</summary>
public string ContentName { get; set; }
/// <summary>
/// Gets or sets the session is used to associate different scan calls, such as if the contents to be scanned belong to the
/// sample original script.
/// </summary>
public IntPtr Session { get; set; }
HRESULT IAmsiStream.GetAttribute(AMSI_ATTRIBUTE attribute, uint dataSize, IntPtr data, out uint retData)
{
byte[] bytes = attribute switch
{
AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_APP_NAME => StringHelper.GetBytes(AppName, true, CharSet.Unicode),
AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_CONTENT_NAME => StringHelper.GetBytes(ContentName, true, CharSet.Unicode),
AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_CONTENT_SIZE => BitConverter.GetBytes((ulong)Length),
AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_CONTENT_ADDRESS => IntPtr.Size == 8 ? BitConverter.GetBytes(Pointer.ToInt64()) : BitConverter.GetBytes(Pointer.ToInt32()),
AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_SESSION => IntPtr.Size == 8 ? BitConverter.GetBytes(Session.ToInt64()) : BitConverter.GetBytes(Session.ToInt32()),
_ => null,
};
if (bytes is not null)
{
retData = (uint)bytes.Length;
if (bytes.Length > dataSize)
return E_INSUFF_BUFFER;
Marshal.Copy(bytes, 0, data, bytes.Length);
return HRESULT.S_OK;
}
retData = 0;
return HRESULT.E_NOTIMPL;
}
HRESULT IAmsiStream.Read(ulong position, uint size, IntPtr buffer, out uint readSize)
{
readSize = 0;
if (buffer == IntPtr.Zero || (long)position > Length)
{
return HRESULT.E_INVALIDARG;
}
long bytesToRead = Math.Min(Length - (long)position, size);
if (bytesToRead > 0)
{
Pointer.Offset((long)position).CopyTo(buffer, bytesToRead);
}
readSize = (uint)bytesToRead;
return HRESULT.S_OK;
}
}
/// <summary>CLSID_Antimalware</summary>
[ComImport, Guid("fdb00e52-a214-4aa1-8fba-4357bb0072ec"), ClassInterface(ClassInterfaceType.None)]
public class CAntimalware { }
}
}