您的位置:首页 > 其它

hook api 保护进程

2008-10-18 14:31 656 查看
hook api 保护进程

用过卡巴斯基的朋友都知道,卡巴斯基的进程是无法杀掉的,在任务管理器中杀卡巴进程的话,会弹

出一个消息框提示拒绝访问!那么这是怎么实现的呢?很简单,就是使用了HOOK API的方法。

兄弟门,做毒别做地根武汉那小子那么傻,有本事你也得搁着点,别去进局子!

我用delphi来写程序好了,先写个dll。

const
PRG_NAME = 'ddos.exe';

var TerminateProcessNext : function (processHandle, exitCode: dword) : bool; stdcall;
NtTerminateProcessNext : function (processHandle, exitCode: dword) : dword; stdcall;

{$R *.res}
function ThisIsOurProcess(processHandle: dword) : boolean;
var pid : dword;
arrCh : array [0..MAX_PATH] of char;
begin
pid := ProcessHandleToId(processHandle);
result := (pid <> 0) and ProcessIdToFileName(pid, arrCh) and
(PosText(PRG_NAME, arrCh) > 0);
end;

function TerminateProcessCallback(processHandle, exitCode: dword) : bool; stdcall;
begin
if ThisIsOurProcess(processHandle) then
begin
result := false;
SetLastError(ERROR_ACCESS_DENIED);
end
else
result := TerminateProcessNext(processHandle, exitCode);

end;

function NtTerminateProcessCallback(processHandle, exitCode: dword) : dword; stdcall;
const STATUS_ACCESS_DENIED = $C0000022;
begin
if ThisIsOurProcess(processHandle) then
begin
result := STATUS_ACCESS_DENIED
end
else
result := NtTerminateProcessNext(processHandle, exitCode);
end;

begin
if GetVersion and $80000000 = 0 then
HookAPI( 'ntdll.dll', 'NtTerminateProcess', @NtTerminateProcessCallback,

@NtTerminateProcessNext)
else HookAPI('kernel32.dll', 'TerminateProcess', @TerminateProcessCallback,

@TerminateProcessNext);
end.
再写个exe调用这个dll,把这个dll插入到系统进程中去。
procedure inject;
begin
try
if not InjectLibrary((CURRENT_SESSION or CURRENT_PROCESS), 'hook.dll') then
begin
ExitProcess(0); //如果没有把hook.dll插入到进程中去,那么程序就自动关闭
end;
except
//
end;
end;

procedure uninject; //把hook.dll从插入的进程中卸载掉
begin
try
UninjectLibrary((CURRENT_SESSION or CURRENT_PROCESS), 'hook.dll');
except
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
inject; //程序一启动就插入dll
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
uninject; //程序退出把dll从进程中卸载,保护进程功能也就失效了。
end;

========================================================================================

HOOK其他进程API和全局HOOK-API

HOOK是一种WINDOWS下存在很久的技术了。
HOOK一般分两种1。HOOK MESSAGE 2。HOOK API 本问讨论的是HOOK API。(如果你是HOOK高手就不要看了)
在最初学HOOK-API的时候通常都是通过覆盖地址和修改IAT的方法。
通过这两种技术,我们基本都可以实现对本进程的API函数进行HOOK了。但是在高兴之余会有点遗憾,
怎么才能HOOK其他进程的API函数呢?怎么才能对一个API函数进行全局的HOOK呢?
下面是我的一个简单的“HOOK其他进程API函数”的实现。(对另一进程的MessageBoxA这个函数进行HOOK)

其实里面的应用了两个技术
1。远程线程注入
2。修改IAT,HOOK-API

好了贴出代码如下:
一共是3个文件
install.c 注入程序
fundll.cpp DLL程序
test.cpp 测试程序

CODE:

//-------------------------install.c--------------------------
//
//install.c

#include "windows.h"
#include "tlhelp32.h"

#pragma comment(lib,"th32.lib")

const char *pkill="fundll.dll"; //DLL文件的路径

//这个路径很有意思,这个路径是相对于目标进程的,而不是自身进程。
//所以要嘛写成绝对路径,要嘛写成相对于目标进程的相对路径。
//如果写成相对于自身的路径就要麻烦了,本程序就找不到DLL文件了。

char *prosess="test.exe"; //要注入的进程名(目标进程名)

int main()
{
HANDLE hSnap;
HANDLE hkernel32; //被注入进程的句柄
PROCESSENTRY32 pe;
BOOL bNext;
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID Luid;
LPVOID p;
FARPROC pfn;

if (!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
{
return 1;
}

if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&Luid))
{
return 1;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp.Privileges[0].Luid = Luid;

if (!AdjustTokenPrivileges(hToken,0,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL))
{
return 1;
}

pe.dwSize = sizeof(pe);
hSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
bNext=Process32First(hSnap, &pe);
while(bNext)
{
if(!stricmp(pe.szExeFile,prosess)) //--->>
{
hkernel32=OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_WRITE|PROCESS_VM_OPERATION,1,pe.th32ProcessID);
break;
}
bNext=Process32Next(hSnap, &pe);
}

CloseHandle(hSnap);

p=VirtualAllocEx(hkernel32,NULL,strlen(pkill),MEM_COMMIT,PAGE_READWRITE);
WriteProcessMemory(hkernel32,p,pkill,strlen(pkill),NULL);
pfn=GetProcAddress(GetModuleHandle("kernel32.dll"),"LoadLibraryA");
CreateRemoteThread(hkernel32,NULL,0,pfn,p,NULL,0);

return 0;
}

//----------------------fundll.cpp-----------------------------
//
//fundll.cpp

#include "windows.h"
#include "process.h"
#include "tlhelp32.h"
#include "stdio.h"

#pragma comment(lib,"th32.lib")

PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNTHeaders;
PIMAGE_OPTIONAL_HEADER pOptHeader;
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
PIMAGE_THUNK_DATA pThunkData;
PIMAGE_IMPORT_BY_NAME pImportByName;
HMODULE hMod;

// 定义MessageBoxA函数原型
typedef int (WINAPI *PFNMESSAGEBOX)(HWND, LPCSTR, LPCSTR, UINT uType);
int WINAPI MessageBoxProxy(IN HWND hWnd, IN LPCSTR lpText, IN LPCSTR lpCaption, IN UINT uType);

int * addr = (int *)MessageBoxA; //保存函数的入口地址
int * myaddr = (int *)MessageBoxProxy;

void ThreadProc(void *param);//线程函数

//-------------------------------------------------------主函数开始

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
if(fdwReason==DLL_PROCESS_ATTACH)
_beginthread(ThreadProc,0,NULL);

return TRUE;
}

//结束进程的函数

void ThreadProc(void *param)
{
//------------hook api----------------
hMod = GetModuleHandle(NULL);

pDosHeader = (PIMAGE_DOS_HEADER)hMod;
pNTHeaders = (PIMAGE_NT_HEADERS)((BYTE *)hMod + pDosHeader->e_lfanew);
pOptHeader = (PIMAGE_OPTIONAL_HEADER)&(pNTHeaders->OptionalHeader);

pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE *)hMod + pOptHeader->DataDirectory[1].VirtualAddress);

while(pImportDescriptor->FirstThunk)
{
char * dllname = (char *)((BYTE *)hMod + pImportDescriptor->Name);

pThunkData = (PIMAGE_THUNK_DATA)((BYTE *)hMod + pImportDescriptor->OriginalFirstThunk);

int no = 1;
while(pThunkData->u1.Function)
{
char * funname = (char *)((BYTE *)hMod + (DWORD)pThunkData->u1.AddressOfData + 2);
PDWORD lpAddr = (DWORD *)((BYTE *)hMod + (DWORD)pImportDescriptor->FirstThunk) +(no-1);

//修改内存的部分
if((*lpAddr) == (int)addr)
{
//修改内存页的属性
DWORD dwOLD;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(lpAddr,&mbi,sizeof(mbi));
VirtualProtect(lpAddr,sizeof(DWORD),PAGE_READWRITE,&dwOLD);

WriteProcessMemory(GetCurrentProcess(),
lpAddr, &myaddr, sizeof(DWORD), NULL);
//恢复内存页的属性
VirtualProtect(lpAddr,sizeof(DWORD),dwOLD,0);
}
//---------
no++;
pThunkData++;
}

pImportDescriptor++;
}
//-------------------HOOK END-----------------
}

//new messagebox function
int WINAPI MessageBoxProxy(IN HWND hWnd, IN LPCSTR lpText, IN LPCSTR lpCaption, IN UINT uType)
{
return ((PFNMESSAGEBOX)addr)(NULL, "gxter_test", "gxter_title", 0);
//这个地方可以写出对这个API函数的处理代码
}

//----------------------------test.cpp------------------------------------
//test.cpp

#include "stdio.h"
#include "windows.h"

int main()
{
printf("test---/n");
while(1)
{
getchar();
MessageBoxA(NULL, "原函数", "09HookDemo", 0);
}
return 0;
}

测试过程:先运行TEST不要关闭(建立目标),再运行install(进行注入)。但要注意FUNDLL和TEST文件位置。

上面的代码进本上就实现了对其他进程的API进行HOOK了。
但还有一个问题就是对“API函数进行全局的HOOK”。我的想法就是注入所有进程就可以实现了。
只要简单的改一下上面的代码就可以实现了。 这好象有点像SetWindowsHookEx这个函数的的实现过程。
以上就是我想法了,如果在什么地方有错误还请纠正。

================================================================================================

通过Hook API调用打造进程监控程序

Hook(钩子)是Windows消息处理过程中的一个监视点,应用程序可以通过Hook拦截Windows消息,这样就可以捕捉到目标进程的消息,并做相应的处理,从而达到修改系统默认行为的目的,另外,在相对封闭的Windows世界里,Hook技术也为我们了解系统内部机制提供了更好的机会。Hook技术在“黑”与“反黑”的对抗中,也扮演着重要的角色,很多木马,后门,都是通过键盘钩子,在后台记录密码,聊天信息。。。通过Hook系统的;可以通过Hook文件操作函数实现对文件的隐藏,躲避查杀;Hook注册表操作函数,实现对注册表中某些键值的隐藏。 另外,杀毒软件,防火墙也可以通过Hook系统的关键函数,实现对系统行为的监控,防止恶意程序在用户不知情的情况下,恶意破坏系统。
下面,我们就通过利用Hook Explorer 进程的CreateProcess函数打造一个小小的进程监控程序,来给大家讲讲如何利用PE文件的导入表实现Hook API的吧。该程序Hook Explore进程的CreateProcess函数后,在windows资源管理器在要运行其它程序时,会弹出这样一个提示框:

点击“是”,则程序运行,点击“否”则,程序被阻止运行。(注意:本例只Hook了Explore进程的CreateProcess,因此,该程序不能监控到系统范围内的程序运行情况,如果要这样,可以通过Hook Windows系统调用来实现,希望以后以机会和大家一起讨论这方面的话题。本例作为一个例子对于说明Hook API 调用的原理已足够)。
我们可以通过SetWindowsHookEx函数安装一个Windows标准钩子,根据你安装的钩子类型,钩子可以是远程的,也可以是局部的。可是,Windows所提供的标准钩子中,并没有直接钩挂API调用的。那么如果我们要钩挂某个API,以达到某些目的,该怎么做呢?
先来复习一下PE文件的导入表的相关知识吧。各种文件都有一定的格试,在windows下,EXE,DLL文件用的就是PE文件格式。如果我们现在要调用的是某个DLL中的一个函数,在代码编译期,使用隐式链接方式时,并不需要有指定的DLL,只要需要有DLL 对应的Lib文件就可以了,很明显,在编译的时候,对应的函数的地址是无法确定下来的。那么,在运行的时候又怎么能够正确调用这个API呢?原因是:PE文件在导入表中记录了它所用到的每一个DLL的名字,以及从相应的DLL导入的函数的名字。(在使用显式链接,通过LoadLibrary/GetProcAddress调用函数时,所需要DLL名,和函数名并不会出现在导入表中)。这样,当运行这个程序的时候,Windows可执行文件装载器,就会搜索并载入出现在该程序的导入表的所有DLL,(如果装载器,找不到导入表中的DLL,就会弹出提示框)然后,根据导入表中的导入的函数名字,在DLL的导出表中找到匹配的函数,取得它的地址,再把这个地址填入可执行文件对应的地方。(如果找到所需要的DLL,但是在该DLL的导出表中找不到可执行文件所导入的函数,就会弹出提示框)这就是动态链接的概念。

/
通过PE文件的头的数据目录中的第二个IMAGE_DATA_DIRECTORY项,我们就可以定位到导入表项。也可以直接用ImageDirectoryEntryToData这个API直接定位的导入表项。而导入表由IMAGE_IMPORT_DESCRIPTOR结构组成。
Typedef struct _IMAGE_IMPORT_DESCRIPTOR{
union{
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
}IMAGE_IMPORT_DESCRIPTOR;
其中Name是一个相对虚拟地址(RVA),它指向所导入的DLL名字的一个字符串。如果一个可执行文件导入了多个DLL,则它的导入表中有多个这样的结构,最后有一个全0的IMAGE_IMPORT_DESCRIPTOR结束。OriginalFirstThunk和FirstThunk都是RVA,它们分别指向两个相同的IMAGE_THUNK_DATA。
typedef struct _IMAGE_THUNK_DATA32{
union{
DWORD ForwarderSting;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData;
}u1;
}IMAGE_THUNK_DATA32;
Typedef IMAGE_THUNK_DATA32 IMAGE_THUNK_DATA;
由此可见,实际上这个联合结构就是一个DWORD,根据规定,当这个DWORD最高位为1时,表示这个导入函数是通过序号形式导入的,否则函数是通过名称导入的,当通过名称导入时,这个DWORD是一个RVA,它指向了一个IMAGE_IMPORT_BY_NAME的结构。
typedef struct _IMAGE_IMPORT_BY_NAME{
WORD Hint;
BYTE Name[?];
}IMAGE_IMPORT_BY_NAME;
这里面的Name是一个变长数组,它存着所导入的函数名。呵呵,如果没有耐心的话,估计你早就头晕了。现在我们来理一理这些结构的关系吧/。
/
现在,OriginalFirstThunk和FirstThunk都指向两个一样的IMAGE_THUNK_DATA,而这两个IMAGE_THUNK_DATA又都指向了包含了函数名的IMAGE_IMPORT_BY_NAME结构。当Windows装入一个可执行文件的时候,它会找到并载入Name指定的DLL,然后,在该DLL的导出表中找到
IMAGE_IMPORT_BY_NAME所指定的函数名称的地址并填入FirstThunk所指的IMAGE_IMPORT_BY_NAME中。所以当载入完成后.

这样的话,当调用CreateProcess,就会到FirstThunk中找到对应的CreateProcess的入口地址。现在,如果在装载后,把CreateProcess的入口地址改成我们指定的某个地址,那么对CreateProcess的调用会不会转到调用我们的代码上来呢?答案是肯定的。但是在Windows中各个进程都有各自独立的虚拟地址空间,如果我们要Hook一个远程进程,就一定得保证把代码映射到目标进程的地址空间中去。这个,可以使用VirtualAllocEx为目标进程分配空间,然后用WriteProcessMemory把代码写入到目标进程的地址空间中去,再把FirstThunk中的CreateProcess的入口地址改成写入代码的首地址。该方法要自己解决地址重定位的问题。另一种方法,就是把代码放到一个DLL中,然后,让目标进程加载这个DLL,这样就把地址重定位的事情交给Windows来做了。再把FirstThunk中的CreateProcess的入口地址改写成这个DLL中的某段代码的地址。但是,又怎么才能让目标进程加载这个DLL呢?这个我们可以通过为目标进程安装一个远程的钩子(SetWindowsHookEx),这样,就可以把一个DLL映射到目标进程的地址空间中去,然后,修改目标进程相关函数的入口地址就可以了。好了,说到这里,相关的基础知识,相信你也清楚了。我们就来看看程序的实现过程吧。程序分为两部分,一部分是主程序,一部分是一个DLL。启动Hook的过程如下:
// 找到Explorer的窗口句柄
hWnd = FindWindow("Progman", "Program Manager");
DWORD dwProcessId; //获取Explorer进程及窗口线程的ID。
DWORD dwThreadId = GetWindowThreadProcessId(hWnd, &dwProcessId);
if (!dwThreadId){
MessageBox(GetActiveWindow(), "Explorer 没有启动", "Explorer 没有启动", MB_OK);
return;
}
HMODULE hModule = LoadLibrary("HookApi.dll"); // HookApi.dll是要注入的DLL
HOOKPROC GetMsgProc = (HOOKPROC)GetProcAddress(hModule, "MsgHook");
hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, hModule, dwThreadId);
PostMessage(hWnd, WM_HOOK, 0, 0);
FreeLibrary(hModule);
上面的代码关键处是用SetWindowsHookEx为Explorer的窗口线程安装了一个WH_GETMESSAGE类型的钩子,这样,每当Explorer的窗口线程从它的消息队列中取到消息的时候,就会先调用HookApi.dll中的GetMsgProc回调函数。注意这里,不能用SendMessage,因为SendMessage并不把消息放入目标窗口的消息队列中,而是直接调用目标窗口的消息回调函数。
接下来,我们看看HookApi.dll中的GetMsgProc回调函数,
__declspec(dllexport) LRESULT CALLBACK MsgHook(int nCode, WPARAM wParam, LPARAM lParam){
PMSG pmsg = (PMSG)lParam;
switch(pmsg->message){
case WM_HOOK:
GetDebugPrivilege(); // 提高进程权限,为写导入表做准备
// 用于同步,我将在后面详细解释为什么。
hEvent = CreateEvent(NULL, TRUE, FALSE, "MyApiHookEvent");
Hook("Kernel32.dll", "CreateProcessA", (PROC)NewCreateProcessA);
Hook("Kernel32.dll", "CreateProcessW", (PROC)NewCreateProcessW);
break;
case WM_UNHOOK:
UnHook();
SetEvent(hEvent);
CloseHandle(hEvent);
break;
default:
CallNextHookEx(NULL, nCode, wParam, lParam);
break;
}
return 0;
}
每当Explorer从它的窗口消息队列中,取到消息的时候,就会先调用MsgHook回调函数,我们在这个函数中,先判断消息是不是我们自己定义的消息WM_HOOK或WM_UNHOOK,如果是,则处理,如果不是,则不是我们关心的消息,调用CallNextHookEx把消息往下传。
void Hook(LPCTSTR lpModule, LPCTSTR lpFunction, PROC pfnNewFunc){
HMODULE hThisModule = GetThisModule(); // 获取本DLL的模块句柄
// 这里取得Explorer进程中的所有模块。
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
MODULEENTRY32 me = {sizeof(me)};
for (BOOL fFlag = Module32First(hSnapShot, &me); fFlag; fFlag = Module32Next(hSnapShot, &me)){
if (me.hModule != hThisModule){
try{
ReplaceFunc(me.hModule, pfnNewFunc, lpModule, lpFunction);
}catch(...){
continue;
}
}
}
CloseHandle(hSnapShot);
}
代码首先获取Explorer进程中的每一个模块,然后调用ReplaceFunc尝试修改每个模块的导入表。
void ReplaceFunc(HMODULE hModule, PROC pfnNew, LPCTSTR lpModule, LPCTSTR lpFunction){
ULONG ulSize;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
// 获取导入表的地址。
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData
(hModule, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
if (pImportDesc == NULL)
return;
// 由于导入表中,可能导入了很多DLL,我们先找到,要Hook的DLL,此例中,为kernel32.dll
while (pImportDesc->Name){
PSTR pszName = (PSTR) ((PBYTE) hModule + pImportDesc->Name);
if (lstrcmpi(pszName, lpModule) == 0)
break; // 找到目标DLL
else
pImportDesc++;
}
if (pImportDesc == 0 || pImportDesc->Name == 0)
return;
// 定位FirstThunk和OriginalThunk,前面说过,在内存中,FirstThunk间接指向导入表中,真实的函数
//地址的结构,而OriginalThunk间接指向真实的函数名。
PIMAGE_THUNK_DATA pFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImportDesc->FirstThunk);
PIMAGE_THUNK_DATA pOriginalThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImportDesc->OriginalFirstThunk);
// 确保函数不是以序号导入的。
if ((pOriginalThunk->u1.ForwarderString & IMAGE_ORDINAL_FLAG32) != 0)
return;
while (pOriginalThunk->u1.Function){ // 找到函数名。
PIMAGE_IMPORT_BY_NAME pImageByName = (PIMAGE_IMPORT_BY_NAME)((PBYTE) hModule + pOriginalThunk->u1.ForwarderString);
// 比较该项的函数名是不是我们要Hook的函数,如果是,则修改对应的导入地址。
if (lstrcmpi(((LPCSTR)&(pImageByName->Name)), lpFunction) == 0){
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE, GetCurrentProcessId());
// 将修改的信息保存起来,以简化UnHook的工作。
PHooker p;
p = new Hooker;
p->pNext = Head;
p->lpFunction = lpFunction;
p->lpModule = lpModule;
p->pfnNew = pfnNew;
p->pfnOld = (PROC)pFirstThunk->u1.Function;
p->lpAddr = &(pFirstThunk->u1.Function);
Head = p;
WriteProcessMemory(hProcess, (LPVOID)(&(pFirstThunk->u1.Function)), (LPVOID)(&pfnNew), sizeof(pfnNew), NULL);
}
pFirstThunk++;
pOriginalThunk++;
}
}
主程序,能过下列方式通知HookApi.dll进行UnHook动作。
PostMessage(hWnd, WM_UNHOOK, 0, 0);
HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "MyApiHookEvent");
WaitForSingleObject(hEvent, INFINITE);
CloseHandle(hEvent);
UnhookWindowsHookEx(hHook);

HookApi.dll中的MsgHook回调函数对WM_UNHOOK消息的处理:
case WM_UNHOOK:
UnHook();
SetEvent(hEvent);
CloseHandle(hEvent);
UnHook过程就非常简单了,前面每修改了一项导入表中的函数地址,就把它的相关信息保存在一个链表中,现在,只要根据这个链表,把每一个函数地址恢复,就完成了UnHook过程。这里我要说的是,当调用PostMessage把消息丢入Explorer的消息队列中,但不能保证Explorer什么时候来处理这条消息,而我们是通过SetWindowsHookEx把HookApi.dll映射到Explorer的进程地址空间中去的,当调用UnhookWindowsHookEx的时候,HookApi.dll会从Explorer进程中卸载。现在,考虑这样一种情况,当用PostMessage发送消息后,由于Explorer因为某种原因没来得及处理这条消息,而主程序调用了UnhookWindowsHookEx,这时候,HookApi.dll在Explorer进程地址空间中的映射被解除,后来,Explorer从它的消息队列中取出WM_UNHOOK,由于消息钩子也已经被解除,所以WM_UNHOOK消息不会再有机会被处理,因而也没有机会把导入表中的函数地址恢复回样,如果在个时候,再调用之前被HOOK了的API,那个地址还是指向HookApi.dll中的某个函数中的地址,但是,这个DLL已经被解除,很明显,由于一个对无效地址的CALL,将直接导致Explorer进程的崩溃。所以,本例中,在WM_HOOK中,建立了一个未通知状态的事件对象,然后,主程序UnHook时,用PostMessage发送消息后,用WaitForSingleObject无限期等待这个事件对象,只有当Explorer的消息钩子处理了WM_UNHOOK消息时,才用SetEvent把这个事件对象设为通知状态,这样,主程序中的UnhookWindowsHookEx才有机会被执行,从而保证了同步。
后记:本文结合消息钩子以及对PE文件导入表的操作,实现了对API调用的拦截,由于篇幅的原因,本文只对关键代码进行了分析,详细的代码请参阅附带的源代码。注意本文的代码,只做说明之用,略去了一些错误处理,用VS 2003编译通过,可执行程序,在作者的Windows XP SP2以及Windows 2K中测试通过,对于其它机器上作者不能保证其正确运行,
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: