Vous êtes sur la page 1sur 28

Este texto trata las tecnicas de cambiar las funciones de la API en Windows.

Tod os los ejemplos aqui descritos funcionan perfectamente en sistemas basados en te cnologia NT version NT 4.0 y superior ( windows NT 4.0, Windows 2000 , Windows X P, Windows 2003). Probablemente tambien funcionara en futuros sistemas Windows. Texto Completo: ===========================[ Hooking Windows API ]============================== Tecnicas de Ganchos a Funciones de la API de Windows ---------------------------------------------------Author: Holy_Father < holy_father@phreaker.net Esta direccin electrnica esta protegida contra spam bots . Necesita activar JavaScript para visualizarla > Traduccion: Kintaro < Kintaro_@hotmail.com Esta direccin electrnica esta protegida contra spam bots. Ne cesita activar JavaScript para visualizarla > Version: 1.0 Spanish Fecha: 30.09.2004 =====[ 1. Contents ]============================================================ 1. Contenidos 2. Introduccion 3. Metodos de enganche 3.1 Ganchos antes de la ejecucion 3.2 Enganchar durante la ejecucion 3.2.1 Enganchar nuestro propio proceso usando IAT 3.2.2 Enganchar nuestro propio proceso reescribiendo el entry point 3.2.3 Enganchar sin modificar las funciones originales 3.2.4 Enganchando otros procesos 3.2.4.1 Inyeccion DLL 3.2.4.2 Codigo independiente 3.2.4.3 Cambio Raw 4. Finalizando

=====[ 2. Introduccion ]======================================================== Este texto trata las tecnicas de enganchar las funciones de la API en Windows. Todos los ejemplos aqui descritos funcionan perfectamente en sistemas basados en tecnologia NT version NT 4.0 y superior ( windows NT 4.0, Windows 2000 , Windows XP, Windows 2003). Probablemente tambien funcionara en futuros sistemas Windows. Deberias familiarzarte con procesos en windows, esamblador, estructura de ficheros PE y algunas funciones de la API , para comprender enteramente el texto. Cuando usamos el termino "Enganchar la API" aqui, me refiero al cambio completo de la API. Asi, cuando una API enganchada es llamada, nuestro codigo es ejecutado inmediatamente. No trato solamente los casos de monitorizar la API. Escribire todo sobre esta tecnica.

=====[ 3. Tecnicas de ganchos ]=================================================

Nuestra meta es normalmente reemplazar el codigo de alguna funcion con nuestro codigo. Este problema puede ser solucionada a veces antes de ejecutar el proceso. Esto puede hacerse la mayoria de las veces con codigo con privilegios de usuario y la meta es, por ejemplo, cambiar el comportamiento del programa. Un ejemplo de esto puede ser crackear una aplicacion: un programa que pide el cd original al iniciarse (como ocurria en el juego Atlantis) y nosotros queremos ejecutarlo sin CD. Si modificamos la funcion que compueba que el tipo de disco sea cdrom, podemos ejecutarlo desde el disco duro. Esto no puede hacerse o no queremos hacerlo cuando queremos enganchar procesos de sistema (como servicios) o en el caso en que no sepamos el proceso victima. Entonces tendremos que usar la tecnica de ganchos en ejecucion. Ejemplos de esta tecnica son los rootkits o los virus con tecnicas anti-antivirus. =====[ 3.1 Ganchos antes de la ejecucion ]====================================== Se trata de cambios del modulo fisico (normalmente .exe o .dll) en donde esta la funcion que nosotros queremos cambiar. Tenemos por lo menos tres posibilidades de hacer esto. La primera es encontrar el punto de entrada de esa funcion y basicamente reescribir el codigo. Esto esta limitado por el tamao de la funcion pero siempre podemos cargar otro modulo dinamicamente (API LoadLibrary) y asi tendriamos suficiente espacio. Las funciones del kernel (kernel32.dll) pueden ser usadas en todos los casos porque todos los procesos en windows tienen su propia copia de este modulo. Otra ventaja es cuando sabemos en que SO sera modificado el modulo. Podemos usar directamente un puntero a la funcion, en este caso seria la direccion de LoadLibraryA. Esto es asi porque la direccion del modulo kernel en memoria es estatica para una determinada version de Windows. Tambien podemos hacer uso del comportamiento de los modulos cargados dinamicamente, por el cual su parte de inicializacion es ejecutada inmediatamente despues de cargar el modulo en memoria. En la parte de inicializacion de un nuevo modulo no estamos limitados. La segunda posibilidad de reemplazar funciones en un modulo reside en la extension. Entonces tenemos que elegir entre reemplazar los 5 primeros bytes con un salto relativo o reescribir la IAT. En el caso de poner un salto relativo, redirigiria la ejecucion del modulo a nuestro codigo. En el otro caso, cuando una es llamada una funcion cuyo registro de IAT ha sido modificado, nuestro codigo seria ejecutado inmediatamente despues de esta llamada. Pero cambiar la extension un modulo no es tarea facil porque hay que controlar muy bien las cabeceras. (Nota del traductor: no se refiere a la extension de fichero .exe o .dll eh? se refiere al tamao que ocupa, que reside en la cabecera del modulo) La tercera posibilidad es reemplazar el modulo entero. Es decir creamos nuestra propia version del modulo qu puede cargar el modulo original y llamar las funciones que no deseamos modificar. Pero reescribimos las funciones que si nos interesan. Este metodo no es util para modulos muy grandes pues aveces exportan cientos de funciones. =====[ 3.2 Ganchos en ejecucion ]=============================================== Los ganchos antes de la ejecucion es mas bien para aplicaciones concretas o un modulo concreto. Si reemplazamos un funcion de kernel32.dll o de ntdll.dll obtendremos el comportamiento deseado en todos aquellos procesos que sean ejecutados despues, aunque es dificil atinar con un codigo perfecto y exacto para las funciones o modulos nuevos. Pero el principal problema es que

nuestros ganchos solo contemplaran los procesos ejecutados posteriormente (asi pues para todos los procesos tendriamos que reiniciar el sistema). Otro problema es el acceso a estos ficheros, que en sistemas NT estan protegidos. Los ganchos en ejecucion solucionan esto. Si bien este metodo requiere muchos mas conocimientos, el resultado es perfecto.Los ganchos en ejecucion pueden aplicarse solo a procesos para los que tengamos acceso a su zona de memoria. Para escribir en esta zona usaremos la funcion APi WriteProcessMemory. Comencemos por enganchar nuestro propio proceso durante la ejecucion. =====[ 3.2.1 Enganchar nuestro propio proceso usando IAT ]====================== Hay muchas posibilidades aqui. En prinicpio te mostrare como enganchar funciones mediante la reescritura de la IAT. Este dibujo muestra la estructura de un fichero PE: +-------------------------------+ | Cabecera MS DOS ("MZ") | +-------------------------------+ | firma de PE ("PE") | +-------------------------------+ | .text | | Codigo de programa | | | +-------------------------------+ | .data | | Datos Inicializados | | | +-------------------------------+ | .idata | | Import Table | | | +-------------------------------+ | .edata | | Export Table | | | +-------------------------------+ | Debug symbols | +-------------------------------+ - offset 0

- codigo de modulo

- datos iniciados (global y static)

- info sobre funciones importadas y datos - info sobre funciones exportadas y datos

La parte importante para nosotros aqui es la Import Address Table(IAT) en la zona .idata . Esta parte contiene una descripcion de las funciones importadas y sus direcciones. Ahora es importante saber como se crean los ficheros PE. Cuando se llama un funcion de la API indirectamente en un lenguaje de programacion (es decir llamar usando el nombre , en vez de su direccion especifica en el SO) el compilador no las convierte en llamadas directas al modulo, sino que las convierte en saltos a direcciones, y estas direcciones se encuentran almacenadas en la IAT. Cuando el proceso se carga el cargador de proceso del Windows se encarga de rellear la IAT para cada sistema. Esta es la razon por la que el mismo binario funciona en versiones distintas del windows, a pesar de que tienen distintas direcciones para un mismo modulo. Por tanto si fuesemos capaz de encontrar una funcion, que queremos enganchar, en la IAT, podriamos cambiar facilmente la direccion y la instruccion jmp redirigiria el flujo de ejecucion a nuestro codigo. Cualquier llamada despues de hacer esto ejecutaria nuestro codigo. (Nota del traductor: He traducido el ultimo parrafo intentando que se comprenda lo mejor posible, pero aun asi prefiero dar una explicacion propia sobre la IAT. Si queremos realizar una llamada a una api de windows podemos escribir:

call direccion_de_la_api_en_memoria ( por ejemplo "call 0x07728188") pero como en cada version de windows esa direccion cambia necesitamos de alguna manera que escribiendo la misma instruccion se ejecute lo mismo Lo que se hace es guardar estas direcciones en la IAT. La IAT es rellenada por el So al crear el PE. La forma de acceder a las direcciones de la IAT es la siguiente: ------------------call direccion api1 ...... codigo ...... direccion: jmp dword ptr [IAT] ; -------------------Asi un PE funciona en cualquier windows ) La ventaja de este metodo es que el resultado es perfecto. La desventaja es que para cambiar el comportamiento de una funcion, deberiamos enganchar varias funciones (me explico, si queremos cambiar el comportamiento de un programa en la busqueda de un fichero tendremos que cambiar FindFirstFile y FindNextFile, pero estas funciones tienen version ANSI y WIDE, asique tendremos que cambiar FindFirstFileA, FindNextFileA, FindFirstFileW y FindNextFileW en la IAT. Y aun quedan otras como FindFirstFileExA y su version WIDE FindFirstFileExW que son llamadas por las funciones comentadas previamente. Sabemos que FindFirstFileW llama a FindFirstFileExW pero esto se hace directamente , sin usar la IAT. Y aun algunas otras. Hay por ejemplo funciones ShellApi como SHGetDesktopFolder que tambien llaman directamente a FindFirstFileW o FindFirstFileExW). Pero si lo conseguimos con todas ellas el resultado sera perfecto. Podemos usar ImageDirectoryEntryToData de imagehlp.dll para encontrar la IAT facilmente. PVOID ImageDirectoryEntryToData( IN LPVOID Base, IN BOOLEAN MappedAsImage, IN USHORT DirectoryEntry, OUT PULONG Size ); Usaremos Instance de nuestra aplicacion como Base (Instance se obtiene con una llamada a GetModuleHandle: hInstance = GetModuleHandleA(NULL); ), y como DirectoryEntry usaremos la constante IMAGE_DIRECTORY_ENTRY_IMPORT. #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 El resultado de esta funcion es un puntero al primer registro de la IAT. Los registros de la IAT son estructuras que estan definidas como IMAGE_IMPORT_DESCRIPTOR. Asi el resultado es un puntero a IMAGE_IMPORT_DESCRIPTOR. typedef struct _IMAGE_THUNK_DATA { union { PBYTE ForwarderString; PDWORD Function;

DWORD Ordinal; PIMAGE_IMPORT_BY_NAME AddressOfData; } ; } IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA; typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; PIMAGE_THUNK_DATA OriginalFirstThunk; } ; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; PIMAGE_THUNK_DATA FirstThunk; } IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR; El valor de Name en IMAGE_IMPORT_DESCRIPTOR es una referencia al nombre del modulo. Si queremos enganchar una funcion, por ejemplo del kernel32.dll, tenemos que encontrar aquella entrada de la tabla que corresponde al descriptor de kernel32.dll. (NOTA del traductor: Aunque Holy_father lo da por supuesto, prefiero aclarar algo antes de continuar, me refiero a la forma de recorrer la tabla. La IAT parece ser que esta implementada como un vector estatico, donde cada casilla contiene la direccion del descriptor. Por tanto si tenemos la direccion de la primera entrada de la tabla, que nos la ha devuelto ImageDirectoryEntryToData, la direccion de la siguiente entrada se incrementa en uno, y asi podemos recorrer la tabla) Llamaremos a ImageDirectoryEntryToData en un principio e intentaremos encontrar aquel descriptor con Name "kernel32.dll" ( puede haber mas de un descriptor con este nombre). Finalmente tendremos que encontrar nuestra funcion en la lista de todas las funciones de ese descriptor (la direccion de nuestra funcion la obtenemos con GetProcAddress). Si la encontramos debemos usar VirtualProtect para cambiar la proteccion de pagina de memoria y despues de esto podremos escribir en esta parte de memoria. Despues de escribir la direccion debemos restaurar la proteccion original. Antes de llamar a VirtualProtect tenemos que saber cierta informacion sobre esta pagina de memoria. Esta info se obtiene con VirtualQuery. Podemos aadir algunas pruebas en caso de algunas llamadas fallen ( por ejemplo no continuar si la primera llamada a VirtualProtect falla, etc) PCSTR pszHookModName = "kernel32.dll",pszSleepName = "Sleep"; HMODULE hKernel = GetModuleHandle(pszHookModName); PROC pfnNew = (PROC)0x12345678, //la nueva direccion aqui pfnHookAPIAddr = GetProcAddress(hKernel,pszSleepName); ULONG ulSize; PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData( hInstance, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize ); while (pImportDesc->Name) { PSTR pszModName = (PSTR)((PBYTE) hInstance + pImportDesc->Name);

if (stricmp(pszModName, pszHookModName) == 0) break; pImportDesc++; } PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE) hInstance + pImportDesc->FirstThunk); while (pThunk->u1.Function) { PROC* ppfn = (PROC*) &pThunk->u1.Function; BOOL bFound = (*ppfn == pfnHookAPIAddr); if (bFound) { MEMORY_BASIC_INFORMATION mbi; VirtualQuery( ppfn, &mbi, sizeof(MEMORY_BASIC_INFORMATION) ); VirtualProtect( mbi.BaseAddress, mbi.RegionSize, PAGE_READWRITE, &mbi.Protect) ) *ppfn = *pfnNew; DWORD dwOldProtect; VirtualProtect( mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &dwOldProtect ); break; } pThunk++; } El resultado de una llamada a Sleep(1000) podria ser: 00407BD8: 68E8030000 00407BDD: E812FAFFFF ........ codigo ........ push 0000003E8h call Sleep

Sleep: ;es es el salto a la IAT 004075F4: FF25BCA14000 jmp dword ptr [00040A1BCh] tabla original: 0040A1BC: 79 67 E8 77 00 00 00 00 nueva tabla: 0040A1BC: 78 56 34 12 00 00 00 00

Asique al final el salto es a 0x12345678. =====[ 3.2.2 Enganchar nuestro propio proceso reescribiendo el entry point ]=== El metodo de reescribir primero unas intrucciones en el punto de entrada de la funcion es realmente simple. Como en el caso de reescribir la IAT, tenemos que cambiar la proteccion de pagina antes de nada. Aqui seran los 5 primeros bytes de la funcion que queremos enganchar. Despues tendremos que localizar memoria dinamica para la estructura MEMORY_BASIC_INFORMATION. El comienzo de la funcion lo obtenemos con GetProcAddres. En esta direccion insertaremos un salto relativo a nuestro codigo. El siguiente programa llama a Sleep(5000) (espera 5 segundos), entonces la funcion Sleep es enganchada y redirigida a new_sleep, y finalmente llama de nuevo a Sleep(5000). Como la nueva funcion new_sleep no hace nada y termina inmediatamente el programa entero tardara solo 5 segundos en lugar de 10. (NOTA del traductor: ---------------------------Holy_father ha puesto este codigo en win32asm. El codigo se entiende bien pero lo voy a poner primero en C porque creo que se queda uno con una vision mas general en la primera lectura. El codigo lo escribo sobre la marcha mientras traduzco pero creo que esta bien. Luego puedes continuar echarle un vistazo a como quedaria en win32asm: ..... static void new_sleep() { ...... } ..... Sleep(5000); HMODULE Hkernel=GetModuleHandleA("kernel32.dll") FARPROC HSleep=GetProcAddress(Hkernel,"Sleep"); LPVOID pmbi=VirtualAlloc(0,sizeof(MEMORY_BASIC_INFORMATION) ,MEM_COMMIT,PAGE_READWRITE); if (pmbi) { DWORD pinfo=VirtualQuery(HSleep,Hcode, sizeof(MEMORY_BASIC_INFORMATION)); if (pinfo) { /* como la funcion Sleep se acaba de ejecutar es probable que aun este en cache asique vaciamos de la cache los 5 primeros bytes de la funcion Sleep */ FlushInstructionCache(GetCurrentProcess(),HSleep,5); //la estructura que hemos reservado es una mbi //tenemos que poner mbi.Protect al valor devuelto por //VirtualProtect en lpflOldProtect //14h (= 20) es el desplazamiento de Protect dentro de la mbi

DWORD dwOldProtect=(DWORD)pmbi + 14h; PDWORD pOldProtect=(PDWORD)dOldProtect; DWORD dwsize=(DWORD)pmbi + 00ch; PDWORD psize=(PDWORD)dwsize; if (VirtualProtect( pmbi , *psize, PAGE_EXECUTE_READWRITE, pOldProtect)) { //el primer byte contieen el hex opcode de jmp PWORD pSleep=(PWORD)HSleep; *(pSleep[0])= (BYTE)0E9h; //jmp DWORD offset_jmp=(DWORD)(PBYTE)new_sleep-(DWORD)(PBYTE)Sleep-5; //los siguientes contienen el offset memcpy(&Sleep[1],&offset_jmp,4); DWORD tmp_oldprotect; VirtualProtect( pmbi , *psize, pOldProtect, &tmp_oldprotect); } } VirtualFree(pmbi,0,MEM_RELEASE); } Sleep(5000); ExitProcess(0); ---------------------------.386p .model flat, stdcall includelib lib\kernel32.lib Sleep PROTO GetModuleHandleA PROTO GetProcAddress PROTO VirtualQuery PROTO VirtualProtect PROTO VirtualAlloc PROTO VirtualFree PROTO FlushInstructionCache PROTO GetCurrentProcess PROTO ExitProcess PROTO .data kernel_name sleep_name old_protect db "kernel32.dll",0 db "Sleep",0 dd ? :DWORD :DWORD :DWORD,:DWORD :DWORD,:DWORD,:DWORD :DWORD,:DWORD,:DWORD,:DWORD :DWORD,:DWORD,:DWORD,:DWORD :DWORD,:DWORD,:DWORD :DWORD,:DWORD,:DWORD :DWORD

MEMORY_BASIC_INFORMATION_SIZE equ 28 PAGE_READWRITE dd 000000004h PAGE_EXECUTE_READWRITE dd 000000040h MEM_COMMIT dd 000001000h

MEM_RELEASE .code start: push call do_hook: push call push push call mov push push push push call test jz mov push push push call test jz call push push push call lea push push lea push push call test jz mov mov sub sub inc stosd push lea push lea push 5000 Sleep

dd 000008000h

offset kernel_name GetModuleHandleA offset sleep_name eax GetProcAddress edi,eax

;la direccion de Sleep

PAGE_READWRITE MEM_COMMIT MEMORY_BASIC_INFORMATION_SIZE 0 VirtualAlloc eax,eax do_sleep esi,eax ;lozalizacion de memoria para MBI MEMORY_BASIC_INFORMATION_SIZE esi edi VirtualQuery ;informacion de la pagina eax,eax free_mem GetCurrentProcess 5 edi eax FlushInstructionCache

;nos aseguramos por si acaso :)

eax,[esi+014h] eax PAGE_EXECUTE_READWRITE eax,[esi+00Ch] [eax] [esi] VirtualProtect ;cambiaremos la proteccion un momento ;para que podamos escribir eax,eax free_mem byte ptr [edi],0E9h eax,offset new_sleep eax,edi eax,5 edi offset old_protect eax,[esi+014h] [eax] eax,[esi+00Ch] [eax] ;escribir el salto relativo

;este es el salto relativo de jmp

push call free_mem: push push push call do_sleep: push call push call new_sleep: ret end start

[esi] VirtualProtect MEM_RELEASE 0 esi VirtualFree 5000 Sleep 0 ExitProcess 004h

;restauramos la proteccion original

;liberar memoria

Resultado de la segunda llamada a Sleep: 004010A4: 6888130000 004010A9: E80A000000 push 000001388h call Sleep

Sleep: ;este es el salto en la direccion de la IAT 004010B8: FF2514204000 jmp dword ptr [000402014h] tabulka: 00402014: 79 67 E8 77 6C 7D E8 77 Kernel32.Sleep: 77E86779: E937A95788 new_sleep: 004010B5: C20400 jmp 0004010B5h ret 004h

=====[ 3.2.3 Enganchar sin modificar las funciones originales]================== La mayoria de las veces solemos necesitar algo mas que un gancho. Por ejemplo en el caso en que no queramos reemplazar una funcion, sino solo comprobar su resultado, o en el caso de que queramos reemplazar el codigo de la funcion solo algunas veces, por ejemplo cuando es llamada con unos parametros determinados. Un buen ejemplo de esto es la ocultacion de ficheros, mencionada antes, que se hace reemplazando funciones FindxxxFile. Asi si queremos esconder unos ficheros especificos y no queremos ser descubiertos, tenemos que dejar la funcion original para el resto de ficheros. Esto es facil cuando usamos el metodo de reescribir la IAT. Para llamar a la funcion original solo tendriamos que obtener la direccion con GetProcAddress y llamarla directamente. Pero el problema viene cuando usamos la reescritura del punto de entrada. Reescribiendo los 5 primeros bytes del punto de entrada de las funciones perdemos una parte de la funcion, imposible de recuperar. Asique lo que hacemos es guardar primero estas intrucciones. Podemos usar la siguiente tecnica. Sabemos que solo reescribiremos los 5 primeros bytes, pero no sabemos cuantas instrucciones hay ni la longitud de estas. Tenemos que reservar suficiente memoria para las primeras instrucciones. 16 bytes podrian ser suficientes porque no suele haber instrucciones largas al principio de una funcion. Probablemente podriamos usar menos de 16. La memoria reservada la llenaremos con operaciones nop (0x90) en el caso de que las instrucciones ocupen menos de esos 16. Los 5 bytes siguiente a esos 16 seran llenados con un salto relativo que luego rellenaremos su direccion.

old_hook:

db 090h,090h,090h,090h,090h,090h,090h,090h db 090h,090h,090h,090h,090h,090h,090h,090h db 0E9h,000h,000h,000h,000h

Ahora ya podemos copiar las primeras instrucciones. Resulta algo largo obtener la longitud de una instruccion, porque trabajaremos con el codigo completo de un desensamblador. Este fue hecho por Z0MBiE. El argumento de entrada es la direccion de la instruccion de la que queremos saber la longitud. La salida se devuelve en eax. ; LDE32, Length-Disassembler Engine, 32-bit, (x) 1999-2000 Z0MBiE ; special edition for REVERT tool ; version 1.05 C_MEM1 C_MEM2 C_MEM4 C_DATA1 C_DATA2 C_DATA4 C_67 C_MEM67 C_66 C_DATA66 C_PREFIX C_MODRM C_DATAW0 equ equ equ equ equ equ equ equ equ equ equ equ equ 0001h 0002h 0004h 0100h 0200h 0400h 0010h 0020h 1000h 2000h 0008h 4000h 8000h ; ; ; ; ; ; ; ; ; ; ; ; ; | |puede usarse simultaneamente | | |puede usarse simultaneamente | usado con C_PREFIX C_67 ? C_MEM2 : C_MEM4 usado con C_PREFIX C_66 ? C_DATA2 : C_DATA4 prefijo. take opcode again MODxxxR/M opc&1 ? C_DATA66 : C_DATA1

p386 model flat locals @@ .code public public public public disasm_main: _disasm_main: @disasm_main: DISASM_MAIN: ; devuelve la longitud de opcode en EAX o -1 si error ; entrada: puntero a opcode ; __fastcall ; __cdecl EAX [ESP+4] disasm_main _disasm_main @disasm_main DISASM_MAIN

;este es mi primer cambio aqui, es la etiqueta solo para llamar esta funcion get_instr_len: mov ecx, [esp+4] ; ECX = opcode ptr

xor xor @@prefix: and mov inc or test jnz cmp je cmp je cmp je cmp je @@cont: test jnz @@dataw0done: test jnz @@exitmodrm: test jnz @@mem67done: test jnz @@data66done: mov sub and add add

edx, edx eax, eax dl, not C_PREFIX al, [ecx] ecx

; flags

edx, table_1[eax*4] dl, C_PREFIX @@prefix al, 0F6h @@test al, 0F7h @@test al, 0CDh @@int al, 0Fh @@0F dh, C_DATAW0 shr 8 @@dataw0 dh, C_MODRM shr 8 @@modrm dl, C_MEM67 @@mem67 dh, C_DATA66 shr 8 @@data66 eax, ecx eax, [esp+4] edx,C_MEM1+C_MEM2+C_MEM4+C_DATA1+C_DATA2+C_DATA4 al, dl al, dh

;mi segundo cambio aqui, habia retn solo en la version original @@exit: ret 00004h @@test: or test jnz or jmp or cmp jne or jmp mov inc dh, C_MODRM shr 8 byte ptr [ecx], 00111000b ; F6/F7 -- test @@cont dh, C_DATAW0 shr 8 @@cont dh, C_DATA1 shr 8 byte ptr [ecx], 20h @@cont dh, C_DATA4 shr 8 @@cont al, [ecx] ecx

@@int:

@@0F:

or cmp jne @@error: @@dataw0: mov jmp xor test jnz xor jmp xor test jnz xor jmp xor test jnz xor jmp mov inc mov and cmp je test jnz @@modrm32: cmp jne mov inc and @@a: cmp je cmp je cmp jne @@mem4: @@mem1: @@modrm16: or jmp or jmp cmp

edx, table_0F[eax*4] edx, -1 @@cont eax, edx @@exit dh, C_DATA66 shr 8 al, 00000001b @@dataw0done dh, (C_DATA66+C_DATA1) shr 8 @@dataw0done dl, C_MEM2 dl, C_67 @@mem67done dl, C_MEM4+C_MEM2 @@mem67done dh, C_DATA2 shr 8 dh, C_66 shr 8 @@data66done dh, (C_DATA4+C_DATA2) shr 8 @@data66done al, [ecx] ecx ah, al ; ah=mod, al=rm ax, 0C007h ah, 0C0h @@exitmodrm dl, C_67 @@modrm16 al, 04h @@a al, [ecx] ecx al, 07h ah, 40h @@mem1 ah, 80h @@mem4 ax, 0005h @@exitmodrm dl, C_MEM4 @@exitmodrm dl, C_MEM1 @@exitmodrm ax, 0006h ; sib

@@mem67:

@@data66:

@@modrm:

je cmp je cmp jne @@mem2: or jmp endp .data ;0F ;F6,F7 ;CD

@@mem2 ah, 40h @@mem1 ah, 80h @@exitmodrm dl, C_MEM2 @@exitmodrm

-- analizado en codigo, sin flags (i.e.flags must be 0) -- --//-- (ttt=000 -- 3 bytes, sino 2 bytes) -- --//-- (6 bytes si CD 20, sino 2 bytes) label ; 00 ; 01 ; 02 ; 03 ; 04 ; 05 ; 06 ; 07 ; 08 ; 09 ; 0A ; 0B ; 0C ; 0D ; 0E ; 0F ; 10 ; 11 ; 12 ; 13 ; 14 ; 15 ; 16 ; 17 ; 18 ; 19 ; 1A ; 1B ; 1C ; 1D ; 1E ; 1F ; 20 ; 21 ; 22 ; 23 ; 24 ; 25 ; 26 ; 27 ; 28 ; 29 dword ; instrucciones normales

table_1 dd C_MODRM dd C_MODRM dd C_MODRM dd C_MODRM dd C_DATAW0 dd C_DATAW0 dd 0 dd 0 dd C_MODRM dd C_MODRM dd C_MODRM dd C_MODRM dd C_DATAW0 dd C_DATAW0 dd 0 dd 0 dd C_MODRM dd C_MODRM dd C_MODRM dd C_MODRM dd C_DATAW0 dd C_DATAW0 dd 0 dd 0 dd C_MODRM dd C_MODRM dd C_MODRM dd C_MODRM dd C_DATAW0 dd C_DATAW0 dd 0 dd 0 dd C_MODRM dd C_MODRM dd C_MODRM dd C_MODRM dd C_DATAW0 dd C_DATAW0 dd C_PREFIX dd 0 dd C_MODRM dd C_MODRM

dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd

C_MODRM C_MODRM C_DATAW0 C_DATAW0 C_PREFIX 0 C_MODRM C_MODRM C_MODRM C_MODRM C_DATAW0 C_DATAW0 C_PREFIX 0 C_MODRM C_MODRM C_MODRM C_MODRM C_DATAW0 C_DATAW0 C_PREFIX 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 C_MODRM C_MODRM C_PREFIX C_PREFIX

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65

dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd

C_PREFIX+C_66 C_PREFIX+C_67 C_DATA66 C_MODRM+C_DATA66 C_DATA1 C_MODRM+C_DATA1 0 0 0 0 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_MODRM+C_DATA1 C_MODRM+C_DATA66 C_MODRM+C_DATA1 C_MODRM+C_DATA1 C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM 0 0 0 0 0 0 0 0 0 0 C_DATA66+C_MEM2 0 0 0 0 0 C_MEM67 C_MEM67

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1

dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd

C_MEM67 C_MEM67 0 0 0 0 C_DATA1 C_DATA66 0 0 0 0 0 0 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_MODRM+C_DATA1 C_MODRM+C_DATA1 C_DATA2 0 C_MODRM C_MODRM C_MODRM+C_DATA1 C_MODRM+C_DATA66 C_DATA2+C_DATA1 0 C_DATA2 0 0 0 0 0 C_MODRM C_MODRM C_MODRM C_MODRM C_DATA1 C_DATA1 0 0 C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD

dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd

C_MODRM C_MODRM C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA1 C_DATA66 C_DATA66 C_DATA66+C_MEM2 C_DATA1 0 0 0 0 C_PREFIX 0 C_PREFIX C_PREFIX 0 0 0 0 0 0 0 0 0 0 C_MODRM C_MODRM

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

DE DF E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF dword ; instrucciones con prefijo 0F

table_0F dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd C_MODRM C_MODRM C_MODRM C_MODRM -1 -1 0 -1 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1

label ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16

dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd

-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52

dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd

-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66 C_DATA66

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E

dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd

C_DATA66 C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM 0 0 0 C_MODRM C_MODRM+C_DATA1 C_MODRM -1 -1 0 0 0 C_MODRM C_MODRM+C_DATA1 C_MODRM -1 C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM -1 -1 C_MODRM+C_DATA1 C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM C_MODRM -1 -1 -1 -1 -1 -1 0 0 0

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA

dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd

0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

CB CC CD CE CF D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF

end Ahora podemos obtener la longitud de la instruccion en una direccion arbitraria. Repetiremos esta llamada hasta que leamos los 5 bytes. Despues esto copiara esos bytes a old_hook. Sabemos la longitud de las primeras instrucciones, asique pordemos rellenar el salto relativo con la direccion

que va a continuacion de estas instrucciones en la funcion original. .386p .model flat, stdcall ... .data kernel_name sleep_name ... MEM_RELEASE dd 000008000h db "kernel32.dll",0 db "Sleep",0

;16 nops + un salto relativo old_sleep db 090h,090h,090h,090h,090h,090h,090h,090h, 090h,090h,090h,090h,090h,090h,090h,090h, 0E9h,000h,000h,000h,000h .code start: push call do_hook: push call push call push mov 5000 Sleep offset kernel_name GetModuleHandleA offset sleep_name push eax GetProcAddress eax esi,eax

xor ecx,ecx mov ebx,esi get_five_bytes: push ecx push ebx call get_instr_len ;llamamos al LDE32 pop ecx add ecx,eax add ebx,eax cmp ecx,5 jb get_five_bytes mov edi,offset old_sleep ;contando la direccion del salto mov [edi+011h],ebx sub [edi+011h],edi sub dword ptr [edi+011h],015h rep movsb pop edi ;el siguiente codigo es lo mismo de antes push push push push PAGE_READWRITE MEM_COMMIT MEMORY_BASIC_INFORMATION_SIZE 0

call test jz mov push push push call test jz call push push push call lea push push lea push push call test jz mov mov sub sub inc stosd push lea push lea push push call free_mem: push push push call do_sleep: push call push call new_sleep: mov add push mov call ret

VirtualAlloc eax,eax do_sleep esi,eax MEMORY_BASIC_INFORMATION_SIZE esi edi VirtualQuery eax,eax free_mem GetCurrentProcess 5 edi eax FlushInstructionCache eax,[esi+014h] eax PAGE_EXECUTE_READWRITE eax,[esi+00Ch] [eax] [esi] VirtualProtect eax,eax free_mem byte ptr [edi],0E9h eax,offset new_sleep eax,edi eax,5 edi offset old_protect eax,[esi+014h] [eax] eax,[esi+00Ch] [eax] [esi] VirtualProtect MEM_RELEASE 0 esi VirtualFree 5000 Sleep 0 ExitProcess eax,dword ptr [esp+004h] eax,eax eax eax,offset old_sleep eax 004h ;doblando el tiempo de espera ;llamada a la function antigua

despues del gancho tenemos lo siguiente: 004010CC: 6888130000 004010D1: E818090000 push 000001388h call Sleep

Sleep: ;este es el salto en la direccion de la IAT 004019EE: FF2514204000 jmp dword ptr [000402014h] tabulka: 00402014: 79 67 E8 77 6C 7D E8 77 Kernel32.Sleep: 77E86779: E95FA95788 new_sleep: 004010DD: 8B442404 004010E1: 03C0 004010E3: 50 004010E4: B827304000 004010E9: FFD0 old_sleep: 00403027: 6A00 00403029: FF742408 0040302D: 90 0040302E: 90 0040302F: 90 00403030: 90 00403031: 90 00403032: 90 00403033: 90 00403034: 90 00403035: 90 00403036: 90 00403037: E94337A877 jmp 0004010DDh mov eax,dword ptr [esp+4] add eax,eax push eax mov eax,000403027h call eax push 0 push dword ptr [esp+8] nop nop nop nop nop nop nop nop nop nop jmp Kernel32.77E8677F

;esta instruccion es colocada un byte despues de la primera instruccion ;en Kernel32.Sleep (77E86779) Kernel32.77E8677F: 77E8677F: E803000000 ... call Kernel32.SleepEx ;lo restante no tiene importancia

Para poner esto mas claro, asi es como aparece la version original de Kernel32.Sleep: Kernel32.Sleep: 77E86779: 6A00 77E8677B: FF742408 77E8677F: E803000000 77E86784: C20400 push 0 push dword ptr [esp+8] call Kernel32.SleepEx ret 00004h

Como puedes ver hemos copiado la primera y segunda instruccion (6 bytes) y el salto relativo apuntab a la siguiente instruccion y asi es como

debe ser. Hemos supuesto que no hay saltos relativos en los primeros bytes de las funciones. Si los hubiera seria un problema. El siguiente problema es con APIs como ntdll.DbgBreakPoint. Son demasiado cortas para este metodo de gancheo.Y para tanto como es llamada por Kernel32.DebugBreak, no es gancheable cambiando la IAT. Pero quien quiere ganchear una funcion que solo consta de la llamada a int 3 ? Nada es imposible. Si piensas sobre esto podrias encontrar la forma. Estuve pensando que se puede enganchar la funcion siguiente a una dada( seria daada al reescribir los 5 bytes primeros de la funcion previa). La funcion DbdBreakPoint son 2 bytes, asique podemos poner algunos flags aqui e intentar escribir un salto condicional en el comienzo de la segunda funcion...Pero este no es nuestro asunto ahora. Con el problema de salvaguardar la funcion original llegamos al desenganche. El desenganche es restaurar los bytes cambiados con el estado original. Cuando reescribimos la IAT tendremos que restaurar la direccion original a la tabla. Cuando usemos el parche de los 5 bytes tendremos que volver a copiar las instrucciones en sus direcciones originales. Ambas formas son muy sencillas y no es necesario escribir mas sobre ello. =====[ 3.2.4 Other process hooking ]============================================ Ahora haremos algo practico con los ganchos en ejecucion. Quien quiere enganchar su propio proceso? Esto servia para aprender la teoria pero no es muy practico. Te mostrare tres metodos de enganchar otros procesos. Dos de ellos usan CreateRemoteThread que esta solo en Windows con tecnologia NT. El problema de los ganchos para mi no es tan interesante en versiones antiguas de windows. Despues de todo intentare explicar un metodo que no lo probe, asique podria no funcionar. Primero veamos un poco sobre CreateRemoteThread. Como la ayuda de esta funcion dice, crea un hilo nuevo en cualquier proceso y ejecuta su codigo. HANDLE CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); El manejador de hProcess lo conseguimos con OpenProcess. Aqui tenemos que tener los privilegios necesarios. El puntero lpStartAddress apunta a un lugar de memoria del procedo OBJETIVO donde esta la primera instruccion del nuevo hilo. Como el nuevo hilo es creado en el proceso objetivo este estara en la zona de memoria del proceso objetivo. El puntero lpParameter apunta a un argumento que sera el del nuevo hilo. =====[ 3.2.4.1 Inyeccion DLL]=================================================== Podemos ejecutar un nuevo hilo desde cualquier parte en la memoria de un proceso objetivo. Esto es inutil amenos que tengamos codigo propio en el. El primer metodo hace esto. Usa GetProcAddress para obtener la direcion actual de LoadLibrary. Entonces pasa como lpStartAddress la direccion de LoadLibrary. La funcion LoadLibrary tiene un solo parametro, igual que la funcion para nuevo hilo en el proceso objetivo.

HINSTANCE LoadLibrary( LPCTSTR lpLibFileName ); Usaremos esta similidad de parametros y pasaremos el nombre la la DLL como lpParameter. Despues de ejecutar el nuevo hilo, lpParameter contendra lpLibFileName. Lo mas importante es el comportamiento descrito arriba. Despues de cargar el nuevo modulo en la memoria del proceso victima, la parte de inicializacion es ejecutada. Si colocamos funciones especificas que enganchen lo que nosotros queramos en el modulo, ya esta todo hecho. Despues de ejecutar la parte de inicializacion, el hilo no tendra nada que hacer y se carrera. Pero nuestro modulo estara todavia en memoria. Ese metodo esta curioso y es facil de implementar. Se le llama Inyeccion DLL. Pero si te pasa como a mi, no te gusta tener que usar DLLs. Pero si no te importa tener librerias esta es la forma mas facil y mas rapida (desde el punto de vista del programador) =====[ 3.2.4.2 Codigo Independiente ]=========================================== Este metodo es muy dificil pero es impresionante. Codigo independiente es el codigo sin direcciones estaticas. Todo es relativo en el hacia cualquier otra parte del codigo propio. Este codigo es hecho la mayoria de las veces si no sabemos la direccion donde este codigo sera ejecutado. Seguramente seria posible obtener esta direccion y reprogramar nuestro codigo para que funconara en la nueva direccion sin errores, pero esto seria incluso mas dificil que haciendo codigo independiente. Un ejemplo de este tipo de codigo puede ser el codigo de un virus. El virus que infecta ejecutables se aade por si mismo al ejecutable. En diferentes ejecutables el codigo del virus estara en sitios diferente dependiendo de las estructuras, la longitud, etc En principio tenemos que insertar nuestro codigo en un proceso objetivo. Entonces la funcion CreateRemoteThread nos permitira ejecutar nuestro codigo.Asi, al principio tenemos que obtener informacion sobre el proceso victima y abrirlo con OpenProcess. La funcion VirtualAllocEx nos reserva espacio en la zona de memoria del proceso victima. Finalmente usamos WriteProcessMemory para escribir nuestro codigo en la memoria reservada y lo ejecutamos. En CreateRemoteThread, lpStartAddress sera la direccion de la memoria reservada y lpParameter puede ser lo que queramos. Me gusta este metodo porque no necesitamos ficheros innecesarios. =====[ 3.2.4.3 Cambio Raw ]==================================================== No hy una funcion CreateRemoteThread en versiones de windows antiguas (sin NT). Asique no podemos usar esta funcion para los ganchos. Hay probablmente mejores metodos para enganchar que el metodo que voy a contar. De hecho nose si funcionara en la practica (uno nunca sabe cuando usa windows) pero teoricamente esta todo correcto. No necesitamos en absoluto tener nuestro codigo en el proceso victima para enganchar sus funciones. Tenemos la funcion WriteProcessMemory (deberia estar en todas las versiones de windows) y tenemos OpenProcess tambien. Lo ultimo que necesitamos es VirtualProtectEx que permite cambiar el acceso a paginas de memoria en otros procesos. No veo ninguna razon por la que no podamos enganchar funciones directamente desde nuestro proceso. =====[ 4. Finalizando ]========================================================= Este pequeo manual termina aqui. Agradecere cualquier comentario que

describa metodos no mencionados de enganche, estoy seguro que hay un monton. Tambien agradecere cualquier comentario en las partes que no estan descritas tan detalladamente. Puedes enviarme codigo fuente si quieres para los problemas de ganchos que por ejemplo estuve demasiado vago para escribir. La meta de este docmento es mostrar detalles de cada tecnica de ganchos. Espero haber hecho parte de esto. Agradecimieno especial a Z0MBiE por su trabajo, que asi no tuve que escribir yo mismo y dedicar aos estudiando tablas para conseguir la longitud de instruccion. ===================================[ Fin ]======================================

Vous aimerez peut-être aussi