c++ - Cómo crear una función de trampolín para gancho
winapi hook (1)
Si desea que su enganche sea seguro cuando lo llamen varios hilos, no desea desenganchar y volver a enganchar constantemente la API original.
Un trampolín es simplemente un poco de código generado que replica la funcionalidad de los primeros bytes de la API original (que sobrescribió con su salto) y luego salta a la API después de los bytes que sobreescribió.
En lugar de desconectar la API, llamarla y volver a conectarla, simplemente llame al trampolín.
Esto es moderadamente complicado de hacer en x86 porque necesita (un mínimo) desensamblador para encontrar los límites de la instrucción. También debe verificar que el código que copia en su trampolín no haga nada en relación con el puntero de instrucción (como un jmp, una bifurcación o una llamada).
Esto es suficiente para hacer que las llamadas al enganche sean seguras para la ejecución de subprocesos, pero no puede crear el enganche si hay varios subprocesos que usan la API. Para esto, necesita conectar la función con un salto cercano de dos bytes (que puede escribirse atómicamente). Las API de Windows suelen estar precedidas por unos pocos NOP (que pueden sobrescribirse con un salto lejano) para proporcionar un objetivo para este salto cercano.
Hacer esto en x64 es mucho más complicado. No se puede simplemente parchear la función con un salto lejano de 64 bits (porque no hay uno, y las instrucciones para simularlo a menudo son demasiado largas). Y, dependiendo de lo que haga su trampolín, es posible que deba agregarlo a la información de desenrollado de la pila del sistema operativo.
Espero que esto no sea demasiado general.
Estoy interesado en conectar y decidí ver si podía conectar algunas funciones. No estaba interesado en usar una biblioteca como desvíos porque quiero tener la experiencia de hacerlo por mi cuenta. Con algunas fuentes que encontré en Internet, pude crear el código a continuación. Es básico, pero funciona bien. Sin embargo, cuando se conectan funciones que son llamadas por múltiples hilos, resulta ser extremadamente inestable. Si se hacen dos llamadas casi al mismo tiempo, se bloqueará. Después de algunas investigaciones, creo que necesito crear una función de trampolín. Después de buscar durante horas, no pude encontrar nada más que una descripción general de lo que era un trampolín. No pude encontrar nada específicamente sobre cómo escribir una función de trampolín o cómo realmente funcionaron. Si alguien me puede ayudar a escribir uno, publicar algunas fuentes o, al menos, apuntarme en la dirección correcta al recomendar algunos artículos, sitios, libros, etc. Lo agradecería enormemente.
A continuación está el código que he escrito. Es realmente básico, pero espero que otros aprendan de él.
test.cpp
#include "stdafx.h"
Hook hook;
typedef int (WINAPI *tMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
DWORD hMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
hook.removeHook();
tMessageBox oMessageBox = (tMessageBox)hook.funcPtr;
int ret =oMessageBox(hWnd, lpText, "Hooked!", uType);
hook.applyHook(&hMessageBox);
return ret;
}
void hookMessageBox()
{
printf("Hooking MessageBox.../n");
if(hook.findFunc("User32.dll", "MessageBoxA"))
{
if(hook.applyHook(&hMessageBox))
{
printf("hook applied! /n/n");
} else printf("hook could not be applied/n");
}
}
hook.cpp
#include "stdafx.h"
bool Hook::findFunc(char* libName, char* funcName)
{
Hook::funcPtr = (void*)GetProcAddress(GetModuleHandleA(libName), funcName);
return (Hook::funcPtr != NULL);
}
bool Hook::removeHook()
{
DWORD dwProtect;
if(VirtualProtect(Hook::funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect))
{
WriteProcessMemory(GetCurrentProcess(), (LPVOID)Hook::funcPtr, Hook::origData, 6, 0);
VirtualProtect(Hook::funcPtr, 6, dwProtect, NULL);
return true;
} else return false;
}
bool Hook::reapplyHook()
{
DWORD dwProtect;
if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect))
{
WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::hookData, 6, 0);
VirtualProtect(funcPtr, 6, dwProtect, NULL);
return true;
} else return false;
}
bool Hook::applyHook(void* hook)
{
return setHookAtAddress(Hook::funcPtr, hook);
}
bool Hook::setHookAtAddress(void* funcPtr, void* hook)
{
Hook::funcPtr = funcPtr;
BYTE jmp[6] = { 0xE9, //jmp
0x00, 0x00, 0x00, 0x00, //address
0xC3 //retn
};
DWORD dwProtect;
if(VirtualProtect(funcPtr, 6, PAGE_EXECUTE_READWRITE, &dwProtect)) // make memory writable
{
ReadProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, Hook::origData, 6, 0); // save old data
DWORD offset = ((DWORD)hook - (DWORD)funcPtr - 5); //((to)-(from)-5)
memcpy(&jmp[1], &offset, 4); // write address into jmp
memcpy(Hook::hookData, jmp, 6); // save hook data
WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, jmp, 6, 0); // write jmp
VirtualProtect(funcPtr, 6, dwProtect, NULL); // reprotect
return true;
} else return false;
}