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.

106 lines
3.7 KiB

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