#nullable enable using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace IPA.AntiMalware.WinAPI { internal class AmsiFileStream : IAmsiStream, IDisposable { private readonly FileInfo file; private readonly IntPtr session; public AmsiFileStream(FileInfo file, IntPtr session) { this.file = file; this.session = session; } public unsafe void GetAttribute([In] AmsiAttribute attribute, [In] uint dataSize, [Out] byte* buffer, out uint writtenData) { switch (attribute) { case AmsiAttribute.AppName: writtenData = WriteWString(AmsiConstants.AppName, dataSize, buffer); return; case AmsiAttribute.Session: *(IntPtr*)buffer = session; writtenData = (uint)sizeof(IntPtr); return; case AmsiAttribute.ContentName: writtenData = WriteWString(file.FullName, dataSize, buffer); return; case AmsiAttribute.ContentSize: *(ulong*)buffer = (ulong)file.Length; writtenData = sizeof(ulong); return; default: throw new NotImplementedException(); // return e_notimpl } static unsafe uint WriteWString(string str, uint dataSize, byte* buffer) { fixed (char* name = str) { return (uint)Encoding.Unicode.GetBytes(name, str.Length, buffer, (int)dataSize); } } } private FileStream? stream; private bool disposedValue; private readonly byte[] readBuffer = new byte[1024]; public unsafe void Read([In] ulong position, [In] uint dataSize, [Out] byte* buffer, out uint readSize) { stream ??= file.OpenRead(); stream.Position = (long)position; var bytesToRead = dataSize; readSize = 0; while (bytesToRead > 0) { var bytesRead = stream.Read(readBuffer, 0, (int)Math.Min(readBuffer.Length, bytesToRead)); if (bytesRead == 0) { break; } fixed (byte* readBufferPtr = readBuffer) { Buffer.MemoryCopy(readBufferPtr, buffer + readSize, dataSize - readSize, bytesRead); } bytesToRead -= (uint)bytesRead; readSize += (uint)bytesRead; } } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { stream?.Dispose(); } disposedValue = true; } } // This does not have unmanagd resources, so it doesn't need to exist // ~AmsiFileStream() // { // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method // Dispose(disposing: false); // } public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } } }