#include "StdAfx.h" #include "CChainHook.h" #include "CAssmCopier.h" #include // a instruction can be max 14 bytes, and the one before might have been 4 bytes and // I need to copy both - allocate 32 bytes for the stub to be sure we have enough free mem #define STUB_SIZE 32 #pragma warning(disable: 4311) #pragma warning(disable: 4312) CChainHook::CChainHook(void) { } CChainHook::~CChainHook(void) { } LPVOID CChainHook::AllocStub(HANDLE hProcess) { DWORD dwMaxSize = STUB_SIZE; DWORD* pdwStubCode = (DWORD*)VirtualAllocEx(hProcess, NULL, dwMaxSize, MEM_COMMIT|MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); return (LPVOID)pdwStubCode; } bool CChainHook::InjectCode(HANDLE hProcess, VOID* pAddress, BYTE* pbCode, int nCodeBytes) { LPVOID pvStub = NULL; if (!CreateStub(hProcess, pAddress, pvStub, nCodeBytes)) return false; // stub has been created, let's inject the code into the function BYTE OpJmpToStub[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; DWORD dwJumpAddressOffset = (DWORD)pvStub - (DWORD)pAddress - sizeof(OpJmpToStub) - nCodeBytes; memcpy(&OpJmpToStub[1], &dwJumpAddressOffset, 4); // change the page-protection for the intercepted function DWORD dwOldProtect; if (!VirtualProtectEx(hProcess, pAddress, sizeof(OpJmpToStub) + nCodeBytes, PAGE_EXECUTE_READWRITE, &dwOldProtect)) return NULL; // // insert a jmp to the stub // DWORD dwWritten = 0; if (!WriteProcessMemory(hProcess, (LPVOID)(((DWORD)pAddress) + nCodeBytes), &OpJmpToStub, sizeof(OpJmpToStub), &dwWritten)) return NULL; // insert the injection code if (NULL != pbCode) { if (!WriteProcessMemory(hProcess, pAddress, pbCode, nCodeBytes, &dwWritten)) return NULL; } // // restore page protection // VirtualProtectEx(hProcess, pAddress, sizeof(OpJmpToStub) + nCodeBytes, dwOldProtect, &dwOldProtect); return true; } bool CChainHook::CreateStub(HANDLE hProcess, LPVOID pIntercept, LPVOID& rStub, int nCodeExtraSize) { _ASSERTE(pIntercept); DWORD dwMaxSize = STUB_SIZE; DWORD* pdwStubCode = (DWORD*)VirtualAllocEx(hProcess, NULL, dwMaxSize, MEM_COMMIT|MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); if (!pdwStubCode) return false; if (!CreateStubEx(hProcess, pIntercept, (LPVOID)pdwStubCode, nCodeExtraSize)) { VirtualFree((LPVOID)pdwStubCode, dwMaxSize, MEM_RELEASE); return false; } rStub = (LPVOID)pdwStubCode; return true; } bool CChainHook::CreateStubEx(HANDLE hProcess, LPVOID pIntercept, LPVOID pStubAddress, int nCodeExtraSize) { // // the instruction to jump from the stub back into the intercepted function... // BYTE OpJmpFromStub[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; // // create the stub // LPVOID pStub = (LPVOID)pStubAddress; DWORD pdwRealCodeToCopy = (DWORD)pIntercept; int iSourceCopied = 0; int iDestCopied = 0; CAssmCopier AssmCopier((BYTE*)pIntercept, (BYTE*)pStub); if (!AssmCopier.CopyAssembler(sizeof(OpJmpFromStub) + nCodeExtraSize, iSourceCopied, iDestCopied)) return false; // insert the JMP statement into the correct function! DWORD pReturnInFunc = (DWORD)pIntercept + (DWORD)iSourceCopied; DWORD pInsertJmpInStub = (DWORD)pStubAddress + (DWORD)iDestCopied; DWORD pReturnInFuncOffset = (DWORD)pReturnInFunc - (DWORD)pInsertJmpInStub - sizeof(OpJmpFromStub); memcpy(&OpJmpFromStub[1], &pReturnInFuncOffset, 4); memcpy((LPVOID)pInsertJmpInStub, &OpJmpFromStub, sizeof(OpJmpFromStub)); return true; } LPVOID CChainHook::HookProc(HANDLE hProcess, VOID* pFunctionToIntercept, VOID* pHook) { // // create a stub // LPVOID pStub = NULL; if (!CreateStub(hProcess, pFunctionToIntercept, pStub)) return false; _ASSERTE(pStub); if (HookProcEx(hProcess, pFunctionToIntercept, pHook, pStub)) return pStub; return NULL; } bool CChainHook::HookProcEx(HANDLE hProcess, VOID* pFunctionToIntercept, VOID* pHook, VOID* pStub) { _ASSERTE(pFunctionToIntercept && pHook && pStub); if (!CreateStubEx(hProcess, pFunctionToIntercept, pStub)) return false; // // create instructions so the intercepted function jumps to the hook! // BYTE OpJmpToHook[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; DWORD dwJumpAddressOffset = (DWORD)pHook - (DWORD)pFunctionToIntercept - sizeof(OpJmpToHook); memcpy(&OpJmpToHook[1], &dwJumpAddressOffset, 4); // change the page-protection for the intercepted function DWORD dwOldProtect; if (!VirtualProtectEx(hProcess, pFunctionToIntercept, sizeof(OpJmpToHook), PAGE_EXECUTE_READWRITE, &dwOldProtect)) return NULL; // // insert a jmp to the hook // DWORD dwWritten = 0; if (!WriteProcessMemory(hProcess, pFunctionToIntercept, &OpJmpToHook, sizeof(OpJmpToHook), &dwWritten)) return NULL; // // restore page protection // VirtualProtectEx(hProcess, pFunctionToIntercept, sizeof(OpJmpToHook), dwOldProtect, &dwOldProtect); return true; } bool CChainHook::RemoveHook(HANDLE hProcess, VOID* pInterceptedFunction, VOID* pStub) { _ASSERTE(pInterceptedFunction && pStub && hProcess); _ASSERTE(*((BYTE*)pInterceptedFunction) == 0xE9); // not intercepted? DWORD dwOldProtect; DWORD dwNum; const int iSizeOfFirstJmp = 5; // I always put a JMP statement into the intercepted function! if (!VirtualProtectEx(hProcess, pInterceptedFunction, iSizeOfFirstJmp, PAGE_EXECUTE_READWRITE, &dwOldProtect)) return false; // // copy code from the stub into the intercepted function // if (!WriteProcessMemory(hProcess, pInterceptedFunction, pStub, iSizeOfFirstJmp, &dwNum)) return false; // // delete the stub (the function is no longer intercepted // if (!VirtualFreeEx(hProcess, pStub, 0, MEM_DECOMMIT)) return false; // // reset the protection // DWORD dwDummy; if (!VirtualProtectEx(hProcess, pInterceptedFunction, iSizeOfFirstJmp, dwOldProtect, &dwDummy)) return false; return true; } #pragma warning(default: 4311) #pragma warning(default: 4312)