生病并调养近半年了,也好久没有写博文了,真是有些抱歉,总有心有余力不足的感觉。
最近,遇到一种使用情形,需要覆盖Windows系统API的默认行为,而且可能不光涉及到Win32 x86,x64,可能还要涉及到wince平台。
本文只是一个简要的技术介绍,有兴趣的朋友可以一步步摸索下去。
首先是Win32 x86/x64下边,比如,我要覆盖'MessageBoxA'的调用(e.g.只是举例,实际可能更复杂一些)
典型的,效率也蛮高的方法是通过改写原API的函数地址,从而达到”覆盖“的目的。
示例如下:
- // hookdemo.cpp : Defines the entry point for the console application.
- //
-
- #include "stdafx.h"
-
- #define FLATJMPCODE_LENGTH 5 //x86 平坦内存模式下,绝对跳转指令长度
- #define FLATJMPCMD_LENGTH 1 //机械码0xe9长度
- #define FLATJMPCMD 0xe9
-
-
- //HOOK函数
- BOOL HookApi(LPVOID ApiFun,LPVOID HookFun)
- {
- BOOL IsSuccess = FALSE;
- DWORD TempProtectVar; //临时保护属性变量
- MEMORY_BASIC_INFORMATION MemInfo; //内存分页属性信息
-
- VirtualQuery(ApiFun,&MemInfo,sizeof(MEMORY_BASIC_INFORMATION));
-
- if(VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
- PAGE_READWRITE,&MemInfo.Protect)) //修改页面为可写
- {
- *(BYTE*)ApiFun = FLATJMPCMD;
- *(DWORD*)((BYTE*)ApiFun + FLATJMPCMD_LENGTH) = (DWORD)HookFun -
- (DWORD)ApiFun - FLATJMPCODE_LENGTH;
-
- VirtualProtect(MemInfo.BaseAddress,MemInfo.RegionSize,
- MemInfo.Protect,&TempProtectVar); //改回原属性
-
- IsSuccess = TRUE;
- }
-
- return IsSuccess;
- }
-
- //我的新API函数
- int __stdcall MyHookFn(HWND hwnd,char* sztext,char* szTitle,int stly)
- {
-
- return printf("%s - %s\n",sztext,"原API函数已被HOOK!"); //
- }
-
- void* MyMalloc(int size)
- {
- printf("size = %d\n", size);
- return NULL;
- }
-
- //我的新私有桩函数
- void MyHookFn2(char* szTxt)
- {
- char* pBuf = (char*)malloc(strlen(szTxt) + sizeof(char*) * 32);
-
- __try
- {
- sprintf(pBuf,"%s - %s",szTxt," 原函数已被HOOK!");
- printf("%s\n",pBuf);
- }
- __finally
- {
- free(pBuf);
- }
- }
-
- //私有测试函数
- void TestFn(char* szTitle)
- {
- printf("%s",szTitle);
- }
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- printf("hello\n");
-
-
- HMODULE hDll, hDll2;
- LPVOID OldFun, of2;
-
-
-
- __try
- {
- hDll = GetModuleHandle("User32.dll");
- OldFun = GetProcAddress(hDll,"MessageBoxA"); //要HOOK的API函数
- hDll2 = GetModuleHandle("msvcr90.dll");
- of2 = GetProcAddress(hDll2, "malloc");
- if(OldFun)
- {
- if(HookApi(OldFun,MyHookFn)) //如果HOOK成功
- MessageBoxA(0,"call Api MessageBox","Is Hookd?",MB_OK); //调用原API,测试HOOK
-
- if (HookApi(of2, MyMalloc))
- malloc(1);
-
- if(HookApi(TestFn,MyHookFn2))
- TestFn("Private Function Hook Test"); //调用私有函数,测试HOOK
- }
- }
- __finally
- {
- if(hDll)
- FreeLibrary(hDll);
- }
-
- return 0;
- }
在release模式下,malloc也会被覆盖。
这样,我的目的还真能达到,我可以随心所欲的覆盖malloc和free,从而让当前进程使用我定制的malloc和free。
这种方式是通过改写函数调用地址的方式来实现的,在x86/x64下边运行没什么障碍,可惜在WinCE平台下边,无法实现。
WinCE下边,上边那些API函数的地址并不是明确固定的。需要专门的Hook API来控制。多年前微软自行开发了套Hook工具库detour,相信这个可以在系统API的拦截和覆盖应用里派上用场。
由于我的场景里头,只需要定制new和delete,所以还没有使用专门的Hook API来实现,而是使用了全局重载操作符new 和 delete来实现,虽然有一定风险,但最终也还是可行的。
在Unix平台下,可以使用PRE_LOAD DLL的相关编译指令,优先加载你定制的DLL(在这个DLL里实现要覆盖的API)来简单实现API覆盖,那是非常简单的技术。有兴趣的可以GOOGLE一下。