Noticias

Keyloggers: Diferentes implementaciones en el sistema operativo Windows (segunda parte)

Índice

El presente artículo es la continuación del anterior trabajo dedicado a los keyloggers y es un análisis detallado de los aspectos técnicos de la realización de estos “espías de teclado”. Como ya hemos dicho en el primer artículo, la idea principal del keylogger consiste en interponerse entre dos eslabones cualquiera en la cadena que sigue la señal desde que se pulsa una tecla hasta que el carácter correspondiente aparece en la pantalla. Haremos un análisis de los eslabones que contiene esta cadena, así como de las diferentes variantes de construcción de los keyloggers de hardware y software.

Este artículo está pensado para los especialistas técnicos y los usuarios cualificados. A los demás lectores les será suficiente recordar que en el SO Windows existen muchas formas de interceptar los datos ingresados desde el teclado. No obstante, la mayor parte de los keyloggers existentes usan sólo dos de ellas (vea el capítulo “Principios de construcción de los keyloggers” en la primera parte del artículo).

Antes de empezar, aclaramos que en el presente artículo no aduciremos ningún ejemplo del código fuente de las diferentes implementaciones de los keyloggers. No compartimos la opinión de algunos expertos en seguridad que consideran que se puede publicar este tipo de información.

Procesamiento de la pulsación de teclas en el SO Windows

Existen varias tecnologías básicas, usadas por una serie de keyloggers, que permiten interceptar las pulsaciones de teclas y eventos del ratón. Sin embargo, antes de considerar los ejemplos reales de los keyloggers, es necesario ponerse al corriente del esquema de procesamiento de las pulsaciones de teclas utilizado en el SO Windows. Para describir todo el esquema, desde la pulsación de la tecla hasta el funcionamiento de la interrupción del sistema del controlador del teclado y la obtención del mensaje WM_KEYDOWN en la ventana activa, utilizaremos tres fuentes:

  1. El libro “El hardware de IBM PC”. Alexandr Frolov, Grigory Frolov, Tomo 2, libro 1: Dialog-MIFI, 1992. Capítulo 2, “El teclado”, donde se describen el principio de funcionamiento del teclado, los puertos e interrupciones que usa;
  2. La sección “Acceso a dispositivo de interfaz humana” de la Biblioteca MSDN, dónde se describe la parte básica (del controlador) del esquema del procesamiento del uso del teclado;
  3. El libro de Jeffrey Richter “Creación de aplicaciones WIN32 efectivas considerando las especificaciones de la versión de 64 bits de Windows”. En el capítulo 27, “El modelo de introducción de datos y el estado local de la introducción” se describe el nivel superior del esquema de procesamiento de las pulsaciones de teclas (realizado en el modo de usuario).

Principios de funcionamiento del teclado como dispositivo mecánico

En la actualidad, la mayoría de los teclados vienen como dispositivos separados que se conectan al ordenador mediante uno de los enchufes, por lo general PS/2 ó USB. Existen dos microcontroladores que sustentan el procesamiento de las pulsaciones: uno en la placa madre del ordenador y el otro en el teclado. Por lo tanto, el teclado del ordenador personal es en sí una especie de ordenador. Está basado en el microcontrolador 8042, que de forma constante escanea las pulsaciones de teclas en el teclado, independientemente de las actividades en el procesador central x86.

Cada una de las teclas tiene asignado un número determinado, que está relacionado con los contactos de la matriz del teclado y no depende de las letras impresas en la superficie de las teclas. Este número se llama scan-code (esta denominación remarca el hecho de que el ordenador escanea el teclado en busca de pulsaciones de teclas). El scan-code es un valor arbitrario, escogido por IBM cuando diseñaba el primer teclado para ordenadores personales. El scan-code no guarda correspondencia con el código ASCII del teclado. Una tecla puede tener varios códigos ASCII. La tabla de scan-codes se puede ver, por ejemplo, aquí.

En realidad, el teclado genera dos scan-codes para cada tecla: uno cuando el usuario la pulsa y otro cuando la suelta. La presencia de dos scan-codes es importante, ya que algunas teclas sólo son útiles cuando están oprimidas (Shift, Control, Alt).



Fig. 1. Esquema simplificado del teclado

Cuando el usuario pulsa una tecla en el teclado, se cierra un contacto eléctrico. Como resultado, en el siguiente barrido, el microcontrolador registrará la pulsación de determinada tecla y enviará al ordenador el scan-code de la tecla pulsada y una solicitud de interrupción. Acciones similares se ejecutan cuando el usuario suelta la tecla.

El segundo microcontrolador recibe el scan-code, lo convierte, lo envía al puerto de entrada-salida 60h y a continuación genera una interrupción de hardware para el ordenador central. Después de esto, el procedimiento de procesamiento de la interrupción puede recibir un scan-code del puerto de entrada-salida indicado.

Vale decir que el teclado contiene una memoria intermedia (buffer) de 16 bytes, mediante el cual realiza el intercambio de datos con el ordenador.

Interacción de bajo nivel con el teclado mediante los puertos de entrada-salida

La interacción con el controlador del teclado se efectúa por medio del puerto de entrada-salida 64h. Al leer el byte de este puerto se puede determinar el estado del controlado del teclado, al grabar el byte, se puede enviar una instrucción al controlador.

La interacción con el microcontrolador ubicado en el teclado se hace mediante los puertos de entrada-salida 60h y 64h. Los bites 0 y 1 en el byte del estado (puerto 64h en modo de lectura) dan la posibilidad de controlar el procedimiento de interacción: antes de escribir datos en estos puertos, el bite 0 del puerto 64h debe ser regresado a 0. Cuando los datos están disponibles para su lectura en los puertos 60h, el bite 1 del puerto 64h es igual a 1. Los bites de activación/desactivación del teclado en el byte de instrucción (puerto 64h en modo de escritura) determina si el teclado está activo y si el controlador del teclado solicitará una interrupción en el sistema cuando el usuario pulse la tecla.

os bytes escritos en el puerto 60h se envían al controlador del teclado, mientras que los bytes escritos en el puerto 64h se envían al controlador del sistema del teclado. Las listas de las instrucciones permitidas para su envío al controlador del teclado están en el documento “8042 KeyboardController IBM Technical Reference Manual” o aquí.

Los bytes leídos del puerto 60h llegan desde el teclado. El puerto 60h en modo de lectura contiene el scan-code de la última tecla pulsada, y en el modo de escritura se usa para el control extendido del teclado. Al usar el puerto 60h para escribir, el programa recibe además las siguientes posibilidades:

  • establecer el tiempo de espera antes de que el teclado empiece a repetir los caracteres;
  • establecer el periodo de generación del scan-code en el modo de repetición;
  • control de los diodos ubicados en el panel del teclado: Scroll Lock, Num Lock, Caps Lock.

En resumen, para leer los datos provenientes del teclado es suficiente saber leer los valores de los puertos de entrada-salida 60h y 64h. Sin embargo, en el sistema operativo Windows, a las aplicaciones en modo de usuario no se les permite trabajar con los puertos, por lo que esta tarea se la encomienda a los controladores del sistema operativo.

Arquitectura de los “dispositivos interactivos de entrada”

Bueno, pero… ¿quién procesa las interrupciones de hardware que se generan cuando en el puerto 60h aparecen datos enviados por el teclado? Evidentemente, es aquel que registró la rutina de procesamiento de interrupción hardware del teclado IRQ 1. En el SO Windows este procesamiento está a cargo del driver i8042prt.sys. A diferencia de los viejos tiempo del MS DOS, cuando cada componente del sistema no guardaba relación con los demás, en Windows todos los componentes están diseñados según una arquitectura rígida y funcionan según severas reglas consagradas en la interfaz de software. Por eso, antes de empezar a examinar el driver i8042prt, analizaremos la arquitectura de los dispositivos de entrada interactivos, en cuyo marco funcionan todos los componentes de software relacionados con el procesamiento de la entrada de datos desde el teclado y el ratón.

Los dispositivos usados para el control directo de las operaciones en el ordenador, se denominan dispositivos interactivos de entrada en el SO Windows (interactive input devices). El teclado es uno de estos dispositivos, al igual que los manipuladores como el ratón, joistick, trackball, pedales, cascos de realidad virtual, etc.

La arquitectura de control de los dispositivos de entrada interactivos se basan en el estándar USB Human Interface Device (HID), propuesto por la organización USB Implementers Forum. Pero esta arquitectura no sólo se limta a los dispositivos Usb y admite otros tipos de dispositivos de entrada, en particular: teclados bluetooth, teclados y ratones PS/2, así como los dispositivos que se conectan al puerto de juego (puerto de E/S 201).

A continuación analizaremos los principios de organización de la pila de drivers y la pila de dispositivos de los teclados PS/2, que desde el punto de vista histórico son el primer tipo de dispositivos usados. Los teclados USB usan una serie de elementos introducidos durante el diseño de soporte hardware de los teclados PS/2.

Drivers del modo del núcleo para teclados PS/2

Pila de drivers para los dispositivos de introducción de datos del sistema

Los drivers del teclado, sin importar cual sea su esquema de conexión física, usan drivers del sistema de la clase “teclado” para procesar las operaciones que no dependen del hardware. Estos drivers se llaman drivers de clase porque cumplen las exigencias del sistema independientemente de la implementación del hardware.

El correspondiente driver de funciones (driver del puerto) implementa las operaciones de entrada-salida de un dispositivo en particular. En el SO Windows para plataformas x86 el teclado (i8042) y el ratón comparten un solo driver del sistema.


Fig. 2. Pila de drivers para los dispositivos de introducción de datos del sistema: teclado y ratón

Pila de drivers para los teclados Plug and Play PS/2



Fig. 3. Pila de drivers para los teclados Plug and Play PS/2

La pila de drivers contiene (desde arriba hacia abajo):

  1. Kbdclass, un filtro-driver de nivel superior de la clase del teclado;
  2. un filtro-driver opcional de nivel superior de la clase del teclado;
  3. i8042prt, el driver funcional del teclado;
  4. el driver raíz del bus.

En el SO Windows 2000 (y superiores) el driver de clase del teclado es el driver Kbdclass, cuyas principales tareas son:

  • garantizar las operaciones generales e independientes del hardware de la clase de dispositivos;
  • brindar soporte a Plug and Play, a la gestión de energía y a Windows Management Instrumentation (WMI);
  • brindar soporte para los dispositivos antiguos;
  • ejecutar simultáneamente las operaciones de varios dispositivos;
  • aplicar la rutina class service callback routine, solicitada por el driver funcional para la transmisión de datos del bufer de entrada del dispositivo al bufer de datos del driver de clase de dispositivos.

En el SO Windows 2000 (y superiores) el driver funcional para los dispositivos de entrada que usan el puerto PS/2 (teclado y ratón) es el driver i8042prt, cuyas principales funciones son:

  • garantizar las operaciones simultáneas dependientes del hardware de los dispositivos de entrada PS/2 (el teclado y el ratón comparten los puertos de entrada-salida, pero usan diferentes interrupciones, rutinas de procesamiento de interrupciones (ISR) y rutinas de conclusión del procesamiento de las interrupciones);
  • brindar soporte a Plug and Play, a la gestión de energía y a Windows Management Instrumentation (WMI);
  • brindar soporte para los dispositivos antiguos;
  • llamar la rutina class service callback routine de las clases del teclado y del ratón para la transmisión de datos del bufer de entrada de datos i8042prt al bufer de datos de driver de clase;
  • la llamada del conjunto de funciones de la llamada inversa que pueden realizar los drivers-filtros de alto nivel para la gestión flexible del dispositivo.


Fig. 4. Lista de recursos hardware usados por el driver i8042prt

En la figura se muestra la lista de recursos hardware usados por el driver i8042prt. Los recursos se pueden visualizar con, por ejemplo, la utilidad DeviceTree, desarrollada por la compañía Open Systems Resources. Para aquellos que han leído las secciones 2.1 y 2.2, los valores de los puertos de entrada-salida (IO) 60h y de interrupción de hardware (IRQ) 1 no tienen nada de sorprendente.

En la pila del driver mostrada el nuevo driver-filtro puede ser agregado, por ejemplo, para el procesamiento específico de la entrada desde el teclado por encima del driver de clase del teclado. Este driver debe admitir el mismo procesamiento de todas las solicitudes de entrada-salida (IRP) e instrucciones de control (IOCTL) que el driver de clase del teclado. En este caso, antes de ser enviados al subsistema del modo del usuario, los datos serán entregados a este driver-filtro para su procesamiento.

Pila de dispositivos para los teclados Plug and Play PS/2

Ahora veremos la pila de dispositivos que crean los drivers indicados más arriba en la pila de drivers del teclado. En la parte derecha de la figura de abajo se representa la pila de dispositivos mencionada en MSDN Library para el teclado PS/2. A la derecha, una captura de pantalla del programa DeviceTree que muestra la situación real en el ordenador del autor de este artículo.



Fig. 5. Configuración de los objetos de dispositivos del teclado Plug and Play PS/2

En general, la pila de dispositivos (para ser más correctos, la pila de objetos de los dispositivos) del teclado PS/2 está conformada por:

  1. el objeto físico del teclado (PDO) creado por el driver del bus (en este caso, el bus PCI): Device�0000066;
  2. el objeto funcional del dispositivo del teclado (FDO) creado y adjuntado al PDO por el driver i8042prt: objeto sin nombre (unnamed);
  3. los filtros-objetos opcionales del dispositivo “teclado”, creados por los filtros-driver del teclado desarrollados por fabricantes;
  4. el filtro objeto de alto nivel del dispositivo de clase “teclado”, creado por el driver de clase Kbdclass: DeviceKeyboardClass0.

Procesamiento por las aplicaciones de la entrada de datos desde el teclado

Flujo de entrada no procesado (recepción de datos desde el driver)

En el capítulo anterior hemos dado ejemplos de la construcción de las pilas de teclado en modo de núcleo del sistema operativo. Ahora veremos cómo transcurre la transmisión de información sobre la pulsación de teclas a las aplicaciones del modo del usuario.

El subsistema Microsoft Win32 recibe acceso al teclado usando el flujo de entrada no procesado (Raw Input Thread, RIT), que es parte del proceso del sistema csrss.exe (user mode part of Win32-subsystem). El sistema operativo crea durante su inicio un RIT y una cola del sistema de entrada de hardware (system hardware input queue, SHIQ).

RIT abre el objeto “dispositivo” del driver de clase del teclado para su uso exclusivo y con la ayuda de la función ZwReadFile le envía una solicitud de entrada-salida (IRP) del tipo IRP_MJ_READ. Recibida la solicitud, el driver Kbdclass lo marca como “pendiente”, lo pone en la cola y devuelve el código STATUS_PENDING. El flujo de entrada no procesado tiene que esperar la conclusión del IRP, para lo cual usa una llamada de procedimiento asincrónico (Asynchronous Procedure Call, APC).

Cuando el usuario pulsa o suelta una de las teclas, el controlador del sistema del teclado genera una interrupción de hardware. El elemento que lo procesa escoge una rutina especial de tratamiento de la interrupción IRQ 1 (interrupt service routine, ISR), registrada en el sistema por el driver i8042prt. Este procedimiento lee en la cola interna del controlador del teclado los datos que aparecieron. El procesamiento de la interrupción del sistema debe ser lo más rápido posible, por lo que ISR pone en la cola la llamada de procedimiento retrasado (Deferred Procedure Call, DPC) I8042KeyboardIsrDpc y concluye su trabajo. En cuanto sea posible (cuando IRQL baje al DISPATCH_LEVEL), DPC será llamada por el sistema. En este momento se llamará al procedimiento de llamada inversa KeyboardClassServiceCallback registrada por el driver Kbdclass y el driver i804prt. KeyboardClassServiceCallback extraerá de su cola la solicitud IRP que espera su conclusión, llenará la mayor cantidad de estructuras KEYBOARD_INPUT_DATA, que contienen toda la información necesaria sobre las pulsaciones de teclas y concluirá el IRP. El flujo de entrada no procesado se activa, procesa la información recibida y de nuevo envía un IRP del tipo IRP_MJ_READ al driver de clase que de nuevo se pone en la cola hasta la siguiente pulsación o liberación de tecla. De esta manera, la pila del teclado siempre tiene por lo menos un IRP en espera que se encuentra en la cola del driver Kbdclass.



Fig. 6. Sucesión (orden) de las solicitudes del RIT al driver del teclado

Mediante la utilidad IrpTracker desarrollada por la compañía Open Systems Resources, podemos hacer un seguimiento de la secuencia de llamadas que tienen lugar durante el procesamiento de la entrada del teclado.


Fig. 7. Procesamiento de la entrada del teclado en el modo del usuario

Pero… ¿de qué manera el RIT procesa la información que le llega? Todos los eventos procedentes del teclado se acumulan en una cola del sistema de entrada de hardware, después de lo cual se convierten sucesivamente en mensajes de Windows (tipo WM_KEY*, WM_?BUTTON* o WM_MOUSEMOVE) y que se ponen al final de la cola de entrada virtual (virtualized input queue, VIQ) del flujo activo (foreground thread). En los mensajes de Windows los scan-codes de las teclas se convierten en códigos de teclas virtuales (virtual-key code), que no corresponden a la ubicación de las teclas en el teclado, sino a las acciones que cumple esta tecla. El mecanismo de transformación de los códigos depende del layout del teclado, de las teclas pulsadas al mismo tiempo (por ejemplo, SHIFT) y otros factores.

Cuando el usuario entra al sistema, el proceso Windows Explorer crea un flujo que a su vez crea el panel de tareas y el escritorio (WinSta0_RIT). Este flujo se encadena al RIT. Si el usuario inicia MS Word, entonces el flujo que crea la ventana se encadena de inmediato al RIT. Después, el flujo que pertenece a Explorer, se desconecta del RIT, ya que sólo un flujo puede estar conectado al RIT al mismo tiempo. Cuando se pulsa una tecla, en el SHIQ aparece el elemento correspondiente que hace que el RIT se active, transforme el evento de entrada hardware en un mensaje de teclado y lo pone en el VIQ del flujo de MS Word.

Procesamiento de los mensajes por una ventana en particular

¿De qué manera se procesan los mensajes del teclado que están en su cola de mensajes?

El ciclo estándar de procesamiento de mensajes suele representarse de la siguiente manera:

while(GetMessage(&msg, 0, 0, 0))
{
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}

Mediante las funciones GetMessage, los eventos del teclado se extraen de la cola y se remiten con la función DispatchMessage a la rutina de la ventana, que ejecuta el procesamiento de mensajes enviados a la ventana dónde en ese momento dado está el foco de entrada (input focus). El foco de entrada es un atributo que se puede asignar a una ventana creada por una aplicación o por Windows. Si la ventana tiene un foco de entrada, la correspondiente función de la misma recibe todos los mensajes del teclado provenientes de la cola del sistema. La aplicación puede pasar el foco de entrada de una ventana a otra, por ejemplo mediante la combinación de teclas Alt+Tab.

La función TranslateMessage suele llamarse antes que la función DispatchMessage. La primera se basa en los mensajes WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP para crear los mensajes “de símbolos” WM_CHAR, WM_SYSCHAR, WM_DEADCHAR y WM_SYSDEADCHAR. Los mensajes de símbolos creados se ponen en la cola de mensajes de la aplicación, pero los mensajes de teclado originales no se eliminan de esta cola.

Masivos del estado de las teclas del teclado

Una de las tareas enfrentadas durante el diseño del modelo de entrada de hardware de Windows era su tolerancia a las fallas. La tolerancia a las fallas la garantiza el procesamiento independiente de la entrada de datos, lo que evita que un flujo tenga efectos indeseables en otro flujo. Pero esto no es suficiente para aislar completamente unos flujos de otros, por lo que el sistema admite un concepto adicional, la tabla local de entrada (local input table). Cada flujo tiene su propio estado de entrada, cuyos datos se guardan en la estructura THREADINFO. En la información sobre este estado se incluyen los datos sobre la cola de entrada virtual y también un grupo de variables. Estas últimas contienen la información de control sobre el estado de la entrada. En lo que respecta al teclado, se admiten los siguientes datos: qué ventana está en el foco del teclado, qué ventana está activa en el momento dado, qué teclas están presionadas, cuál es el estado del cursor de entrada.

La información sobre las teclas pulsadas se guarda en el masivo de estado sincrónico de las teclas (synchronous key state array). Este masivo se incluye en las variables del estado local de entrada de cada flujo. Al mismo tiempo, el masivo del estado asincrónico de las teclas (asynchronous key state array), en el que se contiene información similar, es sólo uno y se comparte entre todos los flujos. Los masivos reflejan el estado de todas las teclas en un momento dado y la función GetAsyncKeyState permite determinar si una tecla dada está siendo pulsada. GetAsyncKeyState siempre muestra 0 (no pulsada) si la llama otro flujo que no sea el que creó la ventana que está en el foco de entrada.

La función GetKeyState se diferencia de GetAsyncKeyState en que muestra el estado del teclado en el momento en que se extrajo el último mensaje de la cola del flujo. Esta función se puede llamar en cualquier momento, para ella no tiene la menor importancia qué ventana está en el foco.

“Trampas” para el teclado

Enlace al capítulo “Hooks” de la sección Win32 and Com Development: Interfaz del usuario.

En el sistema operativo Microsoft Windows se llama “trampa” (hook) al mecanismo de intercepción de los eventos con el uso de una función especial (p.e. la transmisión de mensajes de Windows, la entrada desde el ratón o el teclado antes de que lleguen a la aplicación). Esta función puede, después, reaccionar a los eventos y en ciertos casos, modificarlos o anularlos.

Las funciones que reciben notificaciones sobre los eventos se llaman funciones filtrantes y se diferencian según los tipos de eventos que interceptan. Para que Windows pueda llamar una función filtrante, ésta deberá estar encadenada a una “trampa”, p.e. a una trampa de teclado. El encadenamiento de una o varias funciones filtrantes a una determinada trampa se denomina “instalación del hook”. Para instalar y eliminar las funciones filtrantes, las aplicaciones usan las funciones Win32 API SetWindowsHookEx y UnhookWindowsHookEx. Algunos hooks pueden instalarse para todo el sistema o para un solo flujo en particular.

Si un hook tiene encadenadas varias funciones filtrantes, Windows procesa la cola de funciones. La función encadenada más tarde se ubica en el principio de la cola, y la primera en el final de la cola. La cola de funciones-filtros (ver fig. 8) tiene soporte directo de Windows, lo que permite simplificar la escritura de funciones filtrantes y mejorar la productividad del sistema operativo.

El sistema admite cadenas independientes para cada tipo de hooks. La cadena de hooks es una lista de marcas que apuntan a las funciones filtrantes (funciones especiales de llamada inversa, determinadas por las aplicaciones). Cuando acontece cierto evento relacionado con un tipo de hook en particular, el sistema envía mensajes sucesivos a cada función filtrante en la cadena de hooks. La acción que puede ejecutar la función filtrante depende del tipo de hook: algunas funciones sólo pueden hacer un seguimiento de la aparición de eventos, otras pueden modificar los parámetros de los mensajes o hasta detener el procesamiento de los mensajes, bloqueando la llamada de la siguiente función filtrante en la cadena de hooks o la función de procesamiento de mensajes de la ventana destino.



Fig. 8. Cadena de funciones-filtros en Windows

Cuando se encadena a un hook una o más funciones-filtros y surge un evento que dispare el hook, el SO Windows llama la primera función de la cola de funciones-filtros y con esto concluye su responsabilidad. Después, la función es responsable de llamar la siguiente función en la cadena, para lo cual se usa la función Win32 API CallNextHookEx.

El SO admite varios tipos de hooks, cada uno de los cuales provee acceso a uno de los aspectos del mecanismo de procesamiento de mensajes de Windows.

Para el autor de un keylogger, representan interés los hooks de casi todos los tipos: WH_KEYBOARD, WH_KEYBOARD_LL (intercepción de eventos del teclado al ser puestos en la cola de eventos del flujo), WH_JOURNALRECORD y WH_JOURNALPLAYBACK (grabación y reproducción de eventos del teclado y del ratón), WH_CBT (captura de varios eventos, incluyendo la eliminación de evento del teclado de la cola de sistema de la entrada hardware), WH_GETMESSAGE (captura de los eventos recibidos desde la cola de eventos del flujo).

Esquema general de procesamiento

Resumamos todos los conocimientos expuestos arriba sobre la entrada de datos por el teclado en un solo algoritmo. El algoritmo de recorrido de la señal desde la pulsación de una tecla hasta la aparición de caracteres en la pantalla se puede representar de la siguiente manera:

  1. Durante su inicio, el sistema operativo crea en el proceso del sistema csrss.exe un flujo no procesado de entrada y una cola del sistema de entrada de hardware.
  2. El flujo de entrada no procesado en el ciclo envía solicitudes de lectura al driver de clase del teclado, los cuales quedan en situación de espera hasta la aparición de eventos provenientes del teclado.
  3. Cuando el usuario pulsa o suelta una tecla en el teclado, el microcontrolador registra la pulsación de determinada tecla y enviará al ordenador el scan-code de la tecla pulsada y una solicitud de interrupción.
  4. El microcontrolador del sistema recibe el scan-code, lo convierte, lo envía al puerto de entrada-salida 60h y a continuación genera una interrupción de hardware para el ordenador central.
  5. El controlador de interrupciones llama la rutina de procesamiento de interrupciones IRQ 1, el ISR registrado en el sistema como driver funcional del teclado i8042prt.
  6. La rutina ISR lee de la cola interna del controlador del teclado los datos que van apareciendo, los convierte en scan-codes de teclas virtuales (valores independientes determinados por el sistema) y pone en la cola una llamada de rutina retrasada I8042KeyboardIsrDpc.
  7. En cuanto es posible, el sistema llama el DPC que a su vez llama la rutina de llamada inversa KeyboardClassServiceCallback, registrada por el driver de clase del teclado Kbdclass.
  8. La rutina KeyboardClassServiceCallback extrae de su cola la solicitud en espera del flujo no procesado de entrada y regresa la información sobre la tecla pulsada.
  9. El flujo de entrada no procesado guarda la información recibida en una cola de sistema de entrada de hardware y genera los mensajes de teclado de Windows WM_KEYDOWN, WM_KEYUP, que se ponen en la cola de entrada virtual VIQ del flujo activo.
  10. El ciclo de procesamiento de los mensajes extrae el mensaje de la cola y lo envía a la rutina de ventana correspondiente para su procesamiento. Con esto, se puede llamar la función de sistema TranslateMessage, que sobre la base de los mensajes del teclado crea los mensajes “simbólicos” adicionales WM_CHAR, WM_SYSCHAR, WM_DEADCHAR è WM_SYSDEADCHAR.

Modelo de entrada directa (Raw Input)

El modelo de entrada desde el teclado descrita más arriba tiene una serie de defectos desde el punto de vista de los programadores de aplicaciones. Así, para recibir entrada desde un dispositivo no estándar, la aplicación debe ejecutar una gran cantidad de operaciones: abrir el dispositivo, enviarle solicitudes regularmente, controlar la posibilidad de que el dispositivo sea usado por otra aplicación, etc. Por esto, en las últimas versiones del SO Windows se propuso un modelo alternativo de entrada, llamada modelo de entrada directa (raw input model), llamada a simplificar la programación de aplicaciones que usan dispositivos de entrada no estándar (ver fig. 3, aplicaciones DirectInput).

El modelo de entrada directa es diferente del modelo original de entrada de Windows. En el modelo anterior, la aplicación recibe una entrada que no depende del dispositivo en forma de mensajes (como WM_CHAR) que se envían a las ventanas de la aplicación. En el modelo de entrada directa, la aplicación debe registrar el dispositivo desde el cual quiere recibir la entrada de datos. A continuación la aplicación recibe la entrada de datos del usuario mediante WM_INPUT. Se admiten dos tipos de transmisión de datos: estándar y buferizado. Para la interpretación de los datos ingresados la aplicación debe recibir información sobre la naturaleza del dispositivo de entrada, que se puede hacer con la ayuda de la función GetRawInputDeviceInfo.

Diferentes implementaciones de keyloggers

A continuación analizaremos los principales métodos de implementación de los keyloggers basándonos en los modelos arriba descritos de procesamiento de la entrada de datos del teclado en el SO Windows. Sabiendo la estructura de este modelo, es más fácil entender qué mecanismos pueden usar los keyloggers.

keyloggers pueden introducirse en cualquier lugar de la secuencia de procesamiento, capturando los datos sobre las teclas pulsadas, transmitidas por un subsistema de procesamiento al siguiente subsistema en la cadena de procesadores. Los métodos de implementación de programas keyloggers que analizaremos los hemos dividido en métodos del modo del usuario y métodos del modo del núcleo. En la figura 9 se muestran todos los subsistemas de procesamiento de entrada del teclado y sus relaciones mutuas. Al lado de ciertos subsistemas hay cifras en círculos rojos, que corresponden a la sección del artículo dónde se describe el método de implementación de keyloggers relacionado con la suplantación o utilización del subsistema indicado.



Fig. 9. Esquema general de entrada del teclado en el SO Windows

En esta sección no analizaremos los métodos de implementación de keyloggers basados en hardware. Solo mencionaremos que existen tres variedades de realización de los keyloggers hardware: el keylogger que se incrusta en el mismo teclado, el que se integra en el cable que conecta el teclado y el ordenador y el que se incrusta en el mismo ordenador. El más extendido es el segundo tipo de keylogger hardware. Uno de los ejemplos más famosos es KeyGhost USB Keylogger.

Actualmente, dos de los pocos antivirus que brindan protección contra la amenaza potencial de los keyloggers son los productos de la versión 6.0 de Kaspersky Lab. Para más detalles lea el artículo “Kaspersky Anti-Virus 6.0 y Kaspersky Internet Security 6.0: Protección contra los programas de registro de pulsaciones en el teclado (keyloggers)”.

Keyloggers del modo del usuario

Los keyloggers del modo del usuario son los más fáciles de implementar y de detectar, ya que para capturar los datos usan funciones conocidas y bien documentadas de la interfaz Win32 API.

Instalación de trampas para los mensajes del teclado

Quizás este sea el método más difundido de implementación de los keyloggers. Por medio de la llamada de la función SetWindowsHookEx, el keylogger instala un hook global para los eventos del teclado en todos los flujos del sistema (ver sección 2.6). La función filtrante del hook en este caso se ubica en una biblioteca dinámica aparte, que se incrusta en todos los procesos del sistema que procesa los mensajes. Al extraer de la cola de mensajes cualquier flujo de mensajes del teclado, el sistema llamará la función filtrante instalada.

Entre las virtudes de este método de intercepción están la simplicidad y la captura garantizada de todas las pulsaciones de teclas. Entre sus defectos está la necesidad de un fichero dll y la relativa facilidad de detección, ya que está incrustado en todos los procesos del sistema.

Ejemplos de keyloggers que usan este método: AdvancedKeyLogger, KeyGhost, Absolute Keylogger, Actual Keylogger, Actual Spy, Family Key Logger, GHOST SPY, Haxdoor, MyDoom, etc.

Kaspersky Internet Security detecta de forma proactiva este tipo de keyloggers como invader (loader). Para que esto sea posible, debe estar activa la opción “Incrustación de capturadores de ventanas” del sistema “Análisis de actividades de las aplicaciones en el módulo de defensa proactiva).

Utilización del barrido cíclico del estado del teclado

Es el segundo método de implementación de keyloggers más difundido. El estado de todas las teclas, a pequeños intervalos, se solicita mediante las funciones GetAsyncKeyState y GetKeyState. Estas funciones devuelven masivos del estado sincrónico o asincrónico de las teclas (ver sección 2.5.3). Al analizarlas, se puede entender qué teclas han sido pulsadas o liberadas después del último barrido.

Las ventajas de este método son la extrema simplicidad de su implementación y la ausencia de módulos adicionales (como en el caso de los hooks). Sus defectos: no se garantiza la detección de todas las pulsaciones, pueden haber lagunas; es fácil detectarla mediante la monitorización de los procesos que envían solicitudes al teclado con elevada frecuencia.

Este método de realización se usa en los keyloggers similares a Computer Monitor, Keyboard Guardian, PC Activity Monitor Pro, Power Spy, Powered Keylogger, Spytector, Stealth KeyLogger y Total Spy.

Kaspersky Internet Security detecta de forma proactiva este tipo de keyloggers como Keylogger. Para que esto sea posible, debe estar activa la opción “Incrustación de capturadores de ventanas” del sistema “Análisis de actividades de las aplicaciones en el módulo de defensa proactiva).

Incrustación en el proceso y captura de las funciones de procesamiento de los mensajes

Es un método poco utilizado. El keylogger se incrusta en todos los procesos y captura en estos las funciones GetMessage o PeekMessage (ver sección 2.5.2) de la biblioteca user32.dll. Con este fin se pueden usar diferentes métodos: splicing, modificación de la tabla de direcciones de las funciones importadas por IAT, la captura de la función GetProcAddres, que devuelve la dirección de la función desde la dll cargada. El keylogger puede implementarse en forma de DLL o mediante la incrustación directa de código en el proceso afectado.

Como resultado, tenemos lo siguiente: cuando la aplicación llama, por ejemplo, la función GetMessage para recibir el siguiente mensaje de la cola de mensajes, esta llamada llega al código del interceptor. El interceptor llama la función original GetMessage desde user32.dll y analiza los resultados devueltos según los tipos de mensaje. Al recibir un mensaje del teclado, se extrae toda la información de los parámetros del mensaje y se los registra en el keylogger.

Entre las ventajas de este método está su efectividad: como la difusión de este método es mínima, sólo pocos programas permiten detectarlos; además, contra estos keyloggers son inútiles los teclados de pantalla, ya que los mensajes que estos envía también se interceptan.

Sus defectos: la modificación de la tabla IAT no garantiza la captura, ya que las direcciones de las funciones de la biblioteca user32.dll pueden guardarse antes de que el keylogger se incruste; el spicing tiene sus dificultades relacionadas, por ejemplo, con la necesidad de reescribir en tiempo real el cuerpo de la función.

Kaspersky Internet Security detecta de forma proactiva este tipo de keyloggers como invader. Para que esto sea posible, debe estar activa la opción “Incrustación de capturadores de ventanas” del sistema “Análisis de actividades de las aplicaciones en el módulo de defensa proactiva).

*Splicing: método usado para interceptar las llamadas de las funciones API. La esencia del método consiste en el reemplazo de lo primeros bites (por lo general 5) de la función de la instrucción JMP, que entrega el control al código del interceptor.

Uso de modelos de entrada directa

En el presente tenemos noticias de sólo un ejemplo de implementación de este método: la utilidad de pruebas de la seguridad del sistema contra keyloggers. La esencia del método es el uso del nuevo modelo de entrada directa (ver sección 2.8), para lo cual el keylogger registra el teclado en calidad de dispositivo desde dónde quiere recibir la entrada de datos. Después, el keylogger empieza a recibir datos sobre las teclas pulsadas o liberadas mediante el mensaje WM_INPUT.

Sus ventajas son la simplicidad de implementación y su efectividad: como este método se ha hecho famoso hace poco tiempo, casi ninguno de los programas de defensa tiene, por el momento, la capacidad de hacerle frente.

Sus defectos: es muy fácil detectarlo ya que es necesario registrar la solicitud para que la aplicación obtenga entrada directa (ninguno de los procesos del sistema tiene este permiso por defecto).

Kaspersky Internet Security 7.0 detecta de forma proactiva este tipo de keyloggers como Keylogger. Para que esto sea posible, debe estar activa la opción “Incrustación de capturadores de ventanas” del sistema “Análisis de actividades de las aplicaciones en el módulo de defensa proactiva).

Keyloggers del modo del núcleo

Los keyloggers del modo del núcleo son más complejos que los del modo del usuario, tanto en su implementación como en su detección. Su escritura exige conocimientos especiales, pero se pueden crear keyloggers completamente imperceptibles para todas las aplicaciones del modo del usuario. Para la captura de datos pueden usar métodos documentados en Microsoft Driver Development Kit y métodos no documentados.

Uso de un driver-filtro de clase teclado Kbdclass

Método de captura documentado en el DDK (ver sección 2.4.2). Los keyloggers basados en este método capturan las solicitudes enviadas al teclado mediante la instalación del filtro sobre el dispositivo “DeviceKeyboardClass0”, creado por el driver Kbdclass (ver sección 2.4.3). Se filtran sólo las solicitudes IRP_MJ_READ, ya que son precisamente estos los que permiten recibir los códigos de las teclas pulsadas o liberadas.

Sus ventajas: garantiza la captura de todas las pulsaciones, no se lo puede detectar sin usar un driver, por lo que no todos los antikeyloggers los detectan. Sus defectos: es necesario instalar un driver.

Los más conocidos keyloggers que usan este método de implementación son: ELITE Keylogger y Invisibel KeyLogger Stealth.

Kaspersky Internet Security detecta de forma proactiva este tipo de keyloggers como Keylogger mediante la monitorización de la pila de dispositivos. Para que esto sea posible, debe estar activa la opción “Incrustación de capturadores de ventanas” del sistema “Análisis de actividades de las aplicaciones en el módulo de defensa proactiva).

Uso de un driver-filtro del driver funcional i8042prt

Está documentado en el DDK. Los keyloggers que usan este método capturan las solicitudes al teclado mediante la instalación de un filtro sobre un dispositivo sin nombre creado por el driver i8042prt bajo el dispositivo “DeviceKeyboardClass0” (ver sección 2.4.3). El driver i8042prt es una interfaz software para agregar funciones adicionales de procesamiento de la interrupción IRQ1 (IsrRoutine), en la cual se puede hacer el análisis de los datos recibidos desde el teclado.

Sus ventajas y defectos son similares a los del método anterior. Pero tiene un defecto más: su dependencia del tipo de teclado: el driver i8042prt sólo procesa las solicitudes provenientes de teclados PS/2, por eso este método no sirve para capturar los datos introducidos por un teclado USB o un teclado inalámbrico.

Kaspersky Internet Security detecta de forma proactiva este tipo de keyloggers como Keylogger mediante la monitorización de la pila de dispositivos. Para que esto sea posible, debe estar activa la opción “Incrustación de capturadores de ventanas” del sistema “Análisis de actividades de las aplicaciones en el módulo de defensa proactiva).

Modificación de la tabla de procesamiento de las solicitudes del sistema del driver Kbdclass

Los keyloggers basados en este método capturan las solicitudes al teclado, reemplazando el punto de entrada IRP_MJ_READ en la tabla de procesamiento de solicitudes del sistema (dispatch table) del driver Kbdclass. Por sus funciones es parecido al driver-filtro del driver Kbdclass (ver sección 3.2.1). Tiene las mismas particularidades. En otra implementación se usa la captura de otra función del procesador de solicitudes: IRP_MJ_DEVICECONTROL. En este caso el keylogger se convierte en algo parecido al driver-filtro i8042 (ver sección 3.2.2).

Kaspersky Internet Security detecta de forma proactiva este tipo de keyloggers como Keylogger. Para que esto sea posible, debe estar activa la opción “Incrustación de capturadores de ventanas” del sistema “Análisis de actividades de las aplicaciones en el módulo de defensa proactiva).

Modificación de la tabla de servicios del sistema KeServiceDescriptorTableShadow

Es un método bastante difundido de implementar keyloggers. Sus funciones son similares a las del método del modo del usuario descrito en la sección 3.1.3. Los keyloggers basados en este método capturan las solicitudes al teclado mediante un parche aplicado al punto de entrada NtUserGetMessage/PeekMessage en la segunda tabla de servicios del sistema (KeServiceDescriptorTableShadow) del driver win32k.sys. La información sobre la pulsacion de teclas llega al interceptor cuando en cualquiera de los flujos se concluye la llamada de las funciones GetMessage o PeekMessage.

La ventaja de este método es la dificultad de su detección. Su defecto: la relativa complejidad de su implementación (la búsqueda de la tabla KeServiceDescriptorTableShadow es una tarea difícil; además, otros drivers pueden haber “parchado” el punto de entrada a esta tabla) y la necesidad de instalar un driver independiente.

Kaspersky Internet Security detecta de forma proactiva este tipo de keyloggers como Keylogger. Para que esto sea posible, debe estar activa la opción “Incrustación de capturadores de ventanas” del sistema “Análisis de actividades de las aplicaciones en el módulo de defensa proactiva).

Modificación del código de la función NtUserGetMessage/PeekMessage mediante splicing

Es un tipo de keylogger que se encuentra muy raras veces. Los keyloggers basados en este método capturan las solicitudes al teclado modificando el código de la función NtUserGetMessage o NtUserPeekMessage por medio del splicing. Los datos de la función se realizan en el núcleo del sistema en el driver win32k.sys y se llaman desde las funciones correspondientes de la biblioteca user32.dll. Como se indica en la sección 3.1.3, estas funciones permiten filtrar todos los mensajes recibidos por las aplicaciones y obtener los datos sobre las teclas pulsadas o liberadas de los mensajes del teclado.

La ventaja de este método es la dificultad de su detección, su defecto es la complejidad de su implementación (la necesidad de reescribir el cuerpo de la función en tiempo real, la dependencia de la versión del sistema operativo y los programas instalados, así como la necesidad de instalar un driver.

Sustitución del driver en la pila de drivers del teclado

Este método se basa en la sustitución del driver Kbdclass o de uno de los drivers de bajo nivel por un driver programado por el delincuente. Un evidente defecto de este método es la complejidad de su realización, ya que no se sabe con anterioridad el tipo de teclado usado y por lo tanto es fácil detectar la sustitución del driver. Como resultado, este método prácticamente no se usa.

Realización de un driver-procesador de interrupciones IRQ 1 propio

El método consiste en escribir su propio driver de modo de núcleo que capture las interrupciones del teclado (IRQ 1) y que se dirija directamente a los puertos de entrada-salida (60h, 64h). Como es un método difícil de implementar y no está clara su interacción con el procesador de interrupciones del sistema IRQ 1 (driver i8042prt.sys), en la actualidad es un método netamente teórico.

Conclusiones

En este artículo hemos analizado el algoritmo de recorrido desde la pulsación de una tecla hasta la aparición de un carácter en la pantalla, hemos visto los eslabones de la cadena de procesamiento de la señal y los métodos de implementación de los keyloggers basados en la captura de la entrada del teclado en determinadas etapas de este algoritmo.

  1. El esquema de introducción de datos desde el teclado en el SO Windows es bastante complejo y se puede introducir interceptores prácticamente en cualquier etapa. En algunas etapas del algoritmo ya existen interceptores, en otros todavía no.
  2. Existe una dependencia entre la difusión del keylogger y la complejidad de su realización. Así, los métodos más utilizados de captura (la instalación de un hooker de eventos de entrada y el barrido cíclico del estado del teclado) son al mismo tiempo los más usados. Utilizando estos métodos, hasta una persona que ha aprendido a programar hace una semana puede escribir un keylogger.
  3. La gran mayoría de los keyloggers que existen hoy en día son instrumentos bastante simples de implementar, que pueden utilizarse con fines delictivos, sobre todo para robar la información confidencial introducida por medio del teclado.
  4. Las compañías antivirus deben brindar a los usuarios protección contra la amenaza representada por los keyloggers usados por los delincuentes.

A medida de que se vayan perfeccionando los medios de defensa, los autores de keyloggers tendrán que adoptar implementaciones más complejas que usen drivers del núcleo del sistema operativo Windows. En este campo todavía tienen muchas posibilidades.

Keyloggers: Diferentes implementaciones en el sistema operativo Windows (segunda parte)

Su dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

 

Informes

BlindEagle vuela alto en LATAM

Kaspersky proporciona información sobre la actividad y los TTPs del APT BlindEagle. Grupo que apunta a organizaciones e individuos en Colombia, Ecuador, Chile, Panamá y otros países de América Latina.

MosaicRegressor: acechando en las sombras de UEFI

Encontramos una imagen de firmware de la UEFI infectada con un implante malicioso, es el objeto de esta investigación. Hasta donde sabemos, este es el segundo caso conocido en que se ha detectado un firmware malicioso de la UEFI usado por un actor de amenazas.

Suscríbete a nuestros correos electrónicos semanales

Las investigaciones más recientes en tu bandeja de entrada