Le hook du clavier et de la souris

N'hésitez pas à commenter cet article ! Commentez Donner une note à l'article (5)

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Dans ce tutorial, 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 tutorial 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 passes.

Sinon, intercepter un click 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 renvoi sur l'article de morpheus : http://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

MSDN:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp

Déclaration:

 
Sélectionnez

[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

MSDN:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/unhookwindowshookex.asp

Déclaration :

 
Sélectionnez

[DllImport("user32.dll", CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern int UnhookWindowsHookEx(int idHook);
						

II-A-1-c. CallNextHookEx

MSDN:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/callnexthookex.asp

Déclaration:

 
Sélectionnez

[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

MSDN:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp

Déclaration:

 
Sélectionnez

[DllImport("user32")]
private static extern int ToAscii(
    int uVirtKey,
    int uScanCode,
    byte[] lpbKeyState,
    byte[] lpwTransKey,
    int fuState);
						

II-A-1-e. GetKeyboardState

II-A-1-f. GetKeyState

MSDN:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/getkeystate.asp

Déclaration:

 
Sélectionnez

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern short GetKeyState(int vKey);
						

II-A-1-g. HookProc

MSDN:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/callwndproc.asp

Déclaration:

 
Sélectionnez

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 :

 
Sélectionnez

[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

MSDN:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookstructures/cwpstruct.asp

Déclaration :

 
Sélectionnez

[StructLayout(LayoutKind.Sequential)]
private class MouseHookStruct
{
    /// <summary>
    /// Structure POINT pour les coordonnée 
    /// </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 :

 
Sélectionnez

[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

MSDN:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookstructures/cwpstruct.asp

Déclaration :

 
Sélectionnez

[StructLayout(LayoutKind.Sequential)]
private class KeyboardHookStruct
{
    /// <summary>
    /// Key code virtuel, la valeur doit etre 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 serons utiles.

 
Sélectionnez
//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.

 
Sélectionnez
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éclaré notre évenement pour que notre code puisse s'y attacher.

Pour la souris, nous avons 1 évenements :

  1. OnMouseClick

OnMouseClick

 
Sélectionnez
private MouseEventHandler _onMouseClick;

public event MouseEventHandler OnMouseClick
{
    add
    {
        _onMouseClick += value;
    }
    remove
    {
       _onMouseClick -= value;
    }
}

Maintenant nous allons remplire la methode MouseHookProc. Cette méthode sera appelée dès qu'un bouton de la souris sera appuyé.

 
Sélectionnez
private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
{
    bool processNextHook = true;

    // Verifions si nCode est different de 0 et que nos evenements sont bien attachés
    if ((nCode >= 0) && (_onMouseClick != null))
    {
        //Remplissage de la structure MouseLLHookStruct a 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 click 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.

 
Sélectionnez
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 echoue
    if (hook == 0)
    {
        int errorCode = Marshal.GetLastWin32Error();
        throw new Win32Exception(errorCode);
    }
}

Maintenant nous allons déclarer nos évenements pour que notre code puisse s'y attacher.

Pour le clavier, nous avons 3 évenements :

  1. OnKeyDown
  2. OnKeyUp
  3. OnKeyPress

OnKeyDown

 
Sélectionnez
private KeyPressEventHandler _onKeyPress;

public event KeyPressEventHandler OnKeyPress
{
    add
    {
        _onKeyPress += value;
    }
    remove
    {
       _onKeyPress -= value;
    }
}
 
Sélectionnez
private KeyEventHandler _onKeyUp;

public event KeyEventHandler OnKeyUp
{
    add
    {
        _onKeyUp += value;
    }
    remove
    {
       _onKeyUp -= value;
    }
}
 
Sélectionnez
private KeyEventHandler _onKeyDown;

public event KeyEventHandler OnKeyDown
{
    add
    {
        _onKeyDown += value;
    }
    remove
    {
       _onKeyDown -= value;
    }
}

Maintenant nous allons remplire la methode KeyHookProc. Cette méthode sera appelée dès qu'une touche du clavier sera appuyée.

 
Sélectionnez
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 a 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

Voila, c'est terminé. Vous pouvez à présent intercepter toutes les actions de l'utilisateur.

Personnellement, je suis entrain de me faire un nouveau menu démarrer en interceptant le click sur le bouton démarrez. (http://www.nicolas-humann.com/index.php?2006/10/13/42-nstart-le-nouveau-menu-demarrer-pour-xp)

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Les sources présentées sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une oeuvre intellectuelle protégée par les droits d'auteurs. Copyright © 2005 Nicolas Humann . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.