Created
October 2, 2016 17:39
-
-
Save b0urb4k1/fa044b48c59257825dd484b83849838e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Also note LDR_MODULE info here: http://stackoverflow.com/questions/4242469/detect-when-a-module-dll-is-unloaded | |
class LoaderNotification : IDisposable | |
{ | |
public enum Reason : uint | |
{ | |
Loaded = 1, | |
Unloaded = 2 | |
} | |
public class NotificationEventArgs : EventArgs | |
{ | |
public Reason Reason; | |
public string FullDllName; | |
} | |
public event EventHandler<NotificationEventArgs> LoadNotification; | |
#region PInvoke details | |
// Helper for UNICODE_STRING type - couldn't figure out how to do it simply with Marshaling | |
static class UnicodeString | |
{ | |
// Layout is: | |
// ushort Length; // Bytes | |
// ushort MaximumLength; // Bytes | |
// IntPtr buffer; | |
public static string ToString(IntPtr pUnicodeString) | |
{ | |
short length = (short)Marshal.PtrToStructure(pUnicodeString, typeof(short)); | |
IntPtr buffer = Marshal.ReadIntPtr(pUnicodeString, IntPtr.Size); // The offset is determined by the natural size for the struct packing | |
return Marshal.PtrToStringUni(buffer, length / 2); | |
} | |
} | |
// At the moment, _LDR_DLL_LOADED_NOTIFICATION_DATA and _LDR_DLL_UNLOADED_NOTIFICATION_DATA | |
// are the same, so we don't have to bother with the union. | |
// TODO: Check 64-bit packing | |
[StructLayout(LayoutKind.Sequential)] | |
struct Data | |
{ | |
public uint Flags; // Reserved. | |
public IntPtr FullDllName; // PCUNICODE_STRING // The full path name of the DLL module. | |
public IntPtr BaseDllName; // PCUNICODE_STRING // The base file name of the DLL module. | |
public IntPtr DllBase; // A pointer to the base address for the DLL in memory. | |
public uint SizeOfImage; // The size of the DLL image, in bytes. | |
} | |
enum NtStatus : uint | |
{ | |
// Success | |
Success = 0x00000000, | |
DllNotFound = 0xc0000135, | |
// Many, many others... | |
} | |
delegate void LdrNotification(Reason notificationReason, IntPtr pNotificationData, IntPtr context); | |
// Registers for notification when a DLL is first loaded. This notification occurs before dynamic linking takes place. | |
[DllImport("ntdll.dll")] | |
static extern uint /*NtStatus*/ LdrRegisterDllNotification( | |
uint flags, // This parameter must be zero. | |
LdrNotification notificationFunction, | |
IntPtr context, | |
out IntPtr cookie); | |
[DllImport("ntdll.dll")] | |
static extern uint /*NtStatus*/ LdrUnregisterDllNotification(IntPtr cookie); | |
#endregion | |
IntPtr _cookie; | |
LdrNotification _notificationDelegate; | |
public LoaderNotification() | |
{ | |
IntPtr context = IntPtr.Zero; // new IntPtr(12345); | |
_notificationDelegate = Notification; // To prevent GC of the delegate | |
var status = LdrRegisterDllNotification(0, _notificationDelegate, context, out _cookie); | |
if (status != 0) | |
{ | |
Debug.Print($"@@@@ LoaderNotification Result: {status}"); | |
throw new InvalidOperationException($"Error in LdrRegisterDlLNotification. Result: {status}"); | |
} | |
} | |
// WARNING! LoaderLock danger here | |
// LoadNotification event handler must be very careful, not load any other managed library etc... | |
void Notification(Reason notificationReason, IntPtr pNotificationData, IntPtr context) | |
{ | |
IntPtr pFullDllName = Marshal.ReadIntPtr(pNotificationData, IntPtr.Size); // The offset is determined by the natural size for the struct packing | |
string fullDllName = UnicodeString.ToString(pFullDllName); | |
NotificationEventArgs args = new NotificationEventArgs { Reason = notificationReason, FullDllName = fullDllName }; | |
LoadNotification?.Invoke(this, args); | |
} | |
#region IDisposable Support | |
// CONSIDER: We might not need the finalizer support ... | |
private bool disposedValue = false; // To detect redundant calls | |
protected virtual void Dispose(bool disposing) | |
{ | |
if (!disposedValue) | |
{ | |
if (disposing) | |
{ | |
// TODO: dispose managed state (managed objects). | |
} | |
if (_cookie != IntPtr.Zero) | |
{ | |
var status = LdrUnregisterDllNotification(_cookie); | |
Logger.Initialization.Verbose($"LoaderNotification LdrUnregisterDllNotification Result: {status}"); | |
} | |
disposedValue = true; | |
} | |
} | |
~LoaderNotification() | |
{ | |
// Do not change this code. Put cleanup code in Dispose(bool disposing) above. | |
Dispose(false); | |
} | |
// This code added to correctly implement the disposable pattern. | |
public void Dispose() | |
{ | |
// Do not change this code. Put cleanup code in Dispose(bool disposing) above. | |
Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
#endregion | |
/* | |
[StructLayout(LayoutKind.Sequential)] | |
struct UnicodeString | |
{ | |
// Helper for UNICODE_STRING type, with layout | |
ushort Length; // Bytes | |
ushort MaximumLength; // Bytes | |
IntPtr buffer; | |
public override string ToString() | |
{ | |
return Marshal.PtrToStringUni(buffer, Length / 2); | |
} | |
} | |
// At the moment, _LDR_DLL_LOADED_NOTIFICATION_DATA and _LDR_DLL_UNLOADED_NOTIFICATION_DATA | |
// are the same, so we don't have to bother with the union. | |
// TODO: Check 64-bit packing | |
[StructLayout(LayoutKind.Sequential)] | |
struct Data | |
{ | |
public uint Flags; // Reserved. | |
???? [MarshalAs(UnmanagedType.LPStruct, MarshalTypeRef = typeof(UnicodeString))] | |
public UnicodeString FullDllName; | |
???? [MarshalAs(UnmanagedType.LPStruct, MarshalTypeRef = typeof(UnicodeString))] | |
public UnicodeString BaseDllName; | |
//public IntPtr FullDllName; // PCUNICODE_STRING // The full path name of the DLL module. | |
//public IntPtr BaseDllName; // PCUNICODE_STRING // The base file name of the DLL module. | |
public IntPtr DllBase; // A pointer to the base address for the DLL in memory. | |
public uint SizeOfImage; // The size of the DLL image, in bytes. | |
} | |
delegate void LdrNotification(Reason notificationReason, [In] ref Data notificationData, IntPtr context); | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment