Creating a local hook
A local hook is a hook that targets the same process that your code is running within.
To install a local hook we need to do four things:
- Retrieve the address of the native method to be hooked, for this we will use
LocalHook.GetProcAddress
- Define a delegate type that matches the native method calling convention and parameters
- Write a hook handler method that we want to run in place of the original native method
- Lastly we need to create the hook using
LocalHook.Create
, passing in the original method address and the replacement delegate.
The complete sample is provided below.
The full managed BeepHook tutorial source project can be found here.
1. Retrieving the native method's address
If that the method you wish to hook is exported by the DLL, we can use LocalHook.GetProcAddress
to retrieve the method address. E.g. to retrieve the address of the user32!MessageBeep
method exported by user32.dll we would use the following code:
2. Creating a delegate type that matches the native method
When creating the delegate type it is important that we match the same calling convention as the original and use the correct parameter types and marshalling. Here is the native WINAPI method declaration for MessageBeep:
The corresponding delegate type would then look like:
3. Write the hook handler
The hook handler method must be compatible with the delegate that was created.
A hook handler for the MessageBeep method might look like this:
If you cannot use a DllImport
and you still want to call the original method after retrieving the address in some other manner, you can use the original function address with Marshal.GetDelegateForFunctionPointer
. To call the original method in this manner :
Note: EasyHook implements the hook trampoline code in such a way that calling the original method directly (or indirectly) while still within the hook handler will bypass the hook handler and call the original method.
4. Create and enable the local hook
We now have everything we need to create the hook. This involves two steps:
- Create the LocalHook instance using
LocalHook.Create
, and - Activate the hook by telling it which threads to include/exclude from the hook
Full MessageBeep hook example
Create a new console application, install the EasyHook NuGet package and then replace the existing Program.cs
with the following code.
This example hooks the MessageBeep method in order to prevent it from being called.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using System;
using System.Runtime.InteropServices;
namespace BeepHook
{
class Program
{
// The matching delegate for MessageBeep
[UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)]
delegate bool MessageBeepDelegate(uint uType);
// Import the method so we can call it
[DllImport("user32.dll")]
static extern bool MessageBeep(uint uType);
/// <summary>
/// Our MessageBeep hook handler
/// </summary>
static private bool MessageBeepHook(uint uType)
{
// We aren't going to call the original at all
// but we could using: return MessageBeep(uType);
Console.Write("...intercepted...");
return false;
}
/// <summary>
/// Plays a beep using the native MessageBeep method
/// </summary>
static private void PlayMessageBeep()
{
Console.Write(" MessageBeep(BeepType.Asterisk) return value: ");
Console.WriteLine(MessageBeep((uint)BeepType.Asterisk));
}
static void Main(string[] args)
{
Console.WriteLine("Calling MessageBeep with no hook.");
PlayMessageBeep();
Console.Write("\nPress <enter> to call MessageBeep while hooked by MessageBeepHook:");
Console.ReadLine();
Console.WriteLine("\nInstalling local hook for user32!MessageBeep");
// Create the local hook using our MessageBeepDelegate and MessageBeepHook function
using (var hook = EasyHook.LocalHook.Create(
EasyHook.LocalHook.GetProcAddress("user32.dll", "MessageBeep"),
new MessageBeepDelegate(MessageBeepHook),
null))
{
// Only hook this thread (threadId == 0 == GetCurrentThreadId)
hook.ThreadACL.SetInclusiveACL(new int[] { 0 });
PlayMessageBeep();
Console.Write("\nPress <enter> to disable hook for current thread:");
Console.ReadLine();
Console.WriteLine("\nDisabling hook for current thread.");
// Exclude this thread (threadId == 0 == GetCurrentThreadId)
hook.ThreadACL.SetExclusiveACL(new int[] { 0 });
PlayMessageBeep();
Console.Write("\nPress <enter> to uninstall hook and exit.");
Console.ReadLine();
} // hook.Dispose() will uninstall the hook for us
}
public enum BeepType : uint
{
/// <summary>
/// A simple windows beep
/// </summary>
SimpleBeep = 0xFFFFFFFF,
/// <summary>
/// A standard windows OK beep
/// </summary>
OK = 0x00,
/// <summary>
/// A standard windows Question beep
/// </summary>
Question = 0x20,
/// <summary>
/// A standard windows Exclamation beep
/// </summary>
Exclamation = 0x30,
/// <summary>
/// A standard windows Asterisk beep
/// </summary>
Asterisk = 0x40,
}
}
}
Running this code results in the following output: