什么是“跨进程 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介绍 【转】的更多相关文章

  1. 程序破解之 API HOOK技术 z

    API HOOK,就是截获API调用的技术,在程序对一个API调用之前先执行你的函数,然后根据你的需要可以执行缺省的API调用或者进行其他处理,假设如果想截获一个进程对网络的访问,一般是几个socke ...

  2. API HOOK

    API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...

  3. API HOOK技术

    API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...

  4. API HOOK和PE文件的关系

    api hook技术的难点,并不在于hook技术,而在于对PE结构的学习和理解.如何修改api函数的入口地址?这就需要学习pe可执行文件(.exe,.dll等)如何被系统映射到进程空间中,这需要学习p ...

  5. Windows API Hook

    原文地址:http://blog.sina.com.cn/s/blog_628821950100xmuc.html 原文对我的帮助极大,正是由于看了原文.我才学会了HOOK.鉴于原文的排版不是非常好, ...

  6. API Hook基本原理和实现

    API Hook基本原理和实现 2009-03-14 20:09 windows系统下的编程,消息message的传递是贯穿其始终的.这个消息我们可以简单理解为一个有特定意义的整数,正如我们看过的老故 ...

  7. Odoo 二次开发教程(五)-新API的介绍与应用

    [关于odoo新API的介绍,Internet上资料很少,或者不够完整详实,这会对初学者造成很大的困惑,本篇的目的就是希望能帮助新手了解新API的大概] odoo 新api的实现是借助于python装 ...

  8. Tyk API网关介绍及安装说明

    Tyk API网关介绍及安装说明 Tyk是一个开源的轻量级API网关程序. 什么是API网关 API网关是一个各类不同API的前置服务器.API网关封装了系统内部架构,对外提供统一服务.此外还可以实现 ...

  9. spring3 的restful API RequestMapping介绍

    原文链接:http://www.javaarch.net/jiagoushi/694.htm spring3 的restful API RequestMapping介绍 在spring mvc中 @R ...

随机推荐

  1. [CortexM0--stm32f0308]Option Byte

    问题描写叙述 option byte,算是IC中比較简单的功能,就是用户能够写入数据,对IC的某些功能进行配置.而IC在reset时,会载入当中的内容,进行推断,从而使用户的配置生效. option ...

  2. 浏览器jsp、html之间的关系

    浏览器html.jsp之间的关系 1.HTML能直接通过浏览器打开,而JSP仅仅能公布到Tomcatserver才干打开. 2.HTML中不能嵌套Java代码,而JSP中能够嵌套Java代码: 3.H ...

  3. [NPM] Run npm scripts when files change with onchange

    In this lesson we will look at how we can setup our npm scripts to execute when the file system has ...

  4. Oracle数据库零散知识04 --- 其常用内置函数

    1,数值函数 Select abs(-9),--9 绝对值 Mod(5,3),--2 余数 Sign(-9),-- -1 标记 Ceil(9.4),--10 Floor(9.8),--9 Sqrt(1 ...

  5. Html5实现手机九宫格password解锁功能

    HTML5真的是非常强大,前端时间看到一个canvas实现九宫格的password解锁. 今天抽空模仿了一个,特定分享一下. 效果截图例如以下: 效果看起来还不错吧! 源代码例如以下: <!DO ...

  6. 【物理】概念的理解 —— Phase(相位)

    Phase is the position of a point in time (an instant) on a waveform cycle. 相位指的是波形周期中点在某一时刻的位置.Phase ...

  7. combobox添加选项

    如果不需要绑定字段,只需要显示列表 cmb_Type.Items.AddRange(new object[] {"姓名","年龄","性别" ...

  8. 解决gdb 调试 core 文件函数名显示为问号的问题

    关于gdb调试core文件总是一堆问号的问题 问题描写叙述:已经在编译选项中增加了-g,可是查看core文件时.还是一堆问号,使用的命令为:gdb -c core 解决方式:因为gdb -c core ...

  9. [转载]Android Studio卡在refreshing gradle project的原因和快速解决办法

    转载请注明出处:http://blog.csdn.net/u013005791/article/details/60143750 这个问题遇到过很多次,网上也有很多解决办法,但是好像都没有发现refr ...

  10. Codeforces 106D Treasure Island 预处理前缀+暴力(水

    主题链接:点击打开链接 意甲冠军: 特定n*m矩阵 # 是墙 . 和字母是平地 最多有26个字母(不反复出现) 以下k个指令. 每一个指令代表移动的方向和步数. 若以某个字母为起点,依次运行全部的指令 ...