An application-defined or library-defined callback function used with the SetWindowsHookEx function. The system calls this function every time a new keyboard input event is about to be posted into a thread input queue.
NoteВ В When this callback function is called in response to a change in the state of a key, the callback function is called before the asynchronous state of the key is updated. Consequently, the asynchronous state of the key cannot be determined by calling GetAsyncKeyState from within the callback function.
The HOOKPROC type defines a pointer to this callback function. LowLevelKeyboardProc is a placeholder for the application-defined or library-defined function name.
Syntax
Parameters
nCode [in] Type: int
A code the hook procedure uses to determine how to process the message. If nCode is less than zero, the hook procedure must pass the message to the CallNextHookEx function without further processing and should return the value returned by CallNextHookEx. This parameter can be one of the following values.
Value
Meaning
HC_ACTION 0
The wParam and lParam parameters contain information about a keyboard message.
wParam [in] Type: WPARAM
lParam [in] Type: LPARAM
Return value
If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
If nCode is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_KEYBOARD_LL hooks will not receive hook notifications and may behave incorrectly as a result. If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.
Remarks
An application installs the hook procedure by specifying the WH_KEYBOARD_LL hook type and a pointer to the hook procedure in a call to the SetWindowsHookEx function.
This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.
The keyboard input can come from the local keyboard driver or from calls to the keybd_event function. If the input comes from a call to keybd_event, the input was «injected». However, the WH_KEYBOARD_LL hook is not injected into another process. Instead, the context switches back to the process that installed the hook and it is called in its original context. Then the context switches back to the application that generated the event.
The hook procedure should process a message in less time than the data entry specified in the LowLevelHooksTimeout value in the following registry key:
HKEY_CURRENT_USER\Control Panel\Desktop
The value is in milliseconds. If the hook procedure times out, the system passes the message to the next hook. However, on WindowsВ 7 and later, the hook is silently removed without being called. There is no way for the application to know whether the hook is removed.
NoteВ В Debug hooks cannot track this type of low level keyboard hooks. If the application must use low level hooks, it should run the hooks on a dedicated thread that passes the work off to a worker thread and then immediately returns. In most cases where the application needs to use low level hooks, it should monitor raw input instead. This is because raw input can asynchronously monitor mouse and keyboard messages that are targeted for other threads more effectively than low level hooks can. For more information on raw input, see Raw Input.
Requirements
Minimum supported client
WindowsВ 2000 Professional [desktop apps only]
Windows hook: просто о сложном
Что такое хук? Что такое хук функций и для чего он нужен? В переводе с английского «hook» — ловушка. Поэтому о назначении хуков функции в Windows можно догадаться — это ловушка для функции. Иными словами, мы ловим функцию и берем управление на себя. После этого определения нам открываются заманчивые перспективы: мы можем перехватить вызов любой функции, подменить код на свой, тем самым изменив поведение любой программы на то, которое нам нужно (конечно, в рамках определенных ограничений).
Целью данной статьи является демонстрация установки хука и его непосредственная реализация.
— Нельзя поверить в невозможное! — Просто у тебя мало опыта, – заметила Королева. – В твоем возрасте я уделяла этому полчаса каждый день! В иные дни я успевала поверить в десяток невозможностей до завтрака!
Где мне реально пригодились эти знания
Эти знания являются очень узкоспециализированными, и в повседневной практике разработки маловероятно, что они пригодятся, но знать о них, на мой взгляд, крайне желательно, даже если эти знания чисто теоретические. На моей практики же мне пригодились эти знания для решения следующих задач:
• Контроль входящего http-траффика и подмена «взрослого» контента на более безобидный. • Логирование информации в случае копирования каких-либо файлов с подконтрольной сетевой папки. • Незначительная модификация кода в проекте, от которого были утеряны исходники (да, и такое тоже случается)
Методы установки хуков
Давайте перейдем от общих фраз к более детальному рассмотрению хуков. Мне известно несколько разновидностей реализации хука:
● Использование функции SetWindowsHookEx. Это весьма простой, оттого и ограниченный, метод. Он позволяет перехватывать только определенные функции, в основном связанные с окном (например, перехват событий, которые получает окно, щелчков мышкой, клавиатурного ввода). Достоинством этого метода является возможность установки глобальных хуков (например, сразу на все приложениях перехватывать клавиатурный ввод). ● Использование подмены адресов в разделе импорта DLL. Суть метода заключается в том, что любой модуль имеет раздел импорта, в котором перечислены все используемые в нем другие модули, а также адреса в памяти для экспортируемых этим модулем функций. Нужно подменить адрес в этом модуле на свой и управление будет передано по указанному адресу. ● Использование ключа реестра HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_Dlls. В нем необходимо прописать путь к DLL, но сделать это могут только пользователи с правами администратора. Этот метод хорош, если приложение не использует kernel32.dll (нельзя вызвать функцию LoadLibrary). ● Использование инъектирования DLL в процесс. На мой взгляд, это самый гибкий и самый показательный способ. Его-то мы и рассмотрим более подробно.
Инъектирование возможно, потому что функция ThreadStart, которая передается функции CreateThread, имеет схожую сигнатуру с функцией LoadLibrary (да и вообще структура dll и исполняемого файла очень схожи). Это позволяет указать метод LoadLibrary в качестве аргумента при создании потока.
Алгоритм инъектирования DLL выглядит так:
1. Находим адрес функции LoadLibrary из Kernel32.dll для потока, куда мы хотим инжектировать DLL. 2. Выделяем память для записи аргументов этой функции. 3. Создаем поток и в качестве ThreadStart функции указываем LoadLibrary и ее аргумент. 4. Поток идет на исполнение, загружает библиотеку и завершается. 5. Наша библиотека инъектирована в адресное пространство постороннего потока. При этом при загрузке DLL будет вызван метод DllMain с флагом PROCESS_ATTACH. Это как раз то место, где можно установить хуки на нужные функции. Далее рассмотрим саму установку хука.
Подход, используемый при установке хука, можно разбить на следующие составные части:
1. Находим адрес функции, вызов которой мы хотим перехватывать (например, MessageBox в user32.dll). 2. Сохраняем несколько первых байтов этой функции в другом участке памяти. 3. На их место вставим машинную команду JUMP для перехода по адресу подставной функции. Естественно, сигнатура функции должна быть такой же, как и исходной, т. е. все параметры, возвращаемое значение и правила вызова должны совпадать. 4. Теперь, когда поток вызовет перехватываемую функцию, команда JUMP перенаправит его к нашей функции. На этом этапе мы можем выполнить любой нужный код.
Далее можно снять ловушку, вернув первые байты из п.2 на место.
Итак, теперь нам понятно, как внедрить нужную нам DLL в адресное пространство потока и каким образом установить хук на функцию. Теперь попробуем совместить эти подходы на практике.
Наше тестовое приложение будет довольно простым и написано на С#. Оно будет содержать в себе кнопку для показа MessageBox. Для примера, установим хук именно на эту функцию. Код тестового приложения:
В качестве инъектора рассмотрим два варианта. Инъекторы, написанные на С++ и С#. Почему на двух языках? Дело в том, что многие считают, что С# — это язык, в котором нельзя использовать системные вещи, — это миф, можно :). Итак, код инъектора на С++:
Теперь тоже самое, но только на С#. Оцените, насколько код более компактен, нет буйства типов (HANDLE, LPVOID, HMODULE, DWORD, которые, по сути, означают одно и тоже).
Теперь самое интересное — код библиотеки, которая устанавливает хуки. Эта библиотека написана на С++, пока без аналога на C#.
Ну и несколько картинок напоследок. До установки хука:
И после установки:
В следующем нашей материале мы постараемся написать код библиотеки, которая устанавливает хуки на C#, т. к. механизм инъектирования управляемого кода заслуживает отдельной статьи.
Understanding the low-level mouse and keyboard hook (win32)
I’m trying to capture global mouse and keyboard input.
So everything works here, except if I #define TEST to put in the Sleep , the mouse becomes incredibly sluggish, as might be expected if I suddenly only allow the mouse to update 20 times a second. And without the sleep, I am pegging the CPU at 100%. But that’s okay for now (that goes away if I use GetMessage ).
Now as I understand it, the low-level hooks work by context-switching to the process which installed it, and then sending the process some kind of message to let it execute the hook callback. What confuses me a little, though, is why my program will never print «msg recvd», but it prints «right mouse down/up» whenever i click the right mouse button. This leads me to conclude that my MouseHookProc is being invoked during the PeekMessage call. It just happens to be some kind of special message and PeekMessage returns 0. But I still need to call PeekMessage or some equivalent.
Since my program needs to do a bunch of things, I clearly can’t weigh down my message pumping loop (the one that calls PeekMessage ) by calling another function that takes, say 50ms to return. How might I multithread my program to maintain mouse responsiveness while simultaneously doing a little heavy lifting? In a multithreaded win32 program, there is still just one message queue, right?
Update: After reading up on MS’s documentation I think I know what the right thing for me to do is. I should just spawn a thread in my application which calls SetWindowsHookEx to register the mouse hook, and then sit around in its own message loop, and the system will take care of sending the mouse updates to this thread. It will be free to do whatever it wants within the MouseHookProc , and the rest of my application will run independently.
Low level hook windows
Low Level Hooking for .NET
This library is intended to serve as a reference implementation for Windows low level keyboard hooks. (And mouse hooks, if there is interest.) Very often I see implementations floating around containing noob mistakes, and let’s be honest- should you actually have to roll your own implementation and spend time in the same pitfalls, just to do something this ordinary? I have, several times over the years, and I want to contribute back what I believe is the most optimal implementation. If you spot something that could be done more optimally, please don’t hesitate to comment!
Get up and running
Add a LowLevelHooking NuGet package reference to your project.
Windows low level hooks communicate with your process by sending a message to your thread message pump. That means you’ll only be able to receive low level notifications while running Application.Run (already in place if you’re using Windows Forms) or Dispatcher.PushFrame (already in place if you’re using WPF). SharpDX has RenderLoop.Run . Any UI framework will be running something similar.
If you’re not running UI at all, for example if you’re a console app, you’ll have to implement your own message loop. This is rare, but I do have a .NET Core console app sample planned, so stay tuned.)
With this in mind, and because you want to create as few hooks as possible (read: one), it makes the most logical sense to tie the lifetime of the hook to the lifetime of the message loop rather than to a window:
Once that’s taken care of, you can subscribe and unsubscribe as needed:
If you have questions, critiques or contributions, I can’t wait to hear from you via Gitter or GitHub issues!
About
Windows low level keyboard hooking component
Managing Low-Level Keyboard Hooks with the Windows API
Heretofore we had to resort to low-level interrupt handlers for interrupt 0x9 and interrupt 0x16 to intercept keyboard events. (Occasionally I was prone to writing keyboard snoopers—in a much earlier and delinquent incarnation—and recall that these took a bit of effort to do well, and undetected.) Now that we all have to earn a living (and can be held accountable for mischievous and illegal behavior), we might want to write a hook into the keyboard to prevent or discourage a user from re-booting during a critical file operation or something similar. Fortunately we can use a couple of Windows API methods to do this reliably rather than resorting to assembler and interrupt handlers.
In this article I will demonstrate how to intercept keystrokes that are difficult or impossible to catch with the KeyPress and KeyDown events in VB6. The process is a bit involved—although still easier than writing interrupt handlers—but rest assured I will provide plenty of code and a summary recapturing the whole process before we are done. If you are interested in capturing keystroke combinations like Ctrl+Esc or Alt+Tab before Windows processes them then you will learn how to do that in this article. You will also learn a bit about defining and implementing interfaces, if you need to brush up on these skills. The basic process I will be presenting follows:
Define and declare low-level keyboard API methods
Define an event handler to respond to low-level Windows keyboard events before Windows handles them
Trap specific and varied keystroke combinations
Conceal the complexity of the Windows API methods in a VB6 interface, facilitating reuse of this general keyboard-handling technique
Safely call other handlers that may exist concurrently with your handler
Unhooking the keyboard handler when you are finished
The entire listing is provided at the end of the article. Without further ado, let’s proceed.
Hooking the Keyboard
Most complex tasks in VB6 require some knowledge of the Windows API. This is for the simple reason that VB6 runs on Windows and there is no specific framework capturing this information. The Windows API today is procedural; thus to access capabilities in Windows we have to load a library containing the procedures we want to use and invoke those operations. Fortunately VB6 supports implicit library loading and API invocation by simply declaring the API methods we want to use. (Note: It is worth mentioning that the mechanics of loading and getting API methods are supported by API methods themselves. We will just use the plain old Declare statement though.)
Declaring the SetWindowsHookEx Procedure
To hook the keyboard we will need to declare and invoke SetWindowsHookEx. The declaration can be public in a module or must be private in a class module. The declaration for SetWindowsHookEx follows.
Rather than knowing the declaration from scratch I was aware of the method and used the API Viewer (see figure 1) to get the declaration incorporated correctly. (We will use this technique for all of the declarations in this article.)
By examining the API method we can figure out what to call, or we could look in the MSDN help. The first argument is a Long that represents an instruction to the API describing the kind of hook operation to perform. The second argument is actually a function pointer, called a callback. The third argument is the handle to the application instance, and the fourth argument is the application’s thread ID. The first thing we will do is define an appropriate callback method.
Figure 1: Use the API Viewer to get Windows API method signatures correct.
Stubbing the Keyboard Callback Method
Unfortunately callback methods must be in modules (not class files) in VB6. Thus we will need to place the callback method for SetWindowsHookEx in a module. The signature of the callback is a function that accepts three Long arguments and returns a Long. (You might have to do a little poking around to discover this information, but I will just provide an empty callback stub for you.)
The signature of the callback method looks suspiciously like a generic Windows message handler. Code helps us decide if the message is for us. The wParam argument contains the actual Windows message constant ad lParam plays the role of pointer to keyboard data. We will only test Code for a single constant value and the lParam is actually a pointer to the keyboard data. We will come back to this in a moment.
Hooking the Keyboard
Now that I have the callback method—albeit an empty method—I can hook the keyboard, which means that my method will be called very early in the message handling process. To hook the keyboard I defined a simple method named HookKeyboard that wraps the more complicated call to SetWindowsHookEx.
(All of the code so far is in a single module I named KeyboardHandler.bas.) The constant WH_KEYBOARD_LL defines the kind of hook we want to make: a low-level keyboard hook. There are several other kinds of hooks. For example, you can pass constants that indicate that you want to hook the mouse, a hook for debugging, and one that is suggested as being helpful for computer-based training. The second argument is the AddressOf our KeyboardCallback function. The third argument is the application Windows handle, and we can pass 0 for the thread ID. The value 0 indicates that the hook is associated with all threads on the desktop. By passing 0 we are hooking the keyboard and can effectively trap keys effecting all applications.
With the code we have so far the KeyboardCallback method will be called every time we press a key, whether our application has the focus or not. The next step is to write some code in the callback that evaluates the received keyboard messages.
Implementing the Keyboard Callback Method
Listing 1 contains the implementation I used for the KeyboardCallback function. The code is numbered for convenience, followed by a brief synopsis describing the behavior.
Listing 1: The implementation of my KeybaordCallback procedure.
If we check the online help documentation it says that we can handle HC_ACTION, which is defined as constant with a value of 0. If Code is less than 0 then we need to immediately pass the message to CallNextHookEx. Hence, when Code equals HC_ACTION (0) I handle the data on lines 6 through 15; otherwise I pass the message to the next function in the callback chain. (Keep in mind that other programs may have keyboard hooks too.) Let’s examine the statement on line 17 and 18 first.
Line 17 returns the value of CallNextHookEx. Notice that I pass it the address of the callback I grabbed when I hooked the keyboard, the code, the wParam, and lParam arguments. wParam contains the constant for the Windows message, and the lParam contains a pointer to the keyboard data itself.
On line 6 if the Code is 0 then I want to handle the message. I could also check the wParam to determine if the message is a WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP. (These are Windows message constants that you can define using the API Viewer.) For our purposes we won’t check the wParam. If the Code is 0 then we want to convert the lParam to the type we know it is, a KBDLLHOOKSTRUCT, handle the keyboard input, and return 1 to indicate that the input was handled. I implemented a method IsHooked that afforded me a degree of separation between the murky API stuff and the keys I wanted to block. IsHooked is where you and I can decide what key combinations to manage manually.
Getting the Keyboard Data
Lines 4 and 8 of listing 1 introduced a type and an API method that we haven’t talked about yet. I couldn’t find KBDLLHOOKSTRUCT in my Windows API Viewer, but CopyMemory was there. (Both the type and the API method are shown in listing 2.)
Listing 2: The KBDLLHOOKSTRUCT type and the CopyMemory method.
(I’m not sure how anyone without a lot of knowledge about the API was ever supposed to put this together.) As I mentioned lParam is really a pointer, which we don’t have in VB6. We use long integers to represent pointers, as a pointer is really just a number representing a location in memory. CopyMemory can copy information from one location in memory to a second location. Hence, we use CopyMemory to get the keyboard data from the address pointed to by lParam into our local static variable. (We are basically just copying the keyboard data here.)
The reason I wrote this code as I have shown you so far is that you could easily reuse this code in any context, over and over without revisiting the low-level API stuff. Simple reuse HookKeyboard, KeyboardCallback, and all you would have to do is re-implement IsHooked.
Chaining Hooked Callbacks
The signature for CallNextHookEx is provided for reference here. It is a good idea when you are hooking low-level operations to keep track of the thing that hooked it before you did. In this way your low-level hook will not foul up someone else’s low-level hook. HookKeyboard does that.
I was unable to find CallNextHookEx in the API Viewer. (I have to admit that since .NET was released I haven’t been as diligent about updating my MSDN for Visual Studio 6 as I should be. I am sure you’re more diligent and won’t have any trouble finding these declarations in your API Viewer.)
IsHooked: Checking for Blocked Key Combinations
Tip: Event the low-level keyboard hook will not block some key combinations, for instance, Ctrl+Alt+Del. I believe Ctrl+Alt+Del generates an interrupt 0x19, which is not routed as a keyboard message. You probably have to implement an interrupt handler for interrupt 0x19 to catch Ctrl+Alt+Del.
We now have the low-level API hooking code out of the way, and the data is in the somewhat more manageable KBDLLHOOKSTRUCT. Following my implementation all we have to is define IsHooked to return a Boolean that indicates if we want to handle any particular keyboard combination or not. Listing 3 demonstrates one implementation of IsHooked.
Listing 3: An implementation of my IsHooked method checks for Alt+Tab, Ctrl+Esc, and Alt+Esc.
We have to pass user defined types ByRef. Even in IsHooked you can quickly determine that figuring out what keyboard combinations were being pressed is murky. Checking virtual keys, getting virtual key states asynchronously, and Anding flag values requires too much special knowledge. We only want to write this code one time.
I defined IsHooked to check for Ctrl+Esc, Alt+Tab, and Alt+Esc. If any of these combinations are pressed then I block them. More beneficially you will probably want to write the code to notify your application if any of these combinations are pressed and dynamically allow your application to block them or not based on some application state. (We will take a look at one way to do this in the last section «Genericizing Keyboard Hooks».) Let’s finish our current discussion by implementing the code to unhook the keyboard.
Unhooking the Keyboard
When your application exits you want to release you hook. You need to call UnhookWindowsHookEx. To be symmetric I implemented a simpler method to wrap up the API method. Listing 4 contains the declaration for UnhookWindowsHookEx and the implementation of my wrapper method.
Listing 4: Unhooking the keyboard.
Before we wrap up and I show you the complete listing for the sample code I want to explain the meaning of the three statements in listing 2 that look something like this one: IsHooked = KeyboardHook.BlockAltEscape.
Genericizing Keyboard Hooks
I enjoy discovering nuts and bolts information that is highly specialized, but I have to make a distinction between fun and pragmatics. Figuring all of this stuff out (with some help from MSDN) was a lot of fun. However, as a matter of pragmatics it is too convoluted to ever repeat, especially when writing software for hire. As a result I created a very simple interface that uses named methods to represent the hooked values. Any class or Form that implements my interface can receive a notification when a specific keyboard combination is pressed. Using this strategy I will never have to figure out what the relationship between the flags, the vkCode, and the hexadecimal number &H8000 are again. This is fun only to the extent that I am a factoid junky, but there is a dichotomy between trying to be productive and figuring out these details.
Listing 5 contains the interface that I defined to wire up to the low-level keyboard code. Doing so vastly simplified a Form’s interaction with the API code.
Listing 5: The interface
Finally I defined a public variable KeyboardHook. If I assign an object that implements KeyboardHook to this public variable then the IsHooked method will call the appropriate KeyboardHook method, and that method—completely unaware of the low-level API code—can determine if it want to block that key combination or not. A Form that implements and wires into KeyboardHook is shown in the complete code listing, listing 6.
Listing 6: A single complete source code listing for a Form, class, and module that demonstrates the low-level keyboard hooks and an interface that simplifies the hook behavior.
Summary
It is possible to do just about anything in Visual Basic 6. We just have to lean on the API some of the time, as we do when hooking the keyboard.
To acquire a low-level hook into the keyboard you need several Windows API methods, including: SetWindowsHookEx, UnHookWindowsHookEx, CopyMemory, CallNextHookEx, and GetAsyncKeyState. In addition you will need the KBDLLHOOKSTRUCT structure. The basic idea is to call SetWindowsHookEx passing a callback method that Windows will call when a low-level key operation occurs. If the Code passed to your callback equals 0 then Copy the keyboard data out of the parameter passed to your callback with the CopyMemory method into a local variable of type KBDLLHOOKSTRUCT; otherwise send all of the information to CallNextHookEx. You can examine the value of the KBDLLHOOKSTRUCT in part with the GetAsyncKeyState method, and when you are all finished make sure you call UnHookWindowsHookEx.
Sounds easy. Well, there is quite a bit of cryptic information involved, which suggests that all of this information should be wrapped up as part of a cleaner framework. This will tide you over for now.
About the Author
Paul Kimmel is a freelance writer for Developer.com and CodeGuru.com. Look for his recent book «Advanced C# Programming» from McGraw-Hill/Osborne on Amazon.com. Paul will be a keynote speaker in «Great Debate: .NET or .NOT?» at the Fall 2002 Comdex in Las Vegas, Nevada. Paul Kimmel is available to help design and build your .NET solutions and can be contacted at pkimmel@softconcepts.com.
This article was originally published on November 18th, 2002