diff --git a/IPA.Loader/AntiMalware/AntiMalwareEngine.cs b/IPA.Loader/AntiMalware/AntiMalwareEngine.cs
new file mode 100644
index 00000000..9ca16586
--- /dev/null
+++ b/IPA.Loader/AntiMalware/AntiMalwareEngine.cs
@@ -0,0 +1,25 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IPA.AntiMalware
+{
+ public static class AntiMalwareEngine
+ {
+ public static IAntiMalware Engine { get; } = InitializeEngine();
+
+ private static IAntiMalware InitializeEngine()
+ {
+ IAntiMalware? engine = null;
+#if !NET35
+ engine = WindowsAntiMalware.TryInitialize();
+#endif
+ engine ??= new NoopAntiMalware();
+
+ return engine;
+ }
+ }
+}
diff --git a/IPA.Loader/AntiMalware/IAntiMalware.cs b/IPA.Loader/AntiMalware/IAntiMalware.cs
new file mode 100644
index 00000000..0d974a34
--- /dev/null
+++ b/IPA.Loader/AntiMalware/IAntiMalware.cs
@@ -0,0 +1,11 @@
+#nullable enable
+using System.IO;
+
+namespace IPA.AntiMalware
+{
+ public interface IAntiMalware
+ {
+ ScanResult ScanFile(FileInfo file);
+ ScanResult ScanData(byte[] data, string? contentName = null);
+ }
+}
diff --git a/IPA.Loader/AntiMalware/NoopAntiMalware.cs b/IPA.Loader/AntiMalware/NoopAntiMalware.cs
new file mode 100644
index 00000000..60609898
--- /dev/null
+++ b/IPA.Loader/AntiMalware/NoopAntiMalware.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IPA.AntiMalware
+{
+ internal class NoopAntiMalware : IAntiMalware
+ {
+ public ScanResult ScanData(byte[] data, string contentName = null) => ScanResult.NotDetected;
+ public ScanResult ScanFile(FileInfo file) => ScanResult.NotDetected;
+ }
+}
diff --git a/IPA.Loader/AntiMalware/ScanResult.cs b/IPA.Loader/AntiMalware/ScanResult.cs
new file mode 100644
index 00000000..ba17fde9
--- /dev/null
+++ b/IPA.Loader/AntiMalware/ScanResult.cs
@@ -0,0 +1,11 @@
+
+namespace IPA.AntiMalware
+{
+ public enum ScanResult
+ {
+ KnownSafe,
+ NotDetected,
+ Detected,
+ BlockedByPolicy
+ }
+}
diff --git a/IPA.Loader/AntiMalware/WinAPI/AmsiFileStream.cs b/IPA.Loader/AntiMalware/_HideInNet3/WinAPI/AmsiFileStream.cs
similarity index 100%
rename from IPA.Loader/AntiMalware/WinAPI/AmsiFileStream.cs
rename to IPA.Loader/AntiMalware/_HideInNet3/WinAPI/AmsiFileStream.cs
diff --git a/IPA.Loader/AntiMalware/WinAPI/AmsiMemoryStream.cs b/IPA.Loader/AntiMalware/_HideInNet3/WinAPI/AmsiMemoryStream.cs
similarity index 100%
rename from IPA.Loader/AntiMalware/WinAPI/AmsiMemoryStream.cs
rename to IPA.Loader/AntiMalware/_HideInNet3/WinAPI/AmsiMemoryStream.cs
diff --git a/IPA.Loader/AntiMalware/WinAPI/Constants.cs b/IPA.Loader/AntiMalware/_HideInNet3/WinAPI/Constants.cs
similarity index 53%
rename from IPA.Loader/AntiMalware/WinAPI/Constants.cs
rename to IPA.Loader/AntiMalware/_HideInNet3/WinAPI/Constants.cs
index e841cb6c..65dc96cc 100644
--- a/IPA.Loader/AntiMalware/WinAPI/Constants.cs
+++ b/IPA.Loader/AntiMalware/_HideInNet3/WinAPI/Constants.cs
@@ -9,6 +9,7 @@ namespace IPA.AntiMalware.WinAPI
{
internal static class AmsiConstants
{
- public static const string AppName = "BSIPA/" + Config.SelfConfig.IPAVersion;
+ public const string AppName = "BSIPA/" + Config.SelfConfig.IPAVersion;
+ public static readonly Guid CAntimalwareGuid = new("fdb00e52-a214-4aa1-8fba-4357bb0072ec");
}
}
diff --git a/IPA.Loader/AntiMalware/WinAPI/IAntimalware.cs b/IPA.Loader/AntiMalware/_HideInNet3/WinAPI/IAntimalware.cs
similarity index 100%
rename from IPA.Loader/AntiMalware/WinAPI/IAntimalware.cs
rename to IPA.Loader/AntiMalware/_HideInNet3/WinAPI/IAntimalware.cs
diff --git a/IPA.Loader/AntiMalware/_HideInNet3/WindowsAntiMalware.cs b/IPA.Loader/AntiMalware/_HideInNet3/WindowsAntiMalware.cs
new file mode 100644
index 00000000..d5bc671c
--- /dev/null
+++ b/IPA.Loader/AntiMalware/_HideInNet3/WindowsAntiMalware.cs
@@ -0,0 +1,64 @@
+#nullable enable
+using IPA.AntiMalware.WinAPI;
+using IPA.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IPA.AntiMalware
+{
+ internal class WindowsAntiMalware : IAntiMalware
+ {
+ internal static WindowsAntiMalware? TryInitialize()
+ {
+ try
+ {
+ return new();
+ }
+ catch (Exception e)
+ {
+ Logger.AntiMalware.Warn("Could not initialize antimalware engine:");
+ Logger.AntiMalware.Warn(e);
+ return null;
+ }
+ }
+
+ private readonly IAntimalware amInterface;
+
+ private WindowsAntiMalware()
+ {
+ var amType = Type.GetTypeFromCLSID(AmsiConstants.CAntimalwareGuid, true);
+ amInterface = (IAntimalware)Activator.CreateInstance(amType);
+ }
+
+ private static ScanResult ScanResultFromAmsiResult(AmsiResult result)
+ => result switch
+ {
+ AmsiResult.Clean => ScanResult.KnownSafe,
+ AmsiResult.NotDetected => ScanResult.NotDetected,
+ AmsiResult.Detected => ScanResult.Detected,
+ var a when a is >= AmsiResult.BlockedByAdminStart and <= AmsiResult.BlockedByAdminEnd => ScanResult.BlockedByPolicy,
+ _ => ScanResult.NotDetected,
+ };
+
+ public ScanResult ScanFile(FileInfo file)
+ {
+ using var stream = new AmsiFileStream(file, IntPtr.Zero);
+ amInterface.Scan(stream, out var result, out var provider);
+ Logger.AntiMalware.Debug($"Scanned file '{file}' with {provider.DisplayName()}, and got '{result}'");
+ return ScanResultFromAmsiResult(result);
+ }
+
+ public ScanResult ScanData(byte[] data, string? contentName = null)
+ {
+ contentName ??= $"unknown_data_{Guid.NewGuid()}";
+ using var stream = new AmsiMemoryStream(contentName, data, IntPtr.Zero);
+ amInterface.Scan(stream, out var result, out var provider);
+ Logger.AntiMalware.Debug($"Scanned data named '{contentName}' with {provider.DisplayName()}, and got '{result}'");
+ return ScanResultFromAmsiResult(result);
+ }
+ }
+}
diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj
index 6d3ff5e4..c14a9de5 100644
--- a/IPA.Loader/IPA.Loader.csproj
+++ b/IPA.Loader/IPA.Loader.csproj
@@ -70,8 +70,8 @@
-
-
+
+
diff --git a/IPA.Loader/Logging/Logger.cs b/IPA.Loader/Logging/Logger.cs
index 13d2d8c3..d89f4f50 100644
--- a/IPA.Loader/Logging/Logger.cs
+++ b/IPA.Loader/Logging/Logger.cs
@@ -33,6 +33,7 @@ namespace IPA.Logging
}
}
+ internal static Logger AntiMalware => log.GetChildLogger("AntiMalware");
internal static Logger updater => log.GetChildLogger("Updater");
internal static Logger libLoader => log.GetChildLogger("LibraryLoader");
internal static Logger injector => log.GetChildLogger("Injector");