v 0.1.2
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
// File: Utils/ConcurrentObject.cs
|
||||
|
||||
namespace WebmrAPI.Utils
|
||||
{
|
||||
public class ConcurrentObject
|
||||
{
|
||||
internal readonly object _lock = new object();
|
||||
|
||||
public void LockedSet<T>(ref T dst, T value)
|
||||
{
|
||||
lock (_lock) dst = value;
|
||||
}
|
||||
|
||||
public T LockedGet<T>(ref T src)
|
||||
{
|
||||
lock (_lock) return src;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// File: Utils/LazyConcurrentContainer.cs
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace WebmrAPI.Utils
|
||||
{
|
||||
public class LazyConcurrentContainer<T> : ConcurrentObject
|
||||
{
|
||||
private DateTime _lastScan = DateTime.MinValue;
|
||||
private DateTime _currentScan = DateTime.MinValue;
|
||||
private ConcurrentDictionary<long, T>? _container;
|
||||
|
||||
public DateTime LastUpdate
|
||||
{
|
||||
get => LockedGet(ref _lastScan);
|
||||
}
|
||||
|
||||
public TimeSpan Elapsed
|
||||
{
|
||||
get { lock (_lock) return _currentScan - _lastScan; }
|
||||
}
|
||||
|
||||
public ConcurrentDictionary<long, T>? Container
|
||||
{
|
||||
get => LockedGet(ref _container);
|
||||
set
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_lastScan = _currentScan;
|
||||
_currentScan = DateTime.UtcNow;
|
||||
_container = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<T>? Values
|
||||
{
|
||||
get => _container?.Values.ToList();
|
||||
}
|
||||
|
||||
public void SetContainer(Dictionary<long, T>? container)
|
||||
{
|
||||
Container = container != null ? new ConcurrentDictionary<long, T>(container) : null;
|
||||
}
|
||||
public void SetContainer(ConcurrentDictionary<long, T> container)
|
||||
{
|
||||
Container = container != null ? new ConcurrentDictionary<long, T>(container) : null;
|
||||
}
|
||||
public void CleanContainer()
|
||||
{
|
||||
Container = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
// File: Utils/WindowsProcess.cs
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text.Json.Serialization;
|
||||
using WebmrAPI.Exceptions;
|
||||
using WebmrAPI.Models;
|
||||
|
||||
namespace WebmrAPI.Utils
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
public class WindowsProcess : IDisposable
|
||||
{
|
||||
private IntPtr _ptr = IntPtr.Zero;
|
||||
private int _pid = 0;
|
||||
private bool _readAccess = false;
|
||||
private static int LastError { get => Marshal.GetLastWin32Error(); }
|
||||
private static uint MBISize { get => (uint)Marshal.SizeOf(typeof(MemoryBasicInformation)); }
|
||||
|
||||
[Flags]
|
||||
[JsonConverter(typeof(JsonEnumConverter<MemoryState>))]
|
||||
public enum MemoryState : uint
|
||||
{
|
||||
Commit = 0x00001000,
|
||||
Reserve = 0x00002000,
|
||||
Free = 0x00010000,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
[JsonConverter(typeof(JsonEnumConverter<MemoryType>))]
|
||||
public enum MemoryType : uint
|
||||
{
|
||||
Image = 0x01000000,
|
||||
Mapped = 0x00040000,
|
||||
Private = 0x00020000,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
[JsonConverter(typeof(JsonEnumConverter<MemoryPageProtectionState>))]
|
||||
public enum MemoryPageProtectionState : uint
|
||||
{
|
||||
NoAccess = 0x0001,
|
||||
ReadOnly = 0x0002,
|
||||
ReadWrite = 0x0004,
|
||||
WriteCopy = 0x0008,
|
||||
Execute = 0x0010,
|
||||
ExecuteRead = 0x0020,
|
||||
ExecuteReadWrite = 0x0040,
|
||||
ExecuteWriteCopy = 0x0080,
|
||||
Guard = 0x0100,
|
||||
NoCache = 0x0200,
|
||||
WriteCombine = 0x0400,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ModuleFilterFlags : uint
|
||||
{
|
||||
Default = 0x00,
|
||||
Modules32Bit = 0x01,
|
||||
Modules64Bit = 0x02,
|
||||
ModulesAll = Modules32Bit | Modules64Bit,
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-memory_basic_information
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MemoryBasicInformation
|
||||
{
|
||||
public IntPtr BaseAddress;
|
||||
public IntPtr AllocationBase;
|
||||
public uint AllocationProtect;
|
||||
public ushort PartitionId;
|
||||
public UIntPtr RegionSize;
|
||||
public MemoryState State;
|
||||
public MemoryPageProtectionState PageProtection;
|
||||
public MemoryType Type;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ModuleInformation
|
||||
{
|
||||
public IntPtr BaseAddress;
|
||||
public uint MemorySize;
|
||||
public IntPtr EntryPointAddress;
|
||||
public System.Text.StringBuilder Name;
|
||||
public System.Text.StringBuilder FileName;
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr OpenProcess(
|
||||
uint dwDesiredAccess,
|
||||
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
|
||||
int dwProcessId
|
||||
);
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-virtualqueryex
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern int VirtualQueryEx(
|
||||
IntPtr hProcess,
|
||||
IntPtr lpAddress,
|
||||
out MemoryBasicInformation lpBuffer,
|
||||
uint dwLength
|
||||
);
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocessmodulesex
|
||||
[DllImport("psapi.dll", SetLastError = true)]
|
||||
private static extern bool EnumProcessModulesEx(
|
||||
IntPtr hProcess,
|
||||
[Out] IntPtr[] lphModule,
|
||||
uint cb,
|
||||
out uint lpcbNeeded,
|
||||
ModuleFilterFlags dwFilterFlag
|
||||
);
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmodulefilenameexw
|
||||
[DllImport("psapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern uint GetModuleFileNameEx(
|
||||
IntPtr hProcess,
|
||||
IntPtr hModule,
|
||||
[Out] System.Text.StringBuilder lpFilename,
|
||||
uint nSize
|
||||
);
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmodulebasenamew
|
||||
[DllImport("psapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern uint GetModuleBaseName(
|
||||
IntPtr hProcess,
|
||||
IntPtr hModule,
|
||||
[Out] System.Text.StringBuilder lpBaseName,
|
||||
uint nSize
|
||||
);
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmoduleinformation
|
||||
[DllImport("psapi.dll", SetLastError = true)]
|
||||
private static extern bool GetModuleInformation(
|
||||
IntPtr hProcess,
|
||||
IntPtr hModule,
|
||||
out ModuleInformation lpmodinfo,
|
||||
uint cb
|
||||
);
|
||||
|
||||
public void ForeachMemoryRegion(Action<MemoryBasicInformation> callback, MemoryState filter = MemoryState.Commit | MemoryState.Reserve)
|
||||
{
|
||||
IntPtr cur = IntPtr.Zero;
|
||||
long next = 0;
|
||||
var regions = new Dictionary<long, MemoryRegionInfo>();
|
||||
|
||||
while ((next > cur.ToInt64() && next < 0x7fffffffffff) || next == 0)
|
||||
{
|
||||
MemoryBasicInformation info;
|
||||
|
||||
cur = new IntPtr(next);
|
||||
|
||||
if (VirtualQueryEx(_ptr, cur, out info, MBISize) == 0)
|
||||
{
|
||||
throw new MemoryRegionException($"WinAPI call 'VirtualQueryEx for PID {_pid} at address 0x{cur.ToInt64():X16}' failed.");
|
||||
}
|
||||
|
||||
if ((filter & info.State) != 0)
|
||||
{
|
||||
callback(info);
|
||||
}
|
||||
|
||||
next = info.BaseAddress.ToInt64() + (long)info.RegionSize.ToUInt64();
|
||||
}
|
||||
}
|
||||
|
||||
public void ForeachLoadedModule(Action<ModuleInformation> callback, ModuleFilterFlags filter = ModuleFilterFlags.ModulesAll, bool force = false, bool ignoreErrors = false)
|
||||
{
|
||||
if (!_readAccess && force)
|
||||
{
|
||||
Dispose();
|
||||
_readAccess = true;
|
||||
Open();
|
||||
}
|
||||
|
||||
IntPtr[] modules = new IntPtr[1024];
|
||||
uint nmemb;
|
||||
|
||||
if (EnumProcessModulesEx(_ptr, modules, (uint)(modules.Length * IntPtr.Size), out nmemb, filter))
|
||||
{
|
||||
uint count = nmemb / (uint)IntPtr.Size;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ModuleInformation info;
|
||||
|
||||
info.Name = new System.Text.StringBuilder(256);
|
||||
info.FileName = new System.Text.StringBuilder(1024);
|
||||
|
||||
if ((!GetModuleInformation(_ptr, modules[i], out info, (uint)(2 * IntPtr.Size + sizeof(uint))) ||
|
||||
GetModuleBaseName(_ptr, modules[i], info.Name, (uint)info.Name.Capacity) <= 0 ||
|
||||
GetModuleFileNameEx(_ptr, modules[i], info.FileName, (uint)info.FileName.Capacity) <= 0) && !ignoreErrors)
|
||||
{
|
||||
throw new GettingModuleInfoException($"Failed to get module info for module handle {modules[i]} in PID {_pid}. Error: {LastError}");
|
||||
}
|
||||
|
||||
callback(info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ProcessMonitorException($"EnumProcessModulesEx for PID {_pid} failed. Error: {LastError}");
|
||||
}
|
||||
}
|
||||
|
||||
private void Open()
|
||||
{
|
||||
var access = (uint)(_readAccess ? 0x00000010 : 0) | 0x00000400;
|
||||
_ptr = OpenProcess(access, false, _pid);
|
||||
|
||||
if (_ptr == IntPtr.Zero)
|
||||
{
|
||||
var errno = LastError;
|
||||
|
||||
if (errno == 5)
|
||||
{
|
||||
throw new ProcessAccessDeniedException($"Access denied to process {_pid}");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ProcessMonitorException($"WinAPI call 'OpenProcess for PID {_pid}' failed. Error: {errno}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WindowsProcess(int pid, bool readAccess = false)
|
||||
{
|
||||
_readAccess = readAccess;
|
||||
_pid = pid;
|
||||
Open();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_ptr != IntPtr.Zero) CloseHandle(_ptr);
|
||||
}
|
||||
|
||||
~WindowsProcess()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user