I. Introduction▲
Dans ce tutoriel, nous allons voir comment réaliser un hook sur le clavier et la souris en .net. Certain d'entre vous penserons « enfin », car de très nombreux exemples sont en C/C++.
Le langage utilisé lors du tutoriel est le C#.
I-A. Définition▲
Un programme qui fait un hook clavier / souris, intercepte toutes les touches du clavier sur lesquelles vous tapez et les boutons de la souris, quelle que soit l'application.
I-B. Exemple d'utilisation▲
L'utilisation d'un hook peut être assez variable. Le plus souvent, ce sera pour faire du Key Logging, c'est-à-dire, d'enregistrer dans un fichier la frappe du clavier, et ainsi, récupérer des mots de passe.
Sinon, intercepter un clic pour notre utilisation.
II. Classe du Hook▲
II-A. P/Invoke▲
Pour réaliser un hook, nous devons appeler des fonctions des apis Win32.
Ces fonctions Windows, étant réalisées en C/C++, nous devons faire, ce que l'on appelle du P/Invoke (Plateform Invoke). Pour plus d'informations, je vous renvoie sur l'article de morpheus : https://morpheus.developpez.com/dlldotnet/
II-A-1. Déclaration des fonctions Win32▲
Nous allons utiliser des fonctions de la dll user32.dll.
II-A-1-a. SetWindowsHookEx▲
Déclaration :
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int SetWindowsHookEx(
int idHook,
HookProc lpfn,
IntPtr hMod,
int dwThreadId);II-A-1-b. UnhookWindowsHookEx▲
Déclaration :
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int UnhookWindowsHookEx(int idHook);II-A-1-c. CallNextHookEx▲
Déclaration :
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int CallNextHookEx(
int idHook,
int nCode,
int wParam,
IntPtr lParam);II-A-1-d. ToAscii▲
Déclaration :
[DllImport("user32")]
private static extern int ToAscii(
int uVirtKey,
int uScanCode,
byte[] lpbKeyState,
byte[] lpwTransKey,
int fuState);II-A-1-e. GetKeyboardState▲
Déclaration :
[DllImport("user32")]
private static extern int GetKeyboardState(byte[] pbKeyState);II-A-1-f. GetKeyState▲
Déclaration :
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern short GetKeyState(int vKey);II-A-1-g. HookProc▲
Déclaration :
private delegate int HookProc(int nCode, int wParam, IntPtr lParam);II-A-2. Déclaration des structures▲
Ces fonctions travaillent avec des structures de données. Voici leurs déclarations :
II-A-2-a. POINT▲
MSDN:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/rectangl_0tiq.asp
Déclaration :
[StructLayout(LayoutKind.Sequential)]
private class POINT
{
/// <summary>
/// Coordonnée X.
/// </summary>
public int x;
/// <summary>
/// Coordonnée Y.
/// </summary>
public int y;
}II-A-2-b. MouseHookStruct▲
Déclaration :
[StructLayout(LayoutKind.Sequential)]
private class MouseHookStruct
{
/// <summary>
/// Structure POINT pour les coordonnées
/// </summary>
public POINT pt;
/// <summary>
/// Handle de la window
/// </summary>
public int hwnd;
/// <summary>
/// Specifies the hit-test value. For a list of hit-test values, see the description of the WM_NCHITTEST message.
/// </summary>
public int wHitTestCode;
/// <summary>
/// Specifies extra information associated with the message.
/// </summary>
public int dwExtraInfo;
}II-A-2-c. MouseLLHookStruct▲
Déclaration :
[StructLayout(LayoutKind.Sequential)]
private class MouseLLHookStruct
{
/// <summary>
/// Structure POINT.
/// </summary>
public POINT pt;
public int mouseData;
public int flags;
public int time;
public int dwExtraInfo;
}II-A-2-d. KeyboardHookStruct▲
Déclaration :
[StructLayout(LayoutKind.Sequential)]
private class KeyboardHookStruct
{
/// <summary>
/// Key code virtuel, la valeur doit être entre 1 et 254.
/// </summary>
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}II-A-3. Constantes Windows▲
Voici la liste des constantes qui nous seront utiles.
//Valeurs issues de Winuser.h du SDK de Microsoft.
/// <summary>
/// Windows NT/2000/XP: Installe un hook pour la souris
/// </summary>
private const int WH_MOUSE_LL = 14;
/// <summary>
/// Windows NT/2000/XP: Installe un hook pour le clavier
/// </summary>
private const int WH_KEYBOARD_LL = 13;
private const int WH_MOUSE = 7;
private const int WH_KEYBOARD = 2;
/// <summary>
/// Le message WM_MOUSEMOVE est envoyé quand la souris bouge
/// </summary>
private const int WM_MOUSEMOVE = 0x200;
/// <summary>
/// Le message WM_LBUTTONDOWN est envoyé lorsque le bouton gauche est pressé
/// </summary>
private const int WM_LBUTTONDOWN = 0x201;
/// <summary>
/// Le message WM_RBUTTONDOWN est envoyé lorsque le bouton droit est pressé
/// </summary>
private const int WM_RBUTTONDOWN = 0x204;
/// <summary>
/// Le message WM_MBUTTONDOWN est envoyé lorsque le bouton central est pressé
/// </summary>
private const int WM_MBUTTONDOWN = 0x207;
/// <summary>
/// Le message WM_LBUTTONUP est envoyé lorsque le bouton gauche est relevé
/// </summary>
private const int WM_LBUTTONUP = 0x202;
/// <summary>
/// Le message WM_RBUTTONUP est envoyé lorsque le bouton droit est relevé
/// </summary>
private const int WM_RBUTTONUP = 0x205;
private const int WM_MBUTTONUP = 0x208;
private const int WM_LBUTTONDBLCLK = 0x203;
private const int WM_RBUTTONDBLCLK = 0x206;
private const int WM_MBUTTONDBLCLK = 0x209;
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_KEYDOWN = 0x100;
private const int WM_KEYUP = 0x101;
private const int WM_SYSKEYDOWN = 0x104;
private const int WM_SYSKEYUP = 0x105;
private const byte VK_SHIFT = 0x10;
private const byte VK_CAPITAL = 0x14;
private const byte VK_NUMLOCK = 0x90;II-B. Hook de la Souris et du clavier▲
II-B-1. Méthode StartMouseHook▲
Dans cette méthode, nous allons initialiser le hook de la souris. Pour cela, nous utilisons la fonction Win32 SetWindowsHookEx.
public void StartMouseHook()
{
// Faire une instance de HookProc.
MouseHookProcedure = new HookProc(MouseHookProc);
//installer le hook
int hook = SetWindowsHookEx(
WH_MOUSE_LL,
MouseHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
//Si hook est a 0, donc la fonction SetWindowsHookEx a échoué
if (hook == 0)
{
//Retourn le code d'erreur
int errorCode = Marshal.GetLastWin32Error();
//Lance une exception du type Win32
throw new Win32Exception(errorCode);
}
}Maintenant nous allons déclarer notre événement pour que notre code puisse s'y attacher.
Pour la souris, nous avons un événement :
- OnMouseClick
OnMouseClick
private MouseEventHandler _onMouseClick;
public event MouseEventHandler OnMouseClick
{
add
{
_onMouseClick += value;
}
remove
{
_onMouseClick -= value;
}
}Maintenant nous allons remplir la méthode MouseHookProc. Cette méthode sera appelée dès qu'un bouton de la souris sera appuyé.
private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
{
bool processNextHook = true;
// Verifions si nCode est différent de 0 et que nos événements sont bien attachés
if ((nCode >= 0) && (_onMouseClick != null))
{
//Remplissage de la structure MouseLLHookStruct à partir d'un pointeur
MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));
//Detection du bouton clicker
MouseButtons button = MouseButtons.None;
switch (wParam)
{
case WM_LBUTTONDOWN:
button = MouseButtons.Left;
break;
case WM_RBUTTONDOWN:
button = MouseButtons.Right;
break;
}
//parametre de notre event
MouseEventArgs e = new MouseEventArgs(
button,
1,
mouseHookStruct.pt.x,
mouseHookStruct.pt.y,
0);
//On appelle notre event
OnMouseClick(this, e);
}
//Si processNextHook == true alors on transmet le clic au destinataire, sinon, on le garde pour nous (
if (processNextHook == true)
{
return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
else
return 1;
}II-B-2. Méthode StartKeyHook▲
Dans cette méthode, nous allons initialiser le hook du clavier. Pour cela, nous utilisons toujours la fonction Win32 SetWindowsHookEx.
public void StartKeyHook()
{
KeyboardHookProcedure = new HookProc(KeyHookProc);
//Installer le hook
int hook = SetWindowsHookEx(
WH_KEYBOARD_LL,
KeyboardHookProcedure,
Marshal.GetHINSTANCE(
Assembly.GetExecutingAssembly().GetModules()[0]),
0);
//Si SetWindowsHookEx échoue
if (hook == 0)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode);
}
}Maintenant nous allons déclarer nos événements pour que notre code puisse s'y attacher.
Pour le clavier, nous avons trois événements :
- OnKeyDown ;
- OnKeyUp ;
- OnKeyPress.
OnKeyDown
private KeyPressEventHandler _onKeyPress;
public event KeyPressEventHandler OnKeyPress
{
add
{
_onKeyPress += value;
}
remove
{
_onKeyPress -= value;
}
}private KeyEventHandler _onKeyUp;
public event KeyEventHandler OnKeyUp
{
add
{
_onKeyUp += value;
}
remove
{
_onKeyUp -= value;
}
}private KeyEventHandler _onKeyDown;
public event KeyEventHandler OnKeyDown
{
add
{
_onKeyDown += value;
}
remove
{
_onKeyDown -= value;
}
}Maintenant nous allons remplir la méthode KeyHookProc. Cette méthode sera appelée dès qu'une touche du clavier sera appuyée.
private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
bool handled = false;
//On verifie si tous est ok
if ((nCode >= 0) && (_onKeyDown != null || _onKeyUp != null || _onKeyPress != null))
{
//Remplissage de la structure KeyboardHookStruct à partir d'un pointeur
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
//KeyDown
if (_onKeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
_onKeyDown(this, e);
handled = handled || e.Handled;
}
// KeyPress
if (_onKeyPress != null && wParam == WM_KEYDOWN)
{
// Si la touche Shift est appuyée
bool isShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
// Si la touche CapsLock est appuyée
bool isCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);
byte[] keyState = new byte[256];
GetKeyboardState(keyState);
byte[] inBuffer = new byte[2];
if (ToAscii(MyKeyboardHookStruct.vkCode,
MyKeyboardHookStruct.scanCode,
keyState,
inBuffer,
MyKeyboardHookStruct.flags) == 1)
{
char key = (char)inBuffer[0];
if ((isCapslock ^ isShift) && Char.IsLetter(key))
key = Char.ToUpper(key);
KeyPressEventArgs e = new KeyPressEventArgs(key);
_onKeyPress(this, e);
handled = handled || e.Handled;
}
}
// KeyUp
if (_onKeyUp != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
_onKeyUp(this, e);
handled = handled || e.Handled;
}
}
// si handled est a true, on ne transmet pas le message au destinataire
if (handled)
return 1;
else
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}III. Conclusion▲
Voilà, c'est terminé. Vous pouvez à présent intercepter toutes les actions de l'utilisateur.
Personnellement, je suis en train de me faire un nouveau menu démarrer en interceptant le clic sur le bouton démarrez. (http://www.nicolas-humann.com/index.php?2006/10/13/42-nstart-le-nouveau-menu-demarrer-pour-xp)



