API HOOK介绍 【转】
什么是“跨进程 API Hook”?
众所周知Windows应用程序的各种系统功能是通过调用API函数来实现。API Hook就是给系统的API附加上一段小程序,它能监视甚至控制应用程序对API函数的调用。所谓跨进程也就是让自己的程序来控制别人程序的API调用了。
API Hook 理论
通过对Win32 PE文件的分析(如果你还不熟悉PE文件格式,可以看看Iczelion的PE教程或者LUEVELSMEYER的<<The PE File Format>>)。我们知道在PE文件中的IMPORT TABLE内存储着API函数的很多信息。其中包括API的函数名,调用地址等等。而操作系统在执行PE文件时会先将其映射到内存中。在映射的同时还会把当前版本操作系统中API函数的入口地址写入IMPORT TABLE中一组与API调用相关的结构体内,用于该应用程序的API调用。 当应用程序调用API时,他会在自己内存映像里寻找API的入口地址,然后执行CALL指令。如此一来,我们通过修改应用程序内存映像的IMPORT TABLE中API函数的入口地址,就可以达到重定向API的目的。将API地址改为我们自己函数的地址,这样我们的函数就可以完成对API的监视和控制了。
API Hook 的实现
/* 1 */HANDLE hCurrent = GetModuleHandle(NULL);
/* 2 */IMAGE_DOS_HEADER *pidh;
/* 3 */IMAGE_NT_HEADERS *pinh;
/* 4 */IMAGE_DATA_DIRECTORY *pSymbolTable;
/* 5 */IMAGE_IMPORT_DESCRIPTOR *piid; /* 6 */pidh = (IMAGE_DOS_HEADER *)hCurrent;
/* 7 */pinh = (IMAGE_NT_HEADERS *)((DWORD)hCurrent + pidh->e_lfanew);
/* 8 */pSymbolTable = &pinh->OptionalHeader.DataDirectory[1];
/* 9 */piid =(IMAGE_IMPORT_DESCRIPTOR *)((DWORD)hCurrent + pSymbolTable->VirtualAddress);
/*10 */do {
/*11 */ IMAGE_THUNK_DATA *pitd,*pitd2;
/*12 */ pitd = (IMAGE_THUNK_DATA *)((DWORD)hCurrent + piid->OriginalFirstThunk);
/*13 */ pitd2 = (IMAGE_THUNK_DATA *)((DWORD)hCurrent + piid->FirstThunk);
/*14 */ do {
/*15 */ IMAGE_IMPORT_BY_NAME *piibn;
/*16 */ piibn = (IMAGE_IMPORT_BY_NAME *)((DWORD)hCurrent + *((DWORD *)pitd));
/*17 */ PROC *ppfn = (PROC *)(pitd2->u1.Function);
/*18 */ if (!strcmp("MessageBoxW",(char *)piibn->Name)) {
/*19 */ oldMsg = (MsgBoxType)(ppfn);
/*20 */ DWORD addr = (DWORD)MyMessage;
/*21 */ DWORD written = 0;
/* 改变内存读写状态 */
/*22 */ DWORD oldAccess;
/*23 */ VirtualProtect(&pitd2->u1.Function,sizeof(DWORD),PAGE_WRITECOPY,&oldAccess);
/*24 */ APIAddress = (DWORD)&pitd2->u1.Function;
/* 向内存映像写入数据 */
/*25 */ WriteProcessMemory(GetCurrentProcess(),&pitd2->u1.Function, &addr,sizeof(DWORD), &written);
/*26 */ }
/*27 */ pitd++;pitd2++;
/*28 */ } while (pitd->u1.Function); /*29 */ piid++;
/*30 */} while (piid->FirstThunk + piid->Characteristics
+ piid->ForwarderChain + piid->Name + piid->TimeDateStamp);
分析:
寻觅IMPORT TALBE
在/*1*/中我们使用GetModuleHandle(NULL)来返回当前进程在内存中映像的基地址。但这个值在文档中仅仅被描述为"a module handle for the specified module",虽然他确实是进程内存映像的基地址。如果你不太放心的话也可以使用,GetModuleInformation函数来获得基地址,只不过你要额外包含psapi.h和psapi.lib了(这个库在VC6里没有,所以我就没有用这个函数了)。在/* 6 */里我们先找到IMAGE_DOS_HEADER结构,他的起始地址就是映像的基地址。/*7*/通过IMAGE_DOS_HEADER给出的PE文件头的偏移量,找到IMAGE_NT_HEADERS结构。顺藤摸瓜,IMAGE_NT_HEADERS里的OptionalHeader中的DataDirectory数组里的第二个元素正是指向我们想要的IMPORT TABLE的地址。在/*9*/中我们将其转化为一个IMAGE_IMPORT_DESCRIPTOR的结构指针存入piid中。
替换的API函数入口地址
在/*12*/和/*13*/中我们分别取得OriginalFirstThunk和FirstThunk结构,用于以后得到API函数的名称和入口地址。/*10*/的do循环让我们遍历每一个IMAGE_IMPORT_DESCRIPTOR结构也就是应用程序引用的每个DLL。在/*14*/的循环中我们遍历DLL中的IMAGE_THUNK_DATA结构来一一查询API的信息。/*16*/中我们将OriginalFirstThunk转换为IMAGE_IMPORT_BY_NAME结构用于获得API函数的名称进行比对。在/*18*/我们找到MessageBoxW函数之后,在/*19*/保存其原始入口地址便于以后恢复时使用。在/*23*/我们需要用VirtualProtect改变一下内存区域的读写性,因为一般应用程序的映像都是只读的,直接写入会造成一个非法访问的异常出现。在/*25*/我们写入自己函数的地址。
这样就基本完成一个API函数的重定向。
其他
恢复函数的API入口地址相对比较简单。只要把保存的值再写回去就可以了。上面的程序中/*24*/我用APIAddress保存了存有MessageBoxW入口地址的地方的地址,便于以后调用WriteProcessMemory恢复时使用。
跨进程理论
我们要用自己的函数来替代别人程序里的API函数,但我们的函数与别人的程序处于不同的进程空间内啊。不同的进程空间是不能相互调用函数的。因此我们要想办法把自己的函数放入别人的进程空间去。这时我们就需要使用DLL injection技术了。如果你对她还不是十分熟悉的话,建议看看Jeffrey Richter大师的<<Programming Applications for Microsoft Windows>>,也可以参考陈宽达先生的<<C++ Builder深度历险>>。
简而言之,DLL injection就是想办法让对方的进程加载我们的一个DLL程序,把需要替换的函数放在我们这个DLL里。如此一来,我们的函数就进入了别人的进程空间了。DLL injection方法很多,Richter大师在书中对各方法利弊有详细解释,陈宽大先生的书中也有深入的分析。我在这里使用SetWindowsHookEx函数来达到目的。主要有这几个原因: 1, 不用重新启动系统,调试方便。2, 可以利用消息循环机制进行两个进程之间的通信,可以较好的掌握Hook的状态。便于安装与卸载。
SetWindowsHookEx之所以能完成DLL injection是因为它要给一个应用程序某个环节加上一个Hook,而Hook就要有Hook Procedure也就是Hook函数。如果这个Hook函数在一个DLL中,那么系统就会把这个DLL加载到SetWindowsHookEx的目标进程上。从而也就达到了我们DLL injection的目的了。当然这里我们会用WH_GETMESSAGE的Hook进行injection,因为这个Hook可以用来监视目标进程的消息循环方便我们的进程与目标进程通信。
跨进程的实现和几点注意
/* DllPart.Dll */
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef (WINAPI *MsgBoxType)(HWND,LPCWSTR,LPCWSTR,UINT);
MsgBoxType oldMsg; /*API原入口地址*/
DWORD APIAddress; /*存储API入口地址的地方的地址*/
int WINAPI MyMessage(HWND hWnd ,LPCWSTR M1,LPCWSTR M2, UINT M3) {
/* 这是用来替换的函数 */
return oldMsg(hWnd,buf,M2,MB_OK);
}
const char szApp[] = "DllPart.dll";
HHOOK hHook; /*Hook的句柄*/
HMODULE hInst; /*DLL 模块句柄,用于SetWindowsHookEx函数*/
HWND hTarget; /*目标窗口句柄*/
/*DLL 入口*/
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID lpvReserved)
{
hInst = inst;
switch (reason) {
case DLL_PROCESS_ATTACH:
/*调试信息,表示DLL已经加载*/
MessageBox(NULL,"DLL_PROCESS_ATTACH",szApp,MB_OK);
break;
case DLL_PROCESS_DETACH:
/*调试信息,表示DLL已经卸载*/
MessageBox(NULL,"DLL_PROCESS_DETACH",szApp,MB_OK);
break;
}
return true;
}
/*显示GetLastError的信息*/
void showerr(const char *m) {
char message[255];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,GetLastError()
,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),message,255, 0);
MessageBox(NULL,message,m,MB_OK);
}
//-----------------------
void UnHookApi() {
/*卸载API Hook用*/
}
void HookApi() {
/*加载API Hook同上面介绍的函数一样*/
}
//-----------------------
/*用于WH_GETMESSAGE的Hook Procedure*/
LRESULT CALLBACK GetMsgProc(int nCode,WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
MSG *msg = (MSG *)lParam;
if (msg->message == WM_CHAR) {
if (msg->wParam == 'h') HookApi();
if (msg->wParam == 'u') UnHookApi();
}
}
return CallNextHookEx(hHook,nCode,wParam,lParam);
}
extern "C" __declspec(dllexport) SetAPIHook(HWND handle) {
DWORD ThreadId = GetWindowThreadProcessId(handle, NULL);
hTarget = handle;
MessageBox(NULL, "Enabling CallWndProc Hook", szApp, MB_OK);
hHook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hInst,ThreadId);
if (hHook) {
MessageBox(NULL,"Hook OK!", szApp, MB_OK);
} else {
showerr("SetWindowsHookEx");
}
}
extern "C" __declspec(dllexport) UnHookAPIHook() {
MessageBox(NULL, "Disenabling CallWndProc Hook", szApp, MB_OK);
if (UnhookWindowsHookEx(hHook)) {
MessageBox(NULL,"UnHook OK!", szApp, MB_OK);
} else {
showerr("UnHookWindowsHookEx");
}
}
分析
几个需要注意的问题
SetAPIHook和UnHookAPIHook是我们自己进程调用的用来加载WH_GETMESSAGE Hook的函数。由于我们的程序要用LoadLibrary加载这个Dll因此这两个函数要用__declspec(dllexport)修饰,使其成为导出函数,才能被GetAddressProc函数找到。加上 extern "C"是让编译器使用C语言编码方式。因为C++编译器会进行Dynamic Binding(C++函数重载的实现),将函数的参数类型附加到名称上。是函数的导出名看起来像SetAPIHook@XYTZX之类的,不利于GetAddressProc进行引用。因此使用extern "C"让编译器不使用Dynamic Binding,自然使用extern"C"的函数也就不能被重载了。
不要忘记在GetMsgProc最后要调用CallNextHookEx函数,保证Hook链的完整性。
一定要在Hook Procedure中调用HookApi和UnHookApi函数,因为保存API入口地址的地方在目标进程中,你必须在目标进程的进程空间内完成卸载操作,不能在UnHookAPIHook或是SetAPIHook函数中调用,因为UnHookAPIHook是我们的进程调用的,因此在我们的进程空间中。在这里使用UnHookApi会造成一个非法访问的错误。而使用HookApi会给自己的DLL加上API Hook。
SetWindowsHookEx的最后参数是ThreadId不是Handle,因此要通过调用GetWindowThreadProcessId转换一下。
在跨进程API HOOK时可能用到的其他技术
主进程与目标进程的信息交互和共享
由于使用了WH_GETMESSAGE钩子我们可以利用Windows消息机制实现进程间通讯。需要注意的是应该使用PostThreadMessage来发送让WH_GETMESSAGE得到的消息而不是SendMessage或者PostMessage,因为后两个是用来给窗口发送消息的。而我们的WH_GETMESSAGE是Hook在线程上面的,因此需使用PostThreadMessage.
传递不太大的数据可以使用WM_COPYDATA消息来进行。同样也应该注意,如果使用MFC的窗口过程获得消息就需要用SendMessage发送了。WM_COPYDATA的使用相对简单可以参考MSDN的文档。也可以参考附录里的程序Hook.cpp的showerr函数部分。
如果传递较大的数据或者希望数据共享比较方便可以开辟共享内存来进行数据共享。这里简单分析一下使用共享内存的代码
HANDLE hMap;
switch (reason) {
case DLL_PROCESS_ATTACH:
/*创建/打开共享内存区域*/
hMap = CreateFileMapping((HFILE *)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(GLOBALDATA),ID_MAP);
pg_data = (GLOBALDATA*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0 ,0 ,0);
if (!pg_data) {
MessageBox(NULL,"无法建立共享内存,程序终止!",szApp,MB_OK);
if (hMap) {
CloseHandle(hMap);
hMap = NULL;
return 0;
}
}
pg_data->hInst = hInst;
showerr("共享内存映像文件");
showerr("DLL装载中...",FALSE);
break;
case DLL_PROCESS_DETACH:
if (pg_data) {
UnmapViewOfFile(pg_data);
pg_data = NULL;
}
if (hMap) {
CloseHandle(hMap);
hMap = NULL;
}
break;
}
上面的代码通过CreateFileMapping建立共享区域。将其第一个参数设置为0xFFFFFFFF使其能创建一个内存共享区域而不是文件。并标记为可读写的(PAGE_READWRITE).其大小为我们定义的结构体GLOBALDATA的大小。最后的ID_MAP是一个用来标示这个区域的字符串。打开或者创建完共享区域后,我们用MapViewOfFile来获得这个区域的地址。之后就可以直接使用pg_data来操作共享区域了。不要忘记在DLL退出的时候安全的删除共享区域释放内存。
消息等待与安全卸载
在我们卸载WH_GETMESSAGE钩子之前必须先把目标程序的API调用恢复正常。我们不能再调用UnHookApi之后就立刻调用UnhookWindowsHookEx,因为很有可能UnHookApi还没来得急完成API入口地址的恢复操作,WH_GETMESSAGE钩子就已经被卸载了。因此需要等待一段时间,等UnHookApi完成了恢复操作在调用UnhookWindowsHookEx。以防错误发生。
extern "C" __declspec(dllexport) void UnHookAPIHook() {
/*向目标线程发送消息进行API UNHOOK*/
PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOK,(WPARAM)GetCurrentThreadId(),0);
showerr("WM_DISABLEAPIHOOK");
/*等待目标进程返回WM_UNHOOKOK消息,确认可以将WH_GETMESSAGE的HOOK去掉*/ MSG Msg;
do {
GetMessage(&Msg,NULL,0,0);
}while(Msg.message != WM_UNHOOKOK);
UnhookWindowsHookEx(pg_data->hHook);
PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOKOK,(WPARAM)GetCurrentThreadId(),0);
showerr("UnHookWindowsHookEx");
}
上面的代码中我们使用一个含有GetMessage的循环来等待消息的到达,一旦UnHookApi完成他就会发送WM_UNHOOKOK消息。等我们接收到消息确认一切安全了在来卸载WH_GETMESSAGE钩子。
弄清消息对象
我们一定要清楚代码是在主程序进程空间中执行的还是在目标程序进程空间中执行的。像上面的UnHookAPIHook函数就是通过主程序调用的,因此在主程序进程空间中执行。这样一来用来恢复目标程序API信息的UnHookApi完成后就应该向主程序发送消息,而不是目标程序。
目标进程加载了其他DLL
如果目标进程动态加载了其他的DLL文件,我们必须监视LoadLibrary函数。保证DLL中的API入口地址也被正确修改。防止出现混乱的情况。我从LoadLibrary获得DLL的路径用于GetModuleHandle来取得他的ImageBase的地址。
不知道文章写的如何,希望大家能多给些批评意见。发现问题我马上改正
Email: detrox@yang.com.cn
参考资料
<<Programming Applications for Microsoft Windows>>, Jeffrey Richter, Microsoft Press
<<C++ Builder 深度历险>>,陈宽达,华中科大出版社
<<The PE File Format>>, LUEVELSMEYER
<<Iczelion's PE Tutorial>>, Iczelion
附:跨进程APIHook的例子
先打开一个记事本程序并输入几个字符,运行下面的程序,加载APIHook.之后在记事本的文件菜单中选择新建就会看到API Hook将MessageBoxW 函数Hook的结果了.
代码在WinXP SP1 + VC6.0测试成功。
下载源代码
(特别感谢老罗的代码着色器,比我自己那个好多了)
这是DLL的程序,Hook.dll
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mydef.h" const char szApp[] = "Hook.dll"; /*应用程序名称*/
HANDLE hMap;/*在共享内存映像的句柄*/
GLOBALDATA *pg_data; /*在共享内存中的全局数据*/
LRESULT CALLBACK GetMsgProc(int,WPARAM, LPARAM); /*显示GetLastError指出的错误*/
void showerr(const char *m, BOOL GetError = TRUE) {
char message[127];
char buf[255];
COPYDATASTRUCT cds;
if (GetError)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0
,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),message,127, 0);
else
*message = 0;
if (GetCurrentThreadId() != pg_data->idMain)
sprintf(buf,"目标程序空间DLL: %-30s [%-40s]",m, message);
else
sprintf(buf,"主程序空间DLL : %-30s [%-40s]",m, message);
cds.lpData = buf;
cds.cbData = sizeof(buf);
cds.dwData = 0;
SendMessage(pg_data->hWndMain,WM_COPYDATA,(WPARAM)pg_data->hWndTarget,(LPARAM)&cds);
SetLastError(0);
}
int WINAPI MyMessageBoxW(HWND hWnd ,LPCWSTR M1,LPCWSTR M2, UINT M3) {
wchar_t buf[255];
swprintf(buf,L"!!这个窗口的API被Hook了!!\nHWND: 0x%08X\nMessage: %s\nCaption: %s"
,(DWORD *)hWnd
, M1
, M2);
pg_data->oldAPIFunction(hWnd,buf,M2,MB_OK);
return pg_data->oldAPIFunction(hWnd,M1,M2,M3);
} /*DLL 入口函数*/
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason,LPVOID lpvReserved)
{
switch (reason) {
case DLL_PROCESS_ATTACH:
/*创建/打开共享内存区域*/
hMap = CreateFileMapping((HFILE *)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(GLOBALDATA),ID_MAP);
pg_data = (GLOBALDATA*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0 ,0 ,0);
if (!pg_data) {
MessageBox(NULL,"无法建立共享内存,程序终止!",szApp,MB_OK);
if (hMap) {
CloseHandle(hMap);
hMap = NULL;
return 0;
}
}
pg_data->hInst = hInst;
showerr("共享内存映像文件");
showerr("DLL装载中...",FALSE);
break;
case DLL_PROCESS_DETACH:
showerr("DLL卸载中...",FALSE);
if (pg_data) {
UnmapViewOfFile(pg_data);
pg_data = NULL;
}
if (hMap) {
CloseHandle(hMap);
hMap = NULL;
}
break;
}
return true;
}
/*卸载API Hook*/
void UnHookApi() {
DWORD written = 0;
DWORD oldaddrAPIFunction = (DWORD)pg_data->oldAPIFunction;
WriteProcessMemory(GetCurrentProcess(),(DWORD *)pg_data->addrAPIEntryPoint
, &oldaddrAPIFunction,sizeof(DWORD), &written);
showerr("WriteProcessMemory on UnHook");
/*向主线程发送 API UNHOOK 处理完毕的消息*/
PostThreadMessage(pg_data->idMain,WM_UNHOOKOK,0,0);
} /*加载API Hook*/
void HookApi(const char* szApiName, tAPIFunction newAddr, DWORD ImageBase) {
/*这段代码请参考文章中的分析*/
IMAGE_DOS_HEADER *pidh;
IMAGE_NT_HEADERS *pinh;
IMAGE_DATA_DIRECTORY *pSymbolTable;
IMAGE_IMPORT_DESCRIPTOR *piid;
pidh = (IMAGE_DOS_HEADER *)ImageBase;
pinh = (IMAGE_NT_HEADERS *)((DWORD)ImageBase + pidh->e_lfanew);
pSymbolTable = &pinh->OptionalHeader.DataDirectory[1];
piid =(IMAGE_IMPORT_DESCRIPTOR *)((DWORD)ImageBase + pSymbolTable->VirtualAddress); do {
IMAGE_THUNK_DATA *pitd_org,*pitd_1st;
pitd_org = (IMAGE_THUNK_DATA *)((DWORD)ImageBase + piid->OriginalFirstThunk);
pitd_1st = (IMAGE_THUNK_DATA *)((DWORD)ImageBase + piid->FirstThunk);
do {
IMAGE_IMPORT_BY_NAME *piibn;
piibn = (IMAGE_IMPORT_BY_NAME *)((DWORD)ImageBase + *((DWORD *)pitd_org));
PROC *pAPIFunction = (PROC *)(pitd_1st->u1.Function);
if (!strcmp(szApiName,(char *)piibn->Name)) {
DWORD addrNewAPIFunction = (DWORD)MyMessageBoxW;
DWORD written = 0;
DWORD oldAccess; pg_data->oldAPIFunction = (tAPIFunction)(pAPIFunction);
/*Change Memeory State*/
VirtualProtect(&pitd_1st->u1.Function,sizeof(DWORD),PAGE_WRITECOPY,&oldAccess);
showerr("VirtualProtect");
pg_data->addrAPIEntryPoint = (DWORD)&pitd_1st->u1.Function;
/*Write Process Memory*/
WriteProcessMemory(GetCurrentProcess(),&pitd_1st->u1.Function
, &addrNewAPIFunction,sizeof(DWORD), &written);
showerr("WriteProcessMemory on Hook");
}
pitd_org++;
pitd_1st++;
} while (pitd_1st->u1.Function);
piid++;
} while (piid->FirstThunk + piid->Characteristics
+ piid->ForwarderChain + piid->Name + piid->TimeDateStamp);
} //-----------------------
extern "C" __declspec(dllexport) BOOL SetAPIHook(HWND _target) {
pg_data->hHook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,pg_data->hInst,pg_data->idTarget);
showerr("SetWindowsHookEx");
/*向目标线程发送消息进行API HOOK*/
if (pg_data->hHook) {
PostThreadMessage(pg_data->idTarget,WM_ENABLEAPIHOOK,0,0);
} else {
return FALSE;
}
showerr("WM_ENABLEAPIHOOK");
return TRUE;
} extern "C" __declspec(dllexport) void UnHookAPIHook() {
/*向目标线程发送消息进行API UNHOOK*/
PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOK,(WPARAM)GetCurrentThreadId(),0);
showerr("WM_DISABLEAPIHOOK");
/*等待目标进程返回WM_UNHOOKOK消息,确认可以将WH_GETMESSAGE的HOOK去掉*/
MSG Msg;
do {
GetMessage(&Msg,NULL,0,0);
}while(Msg.message != WM_UNHOOKOK); UnhookWindowsHookEx(pg_data->hHook);
PostThreadMessage(pg_data->idTarget,WM_DISABLEAPIHOOKOK,(WPARAM)GetCurrentThreadId(),0);
showerr("UnHookWindowsHookEx");
} LRESULT CALLBACK GetMsgProc(int nCode,WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
MSG *msg = (MSG *)lParam;
if (msg->message == WM_ENABLEAPIHOOK) {
HookApi("MessageBoxW",MyMessageBoxW,(DWORD)GetModuleHandle(NULL));
}
if (msg->message == WM_DISABLEAPIHOOK) {
UnHookApi();
}
if (msg->message == WM_DESTROY) {
showerr("目标进程退出!",FALSE);
}
}
return CallNextHookEx(pg_data->hHook,nCode,wParam,lParam);
}
这个是主程序的关键部分 HookGui.cpp
用MFC建立一个窗口程序,包含两个按钮和一个ListBox
typedef void (*PUnHookAPIHook)();
typedef BOOL (*PSetAPIHook)(HWND);
HMODULE hDll = NULL;
HWND hNotePad = NULL;
PSetAPIHook SetAPIHook;
PUnHookAPIHook UnHookAPIHook;
GLOBALDATA *pg_data; /*在共享内存中的全局数据*/
HANDLE hMap; /*在共享内存映像的句柄*/
int CHookGUIDlg::showerr(const char* m) {
char message[127];
char buf[255];
int errId = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0
,errId,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),message,127, 0);
sprintf(buf,"主程序 : %-30s [%-40s]\n",m, message);
c_List1.InsertString(c_List1.GetCount(),buf);
UpdateData(FALSE);
return errId;
} void CHookGUIDlg::OnSetapihook()
{
// TODO: Add your control notification handler code here
hDll = LoadLibrary("Hook.dll");
showerr("LoadLibrary");
hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,false,ID_MAP);
showerr("OpenFileMapping");
pg_data = (GLOBALDATA *)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0);
showerr("MapViewOfFile");
if (!pg_data) {
MessageBox("不能打开共享内存程序终止!");
return;
}
SetAPIHook = (PSetAPIHook)GetProcAddress(hDll,"SetAPIHook");
showerr("GetProcAddress-SetAPIHOOK");
pg_data->hWndTarget = ::FindWindow("NotePad",NULL);
pg_data->hWndMain = m_hWnd;
pg_data->idMain = GetWindowThreadProcessId(m_hWnd,NULL);
pg_data->idTarget = GetWindowThreadProcessId(pg_data->hWndTarget,NULL);
if (!showerr("FindWindow")) {
if (SetAPIHook) {
if (SetAPIHook(hNotePad))
PostThreadMessage(pg_data->idTarget
, WM_SETCALLERID,(LPARAM)GetCurrentThreadId(),0);
else {
MessageBox("SetWindowHookEx时出错,程序终止!");
return;
}
} else {
MessageBox("无法取得SetAPIHook函数!程序终止!");
}
} else {
MessageBox("内存中没有找到NOTEPAD.EXE");
}
c_SetApiHook.EnableWindow(FALSE);
c_UnsetApiHook.EnableWindow();
} void CHookGUIDlg::OnUnsetapihook()
{
// TODO: Add your control notification handler code here
if (hDll) {
if ( !IsWindow(pg_data->hWndTarget)) {
MessageBox("目标进程不在内存中");
return;
}
UnHookAPIHook = (PUnHookAPIHook)GetProcAddress(hDll,"UnHookAPIHook");
showerr("GetProcAddress-UnHookAPIHook");
if (UnHookAPIHook)
UnHookAPIHook();
FreeLibrary(hDll);
showerr("FreeLibrary");
hDll = NULL;
} else {
MessageBox("请先加载DLL");
}
c_SetApiHook.EnableWindow();
c_UnsetApiHook.EnableWindow(FALSE);
} void CHookGUIDlg::OnOK()
{
// TODO: Add extra validation here
if (hDll) {
OnUnsetapihook();
}
CDialog::OnOK();
} BOOL CHookGUIDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
// TODO: Add your message handler code here and/or call default
c_List1.InsertString(c_List1.GetCount(),(char *)pCopyDataStruct->lpData);
return CDialog::OnCopyData(pWnd, pCopyDataStruct);
} http://www.cnblogs.com/feng801/archive/2010/08/31/1813763.html
API HOOK介绍 【转】的更多相关文章
- 程序破解之 API HOOK技术 z
API HOOK,就是截获API调用的技术,在程序对一个API调用之前先执行你的函数,然后根据你的需要可以执行缺省的API调用或者进行其他处理,假设如果想截获一个进程对网络的访问,一般是几个socke ...
- API HOOK
API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...
- API HOOK技术
API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...
- API HOOK和PE文件的关系
api hook技术的难点,并不在于hook技术,而在于对PE结构的学习和理解.如何修改api函数的入口地址?这就需要学习pe可执行文件(.exe,.dll等)如何被系统映射到进程空间中,这需要学习p ...
- Windows API Hook
原文地址:http://blog.sina.com.cn/s/blog_628821950100xmuc.html 原文对我的帮助极大,正是由于看了原文.我才学会了HOOK.鉴于原文的排版不是非常好, ...
- API Hook基本原理和实现
API Hook基本原理和实现 2009-03-14 20:09 windows系统下的编程,消息message的传递是贯穿其始终的.这个消息我们可以简单理解为一个有特定意义的整数,正如我们看过的老故 ...
- Odoo 二次开发教程(五)-新API的介绍与应用
[关于odoo新API的介绍,Internet上资料很少,或者不够完整详实,这会对初学者造成很大的困惑,本篇的目的就是希望能帮助新手了解新API的大概] odoo 新api的实现是借助于python装 ...
- Tyk API网关介绍及安装说明
Tyk API网关介绍及安装说明 Tyk是一个开源的轻量级API网关程序. 什么是API网关 API网关是一个各类不同API的前置服务器.API网关封装了系统内部架构,对外提供统一服务.此外还可以实现 ...
- spring3 的restful API RequestMapping介绍
原文链接:http://www.javaarch.net/jiagoushi/694.htm spring3 的restful API RequestMapping介绍 在spring mvc中 @R ...
随机推荐
- 【t042】炮击坦克
Time Limit: 1 second Memory Limit: 128 MB [问题描述] 在一个坐标轴上,有M辆坦克,第i辆坦克在时刻0处于pos[i](pos[i]>0),speed[ ...
- 【SSH2(理论+实践)】--图说Struts2的执行
前几篇文章讨论了有关Struts2的核心机制及一些基础,但同一时候也遗留下了非常多问题.这些问题主要是针对Struts2的一些使用技巧的,该篇文章将会针对Struts2的使用技巧进行讨论, ...
- amazeui中css组件、js组件、web组件的区别
amazeui中css组件.js组件.web组件的区别 一.总结 一句话总结: 1.可直接像调用js插件那样调用:在AmazeUI(妹子UI)中,Web组件可以不编写模板而直接使用,若如此,则与JS插 ...
- KindEditor4.1.10,支持粘贴图片
转载自https://blog.csdn.net/jimmy0021/article/details/73251406 我已经忘记我是不是从这个博主的那里找到的解决kindeditor粘贴图片的方法了 ...
- 【非常高%】【codeforces 733A】Grasshopper And the String
time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...
- 【50.49%】【codeforces 731B】Coupons and Discounts
time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...
- LVS实现负载均衡原理及安装配置
LVS实现负载均衡原理及安装配置 负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均衡设备F ...
- javascript的回调函数 同步 异步
后一个任务等待前一个任务结束再执行.程序执行顺序与任务排列顺序一致的,同步的. 参考: http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%B ...
- Android开发小知识
修改Android app图标(Android Studio) 1. res\drawable 放置icon.png(此图片是你需要修改的图标); 2. 修改AndroidManifest.xml ...
- 深入理解ArrayList与LinkedList的区别
一.先来看看ArrayList与LinkedList 在JDK中所在的位置 从图中可以看出,ArrayList与LinkedList都是List接口的实现类,因此都实现了List的所有未实现的方法,只 ...