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.

111 lines
3.8 KiB

  1. #nullable enable
  2. using IPA.Logging;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Runtime.InteropServices;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. namespace IPA.AntiMalware
  11. {
  12. internal class WindowsWin32AntiMalware : IAntiMalware, IDisposable
  13. {
  14. internal static WindowsWin32AntiMalware? TryInitialize()
  15. {
  16. try
  17. {
  18. return new();
  19. }
  20. catch (Exception e)
  21. {
  22. Logger.AntiMalware.Warn("Could not initialize Win32-based antimalware engine:");
  23. Logger.AntiMalware.Warn(e);
  24. return null;
  25. }
  26. }
  27. private readonly IntPtr handle;
  28. private bool disposedValue;
  29. private WindowsWin32AntiMalware()
  30. {
  31. AmsiInitialize(AmsiConstants.AppName, out handle);
  32. }
  33. private static ScanResult ScanResultFromAmsiResult(AmsiResult result)
  34. => result switch
  35. {
  36. AmsiResult.Clean => ScanResult.KnownSafe,
  37. AmsiResult.NotDetected => ScanResult.NotDetected,
  38. AmsiResult.Detected => ScanResult.Detected,
  39. _ => ScanResult.MaybeMalware
  40. };
  41. public ScanResult ScanFile(FileInfo file)
  42. {
  43. var data = File.ReadAllBytes(file.FullName);
  44. return ScanData(data, file.FullName);
  45. }
  46. public ScanResult ScanData(byte[] data, string? contentName = null)
  47. {
  48. contentName ??= $"unknown_data_{Guid.NewGuid()}";
  49. AmsiScanBuffer(handle, data, (uint)data.Length, contentName, IntPtr.Zero, out var result);
  50. Logger.AntiMalware.Trace($"Scanned data named '{contentName}' and got '{result}'");
  51. return ScanResultFromAmsiResult(result);
  52. }
  53. protected virtual void Dispose(bool disposing)
  54. {
  55. if (!disposedValue)
  56. {
  57. if (disposing)
  58. {
  59. // we have no disposable managed state
  60. }
  61. AmsiUninitialize(handle);
  62. disposedValue = true;
  63. }
  64. }
  65. ~WindowsWin32AntiMalware()
  66. {
  67. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  68. Dispose(disposing: false);
  69. }
  70. public void Dispose()
  71. {
  72. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  73. Dispose(disposing: true);
  74. GC.SuppressFinalize(this);
  75. }
  76. [DllImport("amsi", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true)]
  77. #if !NET35
  78. [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
  79. #endif
  80. private static extern void AmsiInitialize([MarshalAs(UnmanagedType.LPWStr)] string appName, [Out] out IntPtr handle);
  81. [DllImport("amsi", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true)]
  82. #if !NET35
  83. [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
  84. #endif
  85. private static extern void AmsiUninitialize(IntPtr handle);
  86. [DllImport("amsi", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true)]
  87. #if !NET35
  88. [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
  89. #endif
  90. private static extern void AmsiScanBuffer(IntPtr context,
  91. [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] buffer, uint length,
  92. [MarshalAs(UnmanagedType.LPWStr)] string contentName,
  93. IntPtr session,
  94. [Out] out AmsiResult result);
  95. }
  96. }