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