Al implementar un IAmsiStream para realizar un análisis con Windows Defender, en archivos de más de ~20 MB, falla y Value does not fall within the expected range. .
¿Qué le falta a esta implementación?
public class AmsiStream : IAmsiStream { private readonly Stream _input; private readonly string _name; private static readonly byte[] _nullPtr = new byte[Marshal.SizeOf(IntPtr.Zero)]; public AmsiStream(Stream input, string name) { _input = input ?? throw new ArgumentNullException(nameof(input)); _name = name ?? throw new ArgumentNullException(nameof(name)); } public int GetAttribute(AMSI_ATTRIBUTE attribute, int dataSize, byte[] data, out int retData) { const int E_NOTIMPL = unchecked((int)0x80004001); const int E_NOT_SUFFICIENT_BUFFER = unchecked((int)0x8007007A); byte[] bytes = { }; int retValue = 0; switch (attribute) { case AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_APP_NAME: bytes = Encoding.Unicode.GetBytes("TestAmsi" + "\0"); break; case AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_CONTENT_NAME: bytes = Encoding.Unicode.GetBytes(_name + "\0"); break; case AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_CONTENT_SIZE: bytes = BitConverter.GetBytes((ulong)_input.Length); break; case AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_SESSION: bytes = _nullPtr; break; case AMSI_ATTRIBUTE.AMSI_ATTRIBUTE_CONTENT_ADDRESS: retValue = E_NOTIMPL; break; default: retValue = E_NOTIMPL; break; } retData = 0; if (retValue == 0) { retData = bytes.Length; if (dataSize < bytes.Length) return E_NOT_SUFFICIENT_BUFFER; Array.Copy(bytes, data, bytes.Length); } return retValue; } public int Read(long position, int size, byte[] buffer, out int readSize) { _input.Seek(position, SeekOrigin.Begin); readSize = _input.Read(buffer, 0, size); return 0; } }Un caso de prueba es:
[Fact] public void TestWithLargeFile() { const long k = 1024; const long m = 1024 * k; const long fileSize = 21 * m; var c = new Random(42); var fileBuffer = new byte[fileSize]; c.NextBytes(fileBuffer); Equal(AMSI_RESULT.AMSI_RESULT_CLEAN, ScanInternal(new MemoryStream(fileBuffer), "test.file.txt")); } private AMSI_RESULT ScanInternal(Stream streamToCheck, string fileName) { var scanner = (IAntiMalware)new CAntiMalware(); var stream = new AmsiStream(streamToCheck, fileName); var result = scanner.Scan(stream, out AMSI_RESULT scanResult, out IAntiMalwareProvider _); if (result != (ulong)HResult.S_OK) { throw new InvalidOperationException($"Malware scan returned not OK: {result}"); } return scanResult; }Código fuente completo para esta prueba en este repositorio de github
En su lugar, use MalwareScanner de: https://github.com/NewOrbit/MalwareScan.AMSI
var scanner = new MalwareScanner("MyApplications Viruscanner"); var result = scanner.HasVirus(stream, filename);
o para escanear un segmento de bytes de archivo grande entrante por segmento de bytes, consulte:
WindowsCOMAntiMalware: https://github.com/bsmg/BeatSaber-IPA-Reloaded/blob/master/IPA.Loader/AntiMalware/_HideInNet3/WindowsCOMAntiMalware.cs
con 2 implementaciones de IAmsiStream:
AmsiMemoryStream: https://github.com/bsmg/BeatSaber-IPA-Reloaded/blob/master/IPA.Loader/AntiMalware/_HideInNet3/ComAPI/AmsiMemoryStream.cs AmsiFileStream: https://github.com/bsmg/BeatSaber-IPA -Reloaded/blob/master/IPA.Loader/AntiMalware/_HideInNet3/ComAPI/AmsiFileStream.cs
PD: el límite de 16 mb no se encuentra en los documentos oficiales, no creo que sea una limitación de IAmsiStream, IAmsiStream se pasa a IAntimalwareProvider, que es una implementación del proveedor y tal vez el problema esté ahí; debe probar con otro proveedor para ver si obtiene el mismo error:
No creo que pueda transmitir un archivo grande al proveedor de Windows Defender. Según este subproceso de github , parece haber un límite implementado por la plataforma de que si no maneja AMSI_ATTRIBUTE_CONTENT_ADDRESS en GetAttribute , el tamaño máximo de búfer pasado en el método de Read es de 16 MB. Y el propósito del método Read no es transmitir, sino que
Solicita un búfer lleno de contenido para leer.
El código de muestra (C++) proporcionado por Microsoft tampoco puede manejar archivos que superen este límite de tamaño.
Los documentos para desarrolladores de Microsoft declararon:
AMSI está diseñado en particular para combatir el "malware sin archivos"
cual
existe exclusivamente como un artefacto basado en la memoria de la computadora
según Wikipedia .
Para mí, si el tamaño del archivo es superior a 16 MB, puede cargarlo en la memoria y devolver la dirección del búfer manejando AMSI_ATTRIBUTE_CONTENT_ADDRESS . Y para archivos grandes, podría considerar usar un archivo mapeado en memoria :
public unsafe int GetAttribute(AMSI_ATTRIBUTE attribute, int dataSize, [Out]byte[] data, [Out]out int retData) { ... case AMSI_ATTRIBUTE_CONTENT_ADDRESS: retData = sizeof(IntPtr); if (dataSize < retData) { return E_NOT_SUFFICIENT_BUFFER; } // This is an example // Better to open in constructor and close in Dispose var mappedFile = MemoryMappedFile.CreateFromFile(_name, FileMode.Open); var mappedFileAccessor = mappedFile.CreateViewAccessor(0, fileSize); byte* fileContent = null; mappedFileAccessor.SafeMemoryMappedViewHandle.AcquirePointer(ref fileContent); fixed (byte* pData = data) { *(IntPtr*) pData = (IntPtr)fileContent; } return 0; ... }