Powerful x86/x64 Mini Hook-Engine






4.56/5 (5 votes)
This is an alternative for "Powerful x86/x64 Mini Hook-Engine"
- Download demo VB.Net noexe - 22.8 KB
- Download demo VB.Net - 165.4 KB
- Download demo CSharp noexe - 9.2 KB
- Download demo CSharp - 131 KB
Introduction
This code is both in VB.NET and C#. It allows developers who work exclusively with C# or VB.NET to fully implement the brilliant work of Daniel Pistelli's NtHookEngine.dll.
It is an extremely powerful way to implement hooks or "detours" because the only source code you need to work with is VB.Net or C#. It does not require C++ or any unmanaged coding languages to implement them. I recommend reading Daniel Pistelli's original article if you wish to see just how brilliant his engine is designed and built. This code should help people utilize the powerful engine with obsurd effeciency.
Background
Working with C++ is like driving a classic muscle car that turns everyone's head. Of course, without a modern suspension system, air conditioner or any modern safety features. Sure I can take C++ out for a spin which would be never forgotten, but my back would ache, my glands would sweat, and I might not make it without crashing.
Using the Code
Installation/Setup:
Required Prerequisite: Make sure NtHookEngine.dll
is in a folder on your path or the folder containing your compiled executable. Optional: If you would like the source or more details about NtHookEngine
then search for terms like (NtHookEngine
"Mini Hook-Engine" By "Daniel Pistelli") April of 2008 visit the link to the original article.
- The first thing note worthy is
NtDetourEngine
classes do not require and are not intended to be instantiated. In other words, their variables and methods are constants/shared which makes them independent from any instance of the object they define. In simpler words, you do not have to Create an instance of the object via calling the constructor in order to implement the Detours. - The detour method can also be a shared method, so you do not have to instantiate an object to implement the detour. As one can see in the example, no objects are instantiated to fully implement a detour.
- The class structure (as in the example)
Win32.MessageBoxTimeoutW
automatically uses dot net reflection information to set up the invoke method forMessageBoxTimeoutW
in theWin32.dll
..........(reduces typing and duplicate source code) marshalled_Delegate
is the only place marshal definitions are required..(reduces typing and duplicate source code)- Installing a detour/hook is a simple (as in the example) call to set the detour method to point to the "function address" for the detour.
VB Example Code: ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = AddressOf MyDetours.MyMessageBoxTimeoutDetour
C# Example Code:ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = MyDetours.MyMessageBoxTimeoutDetour;
- Uninstalling a detour/hook is a simple (as in the example) call to set the the detour method to NULL/Nothing
VB example: ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = Nothing
C# example:ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = null;
- Invoke is exactly as if you used Dllimport to declare the native method for the sake of making calls to it. The reason I implement Invoke in a class and not via the use of Dllimport is due to working with
NtHookEngine
and in the interest of (reducing typing and duplicate source code) especially marshaling; lets marshal once.
QUICKSTART TO IMPLEMENT YOUR OWN DETOUR:
- Make sure
NtHookEngine.dll
is in a folder on your path or the folder containing your compiled executable. - copy and rename: Public Class
MessageBoxTimeoutW
- edit
marshalled_Delegate
for your new class for the parameters and types the call to the native dll expects - edit callback_Delegate for your new parameter names (no marshal attributes, use
ByRef
(VB.Net)ref
(C#) so the detour can edit parameters) - debug your new detour/hook
Installing a detour/hook is a simple (as in the example) call to set the detour method to point to the "function address" for the detour.
ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = MyDetours.MyMessageBoxTimeoutDetour;
ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = AddressOf MyDetours.MyMessageBoxTimeoutDetour
Uninstaling a detour/hook is a simple (as in the example) call to set the the detour method to NULL/Nothing.
ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = null;
ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = Nothing
Notice how the code uses;
to obtain a pointer to a method.System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate
I will update the article to be more helpful based on comments so please comment if you have ideas but please be as specific as you can.
The code in detail:
public class ntDetourEngine
{
public delegate int GenericDelegate(params object[] VarArg);
[System.Runtime.InteropServices.DllImport("NtHookEngine.dll",
SetLastError = true, EntryPoint = "HookFunction")]
public extern static int SetDetourPtr(System.IntPtr native_ptr, System.IntPtr detour_ptr);
[System.Runtime.InteropServices.DllImport("NtHookEngine.dll",
SetLastError = true, EntryPoint = "UnhookFunction")]
public extern static int ResetDetourPtr(System.IntPtr native_ptr);
[System.Runtime.InteropServices.DllImport("NtHookEngine.dll",
SetLastError = true, EntryPoint = "GetOriginalFunction")]
public extern static System.IntPtr GetOriginalMethodPtr(System.IntPtr detour_ptr);
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
internal static extern bool FreeLibrary(System.IntPtr hModule);
[System.Runtime.InteropServices.DllImport("kernel32.dll",
CharSet = System.Runtime.InteropServices.CharSet.Unicode, SetLastError = true)]
internal static extern System.IntPtr LoadLibraryW([System.Runtime.InteropServices.MarshalAs(
System.Runtime.InteropServices.UnmanagedType.LPWStr)] string lpFileName);
[System.Runtime.InteropServices.DllImport("Kernel32", EntryPoint =
"GetProcAddress", CharSet = System.Runtime.InteropServices.CharSet.Ansi,
CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall, SetLastError = true)]
public extern static System.IntPtr GetProcAddress(System.IntPtr handle,
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]string funcname);
private static System.Collections.SortedList libraryHandles = new System.Collections.SortedList();
private static System.Collections.SortedList methodHandles = new System.Collections.SortedList();
private class SafeLibraryHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
public SafeLibraryHandle(string libraryName)
: base(true)
{
if (!libraryName.Equals(""))
base.SetHandle(LoadLibraryW(libraryName));
}
protected override bool ReleaseHandle()
{
bool result = false;
if (((!base.IsInvalid) && (!base.IsClosed)))
{
result = FreeLibrary(base.handle);
}
return result;
}
}
private static System.IntPtr methodHandle(string libraryName, string methodName)
{
System.IntPtr result = System.IntPtr.Zero;
System.IntPtr l = libraryHandle(libraryName);
if (!l.Equals(System.IntPtr.Zero))
{
result = GetProcAddress(l, methodName);
}
return result;
}
private static System.IntPtr libraryHandle(string libraryName)
{
System.IntPtr result = System.IntPtr.Zero;
string k = libraryName.ToUpper();
if (!libraryHandles.ContainsKey(k))
{
SafeLibraryHandle l = new SafeLibraryHandle(libraryName);
libraryHandles.Add(k, l);
}
if (libraryHandles.ContainsKey(k))
{
object o = libraryHandles[k];
if (o is SafeLibraryHandle)
{
result = ((SafeLibraryHandle)o).DangerousGetHandle();
}
}
return result;
}
public class Kernel
{
private static string libraryName = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name + ".dll";
}
public class User32
{
private static string libraryName = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name + ".dll";
public class MessageBoxTimeoutW
{
public delegate bool callback_Delegate(object Sender, ref System.IntPtr hWnd, ref string lpText,
ref string lpCaption, ref int uType, ref ushort wLanguageId, ref int dwMilliseconds);
private delegate int marshalled_Delegate(System.IntPtr hWnd, [System.Runtime.InteropServices.MarshalAs(
System.Runtime.InteropServices.UnmanagedType.LPWStr)]string lpText,
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
string lpCaption, [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]int
uType, [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.U2)]ushort
wLanguageId, [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)]int dwMilliseconds);
private static string methodName = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name;
private static System.IntPtr methodPtr = methodHandle(libraryName, methodName);
//private static System.IntPtr methodPtrInvoke =
// System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(methodDelegate);
private static System.Delegate methodDelegate = new marshalled_Delegate(Invoke);
private static System.Delegate detourDelegate = new marshalled_Delegate(Detour);
private static System.IntPtr detourPtrInvoke =
System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(detourDelegate);
private static callback_Delegate fDetour = null;
public static int Invoke(System.IntPtr hWnd, string lpText, string lpCaption,
int uType, ushort wLanguageId, int dwMilliseconds)
{
int result = 0;
marshalled_Delegate MethodToInvoke;
if (!(methodPtr.Equals(System.IntPtr.Zero)))
{
MethodToInvoke = (marshalled_Delegate)System.Runtime.InteropServices.Marshal.
GetDelegateForFunctionPointer(methodPtr, typeof(marshalled_Delegate));
result = MethodToInvoke(hWnd, lpText, lpCaption, uType, wLanguageId, dwMilliseconds);
}
return result;
}
private static int Detour(System.IntPtr hWnd, string lpText, _
string lpCaption, int uType, ushort wLanguageId, int dwMilliseconds)
{
int result = 0;
bool fSuppress = false;
if ((((fDetour != null)) && (fDetour is callback_Delegate)))
{
fDetour((object)System.Reflection.MethodBase.GetCurrentMethod(), ref hWnd,
ref lpText, ref lpCaption, ref uType, ref wLanguageId, ref dwMilliseconds);
if (!fSuppress)
{
marshalled_Delegate MethodToInvoke;
MethodToInvoke = (marshalled_Delegate)_
System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(
GetOriginalMethodPtr(detourPtrInvoke), typeof(marshalled_Delegate));
result = MethodToInvoke(hWnd, lpText, lpCaption, uType, wLanguageId, dwMilliseconds);
}
}
return result;
}
public static callback_Delegate DetourMethod
{
get { return fDetour; }
set
{
if ((((fDetour != null)) || (value == null)))
{
ResetDetourPtr(methodPtr);
}
fDetour = value;
if ((fDetour != null))
{
SetDetourPtr(methodPtr, detourPtrInvoke);
}
}
}
}
}
}
public class MyDetours
{
public static bool MyMessageBoxTimeoutDetour(object Sender, ref System.IntPtr hWnd,
ref string lpText, ref string lpCaption, ref int uType, ref ushort wLanguageId, ref int dwMilliseconds)
{
lpText = "My Detour Changed Text!!! " + (char)13 + (char)10 +
"The original text was:" + (char)13 + (char)10 + lpText;
return false;
}
}
namespace ConsoleApplicationNtHookEngine
{
class Program
{
static void Main(string[] args)
{
{
ntDetourEngine.User32.MessageBoxTimeoutW.Invoke(System.IntPtr.Zero,
"Invoking the original MessageBoxTimeoutW", "This is the title.", 64, 0, 0);
ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = MyDetours.MyMessageBoxTimeoutDetour; //Turning on the detour/hook
ntDetourEngine.User32.MessageBoxTimeoutW.Invoke(System.IntPtr.Zero,
"Invoking the original MessageBoxTimeoutW", "This is the title.", 64, 0, 0);
ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = null; //Turning off the detour/hook
ntDetourEngine.User32.MessageBoxTimeoutW.Invoke(System.IntPtr.Zero,
"Invoking the original MessageBoxTimeoutW", "This is the title.", 64, 0, 0);
System.Console.ReadLine();
}
}
}
}
Public Class ntDetourEngine
Public Delegate Function GenericDelegate(ByRef VarArg() As Object) As Boolean
Private Declare Unicode Function SetDetourPtr Lib "NtHookEngine.dll" _
Alias "HookFunction" (ByVal native_ptr As IntPtr, ByVal detour_pointer As IntPtr) As Integer
Private Declare Unicode Function ResetDetourPtr Lib "NtHookEngine.dll" _
Alias "UnhookFunction" (ByVal native_ptr As IntPtr) As Integer
Private Declare Unicode Function GetOriginalMethodPtr Lib "NtHookEngine.dll" _
Alias "GetOriginalFunction" (ByVal detour_ptr As IntPtr) As IntPtr
Private Declare Unicode Function LoadLibraryW Lib "Kernel32.dll" _
(ByVal libFileName As String) As IntPtr ' Note that LoadLibrary returns 0 on failure
Private Declare Ansi Function GetProcAddress Lib "Kernel32.dll" (ByVal hModule As IntPtr, ByVal procname As String) As IntPtr
Private Declare Auto Function FreeLibrary Lib "Kernel32.dll" (ByVal hModule As IntPtr) _
As <System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
Private Shared libraryHandles As New System.Collections.SortedList
Private Shared methodHandles As New System.Collections.SortedList
Private Class SafeLibraryHandle
Inherits Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
Public Sub New(ByVal libraryName As String)
MyBase.New(True)
If Not libraryName.Equals("") Then MyBase.SetHandle(LoadLibraryW(libraryName))
End Sub
Protected Overrides Function ReleaseHandle() As Boolean
Dim result As Boolean = False
If ((Not MyBase.IsInvalid) AndAlso (Not MyBase.IsClosed)) Then
result = FreeLibrary(MyBase.handle)
End If
Return result
End Function
End Class
Private Shared Function methodHandle(ByVal libraryName As String, ByVal methodName As String) As IntPtr
Dim result As IntPtr = IntPtr.Zero
Dim l As IntPtr = libraryHandle(libraryName)
If Not l.Equals(IntPtr.Zero) Then
result = GetProcAddress(l, methodName)
End If
Return result
End Function
Private Shared ReadOnly Property libraryHandle(ByVal libraryName As String) As IntPtr
Get
Dim result As IntPtr = IntPtr.Zero
Dim k As String = libraryName.ToUpper
If Not libraryHandles.ContainsKey(k) Then
Dim l As New SafeLibraryHandle(libraryName)
libraryHandles.Add(k, l)
End If
If libraryHandles.ContainsKey(k) Then
Dim o As Object = libraryHandles(k)
If TypeOf o Is SafeLibraryHandle Then
result = CType(o, SafeLibraryHandle).DangerousGetHandle
End If
End If
Return result
End Get
End Property
Public Class [Kernel]
Private Shared libraryName As String = _
System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name & ".dll"
End Class
Public Class [User32]
Private Shared libraryName As String = _
System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name & ".dll"
Public Class MessageBoxTimeoutW
Public Delegate Function callback_Delegate(ByVal Sender As System.Reflection.MethodBase, _
ByRef hWnd As IntPtr, ByRef lpText As String, ByRef lpCaption As String, _
ByRef uType As Integer, ByRef wLanguageId As UInt16, ByRef dwMilliseconds As Integer) As Boolean
Private Delegate Function marshalled_Delegate(ByVal hWnd As IntPtr, _
<System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)> _
ByVal lpText As String, <System.Runtime.InteropServices.MarshalAs(_
System.Runtime.InteropServices.UnmanagedType.LPWStr)> ByVal lpCaption As String, _
<System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)> _
ByVal uType As Integer, <System.Runtime.InteropServices.MarshalAs(_
System.Runtime.InteropServices.UnmanagedType.U2)> ByVal wLanguageId As UInt16, _
<System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.U4)> _
ByVal dwMilliseconds As Integer) As Integer
Private Shared methodName As String = _
System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name 'Me.GetType.Name '"MessageBoxTimeOutW"
Private Shared methodPtr As IntPtr = methodHandle(libraryName, methodName)
Private Shared methodDelegate As [Delegate] = CType(New marshalled_Delegate(AddressOf Invoke), [Delegate])
'Private Shared methodPtrInvoke As IntPtr = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(methodDelegate)
Private Shared detourDelegate As [Delegate] = CType(New marshalled_Delegate(AddressOf Detour), [Delegate])
Private Shared detourPtrInvoke As IntPtr = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(detourDelegate)
Private Shared fDetour As callback_Delegate = Nothing
Public Shared Function Invoke(ByVal ParamArray VarArg() As Object) As Integer
Return CType(System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(methodPtr, _
GetType(marshalled_Delegate)), marshalled_Delegate)(VarArg(0), _
VarArg(1), VarArg(2), VarArg(3), VarArg(4), VarArg(5))
End Function
Private Shared Function Detour(ByVal ParamArray VarArg() As Object)
Dim fSuppress As Boolean = False
If ((Not fDetour Is Nothing) AndAlso (TypeOf fDetour Is callback_Delegate)) Then
fSuppress = fDetour(System.Reflection.MethodBase.GetCurrentMethod(), _
VarArg(0), VarArg(1), VarArg(2), VarArg(3), VarArg(4), VarArg(5))
End If
Dim result As Integer = 0
If Not fSuppress Then result = CType(System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(_
GetOriginalMethodPtr(detourPtrInvoke), GetType(marshalled_Delegate)), _
marshalled_Delegate)(VarArg(0), VarArg(1), VarArg(2), VarArg(3), VarArg(4), VarArg(5))
Return result
End Function
Public Shared Property DetourMethod() As callback_Delegate
Get
Return fDetour
End Get
Set(ByVal value As callback_Delegate)
If ((Not fDetour Is Nothing) OrElse (value Is Nothing)) Then
ResetDetourPtr(methodPtr)
End If
fDetour = value
If Not fDetour Is Nothing Then
SetDetourPtr(methodPtr, detourPtrInvoke)
End If
End Set
End Property
End Class
End Class
End Class
Public Class MyDetours
Public Shared Function MyMessageBoxTimeoutDetour(ByVal Sender As System.Reflection.MethodBase, _
ByRef hWnd As IntPtr, ByRef lpText As String, ByRef lpCaption As String, _
ByRef uType As Integer, ByRef wLanguageId As UInt16, ByRef dwMilliseconds As Integer) As Boolean
lpText = "My Detour Changed Text!!! " & Chr(13) & Chr(10) & Sender.ReflectedType.Name & Chr(13) & _
Chr(10) & "The original text was:" & Chr(13) & Chr(10) & lpText
Return False
End Function
End Class
Module Module1
Sub Main()
ntDetourEngine.User32.MessageBoxTimeoutW.Invoke(IntPtr.Zero, _
"Invoking the original MessageBoxTimeoutW", "This is the title.", 64, 0, 0)
ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = _
AddressOf MyDetours.MyMessageBoxTimeoutDetour 'Turning on the detour/hook
ntDetourEngine.User32.MessageBoxTimeoutW.Invoke(IntPtr.Zero, _
"Invoking the original MessageBoxTimeoutW", "This is the title.", 64, 0, 0)
ntDetourEngine.User32.MessageBoxTimeoutW.DetourMethod = Nothing 'Turning off the detour/hook
ntDetourEngine.User32.MessageBoxTimeoutW.Invoke(IntPtr.Zero, _
"Invoking the original MessageBoxTimeoutW", "This is the title.", 64, 0, 0)
Console.ReadLine()
End Sub
End Module