Ir al contenido



Foto

[MULTILENGUAJE] HOOK a la API en delphi y en C (trampolín)


  • Por favor identifícate para responder
26 respuestas en este tema

#1 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.773 mensajes
  • LocationMadrid - España

Escrito 05 noviembre 2012 - 12:08

Como en algún sitio comenté, si me animo publico un tutorial sobre este tema...

Una advertencia antes de comenzar. El código de este tutorial es funcional y está diseñado para ser compilado y trabajar en un entorno de 32 bits.


EL HOOK A LA API EN DELPHI

Existen dos formas básicas de realizar un Hook a la API y múltiples variantes. Ninguna técnica es perfecta, todas tienen sus ventajas y sus inconvenientes. Todas requieren inyectar código en el proceso anfitrión.


- EL Hook en la IAT:

Quizás la forma mas sencilla sea el Hook en la IAT. La IAT es una Tabla en la cabecera PE de todo ejecutable y DLL que contiene las direcciones de las API y funciones que importa de forma estática (en tiempo de compilación). Cambiando el valor de esas direcciones a una función Gancho tenemos realizado el Hook. Es un método muy estable pero requiere cambiar el valor de la dirección en todos los módulos del proceso que importen dicha API. No funciona para API importada de forma dinámica, es decir con GetProcAddress.


- El Hook Trampolín:

Con esta técnica modificamos instrucciones asm de la API para provocar un salto a nuestro código. Nuestro código debe ser capaz de llamar, después a la API original sin provocar errores. Esta técnica es algo mas compleja que la anterior, funciona para APIs importadas tanto de forma estática como dinámica pero se pueden cometer errores al medir mal el número de bytes sobrescritos, partiendo una instrucción asm y alterando, con ello, el sentido del código.


Hace un tiempo, seoane publicó en su web un código para realizar un Hook a la IAT escrito en delphi. Yo me voy a centrar en la técnica del trampolín.


La filosofía de un Hook de tipo trampolín:

Como expliqué antes tenemos que buscar una fracción de código de la API original que podamos sobrescribir con cierta tranquilidad. Lo ideal es encontrar una zona de longitud 5 bytes, en una o varias instrucciones completas asm. Si nos fijamos como comienza el código asm de una API, casi siempre se repite la misma secuencia:
 

delphi
  1. 8BFF    mov edi, edi
  2. 55      push ebp
  3. 8BEC    mov ebp, esp

Lo ideal sería que esto fuera una constante, pero me he encontrado APIs con codificación distinta en distintos PCs, posiblemente debido a las distintas actualizaciones del S.O.

Otro buen punto, puede ser buscar una instrucción de salto (jmp) o llamada a subrutina (call) pero lógicamente no la encontraremos en sitios fijos, entorpeciendo un sistema automático de Hook. Esto nos obliga, en cierta forma, a proceder a un desensamblado de parte del código, bien manual para cada API, bien automático. Nosotros vamos a usar un desensamblador de longitud que nos informa de la longitud de las instrucciones asm. Esta herramienta es justo lo que nos hace falta para controlar que no partimos instrucciones al realizar la preparación del hook.

El código que muestro en este tutorial se centra en los 5 primeros bytes de la API. La filosofía será establecer un puntero especial, del mismo tipo que la API que vamos a enganchar. En el código para automatizar Hooks llamaré a este puntero, ToAPI. Vamos a reservar memoria para él de al menos mas de 10 bytes(los 5 o mas copiados desde la API original, más otros 5 para un salto). También escribiremos la función HOOK o suplantadora, que será declarada exactamente como la API original, yo la suelo denominar como la API víctima con el prefijo New. Los primeros 5 o mas Bytes de la API original serán copiados al Buffer apuntado por ToAPI, de forma que se copien instrucciones completas y si hubiese algún salto habría que recalcularlo. Detrás de los 5 o mas bytes copiados, escribiremos una instrucción de salto ($E9) que apunta al resto de código no copiado de la API original. Esto es para poder devolver el flujo del programa al punto donde lo interrumpiremos. En la API original rescribiremos los 5 bytes con otro salto ($E9) que apunta a nuestra función Hook (NewAPI). Cuando nuestro Hook necesite llamar a la API original, usaremos el puntero ToAPI como si fuese una función, que de hecho lo es.

El código de este tutorial es operativo y permite automatizar un Hook a cualquier API a nivel usuario de forma automatizada.

Un ejemplo vale mas que mil palabras, vamos a realizar un Hook a la API MessageBox en nuestro propio proceso. Comenzamos por un esquema de cómo queda el código asm que vamos a alterar:

post-175-1427569522255.png
post-175-1427569522316.png

Archivos adjuntos


  • 1

#2 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.773 mensajes
  • LocationMadrid - España

Escrito 05 noviembre 2012 - 12:12

Para nuestro propósito vamos a necesitar usar determinadas APIs de Windows:

LoadLibrary: Carga una dll, si ya está cargada aumenta el contador en uno.
FreeLibrary: Decrementa el contador de carga de dll y a cero la descarga.
GetModuleHandle: Encuentra el HMODULE de una dll si está cargada.
GetProcAddress: Encuentra el puntero a una función exportada por una dll.
VirtualAlloc: Localiza memoria.
VirtualFree: Libera memoria
VirtualProtectEx: Cambia los permisos de escritura de un bloque de memoria.
FlushInstructionCache: Actualiza el caché de instrucciones del microprocesador.
CopyMemory: Copia un bloque de memoria en otro.


El código de alto nivel:

Comenzamos con los preparativos antes de entrar en la parte automática de bajo nivel:


delphi
  1. // Definición de tipo puntero a la API MessageBox
  2. type PMESSAGEBOX = function(hWnd: THandle; lpText, lpCaption: PCHAR; uType: integer): Integer; stdcall;
  3.  
  4.  
  5. var
  6.   // Puntero a un buffer que representa la API original
  7.   // Al hacer el Hook reservamos memoria y copiamos los 5 bytes
  8.   // y un salto a la API original
  9.   OMessageBox: PMESSAGEBOX = nil;                                   
  10.  
  11.  
  12.  
  13. implementation
  14.  
  15. // La API hookeada
  16. function NewMessageBox(hWnd: THandle; lpText, lpCaption: PCHAR; uType: integer): Integer; stdcall;
  17. begin
  18.   // OMessageBox representa la API original.
  19.   // Cambiamos parámetros para delatar el Hook...
  20.   Result:= OMessageBox(hWnd, 'MessageBox HOOKED    ', lpCaption, uType or MB_ICONEXCLAMATION);
  21. end;

El código de bajo nivel:

Bueno, el resto del trabajo es el duro, bajo nivel y punteros.
El código, que he escrito y os presento, automatiza este trabajo. Creo que si le prestáis atención se puede entender bien.




delphi
  1. // Calcula la dirección de la API original para llamar a la siguiente función
  2. procedure InstallHook(HookAPI: Pointer; var ToAPI: Pointer; dllName, ApiName: PCHAR; Resume: BOOL);
  3. var
  4.   hLib: HMODULE;
  5.   API: PBYTE;
  6. begin
  7.   hLib:= GetModuleHandle(dllName);
  8.   if hLib = 0 then hLib:= LoadLibrary(dllName);
  9.   if hLib <> 0 then
  10.   begin
  11.     API:= GetProcAddress(hLib, ApiName);
  12.     InstallHookFromAddr(HookAPI, ToAPI, API, Resume);
  13.   end;
  14. end;
  15. // Realiza el Hook a la API. Devuelve un puntero a la función puente ToAPI
  16. // Si Resume = true, el hook está operativo.
  17. procedure InstallHookFromAddr(HookAPI: Pointer; var ToAPI: Pointer; API: Pointer; Resume: BOOL);
  18. var
  19.   NBytes: DWORD;
  20.   OldProtect: DWORD;
  21. begin
  22.   // Bytes a sobreescribir de la API
  23.   NBytes  := GetOverloadBytes(API);
  24.   // reservar memoria para ToAPI, el camino de vuelta...
  25.   ToAPI  := VirtualAlloc(nil, NBytes+5, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  26.  
  27.   // Preparar la función ToAPI
  28.   // Autorizo escribir en ToAPI, es un puntero a una función...
  29.   VirtualProtectEx(THandle(-1), ToAPI, NBytes+5, PAGE_EXECUTE_READWRITE, OldProtect);
  30.   // Copio los primeros bytes de la api original
  31.   ASMmemcpy(ToAPI, API, NBytes);
  32.   // Escribo un salto relativo a la API original
  33.   PBYTE(DWORD(ToAPI) + NBytes)^:= $E9 ; //jmp
  34.   PDWORD(DWORD(ToAPI) + NBytes+1)^:= DWORD(API) - DWORD(ToAPI) - 5;
  35.   VirtualProtectEx(THandle(-1), ToAPI, NBytes+5, OldProtect, PDWORD(nil)^);
  36.  
  37.   // Preparar la API a Hookear
  38.   if Resume then ResumeHookFromAddr(HookAPI, API);
  39.  
  40.   // Obliga al procesaror a alcualizar el cache de instruciones
  41.   // pues las hemos modificado...
  42.   FlushInstructionCache(GetCurrentProcess, nil, 0);
  43. end;
  44.  
  45. // Esta función escribe el salto en la API original
  46. // Deja el Hook totalmente operativo
  47. procedure ResumeHookFromAddr(HookFunc, API: Pointer);
  48. var
  49.   OldProtect: DWORD;
  50. begin
  51.   if (HookFunc = nil) or (API = nil) then exit;
  52.  
  53.   // Autorizo escribir en la API a Hookear
  54.   VirtualProtectEx(THandle(-1), API, 5, PAGE_EXECUTE_READWRITE, OldProtect);
  55.   // escribimos el salto: jmp
  56.   PBYTE(API)^:= $E9;
  57.   // escribimos la dirección relativa del salto
  58.   PDWORD(DWORD(API)+1)^:= DWORD(HookFunc) - (DWORD(API) + 5);
  59.   VirtualProtectEx(THandle(-1), API, 5, OldProtect, PDWORD(nil)^);
  60. end;
  61.  
  62. // Para llamar a ResumeHookFromAddr desconociendo la dirección de la API
  63. procedure ResumeHook(HookFunc: Pointer; dllName, ApiName: PCHAR);
  64. var
  65.   API: Pointer;
  66. begin
  67.   if HookFunc = nil then exit;
  68.   API:= GetProcAddress(GetModuleHandle(dllName), ApiName);
  69.   if API <> nil then
  70.     ResumeHookFromAddr(HookFunc, API);
  71. end;

Ya tenemos hecho el Hook. Parte del código no está expuesto. Quiero comentar que la función GetOverloadBytes calcula el número de bytes que debemos traspasar sin peligro al Buffer apuntado por ToAPI. Para calcularlo usa un desensamblador de longitud de instrucción. Se puede prescindir de ello pero se corre el riesgo de colgar la aplicación víctima si la API “hookeada” comienza con instrucciones completas en 6 bytes, por ejemplo; ¡partiríamos una instrucción asm perdiendo el sentido del resto del código...!

Ahora faltan las labores de limpieza. Deberíamos ser capaces de deshacer el hook dejando todo como estaba antes de nuestra intromisión.
 


delphi
  1. // Suspende momentáneamente el Hook sin desinstalar ToHook
  2. // Restaura los Bytes de la API original guardados en ToAPI
  3. procedure SuspendHookFromAddr(ToAPI, API: Pointer);
  4. var
  5.   OldProtect: DWORD;
  6. begin
  7.   if(ToAPI = nil) or (API = nil) then exit;
  8.   // Autorizo escribir en la API Hookeada
  9.   VirtualProtectEx(THandle(-1), API, 5, PAGE_EXECUTE_READWRITE, OldProtect);
  10.   CopyMemory(API, ToAPI, 5);
  11.   VirtualProtectEx(THandle(-1), API, 5, OldProtect, PDWORD(nil)^);
  12. end;
  13.  
  14. procedure SuspendHook(ToAPI: Pointer; dllName, ApiName: PCHAR);
  15. var
  16.   API: Pointer;
  17.   hLib: HMODULE;
  18. begin
  19.   if(ToAPI = nil) or (dllName = nil) or (ApiName = nil) then exit;
  20.  
  21.   hLib:= GetModuleHandle(dllName);
  22.   if hLib = 0 then  hLib:= LoadLibrary(dllName);
  23.   if hLib = 0 then exit;
  24.  
  25.   API:= GetProcAddress(hLib, ApiName);
  26.   if API <> nil then
  27.     SuspendHookFromAddr(ToAPI, API);
  28. end;
  29.  
  30. // ToAPI:  Función preparada para saltar a la API original.
  31. // Contiene los primeros Bytes de la API original y un salto a ella
  32. procedure UnInstallHook(var ToAPI: Pointer; dllName, ApiName: PCHAR);
  33. var
  34.   hLib: HMODULE;
  35.   API: Pointer;
  36. begin
  37.   hLib:= GetModuleHandle(dllName);
  38.   if hLib = 0 then  hLib:= LoadLibrary(dllName);
  39.   if hLib <> 0 then
  40.   begin
  41.     API:= GetProcAddress(hLib, ApiName);
  42.     if API <> nil then UnInstallHookFromAddr(ToAPI, API);
  43.   end;
  44. end;
  45.  
  46. // Suspende el Hook restaurando la API original y libera el Buffer ToAPI
  47. procedure UnInstallHookFromAddr(var ToAPI: Pointer; API: Pointer);
  48. begin
  49.   if (ToAPI = nil) or (API = nil) then exit;
  50.   SuspendHookFromAddr(ToAPI, API);
  51.   VirtualFree(ToAPI, 0, MEM_RELEASE);
  52.   ToAPI:= nil;
  53. end;

Bueno este es el principal del código. Subo un archivo con todo el fuente y un proyecto listo para compilar.

Debéis usar esto con cuidado pues un error va a hacer caer la aplicación. Como comenté antes este ejemplo realiza un Hook a MessageBox en nuestra propia aplicación, como ejemplo vale, pero el objetivo útil sería una aplicación ajena. Para ello escribiremos el Hook en una dll que inyectaremos en el proceso anfitrión. Esa dll, una vez inyectada realizaría el Hook y desde ese momento ya tenemos el control de nuestra API...

Los Platinos podréis disfrutar de un Hook a un par de APIs un tanto especiales en un proceso que no lo es menos.

Espero haber sabido explicarme y que este pequeño tutorial sirva de ayuda.


PD: Subo el código

Saludos.

Archivos adjuntos


  • 1

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 13.812 mensajes
  • LocationMéxico

Escrito 05 noviembre 2012 - 12:18

Es facinante el asunto de acceder a la API de windows, pero dado que mi conocimiento está muy por debajo de lo que expones, quiero preguntar,

¿ Para que nos sirve hacer esto ?

¿ Con ese salto, estás modificando el comportamiento de la función (en este caso) MessageBoxA ?

¿ Que pasa con el asunto de las restricciones a WRT en Windows8 ?

¿ Se está sobreescribiendo el API ?

Perdón pero soy un total ignorante en esto de las API's :)

Saludos
  • 0

#4 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.773 mensajes
  • LocationMadrid - España

Escrito 05 noviembre 2012 - 12:32

Te respondo en desorden...

¿ Se está sobreescribiendo el API ?

Si, la estamos invadiendo, ultrajando... :D

¿ Para que nos sirve hacer esto ?

Para espiarla y variar su comportamiento si procede. Este ejemplo la espía y extrae datos. Este otro hace variar el comportamiento. Este otro hilo está basado en una miniApp antivirus que me tuve que hacer, usaba un Hook a CreateProcessInternalW.

¿ Con ese salto, estás modificando el comportamiento de la función (en este caso) MessageBoxA ?

En este caso si pero puedes desear sólo vigilar...

¿ Que pasa con el asunto de las restricciones a WRT en Windows8 ?

Ya lo iremos viendo...


Saludos.
  • 0

#5 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 06 noviembre 2012 - 08:44

Yo sólo puedo quitarme el sombrero con vuestro conocimiento del API de Wndows :s
  • 0

#6 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.156 mensajes
  • LocationArgentina

Escrito 06 noviembre 2012 - 04:46

La idea y el propósito de un hook entiendo. Ahora bien, meterme conceptualmente en sus berenjales ¡primero debo morir y nacer dos veces! A mi sólo me queda leer y sentirme chiquitito antes semejantes explicaciones de los genios.

Saludos,
  • 0

#7 jeivarmarr

jeivarmarr

    Newbie

  • Miembros
  • Pip
  • 4 mensajes

Escrito 03 mayo 2013 - 06:45

Gracias por este gran tutorial pero me causo curiosidad lo de windows 8 actualmente tengo windows 8 y he intentado lo del hook y nunca me funciono, podrias explicarme mejor sobre la proteccion.
  • 0

#8 jeivarmarr

jeivarmarr

    Newbie

  • Miembros
  • Pip
  • 4 mensajes

Escrito 03 mayo 2013 - 07:06

Una pregunta mas, para hacerlo a un proceso externo por ejemplo Calc.exe, como lo haría?
  • 0

#9 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.773 mensajes
  • LocationMadrid - España

Escrito 05 mayo 2013 - 04:52

jeivarmarr, para realizar un Hook a la API precisas inyectar código en tu proceso anfitrión. El código lo inyectas con una dll o de forma directa. El método mas común de inyectar una dll es obligar su carga con CreateRemoteThread aquí dejé un ejemplo en C. Las técnicas de inyección directa son mas complejas, dejé un tutorial aquí.

No se puede inyectar código compilado de 32bits en procesos de 64bits ni de forma directa ni con dll. Solución: usar un compilador de 64 bits.

El Hook a la API que describo es válido sólo en teoría para win8 pero en la práctica usa un desensamblador de 32 bits lo que lo hace no operativo para 64 bits

No dispongo de un PC con Win8, por el momento, con lo que no puedo hacer pruebas para responderte acerca de los sistemas de seguridad.


Saludos.
  • 0

#10 c0lo

c0lo

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 241 mensajes
  • LocationLima-Peru

Escrito 06 mayo 2013 - 09:07

Hola escafandra,

Si uno desearía hacer el problema inverso, es decir, si uno se encuentro con algunos Hooks en algunas apis como podría uno atacar o empezar a desarrollar una solución o hacer un unhook a dicha api o n-api?

Digamos que no es el clásico trampolín de 5 bytes si no que uno se encuentra con una modificación de la api del tamaño de 6 bytes?

Es decir, tenemos el inicio de una api de la siguiente forma:
 

delphi
  1. mov edi,edi
  2. push ebp
  3. mov ebp,esp
  4. push ecx
  5. or dword ptr [ebp-04],FF

Pero luego de ejecutar alguna programa X, la api su parte inicial es modificada a:

delphi
  1. jmp dword ptr [71A1001E]
  2. or dword ptr [ebp-4], FFFFFFFF

Es decir, en ves de modificar los primeros 5 bytes el hook del programa X ha modificado los primeros 6 bytes, que hizo? o cual fue su tipo de trampolin y como uno podria hacer un unhook en este caso y cualquiera que fuera el unhook?

Saludos  :angel:
  • 0

#11 c0lo

c0lo

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 241 mensajes
  • LocationLima-Peru

Escrito 06 mayo 2013 - 09:19

Epa epa... creo que sufri un lapsus, Porque si deseo hacer el unhook solo sobre escribo los bytes que inicialmente tenia la api o reconstruyo la api y listo. Oh no?

Bueno mi pregunta es como puedo utilizar la misma api con su hook sin realizar el hook, no recuerdo como se llama este metodo... Pero se podria?
  • 0

#12 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.773 mensajes
  • LocationMadrid - España

Escrito 07 mayo 2013 - 12:16

Hola c0lo si se podría reconstruir la API original. Pero puede ser algo complicado. Deberemos desensamblar y seguir el código haciendo énfasis en los saltos. La API original tiene direcciones del orden 7EXXXXXXh y el resto del código (Hook) direcciones mas bajas. Si la API original está hookeada con un jmp seguiremos el hook hasta encontrar el jmp de vuelta (salto relativo a direcciones del orden de 7EXXXXXXh). Unas cuantas instrucciones antes nos enseñarán el clásico "paquete de inicio":


php
  1. mov edi,edi
  2. push ebp
  3. mov ebp,esp
  4. ..... quizás mas instruciones
  5. jmp XXXXX

Ese paquete hasta el salto de vuelta (sin contar éste) será lo que debemos sobreescribir en la API Hookeada para devolverla a su estado original.

El tema se puede complicar si el sistema de hook es con call, entonces buscaremos un ret pero existen trucos para volver con un salto y no con ret. Así mismo en el paquete que debemos restaurar pueden aparecer saltos intermedios que no correspondan al punto de vuelta... En general el salto de vuelta no debe estar mucho mas alejado de esos 5 bytes clásicos del trampolín, si fuere así, ese salto no es el de vuelta, continuaremos un poco mas adelante hasta encontrarlo, pero el salto intermedio deberemos "convertirlo" pues es relativo, para adaptarlo al código de la API cuando la restauremos.

Una técnica mas sencilla es hacer un hook al hook, es decir cambiar el código hookeado colocando un salto directo al "paquete de vuelta" a la API original. Bastará localizar el paquete y escribir el salto justo al inicio del código que hookea la API original.

No tengo escrito código para realizar un unhook automático cuyo hook no halla hecho yo.

Claro, que si lo que quieres es un unhook de un hook que tu mismo hagas, no tienes mas que guardar el paquete original a buen recaudo para restaurarlo cuando quieras.

Quizás las explicaciones son un poco liosas, pero se entenderán mejor si se vuelve la vista atrás sobre las imágenes del hook de este tutorial.


Saludos.


  • 0

#13 FENIXadr

FENIXadr

    Newbie

  • Miembros
  • Pip
  • 2 mensajes

Escrito 11 abril 2014 - 09:26

Hola gente .. estoy buscando por muchos lugares un Hook a CreateProcess para monitorear lo que se ejecuta en mi PC y pregunto.. podemos utilizar este método y reemplazar todo lo referente a MessageBox por CreateProcess??.. de ser así que se debe tener en cuenta además?? serían tan amables de poner un ejemplo??.. desde ya muchas gracias..
  • 0

#14 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.773 mensajes
  • LocationMadrid - España

Escrito 12 abril 2014 - 04:07

Puedes hacer un Hook a cualquier API. En tu caso sería a CreateProcessInternalW. Revisa este hilo y este otro ejemplo.


Saludos.
  • 0

#15 aguml

aguml

    Newbie

  • Miembros
  • Pip
  • 5 mensajes

Escrito 17 junio 2014 - 02:00

hola amigo, tengo un problema y no se si realmente necesito hacer un hook o que es lo que necesito hacer. Estoy creando una clase que te permite crear loaders debuggers y estoy liado con los anti anti debuger y ya tengo solucionados los que usan el peb y ahora queria liarme con NtQueryInformationProcess el cual si le metes como parametro 7 te devuelve en el buffer 0xFFFFFFFF y la cosa es que no devuelva ese valor en el buffer sino otro. Lo mismo me pasará con gettickcount cuando le quiera meter mano. Como el proceso estaria siendo depurado por mi proceso, ¿Tendria que hacer un hook? NtQueryInformationProcess se carga dinamicamente con loadlibrary y getprocaddress ¿Tendria que hacer un hook a getprocaddress y filtrar hasta que quiera cargar la api deseada y ejecutar una funcion mia en lugar de la original?
  • 0

#16 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.773 mensajes
  • LocationMadrid - España

Escrito 17 junio 2014 - 04:43

Un Hook a la API que quieres modificar es mas apropiado que otro a GetProcAddress, entre otras cosas porque puedes dejar trabajar a la API capturada o no, según tu necesidad.


Saludos.
  • 0

#17 aguml

aguml

    Newbie

  • Miembros
  • Pip
  • 5 mensajes

Escrito 18 junio 2014 - 09:49

¿Y se puede hacer que el Hook solo afecte al proceso y sus threads?
Yo hice lo siguiente:

Cree una dll con este codigo:

cpp
  1. #include <vcl.h>
  2. #include <windows.h>
  3. #pragma hdrstop
  4.  
  5. #define HOOKFUNCSDLL_API extern "C" __declspec(dllexport)
  6. #pragma argsused
  7.  
  8. ULONG GetAddressAPI(char* nameDLL,char* nameAPI);
  9. void InlineNtSetInformationThread(void);
  10. void InlineNtQueryInformationProcess(void);
  11.  
  12. int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
  13. {
  14.         InlineNtSetInformationThread();
  15.         InlineNtQueryInformationProcess();
  16.         return 1;
  17. }
  18. //---------------------------------------------------------------------------
  19.  
  20. void InlineNtSetInformationThread() //Funcion para evitar que nos desatachee del proceso usando esa funcion
  21. {
  22.         ULONG Dir;
  23.         BYTE injertoZwSetInformationThread[] = {0x33,0xC0,0xC2,0x10,0x00};
  24.         ULONG nWrite;
  25.  
  26.         MessageBox(NULL,"Entró para parchear NtSetInformationThread.","ATENCION",0);
  27.  
  28.         Dir = GetAddressAPI("ntdll.dll","NtSetInformationThread");
  29.  
  30.         if(Dir == NULL)
  31.         {
  32.                 MessageBox(NULL,"Error al obtener la direccion de NtSetInformationThread.","ERROR",0);
  33.         }else{
  34.                 WriteProcessMemory(GetCurrentProcess(),(LPVOID)Dir,&injertoZwSetInformationThread,sizeof(injertoZwSetInformationThread),&nWrite);
  35.         }
  36. }
  37. //---------------------------------------------------------------------------
  38.  
  39. void InlineNtQueryInformationProcess(void)
  40. {
  41.         ULONG Dir;
  42.         BYTE injertoZwQueryInformationProcess[] = {0xC7,0x01,0x00,0x00,0x00,0x00,0x33,0xC0,0xC2,0x14,0x00};
  43.         ULONG nWrite;
  44.  
  45.         MessageBox(NULL,"Entró para parchear NtQueryInformationProcess.","ATENCION",0);
  46.  
  47.         Dir = GetAddressAPI("ntdll.dll","NtQueryInformationProcess");
  48.  
  49.         if(Dir == NULL)
  50.         {
  51.                 MessageBox(NULL,"Error al obtener la direccion de NtQueryInformationProcess.","ERROR",0);
  52.         }else{
  53.                 WriteProcessMemory(GetCurrentProcess(),(LPVOID)Dir,&injertoZwQueryInformationProcess,sizeof(injertoZwQueryInformationProcess),&nWrite);
  54.         }
  55. }
  56. //---------------------------------------------------------------------------
  57.  
  58. ULONG GetAddressAPI(char* nameDLL,char* nameAPI)
  59. {
  60.         HMODULE hMod;
  61.  
  62.         hMod = LoadLibrary(nameDLL);
  63.         return (ULONG)GetProcAddress(hMod,nameAPI);
  64. }

Luego la funcion que hace la inyeccion de la dll es esta:

cpp
  1. void THiloDebugger::InjectDll( HANDLE hProcess, AnsiString DLL )
  2. {
  3.         ULONG bytesWritten, TID;
  4.         HANDLE pThreadStartRoutine,hThread,Parameters;
  5.  
  6.         //Creamos espacio en el proceso
  7.         Parameters = VirtualAllocEx(hProcess,NULL,DLL.Length()+1,MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  8.         if(Parameters != NULL)
  9.         {
  10.                 WriteProcessMemory(hProcess,Parameters,DLL.c_str(),DLL.Length()+1, &bytesWritten);
  11.  
  12.                 if(bytesWritten == DLL.Length()+1)
  13.                 {
  14.                         pThreadStartRoutine = GetProcAddress(GetModuleHandle("kernel32.dll"),"LoadLibraryA");
  15.  
  16.                         if(pThreadStartRoutine != NULL)
  17.                         {
  18.                                 hThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)pThreadStartRoutine,Parameters,0,&TID);
  19.  
  20.                                 if(hThread == NULL)
  21.                                 {
  22.                                         MessageBox(GetCurrentProcess(),"No se pudo crear el hilo.","Error",0);
  23.                                 }
  24.                         }else{
  25.                                 MessageBox(GetCurrentProcess(),"No se pudo obtener la direccion base de LoadLibraryA.","Error",0);
  26.                         }
  27.                 }else{
  28.                         MessageBox(GetCurrentProcess(),"No se pudo parchear la API.","Error",0);
  29.                 }
  30.         }else{
  31.                 MessageBox(GetCurrentProcess(),"No se pudo obtener memoria para inyectar la dll.","Error",0);
  32.         }

Luego he creado un ejecutable el cual usa esos dos trucos antidebug y no se que pasa porque la funcion de inyeccion de la dll no me da errores pero las funciones de la dll nunca se ejecutan y de forma extraña me salto el truco anti debug de ZwSetInformationThread porque no me desatache y si no uso la inyeccion de la dll si me desatachea y tengo que cerrar el depurador a las bravas.
Esto que yo hago ademas tendria sus inconvenientes ya que estaria asi siempre y daría igual que parametros mandara como por ejemplo la NtQueryInformationProcess que si le pones como parametro 0 te llena una estructura PROCESS_BASIC_INFORMATION pero si le pones el parametro 7 te da el puerto libre para depuracion del proceso y lo ideal seria solo actuar cuando el parametro sea 7. Me interesaria el tema de crear un hook pero no tengo ni idea ni de como hacer una dll, ya lo veras en el codigo  :embarrassed:
A ver si puedes ayudarme porque con el tutorial que has puesto no soy capaz. La idea es que esa api con el parametro 7 ejecute el codigo que yo tengo en el array que es un mov ptr dword[ecx],0; xor eax,eax; retn 14; pero si no es el parametro 7 pues que se ejecute con normalidad.
La de ZwSetInformationThread lo que interesa es que siempre salga sin hacer nada ya que esta api nos desatachea del proceso.
  • 0

#18 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.773 mensajes
  • LocationMadrid - España

Escrito 18 junio 2014 - 11:37

¿Y se puede hacer que el Hook solo afecte al proceso y sus threads?


Un Hook sólo afecta al proceso que esté inyectado. Para realizar un Hook a la API precisamos inyectar código en el proceso o procesos anfitriones. Esa inyección puede ser directa o a través de una dll que es más sencillo. Inyectar una dll es obligar al proceso anfitrión a cargarla "por narices" de forma que se ejecutará el código del punto de entrada obligatoriamente. En el caso de que sólo queramos afectar a nuestro proceso, no tenemos más que cargar la dll o prescindir de ella colocando el código necesario para realizar el hook en nuestro proceso.

La teoría y código para realizar el hook está en este tutorial.


Saludos.


  • 0

#19 aguml

aguml

    Newbie

  • Miembros
  • Pip
  • 5 mensajes

Escrito 19 junio 2014 - 09:17

Amigo estoy mirando atentamente tu código y no veo donde tienes estas funciones implementadas:
function  _SizeOfCode(code: PBYTE): DWORD; cdecl; external;
function  _x_code_flags(code: PBYTE): DWORD; cdecl; external;

La primera supongo que es la que necesito para saber el tamaño del codigo original a copiar en el buffer.
La segunda no se que hace pero tampoco la tengo. ¿no podrias subirlas?
Con lo de cdecl; external supongo que es porque las obtienes desde una dll.
Seria mucho mas facil todo teniendo esas funciones.

Edito:
Estoy traduciendo tu codigo a C++Builder pero me encuentro con cosas que no se como pasarlas.
Esto no se que hace:

cpp
  1. PBYTE(DWORD(ToAPI) + NBytes)^:= $E9 ; //jmp

Lo he dejado así:

cpp
  1. (BYTE*)((DWORD)ToAPI + NBytes) = 0xE9;

y esto:

cpp
  1. PDWORD(DWORD(ToAPI) + NBytes+1)^:= DWORD(API) - DWORD(ToAPI) - 5;

lo traduje como:

cpp
  1. (DWORD*)((DWORD)ToAPI + NBytes+1) = (DWORD)API - (DWORD)ToAPI - 5;

No se si hacen lo mismo pero no compila con eso. ¿como se traduce eso?

Tambien traduje las funciones asi:

cpp
  1. extern "C" cdecl DWORD _SizeOfCode(PBYTE code);
  2. extern "C" cdecl DWORD _x_code_flags(PBYTE code);
  3.  
  4. //procedure InstallHook(HookAPI: Pointer; var ToAPI: Pointer; dllName, ApiName: PCHAR; Resume: BOOL);
  5. void InstallHook(LPVOID HookAPI, LPVOID ToAPI, char *dllName, char *ApiName, bool Resume);
  6. //procedure InstallHookFromAddr(HookAPI: Pointer; var ToAPI: Pointer; API: Pointer; Resume: BOOL);
  7. void InstallHookFromAddr(LPVOID HookAPI, LPVOID ToAPI, LPVOID API, bool Resume);
  8. //procedure SuspendHook(ToAPI: Pointer; dllName, ApiName: PCHAR);
  9. void SuspendHook(LPVOID ToAPI, char *dllName, char *ApiName);
  10. //procedure SuspendHookFromAddr(ToAPI, API: Pointer);
  11. void SuspendHookFromAddr(LPVOID ToAPI, LPVOID API);
  12. //procedure ResumeHook(HookFunc: Pointer; dllName, ApiName: PCHAR);
  13. void ResumeHook(LPVOID HookFunc, char *dllName, char *ApiName);
  14. //procedure ResumeHookFromAddr(HookFunc, API: Pointer);
  15. void ResumeHookFromAddr(LPVOID HookFunc, LPVOID API);
  16. //procedure UnInstallHook(var ToAPI: Pointer; dllName, ApiName: PCHAR);
  17. void UnInstallHook(LPVOID ToAPI, char *dllName, char *ApiName);
  18. //procedure UnInstallHookFromAddr(var ToAPI: Pointer; API: Pointer);
  19. void UnInstallHookFromAddr(LPVOID ToAPI, LPVOID API);
  20. //function  Transfer(Source, Dest: PBYTE): DWORD;
  21. DWORD Transfer(LPVOID Source, LPVOID Dest);
  22.  
  23. //function GetOverloadBytes(pB: PBYTE): DWORD;
  24. DWORD GetOverloadBytes(LPVOID pB);
  25. //function ASMmemcpy(Dest, Source: PBYTE; NBytes: DWORD): DWORD;
  26. DWORD ASMmemcpy(LPVOID Dest, LPVOID Source, DWORD NBytes);

Pero esto ni idea de como pasarlo:

cpp
  1. //function __ltolower(C: integer): integer; cdecl; external 'msvcrt.dll' name '_tolower';
  2. //function __ltoupper(C: integer): integer; cdecl; external 'msvcrt.dll' name '_toupper';
  3. //function __ltowlower(C: integer): integer; cdecl; external 'msvcrt.dll' name 'towlower';
  4. //function __ltowupper(C: integer): integer; cdecl; external 'msvcrt.dll' name 'towupper';
  5. //function  _memcpy(s1, s2: Pointer; n: integer): pointer; cdecl; external 'msvcrt.dll' name 'memcpy';

De momento todo eso.  :grin:
  • 0

#20 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.773 mensajes
  • LocationMadrid - España

Escrito 19 junio 2014 - 10:19

Esas dos funciones son las que usan el desensamblador de longitud que está compilado en el módulo objeto LDasm.obj.
SizeOfCode recibe como parámetro un puntero al opcode asm del que queremos conocer la longitud en Bytes y nos devuelve esa longitud.
x_code_flags también  recibe como parámetro un puntero al opcode asm, pero en este caso nos informa de si estamos en una instrucción de salto relativo, en cuyo caso deberemos recalcular el salto al transferir esos opcodes a la función de vuelta a la API original.

El desensamblador de longitud sólo es necesario para aquellas API que no comienzan por la secuencia habitual de 5 bytes:


asm
  1. 8BFF    mov edi, edi
  2. 55      push ebp
  3. 8BEC    mov ebp, esp



El desensamblador está escrito en C y no escribí yo, aquí tienes el enlace. Aunque por el momento creo que no te aportará más luz al tema de lo que te acabo de explicar de dicho desensamblador.


Saludos.





  • 0