Como decis API voy a centrarme en modo Usuario. Existen diferentes tecnicas pero se basan en dos fundamentos:
A. Aprovechar el formato PE
B. Sobreescribir el mismo codigo
La primera es mas simple al no necesitar saber nada sobre assembly para poder implementarla. Para esta como te decia tenes que buscar informacion sobre el formato PE (mas especificamente sobre las tablas IAT y EAT por ejemplo).
La segunda manera implica, para hacerla bien, implementar al menos un codigo que se encargue de calcular el tamaño de cualquier instruccion. Esto se debe a que normalmente lo que se hace es esto:
Teniendo como ejemplo CreateFileW, normalmente se usaria ZwCreateFile/NtCreateFile (tienen exactemante la misma direccion, una es alias de la otra) pero en este caso no sirven para demostrar el problema del tamaño de las instrucciones:
CreateFileW:
77e4ba58 55 push ebp
77e4ba59 8bec mov ebp,esp
77e4ba5b 83ec58 sub esp,58h
77e4ba5e 8b4518 mov eax,dword ptr [ebp+18h]
77e4ba61 48 dec eax
77e4ba62 0f8477e40000 je kernel32!FindAtomA+0x10 (77e59edf)
77e4ba68 48 dec eax
77e4ba69 0f8401050000 je kernel32!WriteFile+0xb9 (77e4bf70)
El objetivo es cambiar esas instrucciones del comienzo por un jmp a donde este nuestro "hook", entonces esto quedaria asi:
CreateFileW:
77e4ba58 e98bec83ec jmp 6468a6e8 <<hacemos de cuenta que esta es la direccion nuesta funcion "hook"
77e4ba5d 58 pop eax
77e4ba5e 8b4518 mov eax,dword ptr [ebp+18h]
77e4ba61 48 dec eax
77e4ba62 0f8477e40000 je kernel32!FindAtomA+0x10 (77e59edf)
77e4ba68 48 dec eax
77e4ba69 0f8401050000 je kernel32!WriteFile+0xb9 (77e4bf70)
77e4ba6f 48 dec eax
Al haber "destruido" las primeras instrucciones de esta funcion lo que tenemos que hacer en nuestro "hook" si queremos llamar a la funcion original es justamente reproducir estas instrucciones (lo mas facil es reservar memoria y copiarlas) para posteriormente saltar a la primera instruccion bien formada que haya justo despues del jmp que insertamos. Para poder copiar las instrucciones necesarias y saber donde hay que saltar para continuar necesitamos el codigo que calcule el tamaño de las instrucciones.
Finalmente tendriamos en memoria esto:
CreateFileW_Trampoline:
push ebp
mov ebp,esp
sub esp,58h
jmp 77e4ba5e
Como se puede ver ahi arriba, ejecutamos 6 bytes (las tres primeras instrucciones) y saltamos justo a la cuarta que no fue destruida al comenzar en el 7 byte (nosotros modificamos 5 bytes PERO necesitamos 6 al menos para ejecutar instrucciones completas (en este caso).
Entonces desde nuestro hermoso "hook" en C/C++ podemos hacer:
HANDLE __stdcall CreateFileW_Hook(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
//hacemos una comprobacion
if (!dejarArchivo(lpFileName))
{
SetLastError(ACCESS_DENIED)
return INVALID_HANDLE_VALUE;
}
//llamamos a la funcion original
return CreateFile_Trampoline(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttribues, hTemplateFile);
}
IMPORTANTE es nunca olvidarse de respetar la convencion de llamada de la funcion "hookeada", en este caso y en el 99% de la API de Windows se usa stdcall.
En algunas versiones de Windows mas recientes podemos comprobar que muchas funciones comienzan con mov edi, edi este par de instrucciones que no hacen nada estan por una razon: poder hacer lo que Microsoft denomina "hotpatching" que basicamente es aplicar un parche en tiempo real usando un metodo de "hooking". Esas dos instrucciones se combinan con un la adicion de varios bytes al final de una funcion con lo cual podemos hacer un salto corto a la direccion de estos bytes y en la misma poner un salto a donde queramos.
Existen mas tecnicas de sobreescritura pero con esas me parece que tenes de sobra y son la mas "limpias".