一个win32下的api hook方案

来源:互联网 发布:股票数据api接口 编辑:程序博客网 时间:2024/05/17 03:01

api hook还是挺常用的, 成熟的方案有微软自己的,  支持32位Detours。  还有支持32位、64位的开源库MHook。    

按照MHook的api, 自己仿造了一个简化的, 用作学习。   只支持32位的, 要改成64位的话, 要对shellcode进行改写。

思路: 改写函数前面的机器码, 跳转到自己的函数。 要调用原函数时, 先写回原来函数的机器码, 再调用原函数, 调用完毕之后, 再次改写原函数前面的机器码。

坑的地方: 下面这两条质量生成的机器码不一样, 导致调试的时候花了很长时间。


pop dword ptr [eax + 12]  //8F 40 0C
pop [eax + 12] //66 8F 40 0C


hookapi.h

#pragma oncetypedef void * ApiAddress;bool HookApi(ApiAddress *oldFunc, ApiAddress replaceWith);void UnHookApi(ApiAddress *hookedApi);


hookapi.cpp

#include "hookapi.h"#include "stdio.h"#include "windows.h"#define JMPSIZE 6typedef void (* FUNCPTR)();struct HookContext {ApiAddress originalApiAddress;ApiAddress replacedApiAddress;DWORD esi;DWORD eip;CRITICAL_SECTION cs;unsigned char *thunkBytes;unsigned char originalBytes[JMPSIZE];unsigned char replaceBytes[JMPSIZE];HookContext *prev;HookContext *next;};HookContext *ctxListHeader = nullptr, *ctxListTail = nullptr;CRITICAL_SECTION ctxLock;bool ctxLockInitialized;HookContext * WINAPI findContext(ApiAddress api) {//ODPRINTF(("findContext: %p", api));HookContext *ctx = ctxListHeader;while (ctx != nullptr) {if (ctx->originalApiAddress == api) {//ODPRINTF(("findContext:%p", ctx));return ctx;}ctx = ctx->next;}return nullptr;}void WINAPI placeJmp(HookContext *ctx) {//ODPRINTF(("placeJmp:%p", ctx));DWORD oldProtect, myProtect = PAGE_EXECUTE_READWRITE;VirtualProtect((LPVOID)ctx->originalApiAddress, JMPSIZE, PAGE_EXECUTE_READWRITE, &oldProtect);// assign read write protectionmemcpy(ctx->originalApiAddress, ctx->replaceBytes, JMPSIZE); // set jump instruction at the beginning of the original functionVirtualProtect((LPVOID)ctx->originalApiAddress, JMPSIZE, oldProtect, NULL);    // reset protection}void WINAPI removeJmp(HookContext *ctx) {//ODPRINTF(("removeJmp:%p", ctx));DWORD oldProtect, myProtect = PAGE_EXECUTE_READWRITE;//restore orignal functionVirtualProtect((LPVOID)ctx->originalApiAddress, JMPSIZE, PAGE_EXECUTE_READWRITE, &oldProtect);// assign read write protectionmemcpy(ctx->originalApiAddress, ctx->originalBytes, JMPSIZE); // remove jmp VirtualProtect((LPVOID)ctx->originalApiAddress, JMPSIZE, oldProtect, NULL);    // reset protection//ODPRINTF(("removeJmp ok"));}_declspec(naked) void HookThunk() {//push the api address that will be called_asm {push 0x90909090//68 [90 90 90 90]call findContext//e8 [96 fe ff ff]cmp eax, 0//83 f8 00jz END//74 [16]//save eip  pop dword ptr [eax + 12]  //8F 40 0C//save esimov [eax + 8], esi  //89 70 08mov esi, eax//8b f0push esi//56 //acquire locklea eax, [esi+16]//8D 46 10push eax//50call removeJmp//e8 [f6 fe ff ff]. //call EnterCriticalSectioncall  removeJmp//e8 [f6 fe ff ff]//here's placeholder for calling orinal apicall removeJmp//e8 [f1 fe ff ff]push esi//56 call placeJmp//e8 [ab fe ff ff]//恢复原来的eippush [esi + 12]//FF 76 0C//release locklea eax, [esi + 16]push eaxcall removeJmp//call LeaveCriticalSection//恢复esi mov esi, [esi + 8]//8B 76 08END:ret}}_declspec(naked) void HookThunkEnd() {}bool HookApi(ApiAddress *originalFunc, ApiAddress replaceWith) {if (ctxListHeader == nullptr && !ctxLockInitialized) {InitializeCriticalSection(&ctxLock);ctxLockInitialized = true;}EnterCriticalSection(&ctxLock);//check if already hookedHookContext *ctx = ctxListHeader;while (ctx != nullptr) {if (ctx->originalApiAddress == *originalFunc) {LeaveCriticalSection(&ctxLock);return false;}ctx = ctx->next;}ctx = new HookContext();InitializeCriticalSection(&ctx->cs);//ODPRINTF(("hook:%p", ctx));ctx->originalApiAddress = *originalFunc;ctx->replacedApiAddress = replaceWith;memcpy(ctx->originalBytes, *originalFunc, JMPSIZE);DWORD offset = (DWORD_PTR)replaceWith - (DWORD_PTR)*originalFunc - 5;ctx->replaceBytes[0] = 0xE9; //jmpmemcpy(ctx->replaceBytes + 1, &offset, sizeof(offset)); //jump wherectx->replaceBytes[JMPSIZE - 1] = 0xC3; //retDWORD oldProtect = 0;VirtualProtect((LPVOID)ctx->originalApiAddress, JMPSIZE, PAGE_EXECUTE_READWRITE, &oldProtect);// assign read write protectionmemcpy(ctx->originalApiAddress, ctx->replaceBytes, JMPSIZE); // set jump instruction at the beginning of the original functionVirtualProtect((LPVOID)ctx->originalApiAddress, JMPSIZE, oldProtect, NULL);    // reset protectionint thunkLen = (DWORD_PTR)HookThunkEnd - (DWORD_PTR)HookThunk;ctx->thunkBytes = (unsigned char *)malloc(thunkLen);memcpy(ctx->thunkBytes, HookThunk, (DWORD_PTR)HookThunkEnd - (DWORD_PTR)HookThunk); //char buf[1024];//memset(buf, 0, sizeof(buf));//for (int i = 0; i < thunkLen; i++) {//sprintf(buf + i * 3, "%02x ", ctx->thunkBytes[i]);//}//ODPRINTF(("thunk bytes: %s", buf));//fix addressmemcpy(ctx->thunkBytes + 1, &ctx->originalApiAddress, 4);offset = (DWORD)findContext - (DWORD)(ctx->thunkBytes + 10); //call findContextmemcpy(ctx->thunkBytes + 6, &offset, 4);offset = (DWORD)EnterCriticalSection - (DWORD)(ctx->thunkBytes + 33); //call EnterCriticalSectionmemcpy(ctx->thunkBytes + 29, &offset, 4);offset = (DWORD)removeJmp - (DWORD)(ctx->thunkBytes + 29 + 9); //call removeJmpmemcpy(ctx->thunkBytes + 25 + 9, &offset, 4);offset = (DWORD)ctx->originalApiAddress - (DWORD)(ctx->thunkBytes + 34 + 9); //call original apimemcpy(ctx->thunkBytes + 30 + 9, &offset, 4);offset = (DWORD)placeJmp - (DWORD)(ctx->thunkBytes + 40 + 9); //call placeJmpmemcpy(ctx->thunkBytes + 36 + 9, &offset, 4);offset = (DWORD)LeaveCriticalSection - (DWORD)(ctx->thunkBytes + 61); //call LeaveCriticalSectionmemcpy(ctx->thunkBytes + 57, &offset, 4);VirtualProtect(ctx->thunkBytes, thunkLen, PAGE_EXECUTE_READWRITE, &oldProtect);*originalFunc = ctx->thunkBytes;//memset(buf, 0, sizeof(buf));//for (int i = 0; i < thunkLen; i++) {//sprintf(buf + i * 3, "%02x ", ctx->thunkBytes[i]);//}//ODPRINTF(("thunk bytes: %s", buf));if (ctxListHeader == nullptr) {ctx->next = nullptr;ctx->prev = nullptr;ctxListHeader = ctx;ctxListTail = ctx;LeaveCriticalSection(&ctxLock);return true;}ctx->prev = ctxListTail;ctxListTail->next = ctx;ctxListTail = ctx;LeaveCriticalSection(&ctxLock);return true;}void UnHookApi(ApiAddress *hookedApi) {if (!ctxLockInitialized)  return;EnterCriticalSection(&ctxLock);HookContext *ctx = ctxListHeader;while (ctx != nullptr) {if (ctx->thunkBytes == *hookedApi) {//fix listHookContext *prev = ctx->prev,*next = ctx->next;if (ctxListHeader == ctx) ctxListHeader = next;if (ctxListTail == ctx) ctxListTail = prev;if (prev != nullptr) {prev->next = next;}if (next != nullptr) {next->prev = prev;}//copy back orignal bytesDWORD oldProtect = 0;VirtualProtect((LPVOID)ctx->originalApiAddress, JMPSIZE, PAGE_EXECUTE_READWRITE, &oldProtect);// assign read write protectionmemcpy(ctx->originalApiAddress, ctx->originalBytes, JMPSIZE); // set jump instruction at the beginning of the original functionVirtualProtect((LPVOID)ctx->originalApiAddress, JMPSIZE, oldProtect, NULL);    // reset protection*hookedApi = ctx->originalApiAddress;free(ctx->thunkBytes);DeleteCriticalSection(&ctx->cs);delete ctx;break;}ctx = ctx->next;}LeaveCriticalSection(&ctxLock);return;}



测试代码:

typedef int (WINAPI *PMessageBox) (_In_opt_ HWND    hWnd,_In_opt_ char * lpText,_In_opt_ char * lpCaption,_In_     UINT    uType);typedef int (WINAPI *PMessageBeep) (UINT mType);PMessageBox originalMessageBox = nullptr;PMessageBeep originalBeep = nullptr;int WINAPI MyMessageBox(_In_opt_ HWND    hWnd,_In_opt_ char * lpText,_In_opt_ char * lpCaption,_In_     UINT    uType) {ODPRINTF(("MyMessageBox called"));if (originalMessageBox) {return originalMessageBox(hWnd, "hacked", "lll", MB_OK);}return 0;}int WINAPI MyMessageBeep(UINT mType) {ODPRINTF(("MyMessageBeep called"));if (originalBeep) {return originalBeep(mType);}return 0;}HMODULE user32 = GetModuleHandleA("user32.dll");if (user32) {originalMessageBox = (PMessageBox)GetProcAddress(user32, "MessageBoxA");HookApi((ApiAddress *)&originalMessageBox, MyMessageBox);MessageBoxA(NULL, "test", "akk", MB_OK);originalBeep = (PMessageBeep)GetProcAddress(user32, "MessageBeep");HookApi((ApiAddress *)&originalBeep, MyMessageBeep);MessageBeep(0);UnHookApi((ApiAddress *)&originalMessageBox);UnHookApi((ApiAddress *)&originalBeep);MessageBoxA(NULL, "test again", "akk", MB_OK);}