using System;
using System.IO;
using System.Runtime.InteropServices;
using Vanara.Extensions;
using Vanara.InteropServices;
namespace Vanara.PInvoke
{
public static partial class AMSI
{
/// Represents a stream to be scanned.
// 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
{
/// Returns a requested attribute from the stream.
/// Specifies the type of attribute to be returned. See Remarks.
/// The size of the output buffer, data, in bytes.
/// Buffer to receive the requested attribute. data must be set to its size in bytes.
///
/// The number of bytes returned in data. If this method returns E_NOT_SUFFICIENT_BUFFER, retData contains
/// the number of bytes required.
///
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_NOTIMPL
/// The attribute is not supported.
///
/// -
/// E_NOT_SUFFICIENT_BUFFER
///
/// The size of the output buffer, as indicated by data, is not large enough. retData contains the number of bytes required.
///
///
/// -
/// E_INVALIDARG
/// One or more argument is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object is not initialized.
///
///
///
///
/// Depending on the attribute requested in attribute, the following data should be copied to data:
///
///
/// attribute
/// data
///
/// -
/// AMSI_ATTRIBUTE_APP_NAME
/// The name, version, or GUID string of the calling application, copied from a LPWSTR.
///
/// -
/// AMSI_ATTRIBUTE_CONTENT_NAME
/// The filename, URL, unique script ID, or similar of the content, copied from a LPWSTR.
///
/// -
/// AMSI_ATTRIBUTE_CONTENT_SIZE
/// The size of the input, as a ULONGLONG.
///
/// -
/// AMSI_ATTRIBUTE_CONTENT_ADDRESS
/// The memory address if the content is fully loaded into memory.
///
/// -
/// AMSI_ATTRIBUTE_SESSION
///
/// Session is used to associate different scan calls, such as if the contents to be scanned belong to the same original script.
/// Return nullptr if the content is self-contained.
///
///
///
///
// 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);
/// Requests a buffer-full of content to be read.
/// The zero-based index into the content at which the read is to begin.
/// The number of bytes to read from the content.
/// Buffer into which the content is to be read.
/// The number of bytes read into buffer.
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_INVALIDARG
/// One or more argument is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object is not initialized.
///
///
///
// 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);
}
/// Represents the antimalware product.
// 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
{
/// Scan a stream of content.
/// The IAmsiStream stream to be scanned.
/// The result of the scan. See AMSI_RESULT.
/// The IAntimalwareProvider provider of the antimalware product.
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_INVALIDARG
/// One or more argument is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object is not initialized.
///
///
///
// 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);
/// Closes the session.
///
/// Type: ULONGLONG
/// The id/handle of the session to close.
///
/// None
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalware-closesession void CloseSession( [in] ULONGLONG
// session );
[PreserveSig]
void CloseSession(ulong session);
}
/// Represents the antimalware product.
// 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
{
/// Scan a stream of content.
/// The IAmsiStream stream to be scanned.
/// The result of the scan. See AMSI_RESULT.
/// The IAntimalwareProvider provider of the antimalware product.
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_INVALIDARG
/// One or more argument is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object is not initialized.
///
///
///
// 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);
/// Closes the session.
///
/// Type: ULONGLONG
/// The id/handle of the session to close.
///
/// None
// 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);
///
/// Sends to the antimalware product a notification of an arbitrary operation. The notification doesn't imply the request of an
/// antivirus scan. Rather, IAntimalware2::Notify 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.
///
///
/// Type: PVOID
/// The buffer that contains the notification data.
///
///
/// Type: ULONG
/// The length, in bytes, of the data to be read from buffer.
///
///
/// Type: LPCWSTR
/// The filename, URL, unique script ID, or similar of the content being scanned.
///
///
/// Type: LPCWSTR
/// The name of the application sending the AMSI notification.
///
///
/// Type: AMSI_RESULT*
/// The result of the scan.
///
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_INVALIDARG
/// One or more arguments is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object isn't initialized.
///
///
///
// 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);
}
/// Represents the provider of the antimalware product.
///
///
/// 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.
///
///
/// Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AMSI\FeatureBits
///
///
///
/// Value
/// Behavior
///
/// -
/// 0x1
/// The signing check is disabled. This is the default behavior. You can also use this value, temporarily, while testing.
///
/// -
/// 0x2
/// The check for Authenticode signing is enabled.
///
///
/// Deleting the registry value altogether behaves as if the value 0x1 were present.
///
/// Note
/// As a provider, you must use the
/// /ac
/// 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
/// /kp
/// option. If the SignTool returns no error, then your binary is properly signed.
///
///
///
/// Important
///
/// 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.
///
///
///
/// 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.
///
///
/// 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: <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"> <System> <Provider Name="Microsoft-Windows-CodeIntegrity" Guid="{4ee76bd8-3cf4-44a0-a0ac-3937643e37a3}" /> <EventID>3040</EventID> <Version>0</Version> <Level>5</Level> <Task>14</Task> <Opcode>1</Opcode> <Keywords>0x4000000000000000</Keywords> <TimeCreated SystemTime="YYYY-MM-DDT02:26:48.875954700Z" /> <EventRecordID>7</EventRecordID> <Correlation /> <Execution ProcessID="4972" ThreadID="7752" ProcessorID="1" KernelTime="14" UserTime="2" /> <Channel>Microsoft-Windows-CodeIntegrity/Verbose</Channel> <Computer>[COMPUTER_NAME]</Computer> <Security UserID="[USER_SID]" /> </System> <EventData> <Data Name="FileNameLength">40</Data> <Data Name="FileNameBuffer">[PATH_AND_FILENAME]</Data> </EventData> </Event>
///
///
/// 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: <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"> <System> <Provider Name="Microsoft-Windows-CodeIntegrity" Guid="{4ee76bd8-3cf4-44a0-a0ac-3937643e37a3}" /> <EventID>3041</EventID> <Version>2</Version> <Level>5</Level> <Task>14</Task> <Opcode>2</Opcode> <Keywords>0x4000000000000000</Keywords> <TimeCreated SystemTime="YYYY-MM-DDT02:26:48.875964700Z" /> <EventRecordID>8</EventRecordID> <Correlation /> <Execution ProcessID="4972" ThreadID="7752" ProcessorID="1" KernelTime="14" UserTime="2" /> <Channel>Microsoft-Windows-CodeIntegrity/Verbose</Channel> <Computer>[COMPUTER_NAME]</Computer> <Security UserID="[USER_SID]" /> </System> <EventData> <Data Name="Status">0xc0000225</Data> <Data Name="CachedFlags">0x0</Data> <Data Name="CacheSource">0</Data> <Data Name="CachedPolicy">0</Data> </EventData> </Event>
///
///
// 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
{
/// Scan a stream of content.
/// The IAmsiStream stream to be scanned.
/// The result of the scan. See AMSI_RESULT.
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_INVALIDARG
/// One or more argument is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object is not initialized.
///
///
///
// 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);
/// Closes the session.
///
/// Type: ULONGLONG
/// The id/handle of the session to close.
///
/// None
// https://docs.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-iantimalwareprovider-closesession void CloseSession( [in]
// ULONGLONG session );
[PreserveSig]
void CloseSession(ulong session);
/// The name of the antimalware provider to be displayed.
/// A pointer to a LPWSTR that contains the display name.
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_INVALIDARG
/// The argument is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object is not initialized.
///
///
///
// 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);
}
/// Represents the provider of the antimalware product.
/// See Remarks in the IAntimalwareProvider interface topic.
// 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
{
/// Scan a stream of content.
/// The IAmsiStream stream to be scanned.
/// The result of the scan. See AMSI_RESULT.
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_INVALIDARG
/// One or more argument is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object is not initialized.
///
///
///
// 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);
/// Closes the session.
///
/// Type: ULONGLONG
/// The id/handle of the session to close.
///
/// None
// 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);
/// The name of the antimalware provider to be displayed.
/// A pointer to a LPWSTR that contains the display name.
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_INVALIDARG
/// The argument is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object is not initialized.
///
///
///
// 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);
///
/// 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: PVOID
/// The buffer that contains the notification data.
///
///
/// Type: ULONG
/// The length, in bytes, of the data to be read from buffer.
///
///
/// Type: LPCWSTR
/// The filename, URL, unique script ID, or similar of the content being scanned.
///
///
/// Type: LPCWSTR
/// The name of the application sending the AMSI notification.
///
///
/// Type: AMSI_RESULT*
/// The result of the scan.
///
///
/// This method can return one of these values.
///
///
/// Return code
/// Description
///
/// -
/// S_OK
/// Success.
///
/// -
/// E_INVALIDARG
/// One or more arguments is invalid.
///
/// -
/// E_NOT_VALID_STATE
/// The object isn't initialized.
///
///
///
// 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);
}
/// Memory based stream that implements IAmsiStream.
///
///
public class AmsiStream : NativeMemoryStream, IAmsiStream
{
private static readonly HRESULT E_INSUFF_BUFFER = HRESULT.HRESULT_FROM_WIN32(Win32Error.ERROR_INSUFFICIENT_BUFFER);
/// Initializes a new instance of the class.
public AmsiStream() : base()
{
}
/// Initializes a new instance of the class with an initial capacity.
/// The capacity.
public AmsiStream(int capacity) : base(capacity)
{
}
/// Initializes a new instance of the class and inserts the contents of a buffer.
/// The buffer to copy.
/// if set to , the stream is read-write; if , it is read-only.
public AmsiStream(byte[] buffer, bool writable) : base(new SafeCoTaskMemHandle(buffer), access: writable ? FileAccess.ReadWrite : FileAccess.Read)
{
}
/// Initializes a new instance of the class with file information.
/// The file information.
/// if set to , the stream is read-write; if , it is read-only.
public AmsiStream(FileInfo file, bool writable) : this(file is null ? null : File.ReadAllBytes(file.FullName), writable) => ContentName = file.FullName;
/// Initializes a new instance of the class.
/// The memory allocator used to create and extend the native memory.
/// if set to , the stream is read-write; if , it is read-only.
public AmsiStream(SafeAllocatedMemoryHandle mem, bool writable) : this(mem.GetBytes(), writable)
{
}
/// Gets or sets the name, version, or GUID string of the calling application.
public string AppName { get; set; }
/// Gets or sets the filename, URL, unique script ID, or similar of the content.
public string ContentName { get; set; }
///
/// 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.
///
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;
}
}
/// CLSID_Antimalware
[ComImport, Guid("fdb00e52-a214-4aa1-8fba-4357bb0072ec"), ClassInterface(ClassInterfaceType.None)]
public class CAntimalware { }
}
}