PEB Hooker
Publicado: 29 Sep 2010, 15:52
Hola...
Antes que nada aclarar que el texto es una traducción del artículo publicado en la ezine phrack 65..
phook - The PEB Hooker
Index
0.- Antes de leer
1.- Introducción
2.- Conceptos previos
2.1 - Process Environment Block
2.1.1 - LoaderData
2.2 - Import Address Table
2.2.1 - Carga de la Import Address Table
2.3 - Inicio de un proceso en estado suspendido
2.4 - Inyección de una DLL en un proceso
2.5 - Hooks en ring3
2.5.1 - Problemas
3.- Diseño
3.1 - Pasos previos a PEB HOOKING
3.2 - Intercambio de datos en LoaderData
3.3 - Carga de módulos dinámicamente
3.4 - Reparación de la IAT
3.5 - Comienza la ejecución
3.6 - Las APIs que trabajan con módulos
3.7 - Un nuevo concepto: DLL MINIFILTER
3.8 - Problemas frecuentes
4.- phook
4.1 - InjectorDLL
4.2 - Console Control
4.3 - CreateExp
4.3.1 - Forwarder DLL
4.4 - ph_ker32.dll
4.4.1 - Los problemas con la pila
4.4.2 - Los problemas con los registros
4.4.3 - La macro JMP
4.4.4 - Versiones
4.5 - Usando phook
4.5.1 - DLL MINIFILTER
4.6 - Problemas frecuentes
5.- TODO
6.- Testing
7.- Ventajas y posibilidades
8.- Conclusión
9.- Agradecimientos
10.- Trabajos relacionados
11.- Referencias
12.- Código fuente
------[ 0.- Antes de leer
Nomenclaturas:
.- [T.Index]: trabajos relacionados (apartado 10).
.- [R.Index]: referencias (apartado 11).
Index es el identificador de la nomenclatura.
Para entender el documento es necesario tener conocimiento en win32 de:
- Tipos de ejecutables:
- PE32 [R.3]: DLLs, EXE...
- Programacion:
- Uso de APIs [R.20]: LoadLibrary, GetModuleHandle ...
- Hooks [R.10] [R.8] [...]
- Win32 ASM [R.21].
Se usarán dos términos a lo largo de todo el documento:
1.- DLL_FAKE: DLL que suplantará a una DLL legítima (DLL_REAL).
2.- DLL_REAL: DLL que será suplantada por DLL_FAKE.
Cada vez que se mencione la palabra hook/s se referirá a hook/s en win32,
a no ser que se especifique lo contrario.
------[ 1.- Introducción
Los hooks en win32 son muy usados para hacer ingenieria inversa, las
motivaciones más comunes suelen ser el análisis de malware y sistemas de
protección de software (packers). Los hooks también se suelen usar para
monotorizar partes de un software: acceso a ficheros, sockets, modificación
del registro ...
Los métodos actuales para realizar hooks en ring3 (ver apartado 2.5) tienen
diferentes problemas (ver apartado 2.5.1). El problema más importante para
nosotros fue que cierto software los detectan. Hay sistemas de protección
de software que son capaces de alterar el flujo de ejecución cuando
detectan algún tipo de hook conocido, incluso los más sofisticados son
capaces de eliminar algunos tipos de hooks y seguir el flujo de ejecucion
normal.
Otro problema surge cuando se intenta realizar un hook en los virus que
rastrean las direcciones de las APIs en memoria, inutilizando algunos tipos
de hooks como el IAT HOOKING (ver apartado 2.5). Hay sistemas de protección
de software que usan algunas técnicas de virus y viceversa.
Debido a estos problemas hemos creado phook, que usa un método poco
documentado para realizar hooks en ring3 y además consigue que algunas
técnicas de virus usen nuestro hook.
Este documento explica como funciona phook y el método PEB HOOKING [T.1].
phook es una herramienta que usa PEB HOOKING [T.1] para realizar un hook de
una DLL, también permite de forma interactiva realizar otras tareas:
- Listar los módulos cargados.
- Cargar una DLL.
- Descargar una DLL.
- ...
El método PEB HOOKING [T.1] consiste en suplantar una DLL_REAL en memoria
por una DLL_FAKE, de forma que todos los módulos de un proceso que usen la
DLL_REAL pasen a usar la DLL_FAKE.
------[ 2 - Conceptos previos
Para entender el método PEB HOOKING [T.1] y como funciona phook, es
necesario tener claros ciertos conceptos:
------[ 2.1 - Process Environment Block
El Process Environment Block (PEB) es una estructura [R.1] ubicada en el
espacio de usuario, que contiene los datos de entorno del proceso [R.2]:
- Variables de entorno.
- Lista de módulos cargados.
- Direcciones en memoria del Heap.
- Si el proceso está siendo depurado.
- ...
Para realizar PEB HOOKING es necesario usar el campo LoaderData [T.1].
------[ 2.1.1 - LoaderData
Es una estructura [R.1] en la que se encuentran algunos datos de los
módulos de un proceso. Se trata de una lista doblemente enlazada y que se
puede recorrer segun tres criterios [R.2]:
1.- Orden de carga.
2.- Orden en memoria.
3.- Orden de inicialización.
Cada campo flink y blink de LIST_ENTRY son en realidad punteros a
LDR_MODULE.
Los datos que vamos a manipular de LDR_MODULE para realizar PEB HOOKING
son [T.1]:
- BaseAddress: La base del módulo en memoria.
- EntryPoint : Dirección donde se encuentra la primera instrucción a
ejecutar del módulo.
- SizeOfImage: Tamaño del módulo en memoria.
------[ 2.2 - Import Address Table
La Import Address Table (IAT) es una tabla, que tienen los PE32 [R.3], la
cual rellena el cargador de win32 en la carga de un módulo [R.4].
Los símbolos externos que necesita un módulo se llaman importaciones, los
símbolos que un módulo proporciona a otros módulos se llaman exportaciones.
En la IAT [R.3] de un módulo están las direcciones de sus importaciones, o
dicho de otra manera, en la IAT [R.3] de un módulo están las direcciones de
las exportaciones que usa de otros módulos.
------[ 2.2.1 - Carga de la Import Address Table
Para que el cargador de win32 pueda obtener la exportación necesita
conocer: el módulo en el que se encuentra, el nombre de la exportación y/o
el ordinal [R.3].
Los PE32 tienen una estructura llamada IMAGE_IMPORT_DESCRIPTOR [R.5] en la
que destacan los campos:
- Name : Nombre del módulo donde se encuentran las
exportaciones.
- OriginalFirstThunk: Dirección de tabla donde se encuentran los
nombres y/o los ordinales de las exportaciones
que importa el módulo.
- FirstThunk: Dirección de una tabla, idéntica a
OriginalFirstThunk, en la que el cargador de
win32 pone las direcciones de las importaciones.
Cada entrada de la tabla de FirstThunk y OriginalFirstThunk tiene dos
campos [R.3]:
- Hint: si los primeros 31/63 bits son 0x80000000 se importa teniendo
en cuenta solo el ordinal, en caso contrario se usará el
nombre. Los bits 15-0 representan el ordinal.
- Name: Dirección donde se se encuentra el nombre de la exportación.
------[ 2.3 - Inicio de un proceso en estado suspendido
Cuando se quiere crear un proceso en estado suspendido hay que saber de que
tipo es [R.6]:
- Console
- GUI
Los procesos de tipo Console se pueden crear con la API CreateProcess y el
flag CREATE_SUSPENDED.
Los procesos de tipo GUI si se abren con el flag CREATE_SUSPENDED pueden
no funcionar bien, así que hay que crearlos usando las APIs:
1.- CreateProcess: Se crea el proceso sin flag CREATE_SUSPENDED.
2.- WaitForInputIdle: Se espera a la correcta carga del proceso [R.6].
3.- SuspendThread: Se suspende el hilo principal.
------[ 2.4 - Inyección de una DLL en un proceso
Para inyectar una DLL en un proceso hay varios métodos [R.7], el método más
simple es usando las APIs:
1.- VirtualAllocEx: Reservar memoria en el proceso.
2.- WriteProcessMemory: Escribir en el espacio reservado un código que cargue una DLL.
3.- CreateRemoteThread: Se crea un hilo en el proceso que ejecute el código escrito.
4.- VirtualFreeEx: Una vez cargada la DLL se libera la memoria reservada.
------[ 2.5 - Hooks en ring3
Siempre han existido varias formas de realizar "hooks" en win32, tanto
en ring3 como en ring0. El problema de trabajar en ring0 es que si algo
falla el SO puede volverse inestable. El método más estable para el SO es
realizar el "hook" desde ring3.
Los métodos más cónocidos son:
- IAT HOOKING: Se modifican las entradas en la IAT [R.3], que pone el
cargador de win32, para que apunten a otra zona [R.8].
- PUSH + RET: Se introduce en un area de código las instrucciones
PUSH DIRECCION y RET para saltar a la dirección deseada.
Generalmente es necesario pasar el control al area
original, teniendo que restaurarla en un momento
determinado [R.9].
- SetWindowHook...: Con estas APIs, se puede registrar una callback
para diferentes eventos del sistema [R.10].
------[ 2.5.1 - Problemas
Algunos problemas en los métodos para realizar hooks en ring3:
Las llamadas desde ring3 a ring0 usando SYSENTER no se pueden controlar
mediante solo los métodos anteriores. Una llamada al sistema desde ring3 se
puede realizar con SYSENTER [R.11] sin pasar por ninguna DLL, de tal forma
que los métodos anteriores quedan inutilizados en esta situación poco
común.
Debido a los problemas anteriores, hemos decidido usar PEB HOOKING [T.1]
para crear un motor que realiza más que "hooks":
phook - The PEB Hooker.
Nota: Las ventajas y posibilidades de PEB HOOKING [T.1] se explican en el
apartado 7.
Antes que nada aclarar que el texto es una traducción del artículo publicado en la ezine phrack 65..
phook - The PEB Hooker
Index
0.- Antes de leer
1.- Introducción
2.- Conceptos previos
2.1 - Process Environment Block
2.1.1 - LoaderData
2.2 - Import Address Table
2.2.1 - Carga de la Import Address Table
2.3 - Inicio de un proceso en estado suspendido
2.4 - Inyección de una DLL en un proceso
2.5 - Hooks en ring3
2.5.1 - Problemas
3.- Diseño
3.1 - Pasos previos a PEB HOOKING
3.2 - Intercambio de datos en LoaderData
3.3 - Carga de módulos dinámicamente
3.4 - Reparación de la IAT
3.5 - Comienza la ejecución
3.6 - Las APIs que trabajan con módulos
3.7 - Un nuevo concepto: DLL MINIFILTER
3.8 - Problemas frecuentes
4.- phook
4.1 - InjectorDLL
4.2 - Console Control
4.3 - CreateExp
4.3.1 - Forwarder DLL
4.4 - ph_ker32.dll
4.4.1 - Los problemas con la pila
4.4.2 - Los problemas con los registros
4.4.3 - La macro JMP
4.4.4 - Versiones
4.5 - Usando phook
4.5.1 - DLL MINIFILTER
4.6 - Problemas frecuentes
5.- TODO
6.- Testing
7.- Ventajas y posibilidades
8.- Conclusión
9.- Agradecimientos
10.- Trabajos relacionados
11.- Referencias
12.- Código fuente
------[ 0.- Antes de leer
Nomenclaturas:
.- [T.Index]: trabajos relacionados (apartado 10).
.- [R.Index]: referencias (apartado 11).
Index es el identificador de la nomenclatura.
Para entender el documento es necesario tener conocimiento en win32 de:
- Tipos de ejecutables:
- PE32 [R.3]: DLLs, EXE...
- Programacion:
- Uso de APIs [R.20]: LoadLibrary, GetModuleHandle ...
- Hooks [R.10] [R.8] [...]
- Win32 ASM [R.21].
Se usarán dos términos a lo largo de todo el documento:
1.- DLL_FAKE: DLL que suplantará a una DLL legítima (DLL_REAL).
2.- DLL_REAL: DLL que será suplantada por DLL_FAKE.
Cada vez que se mencione la palabra hook/s se referirá a hook/s en win32,
a no ser que se especifique lo contrario.
------[ 1.- Introducción
Los hooks en win32 son muy usados para hacer ingenieria inversa, las
motivaciones más comunes suelen ser el análisis de malware y sistemas de
protección de software (packers). Los hooks también se suelen usar para
monotorizar partes de un software: acceso a ficheros, sockets, modificación
del registro ...
Los métodos actuales para realizar hooks en ring3 (ver apartado 2.5) tienen
diferentes problemas (ver apartado 2.5.1). El problema más importante para
nosotros fue que cierto software los detectan. Hay sistemas de protección
de software que son capaces de alterar el flujo de ejecución cuando
detectan algún tipo de hook conocido, incluso los más sofisticados son
capaces de eliminar algunos tipos de hooks y seguir el flujo de ejecucion
normal.
Otro problema surge cuando se intenta realizar un hook en los virus que
rastrean las direcciones de las APIs en memoria, inutilizando algunos tipos
de hooks como el IAT HOOKING (ver apartado 2.5). Hay sistemas de protección
de software que usan algunas técnicas de virus y viceversa.
Debido a estos problemas hemos creado phook, que usa un método poco
documentado para realizar hooks en ring3 y además consigue que algunas
técnicas de virus usen nuestro hook.
Este documento explica como funciona phook y el método PEB HOOKING [T.1].
phook es una herramienta que usa PEB HOOKING [T.1] para realizar un hook de
una DLL, también permite de forma interactiva realizar otras tareas:
- Listar los módulos cargados.
- Cargar una DLL.
- Descargar una DLL.
- ...
El método PEB HOOKING [T.1] consiste en suplantar una DLL_REAL en memoria
por una DLL_FAKE, de forma que todos los módulos de un proceso que usen la
DLL_REAL pasen a usar la DLL_FAKE.
------[ 2 - Conceptos previos
Para entender el método PEB HOOKING [T.1] y como funciona phook, es
necesario tener claros ciertos conceptos:
------[ 2.1 - Process Environment Block
El Process Environment Block (PEB) es una estructura [R.1] ubicada en el
espacio de usuario, que contiene los datos de entorno del proceso [R.2]:
- Variables de entorno.
- Lista de módulos cargados.
- Direcciones en memoria del Heap.
- Si el proceso está siendo depurado.
- ...
Código: Seleccionar todo
typedef struct _PEB
{
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PPEBLOCKROUTINE FastPebLockRoutine;
PPEBLOCKROUTINE FastPebUnlockRoutine;
...
} PEB, *PPEB;
------[ 2.1.1 - LoaderData
Es una estructura [R.1] en la que se encuentran algunos datos de los
módulos de un proceso. Se trata de una lista doblemente enlazada y que se
puede recorrer segun tres criterios [R.2]:
1.- Orden de carga.
2.- Orden en memoria.
3.- Orden de inicialización.
Código: Seleccionar todo
typedef struct _PEB_LDR_DATA
{
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
LDR_MODULE.
Código: Seleccionar todo
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY * Flink;
struct _LIST_ENTRY * Blink;
} LIST_ENTRY,*PLIST_ENTRY;
son [T.1]:
- BaseAddress: La base del módulo en memoria.
- EntryPoint : Dirección donde se encuentra la primera instrucción a
ejecutar del módulo.
- SizeOfImage: Tamaño del módulo en memoria.
Código: Seleccionar todo
typedef struct _LDR_MODULE
{
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
La Import Address Table (IAT) es una tabla, que tienen los PE32 [R.3], la
cual rellena el cargador de win32 en la carga de un módulo [R.4].
Los símbolos externos que necesita un módulo se llaman importaciones, los
símbolos que un módulo proporciona a otros módulos se llaman exportaciones.
En la IAT [R.3] de un módulo están las direcciones de sus importaciones, o
dicho de otra manera, en la IAT [R.3] de un módulo están las direcciones de
las exportaciones que usa de otros módulos.
------[ 2.2.1 - Carga de la Import Address Table
Para que el cargador de win32 pueda obtener la exportación necesita
conocer: el módulo en el que se encuentra, el nombre de la exportación y/o
el ordinal [R.3].
Los PE32 tienen una estructura llamada IMAGE_IMPORT_DESCRIPTOR [R.5] en la
que destacan los campos:
- Name : Nombre del módulo donde se encuentran las
exportaciones.
- OriginalFirstThunk: Dirección de tabla donde se encuentran los
nombres y/o los ordinales de las exportaciones
que importa el módulo.
- FirstThunk: Dirección de una tabla, idéntica a
OriginalFirstThunk, en la que el cargador de
win32 pone las direcciones de las importaciones.
Código: Seleccionar todo
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
DWORD OriginalFirstThunk;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;
campos [R.3]:
- Hint: si los primeros 31/63 bits son 0x80000000 se importa teniendo
en cuenta solo el ordinal, en caso contrario se usará el
nombre. Los bits 15-0 representan el ordinal.
- Name: Dirección donde se se encuentra el nombre de la exportación.
Código: Seleccionar todo
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;
Cuando se quiere crear un proceso en estado suspendido hay que saber de que
tipo es [R.6]:
- Console
- GUI
Los procesos de tipo Console se pueden crear con la API CreateProcess y el
flag CREATE_SUSPENDED.
Los procesos de tipo GUI si se abren con el flag CREATE_SUSPENDED pueden
no funcionar bien, así que hay que crearlos usando las APIs:
1.- CreateProcess: Se crea el proceso sin flag CREATE_SUSPENDED.
2.- WaitForInputIdle: Se espera a la correcta carga del proceso [R.6].
3.- SuspendThread: Se suspende el hilo principal.
------[ 2.4 - Inyección de una DLL en un proceso
Para inyectar una DLL en un proceso hay varios métodos [R.7], el método más
simple es usando las APIs:
1.- VirtualAllocEx: Reservar memoria en el proceso.
2.- WriteProcessMemory: Escribir en el espacio reservado un código que cargue una DLL.
3.- CreateRemoteThread: Se crea un hilo en el proceso que ejecute el código escrito.
4.- VirtualFreeEx: Una vez cargada la DLL se libera la memoria reservada.
------[ 2.5 - Hooks en ring3
Siempre han existido varias formas de realizar "hooks" en win32, tanto
en ring3 como en ring0. El problema de trabajar en ring0 es que si algo
falla el SO puede volverse inestable. El método más estable para el SO es
realizar el "hook" desde ring3.
Los métodos más cónocidos son:
- IAT HOOKING: Se modifican las entradas en la IAT [R.3], que pone el
cargador de win32, para que apunten a otra zona [R.8].
- PUSH + RET: Se introduce en un area de código las instrucciones
PUSH DIRECCION y RET para saltar a la dirección deseada.
Generalmente es necesario pasar el control al area
original, teniendo que restaurarla en un momento
determinado [R.9].
- SetWindowHook...: Con estas APIs, se puede registrar una callback
para diferentes eventos del sistema [R.10].
------[ 2.5.1 - Problemas
Algunos problemas en los métodos para realizar hooks en ring3:
Nota: Esta tabla solo representa la opinión de los autores.+-------------------------------------------------------------------------+
| Algunos Métodos | Algunos problemas |
+------------------------+------------------------------------------------+
| IAT HOOKING [R.8] | 1.- Hay que cambiar la IAT [R.3] de todos los |
| | los módulos cargados. |
| | 2.- Un módulo no necesita IAT [R.3] para usar |
| | símbolos exportados por otro. |
| | 3.- Es muy conocido. |
| | 4.- Fácil de reparar. |
| | 5.- Puede ser detectable. |
| | 6.- No permite control total desde el inicio. |
|------------------------+------------------------------------------------|
| PUSH + RET [R.9] | 1.- El método no es genérico para todas las |
| | áreas de código. |
| | 2.- Es complicado de implementar. |
| | 3.- Fácil de reparar. |
| | 4.- Puede ser detectable. |
| | 5.- No permite control total desde el inicio. |
|------------------------+------------------------------------------------|
| Otros "hooks": | 1.- No permite control total. |
| SetWindowHook... [R.10]| 2.- Fácil de reparar. |
| | 3.- Puede ser detectable. |
|------------------------+------------------------------------------------|
| PEB HOOKING [T.1] | 1.- Es complicado de implementar. |
| | 2.- La DLL original y la inyectada tienen que |
| | exportar los mismos símbolos en el mismo |
| | orden (como mínimo). |
| | 3.- Puede ser detectable. |
| | 4.- No permite control total desde el inicio. |
+------------------------+------------------------------------------------+
Las llamadas desde ring3 a ring0 usando SYSENTER no se pueden controlar
mediante solo los métodos anteriores. Una llamada al sistema desde ring3 se
puede realizar con SYSENTER [R.11] sin pasar por ninguna DLL, de tal forma
que los métodos anteriores quedan inutilizados en esta situación poco
común.
Debido a los problemas anteriores, hemos decidido usar PEB HOOKING [T.1]
para crear un motor que realiza más que "hooks":
phook - The PEB Hooker.
Nota: Las ventajas y posibilidades de PEB HOOKING [T.1] se explican en el
apartado 7.