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.
- ...

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;
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.

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;
Cada campo flink y blink de LIST_ENTRY son en realidad punteros a
LDR_MODULE.

Código: Seleccionar todo

typedef struct _LIST_ENTRY {
    	struct _LIST_ENTRY * Flink;
    	struct _LIST_ENTRY * Blink;
    	
    } LIST_ENTRY,*PLIST_ENTRY;
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.

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;
------[ 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.

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;
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.

Código: Seleccionar todo

 typedef struct _IMAGE_IMPORT_BY_NAME {
    	WORD Hint;
    	BYTE Name[1];
    	
    } IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;
------[ 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:
+-------------------------------------------------------------------------+
| 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. |
+------------------------+------------------------------------------------+
Nota: Esta tabla solo representa la opinión de los autores.

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.
Imagen


Solo lo mejor es suficiente...
------[ 3.- Diseño

En este apartado se hablará del diseño base para realizar PEB HOOKING [T.1]
con éxito. La implementación no es complicada cuando se comprende por que
se hace cada cosa.

Los pasos:

1.- Cargar DLL_FAKE y DLL_REAL.

2.- En la lista que usa el cargador de win32, en la que se encuentran
todos los módulos cargados en ese momento, hay que intercambiar
varios campos entre DLL_FAKE y DLL_REAL.

3.- Es necesario que las IATs [R.3] de todos los módulos cargados,
excepto DLL_REAL y quizás DLL_FAKE, apunten a las funciones que
exporta la DLL_FAKE.


------[ 3.1 - Pasos previos a PEB HOOKING

Es necesario antes de nada cargar en la memoria del proceso, al que se
quiere realizar PEB HOOKING [T.1], una DLL_FAKE. La DLL_FAKE debe tener
como mínimo las mismas exportaciones y en el mismo orden que DLL_REAL.


------[ 3.2 - Intercambio de datos en LoaderData

Es necesario buscar DLL_FAKE y DLL_REAL por algun campo identificativo de
LDR_MODULE, una vez encontrados se intercambiarán los siguientes datos:

- EntryPoint

- BaseAddress

- SizeOfImage (casi siempre)

La búsqueda usando el campo BaseDllName obtendrá los datos del LDR_MODULE
perteneciente a DLL_FAKE. Algunos virus, packers y APIs usan esta forma de
búsqueda para encontrar la BaseAddress o EntryPoint de un módulo.

El campo SizeOfImage es necesario cambiarlo en caso de que DLL_FAKE y
DLL_REAL no tengan el mismo tamaño en memoria.

Flujo de búsqueda de BaseAddress de kernel32.dll en un proceso sin
PEB HOOKING [T.1]:
0 +---------------------------------+
[ process ] ---------+ | Process Environment Block (PEB) |
| |---------------------------------|
| | InheritedAddressSpace |
| | ReadImageFileExecOptions |
| | BeingDebugged |
| | Spare |
| | Mutant |
| | ImageBaseAddress |
+->| LoaderData |--+
| ... | |
+---------------------------------+ | 1
|
|
+--------------------------------------------------------------+
| +----------------------------+ +----------------------------+
| | LoaderData | | LDR_MODULE |
| +----------------------------+ |----------------------------| flink
| | Length | | InLoadOrderModList |-----+
| | Initialized | | InMemoryOrderModList | |
| | SsHandle | | InInitOrderModList | |
+->| InLoadOrderModList | 2 | ... | |
| InMemoryOrderModList |---->| BaseDllName "ntdll.dll" |---+ |
| InInitOrderModList - Flink | +----------------------------+ | |
+----------------------------+ +------------------------------------+ |
| +----------------------------+ |
| | LDR_MODULE (DLL_REAL) | |
| |----------------------------| |
| | InLoadOrderModList | 6 |
+---------------------+ 3 | | InMemoryOrderModList | |
| ¿Es "kernel32.dll"? |<-------+ | InInitOrderModList | |
+---------------------+ | BaseAddress 7C801000 | |
8 | |4 ^ 7 | ... | |
Si <-+ +-> No +-------------| BaseDllName "kernel32.dll" |<----+
| | 5 | ... |
9 | v +----------------------------+
| NextLdrModule();
v
kernel32.dll = 7C801000


Flujo de búsqueda de BaseAddress de kernel32.dll en el proceso anterior
con PEB HOOKING [T.1]:
0 +---------------------------------+
[ process ] ---------+ | Process Environment Block (PEB) |
| |---------------------------------|
| | InheritedAddressSpace |
| | ReadImageFileExecOptions |
| | BeingDebugged |
| | Spare |
| | Mutant |
| | ImageBaseAddress |
+->| LoaderData |--+
| ... | |
+---------------------------------+ | 1
|
|
+--------------------------------------------------------------+
| +----------------------------+ +----------------------------+
| | LoaderData | | LDR_MODULE |
| +----------------------------+ |----------------------------| flink
| | Length | | InLoadOrderModList |-----+
| | Initialized | | InMemoryOrderModList | |
| | SsHandle | | InInitOrderModList | |
+->| InLoadOrderModList | 2 | ... | |
| InMemoryOrderModList |---->| BaseDllName "ntdll.dll" |---+ |
| InInitOrderModList - Flink | +----------------------------+ | |
+----------------------------+ +------------------------------------+ |
| +----------------------------+ |
| | LDR_MODULE (DLL_REAL) | |
| |----------------------------| 6 |
| | InLoadOrderModList | |
+---------------------+ 3 | | InMemoryOrderModList |flink|
| ¿Es "kernel32.dll"? |<-------+ | InInitOrderModList |--+ |
+---------------------+ | BaseAddress 7C801000 | | |
12 | |4-8 ^ ^ 7 | ... | | |
Si <-+ +-> No | +-------------| BaseDllName "old_k32.dll" |<-|--+
| 5-9 | +------------+ | ... | |
13 | v | +----------------------------+ |
| NextLdrModule(); +-+ |
v | +----------------------------+ |
kernel32.dll = 005C5000 | | LDR_MODULE (DLL_FAKE) | | 10
| |----------------------------| |
11 | | InLoadOrderModList | |
| | InMemoryOrderModList | |
| | InInitOrderModList | |
| | BaseAddress 005C5000 | |
| | ... | |
+-| BaseDllName "kernel32.dll" |<+
| ... |
+----------------------------+

Resultados de la búsqueda en el proceso:

1.- BaseAddress sin PEB HOOKING [T.1]: 7C801000 (DLL_REAL)

2.- BaseAddress con PEB HOOKING [T.1]: 005C5000 (DLL_FAKE)

PD: Generalmente buscando por InLoadOrderModList, el primer campo que
aparece es el LDR_MODULE correspondiente al módulo principal. En el
ejemplo se ha omitido por motivos de claridad.


------[ 3.3 - Carga de módulos dinámicamente

Cuando un proceso, en el que se ha hecho PEB HOOKING [T.1], carga un módulo
dinámicamente [R.12] que tenga importaciones de DLL_REAL, automáticamente
se cargará su IAT [R.3] con las exportaciones necesarias de DLL_FAKE.


------[ 3.4 - Reparación de la IAT

Excepto en los módulos DLL_FAKE y DLL_REAL, se deben reemplazar todas las
IATs [R.3] que tengan exportaciones de DLL_REAL por las correspondientes de
DLL_FAKE. La IAT [R.3] de DLL_FAKE no se debe cambiar en caso de necesitar
usar las exportaciones de DLL_REAL.

Si se ha modificado la IAT [R.3] de DLL_FAKE para que las exportaciones de
DLL_REAL sean las de DLL_FAKE, una llamada a una exportación de DLL_REAL
desde la misma exportación de DLL_FAKE, entrará en un bucle recursivo
infinito, causando un desbordamiento de la pila.
+--------------------------+ +--------------------------------+
| .text DLL_FAKE | | IAT |
|--------------------------| |--------------------------------|
| ... | | LocalAlloc 1 (Nr_LocalAlloc) |
| PUSH EBP | +->| LoadLibrary 2 (Nr_LoadLibrary) |--+
| MOV EBP, ESP | | | .... | |
| ... | | +--------------------------------+ |
| LoadLibrary_FAKE: | | |
+->| PUSH original_lib_name | | 0 |
| | CALL IAT[Nr_LoadLibrary] |--+ |
| | ... | |
| | POP EBP | |
| | RET | |
| | ... | |
| +--------------------------+ |
| 1 |
+-----------------------------------------------------------------------+
El problema real es que nos estamos llamando a nosotros mismos ya sea
directa o indirectamente por una o varias DLLs. No se debe reparar la IAT
[R.3] de un módulo cualquiera (DLL_ANY), cuando DLL_FAKE llama a una
exportación de DLL_ANY que a su vez llama a una exportación de DLL_FAKE que
implique volver a llamar a la misma exportación directa o indirectamente de
DLL_ANY.

Flujo de una llamada a RtlHeapAlloc, cuando se ha hecho PEB HOOKING [T.1]
sobre NTDLL.DLL y se ha cambiado la IAT de kernel32.dll:

Ejemplo:
[ proceso ]
|
| CALL RtlHeapAlloc CALL LoadLibrary
+-------------------> [DLL_FAKE ntdll.dll] ------------------+
0 ^ 1 |
| CALL RtlInitUnicodeString v
+--------------------------- [DLL_ANY kernel32.dll]
2
Flujo de una llamada a RtlHeapAlloc, cuando se ha hecho PEB HOOKING [T.1]
sobre NTDLL.DLL y NO se ha cambiado la IAT [R.3] de kernel32.dll:
[ proceso ]<----------------+
| 4 |
| CALL RtlHeapAlloc | CALL LoadLibrary
+-------------------> [ DLL_FAKE ntdll.dll] ------------------+
0 ^ 1 |
+------------------+ |
| 3 |
| CALL RtlInitUnicodeString v
[DLL_REAL old_nt.dll] <--------------------------- [DLL_ANY kernel32.dll]
2
Nota: El esquema se ha simplificado, omitiendo el resto de llamas de DLL_FAKE.


Flujo de una llamada normal a LoadLibrary en un proceso (sin PEB HOOKING
[T.1]):
CALL IAT[Nr_LoadLibrary] +--------------------------------+
[process] -------------------------+ | IAT |
^ 0 | |--------------------------------|
| | | LocalAlloc 1 (Nr_LocalAlloc) |
| +-----------------------+ +->| LoadLibrary 2 (Nr_LoadLibrary) |-+
| | DLL_REAL kernel32.dll | | .... | |
| |-----------------------| +--------------------------------+ |
| | ... | 1 |
| | LoadLibrary: | <--------------------------------------+
| 2 | PUSH EBP |
| | MOV EBP, ESP |
| | ... |
| | POP EBP |
+----| RET |
| ... |
+-----------------------+
El flujo es normal y pasa directamente por DLL_REAL.

Flujo de una llamada a LoadLibrary en un proceso con PEB HOOKING [T.1]:
CALL IAT[Nr_LoadLibrary] +--------------------------------+
[process] -------------------------+ | IAT |
^ 0 | |--------------------------------|
| | | LocalAlloc 1 (Nr_LocalAlloc) |
| +-------------------------+ +->| LoadLibrary 2 (Nr_LoadLibrary) |-+
| | DLL_FAKE kernel32.dll | | .... | |
| |-------------------------| +--------------------------------+ |
4 | | ... | 1 |
| | Own_LoadLibrary: | <--------------------------------------+
| | PUSH EBP |
| | MOV EBP, ESP | +-----------------------------+
| | // Own functions... | 2 | DLL_REAL old_k32.dll |
| | CALL IAT[Nr_LoadLibrary]|----+ |-----------------------------|
| | POP EBP |<-+ | | ... |
+--| RET | | +->| LoadLibrary: |
| ... | | | PUSH EBP |
+-------------------------+ | | MOV EBP, ESP |
| | ... |
3 | | POP EBP |
| | RET |--+
| | ... | |
| +-----------------------------+ |
+-------------------------------------+
Como se puede observar el flujo pasa antes por DLL_FAKE. Después DLL_FAKE
llama a LoadLibrary original (DLL_REAL).


------[ 3.5 - Comienza la ejecución

Una vez realizado todos los pasos anteriores es el momento de empezar a
ejecutar el proceso y ver si todo funciona.


------[ 3.6 - Las APIs que trabajan con módulos

Las APIs LoadLibrary, GetModuleHandle, EnumProcessModules [R.12] ... usan
el campo LoaderData del PEB [T.1]. Esto quiere decir que siempre que
intenten algo contra la DLL_REAL estarán tratando con DLL_FAKE, por
ejemplo:

Se ha realizado PEB HOOKING [T.1] a USER32.DLL:

- DLL_FAKE

- Nombre en memoria: USER32.DLL
- BaseAddress: 00435622

- DLL_REAL

- Nombre en memoria: OLD_U32.DLL
- BaseAddress: 77D10000

El proceso intenta obtener la base de USER32.DLL:

- HMODULE user32 = GetModuleHandle( "user32.dll" );

Después de ejecutar GetModuleHandle [R.12] la variable user32
valdrá: 00435622 (BaseAddress de DLL_FAKE). Si el proceso después hace un
GetProcAddress [R.12] sobre alguna función exportada por USER32.DLL,
obtendrá la función de DLL_FAKE.

Gracias a PEB HOOKING [T.1] ya no es necesario cambiar el comportamiento de
las APIs que trabajan con módulos para que usen DLL_FAKE.


------[ 3.7 - Un nuevo concepto: DLL MINIFILTER

DLL MINIFILTER es el nombre que hemos dado a la capacidad por la cual una
llamada a una exportación pueda pasar por varias DLL_FAKE. Una de las
ventajas más importantes del método es la de extender o limitar
funcionalidades de forma modular a la llamada de una exportación.

Cuando se hace PEB HOOKING [T.1] sobre una DLL_FAKE, el término DLL_REAL
para la nueva DLL_FAKE, pasa a ser la DLL_FAKE anterior, creando así una
pila de DLL_FAKEs. El flujo pasará desde la última DLL_FAKE, con la que se
ha hecho PEB HOOKING [T.1], hasta la DLL_REAL, en caso de que todas las
DLL_FAKEs llamen a la exportación original.

Flujo de una llamada de un proceso, con PEB HOOKING [T.1], con una sola
DLL_FAKE:
0 1
[proceso] --> [DLL_FAKE] --> [DLL_REAL]
^ |
| 2 |
+----------------------------+
Flujo de una llamada de un proceso, con PEB HOOKING [T.1], con tres
DLL_FAKEs:
0 1 2 3
[proceso] --> [DLL_FAKE 3] --> [DLL_FAKE 2] --> [DLL_FAKE 1] --> [DLL_REAL]
^ |
| 4 |
+---------------------------------------------------------------+
En los ejemplos anteriores, todas las DLL_FAKEs pasan el control a la
DLL_REAL correspondiente.


------[ 3.8 - Problemas frecuentes

A la hora de realizar PEB HOOKING [T.1] pueden ocurrir ciertos problemas, a
continuación se muestra una tabla con los problemas y las posibles
soluciones:
+-------------------------------------------------------------------------+
| Problema | Posible/s Solución/es |
|-------------------------------+-----------------------------------------|
| - Falla el PEB HOOKING [T.1] | - Comprobar que se pueden intercambiar |
| | los campos necesario en el PEB [T.1]. |
| | - Comprobar si se tienen los permisos |
| | correctos para cambiar las IATs [R.3] |
| | necesarias. |
|-------------------------------+-----------------------------------------|
| - Falla la ejecución del | - Comprobar que se recorre bien el PEB |
| proceso | [R.1]. |
| | - Comprobar si se recorren bien las |
| | IATs [R.3] de todos los módulos del |
| | del proceso. |
| | - Comprobar si se han restaurado los |
| | permisos en memoria modificados en el |
| | PEB HOOKING [T.1]. |
+-------------------------------------------------------------------------+
------[ 4.- phook

phook es capaz de realizar PEB HOOKING [T.1] (y otras cosas) de una manera
sencilla. phook es un proyecto de varios módulos:

- InjectorDLL: Programa que crea un proceso suspendido de forma y le
inyecta una DLL.

- Console Control: DLL que se inyecta en el proceso en el que deseemos
hacer PEB HOOKING [T.1]. Permite hacer PEB HOOKING
[T.1] y otras tareas de forma interactiva mediante
una consola de comandos por sockets.

- CreateExp: Programa que genera a partir de una DLL_REAL el código
fuente necesario para realizar una DLL_FAKE.

- ph_ker32.dll: DLL_FAKE de kernel32.dll. ph_ker32.dll monotoriza el
acceso a las APIs: CreateFileA y CreateFileW [R.14].


------[ 4.1 - InjectorDLL

Programa que crea un proceso suspendido y le inyecta una DLL. Para inyectar
la DLL C:\console.dll en el proceso correspondiente a C:\poc.exe:

- Especificar el tipo de proceso:

- CONSOLE:
- InjectorDLL.exe C:\console.dll -c C:\poc.exe

- GUI:
- InjectorDLL.exe C:\console.dll -g C:\poc.exe

- No especificar el tipo de proceso
- InjectorDLL.exe C:\console.dll -u C:\poc.exe

InjectorDLL, con el parámetro -u, suele detectar si un proceso es GUI o
Console para saber como crearlo suspendido (ver apartado 2.3). El método
que hemos creado consiste en crear el proceso con la API CreateProcess y el
flag CREATE_SUSPENDED [R.6]. Después se llama a WaitForInputIdle, si la
espera falla se trata de un proceso Console, en caso contrario será GUI.

Código: Seleccionar todo

    CreateProcess
    (  
        program_name                          , 
        NULL                                  , 
        NULL                                  , 
        NULL                                  ,  
        FALSE                                 ,
        CREATE_SUSPENDED | CREATE_NEW_CONSOLE , 
        NULL                                  , 
        NULL                                  , 
        pstart_inf                            , 
        ppro_inf
    ) 
    
    // Hay que comprobar la creación correcta del proceso...
    
    if ( WaitForInputIdle( ppro_inf->hProcess, 0 ) == WAIT_FAILED )
        // "Console process"
    else
        // "GUI process"
Una vez que se sabe el tipo de proceso, ya se sabemos como crearlo
suspendido correctamente (ver apartado 2.3).

Nota: el método puede no funcionar siempre, en alguna ocasión un
"Console process" lo detecta como "GUI process".

El código que carga la DLL se mete en una estructura llamada LOADER_DLL_s
(ver apartado 2.3). LOADER_DLL_s se carga con las instrucciones en
ensamblador y los datos necesarios. Hay que escribir en el proceso creado
la estructura LOADER_DLL_s y llamar a CreateRemoteThread, pasándole como
entrypoint el inicio de la estructura, para que se ejecute el código de
LOADER_DLL_s.

Una vez cargada la DLL se suspende el hilo desde el cual se está ejecutando
LOADER_DLL_s y incrementa un flag para indicarlo.

Código: Seleccionar todo

    typedef struct LOADER_DLL_s
    {
        /* - CODE ------------------------------------------------------ */
        PUSH_ASM_t      push_name_dll;           /* PUSH "DLL_INJECT.DLL"*/
        CALL_ASM_t      call_load_library;       /* CALL  LoadLibraryA   */
        
        CALL_ASM_t      call_get_current_thread; /* CALL GetCurrentThread*/
        INC_BYTE_MEM_t  inc_flag;                /* INC [FLAG]           */
        char            PUSH_EAX;                /* PUSH EAX             */
        CALL_ASM_t      call_suspendthread;      /* CALL SuspendThread   */
        
        /* - DATA ------------------------------------------------------ */
        char            name_dll[MAX_PATH];      /* DLL_INJECT.DLL'\0'   */
        char            flag;                    /* [FLAG]               */ 
          
    } LOADER_DLL_t;
    
------[ 4.2 - Console Control

Console Control es la DLL que se inyecta en el proceso en el que se quiera
hacer PEB HOOKING [T.1]. Permite hacer PEB HOOKING [T.1] y otras tareas de
forma interactiva mediante una consola de comandos por sockets. El puerto
que escucha lo escribe en el fichero C:\ph_listen_ports.log, con la
nomenclatura PID - PORT. Ejemplo de un proceso con PID 2456,
escuchando en el puerto 1234: 2456 - 1234.

Actualmente tiene la siguiente lista de comandos:
help - Muestra esta pantalla
exit - Cierra y descarga la consola
suspend - Pausa la ejecución del programa
resume - Resume la ejecución del programa
showmodules - Muestra la lista de modulos
load [param1] - Carga en memoria la biblioteca
especificada en [param1]
unload [param1] - Descarga una biblioteca en memoria
especificada en [param1]
pebhook [param1] [param2] - Realiza PEB HOOKING [T.1] sobre una dll
[param1]: Nombre de la dll original
[param2]: Ruta a la DLL_FAKE
Es facil entender cada uno de los comandos que admite nuestra consola, asi
que explicaremos el funcionamiento de "showmodules", "pebhook" y "suspend".

El comando "showmodules" realiza una búsqueda en el PEB [R.1] de los
módulos cargados sin usar APIs.

pebhook es el comando que realiza todo el proceso de PEB HOOKING (ver
apartado 3).

Si se quiere hacer PEB HOOKING [T.1] sobre kernel32.dll, usando como
DLL_FAKE "C:\phook\bin\windows_xp_sp2\ph_ker32.dll", para el SO Windows XP
SP2, solo es necesario enviar el comando:

- pebhook kernel32.dll c:\phook\bin\windows_xp_sp2\ph_ker32.dll

El comando suspend es capaz de suspender la ejecución del hilo principal
del proceso. El TID del hilo principal se obtiene recorriendo los
THREADENTRY32 [R.13] del sistema hasta llegar al primero del proceso:

Código: Seleccionar todo

    BOOL GetMainThreadId( DWORD * thread_id )
    {
        HANDLE        hThreadSnap; 
        THREADENTRY32 th32; 
        BOOL          return_function;
        DWORD         process_id;
        
        process_id      = GetCurrentProcessId();
        hThreadSnap     = INVALID_HANDLE_VALUE; 
        return_function = FALSE;
    
        hThreadSnap = \
            CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, process_id ); 
        
        if( hThreadSnap == INVALID_HANDLE_VALUE ) 
        { 
            ShowGetLastErrorString
                ( " GetMainThreadId() - CreateToolhelp32Snapshot()" ); 
            return FALSE;
        } 
        
        th32.dwSize = sizeof( THREADENTRY32 );     
        if( !Thread32First( hThreadSnap, & th32 ) ) 
            ShowGetLastErrorString( "GetMainThreadId() - Thread32First()");  
        
        do
        {
            if ( th32.th32OwnerProcessID == process_id )
            {
                * thread_id     = th32.th32ThreadID;
                return_function = TRUE;
            }
            
        } 
        while 
        ( 
            Thread32Next( hThreadSnap, & th32 ) && return_function != TRUE 
        );
        
        CloseHandle( hThreadSnap );
        
        return return_function;
    }
    
------[ 4.3 - CreateExp

CreateExp es un programa que genera a partir de una DLL_REAL el código
fuente necesario para realizar una DLL_FAKE. Actualmente crea los ficheros
.c y .def, para usar con mingw.

Para crear una DLL_FAKE de kernel32.dll es necesario ejecutar:

- CreateExp C:\WINDOWS\SYSTEM32\KERNEL32.DLL C:\ph_ker32

Si ha funcionado bien se crearán los ficheros C:\ph_ker32.c y
C:\ph_ker32.def


ph_ker32.c contiene las definiciones de las exportaciones de kernel32.dll
y salta automáticamente a las originales.

ph_ker32.def contiene los alias y los nombres de las exportaciones de
kernel32.dll.

Por defecto las exportaciones de DLL_FAKE saltarán a la exportación
correspondiente de DLL_REAL.


------[ 4.3.1 - Forwarder DLL

CreateExp tranforma las Forwarder DLL [R.3] en exportaciones, así que se
puede hacer PEB HOOKING de una función Forwarder.

Ejemplo: kernel32.dll tiene como Forwarder HeapAlloc que va a la
exportación RtlAllocateHeap de NTDL.DLL. Cuando un módulo importa
HeapAlloc de kernel32.dll, automáticamente el Loader de win32 pone
la dirección de la exportación de NTDLL.DLL y nunca pasa por
kernel32.dll:
CALL HeapAlloc
[proceso] ------------------> [NTDLL.DLL]
^ 0 |
+-------------------------------+
1

Si se crea una DLL_FAKE de kernel32.dll con CreateExp, el flujo será:

CALL HeapAlloc (DLL_FAKE)
[proceso] ------------------> [KERNEL32.DLL] --------> [NTDLL.DLL]
^ 0 1 |
+-----------------------------------------------------+
2

De tal forma que podemos implementar un hook de HeapAlloc (kernel32.dll).


------[ 4.4 - ph_ker32.dll

ph_ker32.dll fue creado para hacer PEB HOOKING [T.1] a kernel32.dll;
monotoriza el acceso a las APIs "CreateFileA" y "CreateFileW" [R.14], y
cuando se llama a cualquier otra API automáticamente se salta la original.

Para facilitar el salto a una API se ha creado la macro JMP, hay que
pasarle el nombre de la DLL y el ordinal de la exportación (para ver la
macro JMP ver apartado 4.4.2
).

ph_ker32.c creado con CreateExp (se ha omitido la macro JMP):

Código: Seleccionar todo

    #define FAKE_LIB "ph_ker32.dll"                    

    DLLEXPORT void _ActivateActCtx ( void )
    {
        JMP( FAKE_LIB, 1 );
    }
    
    DLLEXPORT void _AddAtomA ( void )
    {
        JMP( FAKE_LIB, 2 );
    }
    
    DLLEXPORT void _AddAtomW ( void )
    {
        JMP( FAKE_LIB, 3 );
    }
    
    DLLEXPORT void _AddConsoleAliasA ( void )
    {
        JMP( FAKE_LIB, 4 );
    } 
    ....
    

Hay que recordar que una vez se ha realizado PEB HOOKING [T.1],
kernel32.dll tendrá de nombre ph_ker32.dll, por eso se indica
ph_ker32.dll en la constante simbólica FAKE_LIB.

ph_ker32.def creado con CreateExp:

Código: Seleccionar todo

    
    LIBRARY    default
    EXPORTS    
    ActivateActCtx=_ActivateActCtx           @ 1
    AddAtomA=_AddAtomA                       @ 2
    AddAtomW=_AddAtomW                       @ 3
    ...
    
Por motivos de claridad la implementación de las APIs CreateFileA
y CreateFileW
[R.14] se ha puesto en el fichero owns.c. Cuando se llama
a CreateFileA y a CreateFileW [R.14] se escribe el parámetro lpFileName
en el fichero C:\CreateFile.log

owns.c:

Código: Seleccionar todo

    #define FILE_LOG C:\CreateFile.log
    
    DLLEXPORT 
    HANDLE _stdcall _CreateFileW
    (
        LPCWSTR lpFileName,	
        DWORD dwDesiredAccess, 
        DWORD dwShareMode, 
        LPSECURITY_ATTRIBUTES lpSecurityAttributes,	
        DWORD dwCreationDistribution, 
        DWORD dwFlagsAndAttributes, 
        HANDLE hTemplateFile 
    )
    {
        char   asc_str[MAX_PATH];
        
        if ( UnicodeToANSI( (WCHAR *) lpFileName, asc_str ) == 0 )
            CreateFileLogger( asc_str );
        
        return CreateFileW( 
            lpFileName, 
            dwDesiredAccess, 
            dwShareMode, 
            lpSecurityAttributes, 
            dwCreationDistribution, 
            dwFlagsAndAttributes, 
            hTemplateFile );
    }
    
    DLLEXPORT 
    HANDLE _stdcall _CreateFileA
    (
        LPCSTR lpFileName,	
        DWORD dwDesiredAccess, 
        DWORD dwShareMode, 
        LPSECURITY_ATTRIBUTES lpSecurityAttributes,	
        DWORD dwCreationDistribution, 
        DWORD dwFlagsAndAttributes, 
        HANDLE hTemplateFile 
    )
    {
        char   asc_str[MAX_PATH];
        
        CreateFileLogger( lpFileName );
        
        return CreateFileA( 
            lpFileName, 
            dwDesiredAccess, 
            dwShareMode, 
            lpSecurityAttributes, 
            dwCreationDistribution, 
            dwFlagsAndAttributes, 
            hTemplateFile );
    }
    
    static void 
    CreateFileLogger( const char * file_to_log )
    {
        HANDLE file;
        DWORD  chars;    
        
        file = \
            CreateFileA
            ( 
                FILE_LOG                            , 
                GENERIC_WRITE    | GENERIC_READ     , 
                0                                   , 
                NULL                                , 
                OPEN_ALWAYS                         , 
                0                                   , 
                NULL 
            );
            
        if ( file != INVALID_HANDLE_VALUE )
        {        
            if ( SetFilePointer( file, 0, NULL, FILE_END ) != -1 )
            {
                WriteFile
                ( 
                    file, file_to_log, strlen( file_to_log ), &chars, NULL 
                );
                WriteFile( file, "\x0D\x0A", 2, &chars, NULL );
            }
            CloseHandle( file );         
        }    
    }
    
------[ 4.4.1 - Los problemas en la pila

Cuando se quiere pasar el control directamente a una API a la que no se
conoce el prototipo de forma genérica, hay que pasarle la pila intacta a
la API original. Esto se consigue en mingw con la opción del compilador
-fomit-frame-pointer [R.15] y un JMP (ASM) a la API original.

Las funciones que se implementan hay que ponerlas prototipo y deben ser
de tipo _stdcall. Las funciones de tipo _stdcall tienen una sintaxis
diferente en el fichero .def:

- Nombre_exportacion=Alias@argumentos * 4 @ Ordinal

Ejemplo de fichero .def con las APIs de tipo _stdcall CreateFileA y
CreateFileW [R.14] (las dos tienen siete argumentos):

Código: Seleccionar todo

  
    LIBRARY    ph_ker32
    EXPORTS
    
    ; Name Exp | Alias      | Nº Args * 4    | Ordinal Windows XP SP2
    CreateFileW=_CreateFileW@28              @ 83
    CreateFileA=_CreateFileA@28              @ 80
    
Las funciones de tipo _stdcall no se deben compilar con la opción
-fomit-frame-pointer [R.15].


------[ 4.4.2 - Los problemas con los registros

No solo es necesario pasar la pila intacta a una exportación, en ocasiones
las exportaciones usan directamente valores de los registros directamente.
Antes de pasar el control a la exportación original es necesario dejar los
registros intactos, esto se consigue metiendo entre el código las
instrucciones PUSHAD y POPAD:

[PUSHAD] [ CÓDIGO NECESARIO PARA SALTAR A LA EXPORTACIÓN ] [POPAD]

Un ejemplo de exportación que usa directamente los registros es _chkstk de
NTDLL.DLL:

_chkstk en NTDLL.DLL (WINDOWS XP SP2):

Código: Seleccionar todo

    
    7C911A09 >/$ 3D 00100000    CMP EAX,1000
    7C911A0E  |. 73 0E          JNB SHORT ntdll.7C911A1E
    7C911A10  |. F7D8           NEG EAX
    7C911A12  |. 03C4           ADD EAX,ESP
    7C911A14  |. 83C0 04        ADD EAX,4
    7C911A17  |. 8500           TEST DWORD PTR DS:[EAX],EAX
    7C911A19  |. 94             XCHG EAX,ESP
    7C911A1A  |. 8B00           MOV EAX,DWORD PTR DS:[EAX]
    7C911A1C  |. 50             PUSH EAX
    7C911A1D  |. C3             RETN
    7C911A1E  |> 51             PUSH ECX
    7C911A1F  |. 8D4C24 08      LEA ECX,DWORD PTR SS:[ESP+8]
    7C911A23  |> 81E9 00100000  /SUB ECX,1000
    7C911A29  |. 2D 00100000    |SUB EAX,1000
    7C911A2E  |. 8501           |TEST DWORD PTR DS:[ECX],EAX
    7C911A30  |. 3D 00100000    |CMP EAX,1000
    7C911A35  |.^73 EC          \JNB SHORT ntdll.7C911A23
    7C911A37  |. 2BC8           SUB ECX,EAX
    7C911A39  |. 8BC4           MOV EAX,ESP
    7C911A3B  |. 8501           TEST DWORD PTR DS:[ECX],EAX
    7C911A3D  |. 8BE1           MOV ESP,ECX
    7C911A3F  |. 8B08           MOV ECX,DWORD PTR DS:[EAX]
    7C911A41  |. 8B40 04        MOV EAX,DWORD PTR DS:[EAX+4]
    7C911A44  |. 50             PUSH EAX
    7C911A45  \. C3             RETN

------[ 4.4.3 - La macro JMP

La macro JMP es necesaria ya que no siempre se tienen en la cabecera de
una DLL (fichero .h) todas sus declaraciones. Con la macro JMP se obtiene
la dirección de la exportación con GetProcAddress [R.12] en tiempo de
ejecución.

Código: Seleccionar todo


    unsigned long tmp;
    
    #define JMP( lib, func )        \
       asm ( "pushad" );           \
       asm                           \
       (                             \
           " push edx             \n"      \
           " push %1              \n"      \
           " call eax             \n"      \
           " pop edx              \n"      \
           " push %2              \n"      \
           " push eax             \n"      \
           " call edx             \n"      \
           " mov %4, eax          \n"      \
           " popad                \n"      \
                                              \
           : :                                \
           "a" (GetModuleHandle) ,         \
           "g" (lib)             ,         \
           "g" (func)            ,         \
           "d" (GetProcAddress)  ,         \
           "g" (tmp)                       \
        );                                   \
       asm ( "jmp %0" : : "g" (tmp) );   

El código es para mingw [R.16] con la opción del compilador -masm=intel.


------[ 4.4.4 - Versiones

Hemos incluidos en phook varias versiones de ph_ker32 para los sistemas:

- Windows XP SP2 v5.1.2600
- Windows Server 2003 R2 v5.2.3790
- Windows Vista v6.0.6000

Código fuente en ph_ker32/SO y binarios en bin/SO.


------[ 4.5 - Usando phook

Imaginemos que queremos hacer PEB HOOKING [T.1] a kernel32.dll con
ph_ker32.dll, se ha elegido el programa poc.exe para el ejemplo (viene el
carpeta bin\ de phook).

Pasos a seguir:

1.- Ejecutar InjectorDLL indicando un programa a ejecutar y la DLL de la
consola que se inyectará en el proceso:

- InjectorDLL.exe console.dll -u poc.exe

El proceso se quedará en estado suspendido y habrá un socket escuchando
en el puerto indicado en el fichero C:\ph_listen_ports.log

C:\phook\bin>InjectorDll.exe console.dll -u poc.exe
________________________________________________________________
| InjectorDLL v1.0 |
| |
| Juan Carlos Montes [email protected] |
| David Reguera Garcia [email protected] / [email protected] |
| -------------------------------------------------------------- |
| [Enlace externo eliminado para invitados] |
|________________________________________________________________|

Showing injection data .....
Program to inject : poc.exe
Library to inject: console.dll

[OK] - CONSOLE.
[OK] - Create process:
[INFO] PID: 0x0960
[INFO] P. HANDLE: 0x000007B8
[INFO] TID: 0x0AE0
[INFO] T. HANDLE: 0x000007B0
[INFO] - Injecting DLL...
[OK] - Allocate memory in the extern process.
[INFO] - Address reserved on the other process: 0x00240000
[INFO] - Space requested: 306
[OK] - Creating structure for the dll load.
[OK] - Writing structure for the dll load.
[OK] - Creating remote thread.
[INFO] - Thread created with TID: 0x0B28
[INFO] - Attempt: 1
[INFO] - Thread has entered suspension mode.
[OK] - Injection thread ended.
[OK] - Memory in remote thread freed.
[OK] - DLL injected.

[OK] - Injection ended.

2.- Es necesario conectarse con un cliente de tipo netcat al puerto
abierto, en este caso: 1234.

C:\>nc 127.0.0.1 1234
________________________________________________________________
| Phook Prompt v1.0 |
| Juan Carlos Montes [email protected] |
| David Reguera Garcia [email protected] / [email protected] |
| -------------------------------------------------------------- |
| [Enlace externo eliminado para invitados] |
|________________________________________________________________|


ph > help
_________________________________________________________________
| Phook Prompt v1.0 |
| |
| Command list: |
| --------------------------------------------------------------- |
| help - Shows this screen |
| exit - Closes and unloads the console |
| suspend - Pauses the programs execution |
| resume - Resumes the programs execution |
| showmodules - Shows the modules list |
| load [param1] - Loads in memory the library |
| especified in [param1] |
| unload [param1] - Unloads a librery in memory |
| especified in [param1] |
| pebhook [param1] [param2] - Performs PEB Hook over a dll |
| [param1]: Name of the original dll |
| [param2]: Path to the DLL hook |
|_________________________________________________________________|

3.- Se realiza PEB HOOKING [T.1] a kernel32.dll con la ph_ker32.dll:
ph > pebhook kernel32.dll C:\phook\bin\windows_xp_sp2\ph_ker32.dll

4.- Se envía el comando resume para que empiece la ejecución del proceso.

ph > resume
ph >
C:\phook\bin>

5.- poc.exe crea los ficheros en C:\
- file
- file2
- file3

6.- ph_ker32.dll registra las llamadas con éxito a las APIs CreateFileA
y CreateFileW [R.14] en el fichero C:\CreateFile.log

7.-
C:\>more CreateFile.log

C:\file1
C:\file2
C:\file3


------[ 4.5.1 - DLL MINIFILTER

phook permite realizar DLL MINIFILTER (ver apartado 3.7) de una manera
sencilla. Solo hay que realizar PEB HOOKING [T.1], con el comando pebhook,
sobre el nombre de DLL_FAKE, que es el que tenía la primera DLL_REAL.

Suponiendo que tenemos dos DLL_FAKEs:

- ph_ker32_1.dll: Monotoriza acceso a las APIs CreateFile [R.14].

- ph_ker32_2.dll: Monotoriza el acceso de las API ReadFile [R.17].

Realizar DLL MINIFILTER es tan sencillo como:

C:\>nc 127.0.0.1 1234
________________________________________________________________
| Phook Prompt v1.0 |
| Juan Carlos Montes [email protected] |
| David Reguera Garcia [email protected] / [email protected] |
| -------------------------------------------------------------- |
| [Enlace externo eliminado para invitados] |
|________________________________________________________________|


ph > pebhook kernel32.dll C:\phook\bin\windows_xp_sp2\ph_ker32_1.dll
ph > pebhook kernel32.dll C:\phook\bin\windows_xp_sp2\ph_ker32_2.dll

Flujo de una llamada del proceso a kernel32.dll:
0 1 2
[proceso] --> [ph_ker32_2.dll] --> [ph_ker32_2.dll] -> [kernel32.dll]
^ |
| 3 |
+------------------------------------------------------+
------[ 4.6 - Problemas frecuentes

Además de los problemas del apartado 3.8, existen otros:
+-------------------------------------------------------------------------+
| Problema | Posible/s Solución/es |
|-------------------------------+-----------------------------------------|
| - Falla la compilación de | - Comprobar que no se repiten las |
| DLL_FAKE | funciones que van directamente a |
| | DLL_REAL y las que se implementan. |
| | - Comprobar que las funciones |
| | implementadas (que deben ser de tipo |
| | _stdcall) están bien definidas en el |
| | fichero .def (ver apartado 4.4.1). |
|-------------------------------+-----------------------------------------|
| - Falla la ejecución del | - Comprobar que las funciones que van |
| proceso | directamente a DLL_REAL han sido |
| | compiladas con la opcion |
| | -fomit-frame-pointer (ver apartado |
| | 4.4.1). |
| | - Comprobar que las funciones |
| | implementadas son de tipo _stdcall. |
| | - Comprobar que DLL_FAKE se ha creado |
| | a partir de la DLL_REAL y no de otra. |
| | - Comprobar si InjectorDLL ha detectado |
| | correctamente el tipo real de proceso |
| | (GUI o CONSOLE). |
|-------------------------------+-----------------------------------------|
| - No se puede conectar a | - Comprobar que el puerto 1234 está |
| la consola | abierto antes de hacer PEB HOOKING |
| | [T.1]. |
| | - Comprobar bloqueos de firewall... |
| | - Comprobar que se ha indicado la ruta |
| | completa de console.dll en |
| | InjectorDLL. |
|-------------------------------+-----------------------------------------|
| - No funciona InjectorDLL | - Comprobar que se tienen privilegios |
| | para inyectar una DLL |
| | (CreateRemoteThread..) |
| | - Comprobar bloqueo de anti-virus... |
|-------------------------------+-----------------------------------------|
| - No funciona CreateExp | - Comprobar que la ruta de DLL_REAL es |
| | un PE32 correcto y que el EXPORT |
| | DIRECTORY no está corrupto [R.3]. |
+-------------------------------------------------------------------------+
Pueden existir otros problemas debidos a fallos de programación y/o de
diseño.
Imagen


Solo lo mejor es suficiente...
------[ 5.- TODO

Actualmente estamos intentando:

- Realizar PEB HOOKING [T.1] antes de la ejecución de:

- TLS Table y DLLMain [R.3].

- Crear ficheros de debug y configuración para la consola.

- Reglas para reparación de IATs [R.4].
- Lista personalizada de puertos de escucha.
- ...
- Mejorar InjectorDLL:

- Detección automática de "GUI process" y "Console process".


------[ 6.- Testing

Se han realizado pruebas con phook en diferentes versiones de Windows y
programas.

Windows:

- Windows XP SP2 v5.1.2600
- Windows Server 2003 R2 v5.2.3790
- Windows Vista v6.0.6000

Y teóricamente debería funcionar en Windows 2000, pero no lo hemos
comprobado.

Programas:

- Microsoft Word 10.0.2627.0
- Regedit 5.1.2600.2180
- Notepad 5.1.2600.2180
- Calc 5.1.2600.0
- CMD 5.1.2600.2180
- piathook 1.4
- pebtry Beta 5
- pe32analyzer Beta 2


------[ 7.- Ventajas y posibilidades

La mayor ventaja de PEB HOOKING [T.1] frente a los otros métodos de hooking
es que solo se tiene que aplicar una vez. En el momento que se ha hecho un
hook a una DLL, cualquier módulo que se cargue tendrá automáticamente en su
IAT [R.3] las exportaciones que use de DLL_FAKE. El resto de los métodos
tienen que aplicar el hook cada vez que se cargue un módulo.

Otras de las ventajas de usar PEB HOOKING [T.1]:

- Una búsqueda en el PEB (usando el campo BaseDllName) para llegar a
DLL_REAL, llegará a DLL_FAKE.

- PEB HOOKING es un método más estable para el SO que otros en ring0.

- Algunos packers no detectan PEB HOOKING [T.1] al ser un método poco
documentado.

- No es necesario cambiar el comportamiento de las APIs que trabajan
con módulos. Cuando un módulo intenta obtener el manejador de
DLL_REAL, obtendrá automáticamente el manejador DLL_FAKE.

- Posibilidad de crear DLL MINIFILTER (ver apartado 3.7).

- Se puede hacer PEB HOOKING de una exportación Forwarder[R.3] sin
hacer PEB HOOKING a la Forwarder DLL.


El espectro de posibilidades que permite el método PEB HOOKING [T.1] y
phook es bastante amplio, a continuación planteamos algunos ejemplos:

- Monotorizar/virtualizar el acceso a registro de un proceso.

- POC [R.18]:

1.- Usar la herramienta CreateExp (ver apartado 4.3) sobre
"advapi32.dll".

2.- En funcion de lo que se desee hacer es necesario
implementar la monitorizacion/virtualizacion en las
siguientes APIs:

- RegCloseKey
- RegCreateKeyA/RegCreateKeyW
- RegCreateKeyExA/RegCreateKeyExW
- RegDeleteKeyA/RegDeleteKeyW
- RegLoadKeyA/RegLoadKeyW
- RegOpenKeyA/RegOpenKeyW
- RegOpenKeyExA/RegOpenKeyExW
- RegQueryValueA/RegQueryValueW
- RegQueryValueExA/RegQueryValueExW
- RegReplaceKeyA/RegReplaceKeyW
- RegRestoreKeyA/RegRestoreKeyW
- RegSaveKeyA/RegSaveKeyW
- RegSaveKeyExA/RegSaveKeyExW
- RegSetValueA/RegSetValueW
- RegSetValueExA/RegSetValueExW
- RegUnLoadKeyA/RegUnLoadKeyW
...

- Monotorizar/virtualizar conexiones.

- POC [R.20]:

1.- Usar la herramienta CreateExp (ver apartado 4.3) sobre
"ws2_32.dll".

2.- En funcion de lo que se desee hacer es necesario
implementar la monitorizacion/virtualizacion en las
siguientes APIs:

- accept
- bind
- closesocket
- connect
- listen
- recv
- recvfrom
- send
- sendto
- socket
- WSAAccept
- WSAConnect
- WSARecv
- WSARecvFrom
- WSASend
- WSASendTo
- WSASocketA/W
...

- Syscall Proxy de ficheros:

- POC [R.19]:

1.- Usar la herramienta CreateExp (ver apartado 4.3) sobre
"kernel32.dll".

2.- En funcion de lo que se desee hacer es necesario
implementar la redireccion de las siguientes APIs:

- CreateFileA/CreateFileW
- CreateFileExA/CreateFileExW
- ReadFile
- ReadFileEx
- WriteFile
- WriteFileEx
...

- ... and free your mind ;-)


------[ 8.- Conclusión

Si es necesario hacer un hook a una API/exportación, se puede usar
cualquier método actual. Pero si se necesita monotorizar o virtualizar el
acceso a varias APIs/exportaciones con phook se hace mucho más sencilla la
implementación, puesto que solo es necesario programar la funcionalidad de
las APIs/exportaciones.

Además, es un método orientado a la ingenieria inversa de sistemas de
protección de software y malware, ya que dificulta métodos alternativos de
búsqueda de exportaciones y eliminación de hooks.


------[ 9.- Agradecimientos

Recomendaciones para el paper:

- phrack staff
- Miguel Tarascó Acuña (aka Tarako)

Traduccion a inglés de las cadenas de phook:

- Javier Núñez (aka Southern)
- LogicMan
- XENMAX

Traducciones del paper a inglés:

- BETA : Ana Hijosa
- BETA 2: delcoyote
- ACTUAL: LogicMan

Virii scene:

- GriYo, zert, Slow, pluf, xezaw, sha0 ...

Reversing scene:

- pOpE, JKD, ilo, Ripe, int27h, at4r, uri, numitor, vikt0ry, kania,
remains, S-P-A-R-K ...

Other scene:

- sync, ryden, xenmax, ozone/membrive, \^snake^\, topo, fixgrain, ia64,
overdrive, success, scorpionn, oyzzo, simkin, !dSR ...

ALL vx.7a69ezine.org and 7a69ezine.org people ;-)

Y especialmente gracias a YJesus - [Enlace externo eliminado para invitados]


------[ 10.- Trabajos relacionados

[T.1] .- No conocemos ningún trabajo parecido a phook, pero existe un
artículo que habla sobre PEB HOOKING escrito por Deroko: "PEB DLL
Hooking Novel method to Hook DLLs"
. El artículo fue publicado en
la ARTeam-Ezine número 2.

- [Enlace externo eliminado para invitados] ... nload1.php?
file=ARTeam.eZine.Number2.rar


------[ 11.- Referencias

[R.1] .- Estructuras del PEB:

- [Enlace externo eliminado para invitados]

[R.2] .- Gaining important datas from PEB under NT boxes:

- [Enlace externo eliminado para invitados]

[R.3] .- Visual Studio, Microsoft Portable Executable and Common Object
File Format Specification. Revision 8.0 - May 16, 2006:

- [Enlace externo eliminado para invitados]
PECOFF.mspx

[R.4] .- What Goes On Inside Windows 2000: Solving the Mysteries of the
Loader:

- [Enlace externo eliminado para invitados]

[R.5] .- winnt.h (DEV-CPP):

- [Enlace externo eliminado para invitados]

[R.6] - CreateProcess:

- [Enlace externo eliminado para invitados]

[R.7] - Three Ways to Inject Your Code into Another Process:

- [Enlace externo eliminado para invitados]

[R.8] - Import address table hooks:

- [Enlace externo eliminado para invitados]

[R.9] - Code overwriting:

- [Enlace externo eliminado para invitados]

[R.10] - Hooks:

- [Enlace externo eliminado para invitados]

[R.11] - System Call Optimization with the SYSENTER Instruction:

- [Enlace externo eliminado para invitados] ... 00440.aspx

[R.12] - Run-Time Dynamic Linking

- [Enlace externo eliminado para invitados]

[R.13] - Thread Walking

- [Enlace externo eliminado para invitados]

[R.14] - CreateFile

- [Enlace externo eliminado para invitados]

[R.15] - MAN GCC (-fomit-frame-pointer):

- [Enlace externo eliminado para invitados] ... gcc.1.html

[R.16] - MINGW:

- [Enlace externo eliminado para invitados]

[R.17] - ReadFile:

- [Enlace externo eliminado para invitados]

[R.18] - Registry Functions:

- [Enlace externo eliminado para invitados]

[R.19] - File Management Functions:

- [Enlace externo eliminado para invitados]

[R.20] - Winsock Functions:

- [Enlace externo eliminado para invitados]

[R.20] - MSDN LIBRARY:

- [Enlace externo eliminado para invitados]

[R.21] - Iczelion's Win32 Assembly Homepage:

- [Enlace externo eliminado para invitados]
Imagen


Solo lo mejor es suficiente...
------[ 12.- Código fuente

Download:

phook 1.0

[Enlace externo eliminado para invitados]

phook 1.0.1

[Enlace externo eliminado para invitados]

With SP3 support for ph_ker32.dll and others things.

Espero que les sea de ayuda a los programadores..

Saludos !
Imagen


Solo lo mejor es suficiente...
Responder

Volver a “Otros lenguajes”