24 abril 2013

WHF Windows Hooking Framework II

Primera entrega de esta serie de posts:

WHF Windows Hooking Framework I

--------

En esta segunda entrega me voy a centrar en los hooks utilizando las callback disponibles en el API de programación de drivers y en el API hook.

A modo de recordatorio:

Driver Hook. Los drivers permiten instalar callbacks que se ejecutarán cuando ocurran ciertos eventos en el sistema operativo, como puede ser el inicio o el fin de un proceso, de un thread o el mapeo de ficheros binarios a memoria entre muchos otros.

API hook. Son hooks que interceptan las llamadas a funciones que realizan los programas. Ejemplos clásicos pueden ser interceptar llamadas a funciones del sistema de ficheros como son OpenFile, WriteFile o ReadFile, llamadas relacionadas con el registro de Windows como RegOpenKeyEx, RegEnumValue o RegCloseKey y funciones para comunicarse en red como connect, send o recv.

Cuando se utiliza este tipo de hooks, se consigue modificar el comportamiento de un programa realizando alguna de las siguientes acciones:
  1. Filtrado de llamadas: Según las condiciones que establezcamos se llamará o no a la función
  2. Modificación de los parámetros de entrada de la función, alterando así el resultado de la función
  3. Modificación del valor de retorno de la función, si es que devuelve algún valor, 
Una forma rápida de ver a que funciones utiliza un programa es mediante el Dependecy Walker (depends.exe). Se puede lanzar desde ProcessExplorer pinchando con el boton derecho del ratón sobre cualquier proceso que se este ejecutando en ese instante en el sistema. En la imagen inferior se muestra Dependecy Walker mostrando las DLL que utiliza MSPAINT y las funciones que utiliza de Kernel32.dll



Los hooks de API pueden llevarse a cabo en user-space (mediante la inyección de una dll) o en kernel-space (mediante un driver). La principal diferencia es que mediante el hook en user-space afecta a un proceso y en kernel-space afecta a todos los procesos en ejecución.

Para enterder mejor a que me refiero con user-space y kernel-space es necesario saber que los procesadores suelen implementan un modelo de seguridad basado en anillos. Los procesadores Intel implementan 4 niveles de ejecución, desde el ring3 al ring0. El ring3 es el menos permisivo, permitiendo ejecutar un conjunto de instrucciones mas reducido. El ring2 es un poco más permisivo y así hasta el nivel 0 que es el nivel más permisivo y donde se tiene acceso a todas las instrucciones del procesador. Microsoft por motivos de compatibilidad con otras arquitecturas sólo utiliza dos niveles, el ring3 para ejecutan los procesos de usuario (user-space) y el ring0 donde ejecuta el kernel del sistema operativo y los drivers (kernel-space).  


Ejemplo 4, Monitorizando, suspendiendo y matando procesos.

En este ejemplo vamos a utilizar un callback que se llamara cuando se crean o destruyen procesos. En la figura inferior se puede observar cómo se define el callback en la función DriverEntry.



Se tienen varias opciones para registrar, suspender o matar los procesos que hayamos indicado en la configuración.

En este ejemplo, supongamos que sólo se quiere permitir navegar con el Internet Explorer a los usuarios, la configuración que creamos registra los inicios del Internet Explorer, suspende los Firefox y mata los Chrome, pero sólo los mataremos de Lunes a Viernes desde el 7 de Marzo de 2013 y entre las 9:00 de la mañana hasta las 18:00. De esta forma cuando sea fin de semana o estemos fuera del horario laboral el usuario puede utilizar el Chrome.


En este caso muestro sólo la configuración de Chrome. Para configurar iexplore habría que seleccionar la opción register dentro de Process y para firefox se selecciona suspend


Si tenéis Process Explorer ponedlo en primer plano, ya que veréis mucho mas claramente el comportamiento de este hook. Lanzamos hooklauncher y arrancamos Internet Explorer, Firefox y Chrome. En el primer caso (IE) podréis utilizarlo sin ningun problema. Al lanzar Firefox observareis que se crea un proceso, pero Process Explorer lo marca en gris por que se ha suspendido. Al lanzar Chrome si estáis dentro del schedule configurado, matará el proceso Chrome, marcandolo en rojo, como se puede ver en la imagen inferior.


Además en C:\WHF\TestingPath\ProcWatcher\<dia mes año>.txt deja un log de las actuaciones que ha relizado. En la imagen inferior se muestra el log después de la ejecución realizada en mi máquina.


Ejemplo 5, API hook user space.

Como ya indiqué, este tipo de hook nos permite modificar el comportamiento de un programa al interceptar las llamadas a una o varias funciones.

En espacio de usuario, a día de hoy, sólo tengo implementado el hook mediante de modificación de la IAT, Import Adress Table. Esta estructura es una tabla existente en el formato de los ficheros PE, ejecutables de Windows. Esta estructura se completa en tiempo de carga en memoria del programa, con las direcciones de memoria de las funciones utilizadas por el programa. Por medio de una inyección de DLL en el espacio de memoria del proceso victima se sobrescriben las direcciones de memoria de las funciones a interceptar con las direcciones de memoria de nuestras funciones. Nuestra función ha de tener los mismos parámetros de entrada y retornar el mismo tipo de datos que la función hookeada

En el ejemplo la DLL testIAT.dll, en el fichero dllmain.cpp se ha decidido modificar el comportamiento de SetWindowTextA. Esta función tiene dos parámetros de entrada, el Handle de la ventana donde se va a actuar y el texto que se va a establecer para la misma y retorna un BOOL.

La función por la que la vamos a sustituir es la que se muestra a continuación. En ella se recupera la dirección de la función reemplazada en OldFn y si se cumplen los valores del schedule, si es que se establece alguno, se modifica el texto a mostrar por Hook3d PabloWinSSSSS. Si no se cumple el schedule se llamará a la función sin modificar ningún parámetro.


Además de la función hay que completar una estructura en la que se indica el nombre de la DLL donde esta definida la función a hookear, el nombre de la función a hookear y la función que hemos definido  para reemplazarla, tal y como se puede ver en la siguiente imagen.


Para este ejemplo utilizaré una app a modo de ejemplo App2 cuyo funcionamiento normal es mostrar el PID y el path de ella misma. La configuración que se debe establecer es la que se muestra a continuación.


Para realizar la inyección de DLL vamos a establecer un hook sobre en ratón  pero esta vez utilizaremos la DLL testIAT.dll y la funcion HookMouse.



Una vez lanzado el hooklauncher, el ejecutable App2.exe cambia su comportamiento y el mensaje mostrado ya no es el PID o el path del ejecutable. Cuando paremos el hooklauncher la aplicación vuelve a su comportamiento original.



Ejemplo 6, API hook kernel space.

La principal diferencia con respecto al ejemplo anterior es que este hook se ejecuta a nivel de kernel, lo que significa que en lugar de afectar únicamente al proceso donde inyectamos la DLL, al modificar el comportamiento de una funcion afectamos a todos procesos del sistema.

Este tipo de hook es muy atractivo por afectar a todo el sistema, pero tiene sus contrapartidas. La primera es que si no se es cuidadoso al programar se acabará generando el clásico pantallazo azul de Windows y la segunda es que la forma de programar es diferente a la que suelen estar acostumbradas las personas que no hayan programado drivers.

Para realizar este hook se utiliza un driver, que localiza la SSDT, le quita la protección contra escritura, modifica el contenido de la misma y vuelve a establecer la protección frente a escrituras. La SSDT, System Service Dispatch Table es una tabla del kernel de Windows donde se guardan las direcciones de memoria donde se resuelven las funciones en espacio de kernel.

En este ejemplo lo que vamos a hacer es ocultar los procesos que indiquemos. Como se puede ver en la imagen inferior he seleccionado prácticamente todos los procesos del sistema.


Mediante el boton Global Edit, asigamos a todas las reglas la misma funcionalidad. Seleccionamos Global API Hook y en el combo ProcHider.


El driver reemplazar la dirección de la funcion ZwQuerySystemInformation contenida en la SSDT con la dirección de la función NewZwQuerySystemInformation definida dentro del mismo driver. La función NewZwQuerySystemInformation manipula los resultados obtenidos en la ejecución de la función ZwQuerySystemInformation consiguiendo que se oculten los procesos seleccionados como se puede ver en la imagen inferior con el antes y el después de la ejecución del hooklauncher


ToDo

En este proyecto queda mucho por hacer, entre las tareas pendientes destacan las siguientes:

Servidor de configuración y servicio hooklauncher: No utilizar el registro de Windows como repositorio de la configuración de un usuario y en su lugar utilizar BBDD, o Active Directory. y convertir el hooklauncher en un servicio que se arranque junto con el SO

Implementar otras técnicas de API hooks, en user-space, actualmente sólo se realizan hooks en la IAT y en kernel-space sólo se actúa sobre la SSDT

Estudiar las posibilidades que puedan aportar los Minifilters en este proyecto.

Antes de terminar me gustaría agredecer a SbD y a Yago en particular por picarme para escribir este artículo, a Borja Garnelo, Paco Sáa, Jose Ramon Palanco y a Javi GDT. Sus comentarios me animan a la hora de continuar con el proyecto.

-------------------------------------------------------------

Artículo cortesía de Pablo San Emeterio @psaneme