You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
3.8 KiB

  1. #nullable enable
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace IPA.AntiMalware.ComAPI
  10. {
  11. internal class AmsiMemoryStream : IAmsiStream, IDisposable
  12. {
  13. private readonly string contentName;
  14. private readonly byte[] data;
  15. private readonly GCHandle dataHandle;
  16. private readonly IntPtr session;
  17. private bool disposedValue;
  18. public AmsiMemoryStream(string contentName, byte[] data, IntPtr session)
  19. {
  20. this.data = data;
  21. dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
  22. this.session = session;
  23. this.contentName = contentName;
  24. }
  25. public unsafe void GetAttribute([In] AmsiAttribute attribute, [In] uint dataSize, [Out] byte* buffer, out uint writtenData)
  26. {
  27. switch (attribute)
  28. {
  29. case AmsiAttribute.AppName:
  30. writtenData = WriteWString(AmsiConstants.AppName, dataSize, buffer);
  31. return;
  32. case AmsiAttribute.Session:
  33. *(IntPtr*)buffer = session;
  34. writtenData = (uint)sizeof(IntPtr);
  35. return;
  36. case AmsiAttribute.ContentName:
  37. writtenData = WriteWString(contentName, dataSize, buffer);
  38. return;
  39. case AmsiAttribute.ContentSize:
  40. *(ulong*)buffer = (ulong)data.Length;
  41. writtenData = sizeof(ulong);
  42. return;
  43. case AmsiAttribute.ContentAddress:
  44. // because our data is pinned, it can't move while this object exists so we can pass out the fixed address
  45. fixed (byte* dataAddr = data)
  46. {
  47. *(byte**)buffer = dataAddr;
  48. }
  49. writtenData = (uint)sizeof(IntPtr);
  50. return;
  51. default:
  52. throw new NotImplementedException(); // return e_notimpl
  53. }
  54. static unsafe uint WriteWString(string str, uint dataSize, byte* buffer)
  55. {
  56. fixed (char* name = str)
  57. {
  58. return (uint)Encoding.Unicode.GetBytes(name, str.Length, buffer, (int)dataSize);
  59. }
  60. }
  61. }
  62. public unsafe void Read([In] ulong position, [In] uint dataSize, [Out] byte* buffer, out uint readSize)
  63. {
  64. if (position >= (ulong)data.Length)
  65. {
  66. throw new EndOfStreamException();
  67. }
  68. fixed (byte* dataPtr = data)
  69. {
  70. var toRead = Math.Min((ulong)data.Length - position, dataSize);
  71. Buffer.MemoryCopy(dataPtr + position, buffer, dataSize, toRead);
  72. readSize = (uint)toRead;
  73. }
  74. }
  75. protected virtual void Dispose(bool disposing)
  76. {
  77. if (!disposedValue)
  78. {
  79. if (disposing)
  80. {
  81. // no managed stae to dispose
  82. }
  83. dataHandle.Free();
  84. disposedValue = true;
  85. }
  86. }
  87. ~AmsiMemoryStream()
  88. {
  89. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  90. Dispose(disposing: false);
  91. }
  92. public void Dispose()
  93. {
  94. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  95. Dispose(disposing: true);
  96. GC.SuppressFinalize(this);
  97. }
  98. }
  99. }