typedef struct _APIHOOK32_ENTRY
{
char szAPIName[MAX_PATH];
char szCallerModuleName[MAX_PATH];
PROC pfnOriginApiAddress;
DWORD dwOldBytes[2];// = {0,0};
//修改API入口为 mov eax 00400000;jmp eax是程序能跳转到自己的函数
BYTE byNewBytes[8];// = { 0xB8, 0x0, 0x0, 0x40, 0x0, 0xFF, 0xE0, 0x0 };
DWORD dwNewAddress;
HMODULE hModCallerModule;
//事务,解决同步问题
HANDLE hEvent;
HANDLE hProcess;
_APIHOOK32_ENTRY(const char* v_szDllName, const char* v_szAPIName, DWORD v_dwProcessID)
{
strcpy(szAPIName, v_szAPIName);
strcpy(szCallerModuleName, v_szDllName);
//创建事务
hEvent = CreateEvent( NULL, FALSE, TRUE, NULL );
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, v_dwProcessID);
memset(dwOldBytes, 0, sizeof(dwOldBytes));
memset(byNewBytes, 0, sizeof(byNewBytes));
byNewBytes[0] = 0xB8;
byNewBytes[3] = 0x40;
byNewBytes[5] = 0xFF;
byNewBytes[6] = 0xE0;
}
~_APIHOOK32_ENTRY()
{
if ( NULL != hEvent)
{
WaitForSingleObject( hEvent, INFINITE );
DWORD dwOldProc;
DWORD dwNewProc;
//改变页面属性为读写
VirtualProtectEx( hProcess, (void*)pfnOriginApiAddress, 8, PAGE_READWRITE, &dwOldProc );
//恢复API的首8个字节
WriteProcessMemory( INVALID_HANDLE_VALUE, (void*)pfnOriginApiAddress, (void*)dwOldBytes, sizeof(DWORD)*2, NULL );
//恢复页面文件的属性
VirtualProtectEx( hProcess, (void*)pfnOriginApiAddress, 8, dwOldProc, &dwNewProc );
CloseHandle(hEvent);
hEvent = NULL;
}
}
}APIHOOK32_ENTRY, *PAPIHOOK32_ENTRY;
mov eax 00400000
jmp eax
PAPIHOOK32_ENTRY phk = NULL; //简单设成全局变量,正常做法是在New完后保存到全局缓存里面
/*
* API劫持实现方式:重写API开头8个字节,写入自己的跳转语句
*/
void InitHook(const char *v_szDllName, const char *v_szAPIName, DWORD v_dwNewAddress)
{
phk = new APIHOOK32_ENTRY(v_szDllName, v_szAPIName, g_dwProcessID);
phk->dwNewAddress = v_dwNewAddress;
//重写API开头的8字节
phk->hModCallerModule = LoadLibrary( phk->szCallerModuleName );
if ( NULL == phk->hModCallerModule)
return;
phk->pfnOriginApiAddress = GetProcAddress( phk->hModCallerModule, phk->szAPIName );
//保存原始字节
ReadProcessMemory( INVALID_HANDLE_VALUE, (void*)phk->pfnOriginApiAddress, (void*)phk->dwOldBytes, sizeof(DWORD) * 2, NULL );
//将00400000改写为我们函数的地址,why +1? 因为NewBytes第一个字节0xB8为mov eax
*(DWORD*)(phk->byNewBytes + 1) = phk->dwNewAddress;
WriteProcessMemory( INVALID_HANDLE_VALUE, (void*)phk->pfnOriginApiAddress, (void*)phk->byNewBytes, sizeof(DWORD) * 2, NULL );
}
InitHook("user32.dll", "GetClipboardData", (DWORD)hook_GetClipboardData);
//简单的限制剪切板函数,粘贴的时候直接返回NULL,粘贴不成功
HANDLE _stdcall hook_GetClipboardData( UINT uFormat )
{
return NULL;
}
#define GET_EVENT(p) \
WaitForSingleObject( p->hEvent, INFINITE );\
ResetEvent(p->hEvent);
#define SET_EVENT(p) \
SetEvent(p->hEvent);
#define RESTORE_OLDADDRESS(p) \
GET_EVENT(p); \
WriteProcessMemory( INVALID_HANDLE_VALUE, (void*)p->pfnOriginApiAddress, (void*)p->dwOldBytes, sizeof(DWORD)*2, NULL );
#define SET_NEWADDRESS(p) \
WriteProcessMemory( INVALID_HANDLE_VALUE, (void*)p->pfnOriginApiAddress, (void*)p->byNewBytes, sizeof(DWORD)*2, NULL ); \
SET_EVENT(p);
HANDLE _stdcall hook_GetClipboardData( UINT uFormat )
{
HANDLE hRet;
//恢复API头8个字节
RESTORE_OLDADDRESS(phk); //正常这个phk要缓存到内存里面,编写实例就不实现了
/* 这里可以添加想要进行的处理过程*/
//真正执行API函数
hRet = GetClipboardData( uFormat );
//写入跳转语句,继续Hook
SET_NEWADDRESS(phk);
return hRet;
}
这边有个注意点:在恢复系统API头8个字节的这个过程,如果进程中的其他线程刚好也调用此API,则不会被我们Hook,这是Inline Hook的一个不足点。MHook之类的框架,对这点有进行改进,贴一段Mhook的源码:
BOOL Mhook_SetHook(PVOID *ppSystemFunction, PVOID pHookFunction) {
MHOOKS_TRAMPOLINE* pTrampoline = NULL;
PVOID pSystemFunction = *ppSystemFunction;
// ensure thread-safety
EnterCritSec();
ODPRINTF((L"mhooks: Mhook_SetHook: Started on the job: %p / %p", pSystemFunction, pHookFunction));
// find the real functions (jump over jump tables, if any)
pSystemFunction = SkipJumps((PBYTE)pSystemFunction);
pHookFunction = SkipJumps((PBYTE)pHookFunction);
ODPRINTF((L"mhooks: Mhook_SetHook: Started on the job: %p / %p", pSystemFunction, pHookFunction));
// figure out the length of the overwrite zone
MHOOKS_PATCHDATA patchdata = {0};
DWORD dwInstructionLength = DisassembleAndSkip(pSystemFunction, MHOOK_JMPSIZE, &patchdata);
if (dwInstructionLength >= MHOOK_JMPSIZE) {
ODPRINTF((L"mhooks: Mhook_SetHook: disassembly signals %d bytes", dwInstructionLength));
// suspend every other thread in this process, and make sure their IP
// is not in the code we're about to overwrite.
SuspendOtherThreads((PBYTE)pSystemFunction, dwInstructionLength);
// allocate a trampoline structure (TODO: it is pretty wasteful to get
// VirtualAlloc to grab chunks of memory smaller than 100 bytes)
pTrampoline = TrampolineAlloc((PBYTE)pSystemFunction, patchdata.nLimitUp, patchdata.nLimitDown);
if (pTrampoline) {
ODPRINTF((L"mhooks: Mhook_SetHook: allocated structure at %p", pTrampoline));
// open ourselves so we can VirtualProtectEx
HANDLE hProc = GetCurrentProcess();
DWORD dwOldProtectSystemFunction = 0;
DWORD dwOldProtectTrampolineFunction = 0;
// set the system function to PAGE_EXECUTE_READWRITE
if (VirtualProtectEx(hProc, pSystemFunction, dwInstructionLength, PAGE_EXECUTE_READWRITE, &dwOldProtectSystemFunction)) {
ODPRINTF((L"mhooks: Mhook_SetHook: readwrite set on system function"));
// mark our trampoline buffer to PAGE_EXECUTE_READWRITE
if (VirtualProtectEx(hProc, pTrampoline, sizeof(MHOOKS_TRAMPOLINE), PAGE_EXECUTE_READWRITE, &dwOldProtectTrampolineFunction)) {
ODPRINTF((L"mhooks: Mhook_SetHook: readwrite set on trampoline structure"));
// create our trampoline function
PBYTE pbCode = pTrampoline->codeTrampoline;
// save original code..
for (DWORD i = 0; i<dwInstructionLength; i++) {
pTrampoline->codeUntouched[i] = pbCode[i] = ((PBYTE)pSystemFunction)[i];
}
pbCode += dwInstructionLength;
// plus a jump to the continuation in the original location
pbCode = EmitJump(pbCode, ((PBYTE)pSystemFunction) + dwInstructionLength);
ODPRINTF((L"mhooks: Mhook_SetHook: updated the trampoline"));
// fix up any IP-relative addressing in the code
FixupIPRelativeAddressing(pTrampoline->codeTrampoline, (PBYTE)pSystemFunction, &patchdata);
DWORD_PTR dwDistance = (PBYTE)pHookFunction < (PBYTE)pSystemFunction ?
(PBYTE)pSystemFunction - (PBYTE)pHookFunction : (PBYTE)pHookFunction - (PBYTE)pSystemFunction;
if (dwDistance > 0x7fff0000) {
// create a stub that jumps to the replacement function.
// we need this because jumping from the API to the hook directly
// will be a long jump, which is 14 bytes on x64, and we want to
// avoid that - the API may or may not have room for such stuff.
// (remember, we only have 5 bytes guaranteed in the API.)
// on the other hand we do have room, and the trampoline will always be
// within +/- 2GB of the API, so we do the long jump in there.
// the API will jump to the "reverse trampoline" which
// will jump to the user's hook code.
pbCode = pTrampoline->codeJumpToHookFunction;
pbCode = EmitJump(pbCode, (PBYTE)pHookFunction);
ODPRINTF((L"mhooks: Mhook_SetHook: created reverse trampoline"));
FlushInstructionCache(hProc, pTrampoline->codeJumpToHookFunction,
pbCode - pTrampoline->codeJumpToHookFunction);
// update the API itself
pbCode = (PBYTE)pSystemFunction;
pbCode = EmitJump(pbCode, pTrampoline->codeJumpToHookFunction);
} else {
// the jump will be at most 5 bytes so we can do it directly
// update the API itself
pbCode = (PBYTE)pSystemFunction;
pbCode = EmitJump(pbCode, (PBYTE)pHookFunction);
}
// update data members
pTrampoline->cbOverwrittenCode = dwInstructionLength;
pTrampoline->pSystemFunction = (PBYTE)pSystemFunction;
pTrampoline->pHookFunction = (PBYTE)pHookFunction;
// flush instruction cache and restore original protection
FlushInstructionCache(hProc, pTrampoline->codeTrampoline, dwInstructionLength);
VirtualProtectEx(hProc, pTrampoline, sizeof(MHOOKS_TRAMPOLINE), dwOldProtectTrampolineFunction, &dwOldProtectTrampolineFunction);
} else {
ODPRINTF((L"mhooks: Mhook_SetHook: failed VirtualProtectEx 2: %d", gle()));
}
// flush instruction cache and restore original protection
FlushInstructionCache(hProc, pSystemFunction, dwInstructionLength);
VirtualProtectEx(hProc, pSystemFunction, dwInstructionLength, dwOldProtectSystemFunction, &dwOldProtectSystemFunction);
} else {
ODPRINTF((L"mhooks: Mhook_SetHook: failed VirtualProtectEx 1: %d", gle()));
}
if (pTrampoline->pSystemFunction) {
// this is what the application will use as the entry point
// to the "original" unhooked function.
*ppSystemFunction = pTrampoline->codeTrampoline;
ODPRINTF((L"mhooks: Mhook_SetHook: Hooked the function!"));
} else {
// if we failed discard the trampoline (forcing VirtualFree)
TrampolineFree(pTrampoline, TRUE);
pTrampoline = NULL;
}
}
// resume everybody else
ResumeOtherThreads();
} else {
ODPRINTF((L"mhooks: disassembly signals %d bytes (unacceptable)", dwInstructionLength));
}
LeaveCritSec();
return (pTrampoline != NULL);
}