mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Add separate handler for process modules (#1947)
This commit is contained in:
parent
4b73e26443
commit
7707da079c
2 changed files with 141 additions and 1 deletions
138
Dalamud/Support/CurrentProcessModules.cs
Normal file
138
Dalamud/Support/CurrentProcessModules.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
namespace Dalamud.Support;
|
||||||
|
|
||||||
|
/// <summary>Tracks the loaded process modules.</summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
|
internal sealed unsafe partial class CurrentProcessModules : IInternalDisposableService
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentQueue<string> LogQueue = new();
|
||||||
|
private static readonly SemaphoreSlim LogSemaphore = new(0);
|
||||||
|
|
||||||
|
private static Process? process;
|
||||||
|
private static nint cookie;
|
||||||
|
|
||||||
|
private readonly CancellationTokenSource logTaskStop = new();
|
||||||
|
private readonly Task logTask;
|
||||||
|
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private CurrentProcessModules()
|
||||||
|
{
|
||||||
|
var res = LdrRegisterDllNotification(0, &DllNotificationCallback, 0, out cookie);
|
||||||
|
if (res != STATUS.STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
Log.Error("{what}: LdrRegisterDllNotification failure: 0x{err}", nameof(CurrentProcessModules), res);
|
||||||
|
cookie = 0;
|
||||||
|
this.logTask = Task.CompletedTask;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logTask = Task.Factory.StartNew(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
while (!this.logTaskStop.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
LogSemaphore.Wait();
|
||||||
|
while (LogQueue.TryDequeue(out var log))
|
||||||
|
Log.Verbose(log);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
this.logTaskStop.Token,
|
||||||
|
TaskCreationOptions.LongRunning,
|
||||||
|
TaskScheduler.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum LdrDllNotificationReason : uint
|
||||||
|
{
|
||||||
|
Loaded = 1,
|
||||||
|
Unloaded = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets all the loaded modules, up to date.</summary>
|
||||||
|
public static ProcessModuleCollection ModuleCollection =>
|
||||||
|
(cookie == 0 ? Process.GetCurrentProcess() : process ??= Process.GetCurrentProcess()).Modules;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IInternalDisposableService.DisposeService()
|
||||||
|
{
|
||||||
|
if (Interlocked.Exchange(ref cookie, 0) is var copy and not 0)
|
||||||
|
LdrUnregisterDllNotification(copy);
|
||||||
|
if (!this.logTask.IsCompleted)
|
||||||
|
{
|
||||||
|
this.logTaskStop.Cancel();
|
||||||
|
LogSemaphore.Release();
|
||||||
|
this.logTask.Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly]
|
||||||
|
private static void DllNotificationCallback(
|
||||||
|
LdrDllNotificationReason reason,
|
||||||
|
LdrDllNotificationData* data,
|
||||||
|
nint context)
|
||||||
|
{
|
||||||
|
process = null;
|
||||||
|
var name = new ReadOnlySpan<char>(data->FullDllName->Buffer, data->FullDllName->Length / 2);
|
||||||
|
LogQueue.Enqueue(
|
||||||
|
$"[{nameof(CurrentProcessModules)}]: {reason}: {name} @ 0x{data->DllBase:X} ({data->SizeOfImage}:X bytes)");
|
||||||
|
LogSemaphore.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers for notification when a DLL is first loaded.
|
||||||
|
/// This notification occurs before dynamic linking takes place.<br /><br />
|
||||||
|
/// <a href="https://learn.microsoft.com/en-us/windows/win32/devnotes/ldrregisterdllnotification">Docs.</a>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flags">This parameter must be zero.</param>
|
||||||
|
/// <param name="notificationFunction">A pointer to a callback function to call when the DLL is loaded.</param>
|
||||||
|
/// <param name="context">A pointer to context data for the callback function.</param>
|
||||||
|
/// <param name="cookie">A pointer to a variable to receive an identifier for the callback function.
|
||||||
|
/// This identifier is used to unregister the notification callback function.</param>
|
||||||
|
/// <returns>Returns an NTSTATUS or error code.</returns>
|
||||||
|
[LibraryImport("ntdll.dll", SetLastError = true)]
|
||||||
|
private static partial int LdrRegisterDllNotification(
|
||||||
|
uint flags,
|
||||||
|
delegate* unmanaged<LdrDllNotificationReason, LdrDllNotificationData*, nint, void>
|
||||||
|
notificationFunction,
|
||||||
|
nint context,
|
||||||
|
out nint cookie);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cancels DLL load notification previously registered by calling the LdrRegisterDllNotification function.<br />
|
||||||
|
/// <br />
|
||||||
|
/// <a href="https://learn.microsoft.com/en-us/windows/win32/devnotes/ldrunregisterdllnotification">Docs.</a>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cookie">A pointer to the callback identifier received from the LdrRegisterDllNotification call
|
||||||
|
/// that registered for notification.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>Returns an NTSTATUS or error code.</returns>
|
||||||
|
[LibraryImport("ntdll.dll", SetLastError = true)]
|
||||||
|
private static partial int LdrUnregisterDllNotification(nint cookie);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct LdrDllNotificationData
|
||||||
|
{
|
||||||
|
/// <summary>Reserved.</summary>
|
||||||
|
public uint Flags;
|
||||||
|
|
||||||
|
/// <summary>The full path name of the DLL module.</summary>
|
||||||
|
public UNICODE_STRING* FullDllName;
|
||||||
|
|
||||||
|
/// <summary>The base file name of the DLL module.</summary>
|
||||||
|
public UNICODE_STRING* BaseDllName;
|
||||||
|
|
||||||
|
/// <summary>A pointer to the base address for the DLL in memory.</summary>
|
||||||
|
public nint DllBase;
|
||||||
|
|
||||||
|
/// <summary>The size of the DLL image, in bytes.</summary>
|
||||||
|
public uint SizeOfImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,8 @@ using Windows.Win32.Storage.FileSystem;
|
||||||
using Windows.Win32.System.Memory;
|
using Windows.Win32.System.Memory;
|
||||||
using Windows.Win32.System.Ole;
|
using Windows.Win32.System.Ole;
|
||||||
|
|
||||||
|
using Dalamud.Support;
|
||||||
|
|
||||||
using static TerraFX.Interop.Windows.Windows;
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
||||||
using Win32_PInvoke = Windows.Win32.PInvoke;
|
using Win32_PInvoke = Windows.Win32.PInvoke;
|
||||||
|
|
@ -191,7 +193,7 @@ public static class Util
|
||||||
public static unsafe string DescribeAddress(nint p)
|
public static unsafe string DescribeAddress(nint p)
|
||||||
{
|
{
|
||||||
Span<char> namebuf = stackalloc char[9];
|
Span<char> namebuf = stackalloc char[9];
|
||||||
var modules = Process.GetCurrentProcess().Modules;
|
var modules = CurrentProcessModules.ModuleCollection;
|
||||||
for (var i = 0; i < modules.Count; i++)
|
for (var i = 0; i < modules.Count; i++)
|
||||||
{
|
{
|
||||||
if (p < modules[i].BaseAddress) continue;
|
if (p < modules[i].BaseAddress) continue;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue