Browse Source

Implement core Amsi types for antimalware integration

pull/72/head
Anairkoen Schno 3 years ago
parent
commit
c00085893b
Signed by: DaNike GPG Key ID: BEFB74D5F3FC4387
6 changed files with 542 additions and 238 deletions
  1. +113
    -0
      IPA.Loader/AntiMalware/WinAPI/AmsiFileStream.cs
  2. +114
    -0
      IPA.Loader/AntiMalware/WinAPI/AmsiMemoryStream.cs
  3. +14
    -0
      IPA.Loader/AntiMalware/WinAPI/Constants.cs
  4. +58
    -0
      IPA.Loader/AntiMalware/WinAPI/IAntimalware.cs
  5. +5
    -0
      IPA.Loader/IPA.Loader.csproj
  6. +238
    -238
      IPA.Loader/Loader/LibLoader.cs

+ 113
- 0
IPA.Loader/AntiMalware/WinAPI/AmsiFileStream.cs View File

@ -0,0 +1,113 @@
#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);
}
}
}

+ 114
- 0
IPA.Loader/AntiMalware/WinAPI/AmsiMemoryStream.cs View File

@ -0,0 +1,114 @@
#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 AmsiMemoryStream : IAmsiStream, IDisposable
{
private readonly string contentName;
private readonly byte[] data;
private readonly GCHandle dataHandle;
private readonly IntPtr session;
private bool disposedValue;
public AmsiMemoryStream(string contentName, byte[] data, IntPtr session)
{
this.data = data;
dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
this.session = session;
this.contentName = contentName;
}
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(contentName, dataSize, buffer);
return;
case AmsiAttribute.ContentSize:
*(ulong*)buffer = (ulong)data.Length;
writtenData = sizeof(ulong);
return;
case AmsiAttribute.ContentAddress:
// because our data is pinned, it can't move while this object exists so we can pass out the fixed address
fixed (byte* dataAddr = data)
{
*(byte**)buffer = dataAddr;
}
writtenData = (uint)sizeof(IntPtr);
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);
}
}
}
public unsafe void Read([In] ulong position, [In] uint dataSize, [Out] byte* buffer, out uint readSize)
{
if (position >= (ulong)data.Length)
{
throw new EndOfStreamException();
}
fixed (byte* dataPtr = data)
{
var toRead = Math.Min((ulong)data.Length - position, dataSize);
Buffer.MemoryCopy(dataPtr + position, buffer, dataSize, toRead);
readSize = (uint)toRead;
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// no managed stae to dispose
}
dataHandle.Free();
disposedValue = true;
}
}
~AmsiMemoryStream()
{
// 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);
}
}
}

+ 14
- 0
IPA.Loader/AntiMalware/WinAPI/Constants.cs View File

@ -0,0 +1,14 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.AntiMalware.WinAPI
{
internal static class AmsiConstants
{
public static const string AppName = "BSIPA/" + Config.SelfConfig.IPAVersion;
}
}

+ 58
- 0
IPA.Loader/AntiMalware/WinAPI/IAntimalware.cs View File

@ -0,0 +1,58 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace IPA.AntiMalware.WinAPI
{
[Guid("82d29c2e-f062-44e6-b5c9-3d9a2f24a2df")]
[ComVisible(true)]
internal interface IAntimalware
{
void Scan([In] IAmsiStream stream, [Out] out AmsiResult result, [Out] out IAntimalwareProvider provider);
void CloseSession([In] ulong session);
}
[Guid("3e47f2e5-81d4-4d3b-897f-545096770373")]
[ComVisible(true)]
internal interface IAmsiStream
{
unsafe void GetAttribute([In] AmsiAttribute attribute, [In] uint dataSize, [Out] byte* buffer, out uint writtenData);
unsafe void Read([In] ulong position, [In] uint dataSize, [Out] byte* buffer, out uint readSize);
}
[Guid("b2cabfe3-fe04-42b1-a5df-08d483d4d125")]
[ComVisible(true)]
internal interface IAntimalwareProvider
{
[return: MarshalAs(UnmanagedType.LPWStr)] string DisplayName();
AmsiResult Scan([In] IAmsiStream stream);
void CloseSession([In] ulong session);
}
internal enum AmsiResult
{
Clean = 0,
NotDetected = 1,
BlockedByAdminStart = 0x4000,
BlockedByAdminEnd = 0x4fff,
Detected = 32768
}
internal enum AmsiAttribute
{
AppName = 0,
ContentName = 1,
ContentSize = 2,
ContentAddress = 3,
Session = 4,
RedirectChainSize = 5,
RedirectChainAddress = 6,
AllSize = 7,
AllAddress = 8,
}
}

+ 5
- 0
IPA.Loader/IPA.Loader.csproj View File

@ -69,6 +69,11 @@
<None Include="Updating\BeatMods\*.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net35'">
<Compile Remove="AntiMalware\**" />
<None Include="AntiMalware\**" />
</ItemGroup>
<Import Project="..\Common.targets" />
</Project>

+ 238
- 238
IPA.Loader/Loader/LibLoader.cs View File

@ -1,248 +1,248 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Linq;
using IPA.Logging;
using IPA.Utilities;
using Mono.Cecil;
#if NET3
using Net3_Proxy;
using Directory = Net3_Proxy.Directory;
using Path = Net3_Proxy.Path;
using File = Net3_Proxy.File;
#endif
namespace IPA.Loader
{
internal class CecilLibLoader : BaseAssemblyResolver
{
private static readonly string CurrentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
private static readonly string CurrentAssemblyPath = Assembly.GetExecutingAssembly().Location;
public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
LibLoader.SetupAssemblyFilenames();
if (name.Name == CurrentAssemblyName)
return AssemblyDefinition.ReadAssembly(CurrentAssemblyPath, parameters);
if (LibLoader.FilenameLocations.TryGetValue($"{name.Name}.dll", out var path))
{
if (File.Exists(path))
return AssemblyDefinition.ReadAssembly(path, parameters);
}
else if (LibLoader.FilenameLocations.TryGetValue($"{name.Name}.{name.Version}.dll", out path))
{
if (File.Exists(path))
return AssemblyDefinition.ReadAssembly(path, parameters);
}
return base.Resolve(name, parameters);
}
}
internal static class LibLoader
{
internal static string LibraryPath => Path.Combine(Environment.CurrentDirectory, "Libs");
internal static string NativeLibraryPath => Path.Combine(LibraryPath, "Native");
internal static Dictionary<string, string> FilenameLocations;
internal static void Configure()
{
SetupAssemblyFilenames(true);
AppDomain.CurrentDomain.AssemblyResolve -= AssemblyLibLoader;
AppDomain.CurrentDomain.AssemblyResolve += AssemblyLibLoader;
}
internal static void SetupAssemblyFilenames(bool force = false)
{
if (FilenameLocations == null || force)
{
FilenameLocations = new Dictionary<string, string>();
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Linq;
using IPA.Logging;
using IPA.Utilities;
using Mono.Cecil;
#if NET3
using Net3_Proxy;
using Directory = Net3_Proxy.Directory;
using Path = Net3_Proxy.Path;
using File = Net3_Proxy.File;
#endif
namespace IPA.Loader
{
internal class CecilLibLoader : BaseAssemblyResolver
{
private static readonly string CurrentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
private static readonly string CurrentAssemblyPath = Assembly.GetExecutingAssembly().Location;
public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
LibLoader.SetupAssemblyFilenames();
if (name.Name == CurrentAssemblyName)
return AssemblyDefinition.ReadAssembly(CurrentAssemblyPath, parameters);
if (LibLoader.FilenameLocations.TryGetValue($"{name.Name}.dll", out var path))
{
if (File.Exists(path))
return AssemblyDefinition.ReadAssembly(path, parameters);
}
else if (LibLoader.FilenameLocations.TryGetValue($"{name.Name}.{name.Version}.dll", out path))
{
if (File.Exists(path))
return AssemblyDefinition.ReadAssembly(path, parameters);
}
return base.Resolve(name, parameters);
}
}
internal static class LibLoader
{
internal static string LibraryPath => Path.Combine(Environment.CurrentDirectory, "Libs");
internal static string NativeLibraryPath => Path.Combine(LibraryPath, "Native");
internal static Dictionary<string, string> FilenameLocations;
internal static void Configure()
{
SetupAssemblyFilenames(true);
AppDomain.CurrentDomain.AssemblyResolve -= AssemblyLibLoader;
AppDomain.CurrentDomain.AssemblyResolve += AssemblyLibLoader;
}
internal static void SetupAssemblyFilenames(bool force = false)
{
if (FilenameLocations == null || force)
{
FilenameLocations = new Dictionary<string, string>();
foreach (var fn in TraverseTree(LibraryPath, s => s != NativeLibraryPath))
{
if (FilenameLocations.ContainsKey(fn.Name))
Log(Logger.Level.Critical, $"Multiple instances of {fn.Name} exist in Libs! Ignoring {fn.FullName}");
if (FilenameLocations.ContainsKey(fn.Name))
Log(Logger.Level.Critical, $"Multiple instances of {fn.Name} exist in Libs! Ignoring {fn.FullName}");
else FilenameLocations.Add(fn.Name, fn.FullName);
}
if (!SetDefaultDllDirectories(LoadLibraryFlags.LOAD_LIBRARY_SEARCH_USER_DIRS | LoadLibraryFlags.LOAD_LIBRARY_SEARCH_SYSTEM32
| LoadLibraryFlags.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LoadLibraryFlags.LOAD_LIBRARY_SEARCH_APPLICATION_DIR))
{
var err = new Win32Exception();
Log(Logger.Level.Critical, $"Error configuring DLL search path");
Log(Logger.Level.Critical, err);
return;
}
static void AddDir(string path)
{
var retPtr = AddDllDirectory(path);
if (retPtr == IntPtr.Zero)
{
var err = new Win32Exception();
Log(Logger.Level.Warning, $"Could not add DLL directory {path}");
Log(Logger.Level.Warning, err);
}
}
if (Directory.Exists(NativeLibraryPath))
{
AddDir(NativeLibraryPath);
_ = TraverseTree(NativeLibraryPath, dir =>
{ // this is a terrible hack for iterating directories
AddDir(dir); return true;
}).All(f => true); // force it to iterate all
}
//var unityData = Directory.EnumerateDirectories(Environment.CurrentDirectory, "*_Data").First();
//AddDir(Path.Combine(unityData, "Plugins"));
foreach (var dir in Environment.GetEnvironmentVariable("path")
.Split(Path.PathSeparator)
}
if (!SetDefaultDllDirectories(LoadLibraryFlags.LOAD_LIBRARY_SEARCH_USER_DIRS | LoadLibraryFlags.LOAD_LIBRARY_SEARCH_SYSTEM32
| LoadLibraryFlags.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LoadLibraryFlags.LOAD_LIBRARY_SEARCH_APPLICATION_DIR))
{
var err = new Win32Exception();
Log(Logger.Level.Critical, $"Error configuring DLL search path");
Log(Logger.Level.Critical, err);
return;
}
static void AddDir(string path)
{
var retPtr = AddDllDirectory(path);
if (retPtr == IntPtr.Zero)
{
var err = new Win32Exception();
Log(Logger.Level.Warning, $"Could not add DLL directory {path}");
Log(Logger.Level.Warning, err);
}
}
if (Directory.Exists(NativeLibraryPath))
{
AddDir(NativeLibraryPath);
_ = TraverseTree(NativeLibraryPath, dir =>
{ // this is a terrible hack for iterating directories
AddDir(dir); return true;
}).All(f => true); // force it to iterate all
}
//var unityData = Directory.EnumerateDirectories(Environment.CurrentDirectory, "*_Data").First();
//AddDir(Path.Combine(unityData, "Plugins"));
foreach (var dir in Environment.GetEnvironmentVariable("path")
.Split(Path.PathSeparator)
.Select(Environment.ExpandEnvironmentVariables))
{
AddDir(dir);
}
}
}
public static Assembly AssemblyLibLoader(object source, ResolveEventArgs e)
{
var asmName = new AssemblyName(e.Name);
return LoadLibrary(asmName);
}
internal static Assembly LoadLibrary(AssemblyName asmName)
{
Log(Logger.Level.Debug, $"Resolving library {asmName}");
SetupAssemblyFilenames();
var testFile = $"{asmName.Name}.dll";
Log(Logger.Level.Debug, $"Looking for file {asmName.Name}.dll");
if (FilenameLocations.TryGetValue(testFile, out var path))
{
Log(Logger.Level.Debug, $"Found file {testFile} as {path}");
if (File.Exists(path))
return Assembly.LoadFrom(path);
Log(Logger.Level.Critical, $"but {path} no longer exists!");
}
else if (FilenameLocations.TryGetValue(testFile = $"{asmName.Name}.{asmName.Version}.dll", out path))
{
Log(Logger.Level.Debug, $"Found file {testFile} as {path}");
Log(Logger.Level.Warning, $"File {testFile} should be renamed to just {asmName.Name}.dll");
if (File.Exists(path))
return Assembly.LoadFrom(path);
Log(Logger.Level.Critical, $"but {path} no longer exists!");
}
Log(Logger.Level.Critical, $"No library {asmName} found");
return null;
}
internal static void Log(Logger.Level lvl, string message)
{ // multiple proxy methods to delay loading of assemblies until it's done
if (Logger.LogCreated)
AssemblyLibLoaderCallLogger(lvl, message);
else
if (((byte)lvl & (byte)StandardLogger.PrintFilter) != 0)
Console.WriteLine($"[{lvl}] {message}");
}
internal static void Log(Logger.Level lvl, Exception message)
{ // multiple proxy methods to delay loading of assemblies until it's done
if (Logger.LogCreated)
AssemblyLibLoaderCallLogger(lvl, message);
else
if (((byte)lvl & (byte)StandardLogger.PrintFilter) != 0)
Console.WriteLine($"[{lvl}] {message}");
}
private static void AssemblyLibLoaderCallLogger(Logger.Level lvl, string message) => Logger.libLoader.Log(lvl, message);
private static void AssemblyLibLoaderCallLogger(Logger.Level lvl, Exception message) => Logger.libLoader.Log(lvl, message);
// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree
private static IEnumerable<FileInfo> TraverseTree(string root, Func<string, bool> dirValidator = null)
{
if (dirValidator == null) dirValidator = s => true;
var dirs = new Stack<string>(32);
if (!Directory.Exists(root))
throw new ArgumentException("Directory does not exist", nameof(root));
dirs.Push(root);
while (dirs.Count > 0)
{
string currentDir = dirs.Pop();
string[] subDirs;
try
{
subDirs = Directory.GetDirectories(currentDir);
}
catch (UnauthorizedAccessException)
{ continue; }
catch (DirectoryNotFoundException)
{ continue; }
string[] files;
try
{
files = Directory.GetFiles(currentDir);
}
catch (UnauthorizedAccessException)
{ continue; }
catch (DirectoryNotFoundException)
{ continue; }
foreach (string str in subDirs)
if (dirValidator(str)) dirs.Push(str);
foreach (string file in files)
{
FileInfo nextValue;
try
{
nextValue = new FileInfo(file);
}
catch (FileNotFoundException)
{ continue; }
yield return nextValue;
}
}
}
}
}
}
public static Assembly AssemblyLibLoader(object source, ResolveEventArgs e)
{
var asmName = new AssemblyName(e.Name);
return LoadLibrary(asmName);
}
internal static Assembly LoadLibrary(AssemblyName asmName)
{
Log(Logger.Level.Debug, $"Resolving library {asmName}");
SetupAssemblyFilenames();
var testFile = $"{asmName.Name}.dll";
Log(Logger.Level.Debug, $"Looking for file {asmName.Name}.dll");
if (FilenameLocations.TryGetValue(testFile, out var path))
{
Log(Logger.Level.Debug, $"Found file {testFile} as {path}");
if (File.Exists(path))
return Assembly.LoadFrom(path);
Log(Logger.Level.Critical, $"but {path} no longer exists!");
}
else if (FilenameLocations.TryGetValue(testFile = $"{asmName.Name}.{asmName.Version}.dll", out path))
{
Log(Logger.Level.Debug, $"Found file {testFile} as {path}");
Log(Logger.Level.Warning, $"File {testFile} should be renamed to just {asmName.Name}.dll");
if (File.Exists(path))
return Assembly.LoadFrom(path);
Log(Logger.Level.Critical, $"but {path} no longer exists!");
}
Log(Logger.Level.Critical, $"No library {asmName} found");
return null;
}
internal static void Log(Logger.Level lvl, string message)
{ // multiple proxy methods to delay loading of assemblies until it's done
if (Logger.LogCreated)
AssemblyLibLoaderCallLogger(lvl, message);
else
if (((byte)lvl & (byte)StandardLogger.PrintFilter) != 0)
Console.WriteLine($"[{lvl}] {message}");
}
internal static void Log(Logger.Level lvl, Exception message)
{ // multiple proxy methods to delay loading of assemblies until it's done
if (Logger.LogCreated)
AssemblyLibLoaderCallLogger(lvl, message);
else
if (((byte)lvl & (byte)StandardLogger.PrintFilter) != 0)
Console.WriteLine($"[{lvl}] {message}");
}
private static void AssemblyLibLoaderCallLogger(Logger.Level lvl, string message) => Logger.libLoader.Log(lvl, message);
private static void AssemblyLibLoaderCallLogger(Logger.Level lvl, Exception message) => Logger.libLoader.Log(lvl, message);
// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree
private static IEnumerable<FileInfo> TraverseTree(string root, Func<string, bool> dirValidator = null)
{
if (dirValidator == null) dirValidator = s => true;
var dirs = new Stack<string>(32);
if (!Directory.Exists(root))
throw new ArgumentException("Directory does not exist", nameof(root));
dirs.Push(root);
while (dirs.Count > 0)
{
string currentDir = dirs.Pop();
string[] subDirs;
try
{
subDirs = Directory.GetDirectories(currentDir);
}
catch (UnauthorizedAccessException)
{ continue; }
catch (DirectoryNotFoundException)
{ continue; }
string[] files;
try
{
files = Directory.GetFiles(currentDir);
}
catch (UnauthorizedAccessException)
{ continue; }
catch (DirectoryNotFoundException)
{ continue; }
foreach (string str in subDirs)
if (dirValidator(str)) dirs.Push(str);
foreach (string file in files)
{
FileInfo nextValue;
try
{
nextValue = new FileInfo(file);
}
catch (FileNotFoundException)
{ continue; }
yield return nextValue;
}
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
#if NET461
#if NET461
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
#endif
private static extern IntPtr AddDllDirectory(string lpPathName);
[Flags]
private enum LoadLibraryFlags : uint
{
None = 0,
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,
}
#endif
private static extern IntPtr AddDllDirectory(string lpPathName);
[Flags]
private enum LoadLibraryFlags : uint
{
None = 0,
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,
}
[DllImport("kernel32.dll", SetLastError = true)]
#if NET461
#if NET461
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
#endif
private static extern bool SetDefaultDllDirectories(LoadLibraryFlags dwFlags);
}
}
#endif
private static extern bool SetDefaultDllDirectories(LoadLibraryFlags dwFlags);
}
}

Loading…
Cancel
Save