In depth analysis of the C keyboard hook of Hook interceptor shielding the details of keyboard activity

  • 2020-05-12 03:04:29
  • OfStack

Hooks (Hook), a platform for the Windows message processing mechanism, allow applications to set subroutines to monitor certain messages in a specified window, which can be created by other processes. When the message arrives, it is processed before the target window handler. The hook mechanism allows applications to intercept and process window messages or specific events.
A hook is actually a segment of a program that processes a message and is hung into the system via a system call. Each time a particular message is sent, the hook program captures the message before it reaches the destination window, which means that the hook function takes control first. The hook function can either process (change) the message, or continue the message without processing it, or force the end of the message.
Operation mechanism
1. Hook list and hook procedure:
Each Hook has a list of Pointers associated with it, called a hook list, which is maintained by the system. The pointer to this list points to the specified, application-defined callback function called by the Hook subroutine, which is the individual processing subroutine of the hook. When a message with the specified Hook type occurs, the system passes the message to the Hook subroutine. Some Hook subroutines can only monitor messages, or modify messages, or stop messages from progressing to the next Hook subroutine or destination window. The most recently installed hooks are placed at the beginning of the chain, and the earliest installed hooks are placed at the end, where the latecomers gain control.

Windows does not require that the uninstall order 1 of the hook procedure be the opposite of the install order. Each time a hook is unloaded, Windows frees up its memory and updates the entire Hook list. If a program installs a hook but ends before it has uninstalled the hook, the system will automatically uninstall the hook for it.

The hook procedure is an application-defined callback function (CALLBACK Function), which cannot be defined as a member function of a class but can only be defined as a normal C function. To monitor a system or a particular type of event, which can be associated with a particular thread or for all threads in the system.
The hook procedure must follow the following syntax:

LRESULT CALLBACKHookProc
(
int nCode, 
      WPARAM wParam, 
      LPARAM lParam
);

HookProc is the name of the application definition.
The nCode parameter is the Hook code, and the Hook subprogram USES this parameter to determine the task. The value of this parameter depends on the Hook type, and each Hook has its own Hook character set of code characteristics.
The values of the wParam and lParam parameters depend on the Hook code, but their typical values contain information about sending or receiving messages.
2. Hook installation and release:
Install an application-defined hook procedure into the hook list using the API function SetWindowsHookEx(). The SetWindowsHookEx function always installs the Hook subroutine at the beginning of the Hook chain. When an event of the specified type is monitored by Hook, the system calls the Hook subroutine at the beginning of the Hook chain associated with this Hook. Each Hook subroutine in the Hook chain decides whether to pass the event to the next Hook subroutine. The Hook subroutine needs to call the CallNextHookEx function to pass the event to the next Hook subroutine.

HHOOKSetWindowsHookEx( 
int idHook,      //  The type of hook, that is, the type of message it handles 
HOOKPROC lpfn,   //  The address pointer of the hook procedure. if dwThreadId Parameters for 0
      //  or 1 The identity of a thread created by another process, 
      // lpfn Must point to DLL The hook procedure in. 
      //  In addition to that, lpfn You can point to the current process 1 Segment hook procedure code. 
      //  The entry address of the hook function, which is called when the hook hooks any message. 
HINSTANCE hMod, //  Handle to the application instance. identifies lpfn Of a subprocedure 
DLL . 
      //  if dwThreadId  Identifies the creation of the current process 1 A thread, 
      //  And the subroutine code is in the current process, hMod Must be NULL . 
      //  You can easily set it to the instance handle of your application. 
DWORD dwThreadId //  The identifier of the thread associated with the installed hook procedure. 
      //  If it is 0 , the hook procedure is associated with all threads, that is, the global hook. 
            );

The function returns a handle to the hook procedure on success and NULL on failure.
The hook procedure associated with a thread mentioned above means that the message sent to the thread in the 1 hook list is sent to the hook procedure at the same time and is processed by the hook procedure first.
Calling the hook function that takes control in the hook procedure. After processing the message, if the message is to continue, it must call the API function CallNextHookEx in another SDK to pass it to execute the next hook procedure referred to in the hook list. This function returns the return value of the next hook procedure in the hook chain on success. The type of return value depends on the type of hook. The prototype of this function is as follows:

LRESULTCallNextHookEx
   (
    HHOOK hhk;
    int nCode;
    WPARAM wParam;
    LPARAM lParam;
    );

hhk is the handle to the current hook and is returned by the SetWindowsHookEx() function.
NCode is the event code passed to the hook procedure.
wParam and lParam are the values of wParam passed to the hook procedure, respectively, and their specific meanings are related to the hook type.
The hook function can also discard the message and block its delivery by returning TRUE directly. Otherwise, other applications with hooks installed will not receive hook notifications and may produce incorrect results.
The hook needs to be uninstalled using UnHookWindowsHookEx() after use, otherwise it will cause trouble. Releasing the hook is easier, with UnHookWindowsHookEx() having only one argument. The function prototype is as follows:

UnHookWindowsHookEx
(
HHOOK hhk;
);

The function returns TRUE on success or FALSE otherwise.
3. 1 some operating mechanisms:
In an Win16 environment, DLL's global data is the same for every process that loads it; In the Win32 environment, however, the situation has changed, and any object (including a variable) created by the code in the DLL function is owned by the thread or process calling it. When a process loads DLL, the operating system automatically maps the DLL address to the process's private space, which is the virtual address space of the process, and also copies a copy of the global data of DLL into the process space. This means that each process has the same global data for DLL, with the same name, but not necessarily the same value, and it does not interfere with each other.

Therefore, in an Win32 environment, the necessary setup must be made if data is to be Shared across multiple processes. Shared memory between processes accessing the same 1 Dll is implemented using memory-mapped file technology. You can also isolate the data that needs to be Shared, place it in a separate data segment, and set the attributes of that segment to share. You must assign initial values to these variables, or the compiler will place uninitialized variables in a data segment called uninitialized.
The #pragma data_seg preprocessor directive is used to set up the Shared data segment. Such as:

#pragmadata_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()

All variables between #pragma data_seg("SharedDataName") and #pragma data_seg() will be seen and Shared by all processes accessing this Dll. Add 1 instruction # pragmacomment (linker, "/ section: SharedDataName, rws"), then the data in the data section can share between all DLL instance. All operations on this data are for the same instance, rather than having one copy in each process's address space.
When a process implicitly or explicitly calls a function in a dynamic library, the system maps the dynamic library to the virtual address space of the process (" address space "). This makes DLL a part of the process, executing as the process, using the process's stack.
4. System hook and thread hook:
The last argument to the SetWindowsHookEx() function determines whether the hook is a system hook or a thread hook.
Thread hooks are used to monitor event messages for a specified thread. Thread tick 1 is usually in the current thread or thread derived from the current thread.
System hooks monitor event messages for all threads in the system. Because system hooks affect all applications in the system, the hook function must be placed in a separate dynamic link library (DLL). The system automatically maps DLL containing the hook callback function into the address space of all processes affected by the hook function, into which the DLL is injected.
Some notes:
(1) if both the thread tick and the system tick are installed for the same event (such as mouse message), then the system will automatically call the thread tick and then call the system tick.
(2) multiple hook processes can be installed for the same 1 event message, and these hook processes form a hook chain. The ticker information should be passed to the next ticker function after the current ticker processing.
(3) hooks, especially system hooks, will consume message processing time and reduce system performance. Install hooks only when necessary, and uninstall them after use
C# keyboard tick (Hook) interceptor, blocks keyboard activity source code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;
using Microsoft.Win32;
namespace Vjsdn.Tech.KeyboardHook
{
   public partial class frmKeyboardHook : Form
   {
      // Tick management class  
      private KeyboardHookLib _keyboardHook = null;

      public frmKeyboardHook()
      {
         InitializeComponent();
      }

      private void button1_Click(object sender, EventArgs e)
      {
         // Install the hook  
         _keyboardHook = new KeyboardHookLib();
         _keyboardHook.InstallHook(this.OnKeyPress);
      }

      private void button2_Click(object sender, EventArgs e)
      {
         // Cancel the hook  
         if (_keyboardHook != null) _keyboardHook.UninstallHook();
      }

      /// <summary> 
      ///  The client keyboard captures events . 
      /// </summary> 
      /// <param name="hookStruct"> by Hook The key information sent by the program </param> 
      /// <param name="handle"> Whether to intercept </param> 
      public void OnKeyPress(KeyboardHookLib.HookStruct hookStruct, out bool handle)
      {
         handle = false; // No keys are intercepted by default  

         if (hookStruct.vkCode == 91) //  Intercept left win( Start menu key )
         {
            handle = true;
         }

         if (hookStruct.vkCode == 92)//  Intercept right win
         {
            handle = true;
         }

         // intercept Ctrl+Esc 
         if (hookStruct.vkCode == (int)Keys.Escape && (int)Control.ModifierKeys == (int)Keys.Control)
         {
            handle = true;
         }

         // intercept alt+f4 
         if (hookStruct.vkCode == (int)Keys.F4 && (int)Control.ModifierKeys == (int)Keys.Alt)
         {
            handle = true;
         }

         // intercept alt+tab 
         if (hookStruct.vkCode == (int)Keys.Tab && (int)Control.ModifierKeys == (int)Keys.Alt)
         {
            handle = true;
         }

         // intercept F1 
         if (hookStruct.vkCode == (int)Keys.F1)
         {
            handle = true;
         }

         // intercept Ctrl+Alt+Delete 
         if ((int)Control.ModifierKeys == (int)Keys.Control + (int)Keys.Alt + (int)Keys.Delete)
         {
            handle = true;
         }

         // If the key A~Z 
         if (hookStruct.vkCode >= (int)Keys.A && hookStruct.vkCode <= (int)Keys.Z)
         {
            // In the B key  
            if (hookStruct.vkCode == (int)Keys.B)
            hookStruct.vkCode = (int)Keys.None; // Set a key for 0 

            handle = true;
         }

         Keys key = (Keys)hookStruct.vkCode;
         label1.Text = " If you press :" + (key == Keys.None ? "" : key.ToString());

      }
   }
}

KeyboardHookLib.cs
Keyboard management class

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;
using Microsoft.Win32;
using System.Windows.Forms;
namespace Vjsdn.Tech.KeyboardHook
{
   /// <summary> 
   ///  The keyboard Hook Management class , by www.vjsdn.com  Learn original  
   /// </summary> 
   public class KeyboardHookLib
   {
      private const int WH_KEYBOARD_LL = 13; // The keyboard  

      // The keyboard handles event delegates  , The method that defines this delegate is called when keyboard input is captured . 
      private delegate int HookHandle(int nCode, int wParam, IntPtr lParam);

      // The client keyboard handles events  
      public delegate void ProcessKeyHandle(HookStruct param, out bool handle);

      // receive SetWindowsHookEx The return value  
      private static int _hHookValue = 0;

      // The tick subroutine handles events  
      private HookHandle _KeyBoardHookProcedure;

      //Hook structure  
      [StructLayout(LayoutKind.Sequential)]
      public class HookStruct
      {
         public int vkCode;
         public int scanCode;
         public int flags;
         public int time;
         public int dwExtraInfo;
      }

      // Set the hook  
      [DllImport("user32.dll")]
      private static extern int SetWindowsHookEx(int idHook, HookHandle lpfn, IntPtr hInstance, int threadId);

      // Cancel the hooks  
      [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
      private static extern bool UnhookWindowsHookEx(int idHook);

      // Under the call 1 A hook  
      [DllImport("user32.dll")]
      private static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);

      // Get the current thread ID 
      [DllImport("kernel32.dll")]
      private static extern int GetCurrentThreadId();

      //Gets the main module for the associated process. 
      [DllImport("kernel32.dll")]
      private static extern IntPtr GetModuleHandle(string name);

      private IntPtr _hookWindowPtr = IntPtr.Zero;

      // The constructor  
      public KeyboardHookLib() { }

      // The keyboard of the external call handles events  
      private static ProcessKeyHandle _clientMethod = null;

      /// <summary> 
      ///  Install the hook  
      /// </summary> 
      /// <param name="hookProcess"> The keyboard of the external call handles events </param> 
      public void InstallHook(ProcessKeyHandle clientMethod)
      {
         _clientMethod = clientMethod;

         //  Install keyboard hook  
         if (_hHookValue == 0)
         {
            _KeyBoardHookProcedure = new HookHandle(OnHookProc);

            _hookWindowPtr = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);

            _hHookValue = SetWindowsHookEx(
            WH_KEYBOARD_LL,
            _KeyBoardHookProcedure,
            _hookWindowPtr,
            0);

            // If setting the hook fails . 
            if (_hHookValue == 0) UninstallHook();
         }
      }

      // Cancel hook event  
      public void UninstallHook()
      {
         if (_hHookValue != 0)
         {
            bool ret = UnhookWindowsHookEx(_hHookValue);
            if (ret) _hHookValue = 0;
         }
      }

      // Hook event internal call , call _clientMethod Method is forwarded to the client application.  
      private static int OnHookProc(int nCode, int wParam, IntPtr lParam)
      {
         if (nCode >= 0)
         {
            // Conversion structure  
            HookStruct hookStruct = (HookStruct)Marshal.PtrToStructure(lParam, typeof(HookStruct));

            if (_clientMethod != null)
            {
               bool handle = false;
               // Invoke the event handler provided by the customer.  
               _clientMethod(hookStruct, out handle);
               if (handle) return 1; //1: Represents the interception keyboard ,return  exit  
            }
         }
         return CallNextHookEx(_hHookValue, nCode, wParam, lParam);
      }

   }
}


Related articles: