轻问一下怎么实现Hook函数?即拦截一个函数的调用?

f_acme 2006-10-29 10:35:31
网上找了一下,发现几乎全是什么键盘钩子函数之类的,而且用的全是Windows的API,请问一下有没有用纯C语言实现的例子?哪位举个例子来看一下。
...全文
906 13 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
springlie 2006-10-30
  • 打赏
  • 举报
回复
需要嵌入汇编?不懂,不知道
beingstudio 2006-10-30
  • 打赏
  • 举报
回复
用hook函数截获消息然后调用自己的函数里就可以
但必须用hook截获消息————我感觉,如果你有其他办法告诉我
f_acme 2006-10-30
  • 打赏
  • 举报
回复
难到linux下没有这种机制么?
如果有,那不叫hook,叫什么?
hailongchang 2006-10-30
  • 打赏
  • 举报
回复
我们知道,系统函数都是以DLL封装起来的,应用程序应用到系统函数时,应首先把该DLL加载到当前的进程空间中,调用的系统函数的入口地址,可以通过GetProcAddress函数进行获取。当系统函数进行调用的时候,首先把所必要的信息保存下来(包括参数和返回地址,等一些别的信息),然后就跳转到函数的入口地址,继续执行。其实函数地址,就是系统函数“可执行代码”的开始地址。那么怎么才能让函数首先执行我们的函数呢?呵呵,应该明白了吧,把开始的那段可执行代码替换为我们自己定制的一小段可执行代码,这样系统函数调用时,不就按我们的意图乖乖行事了吗?其实,就这么简单。Very very简单。 :P.实际的说,就可以修改系统函数入口的地方,让他调转到我们的函数的入口点就行了。采用汇编代码就能简单的实现Jmp XXXX, 其中XXXX就是要跳转的相对地址。

我们的做法是:把系统函数的入口地方的内容替换为一条Jmp指令,目的就是跳到我们的函数进行执行。而Jmp后面要求的是相对偏移,也就是我们的函数入口地址到系统函数入口地址之间的差异,再减去我们这条指令的大小。用公式表达如下:

int nDelta = UserFunAddr – SysFunAddr - (我们定制的这条指令的大小);

Jmp nDleta;

为了保持原程序的健壮性,我们的函数里做完必要的处理后,要回调原来的系统函数,然后返回。所以调用原来系统函数之前必须先把原来修改的系统函数入口地方给恢复,否则,系统函数地方被我们改成了Jmp XXXX就会又跳到我们的函数里,死循环了。



那么说一下程序执行的过程。

我们的dll“注射”入被hook的进程 -> 保存系统函数入口处的代码 -> 替换掉进程中的系统函数入口指向我们的函数 -> 当系统函数被调用,立即跳转到我们的函数 -> 我们函数进行处理 -> 恢复系统函数入口的代码 -> 调用原来的系统函数 -> 再修改系统函数入口指向我们的函数(为了下次hook)-> 返回。

于是,一次完整的Hook就完成了。



好,这个问题明白以后,讲一下下个问题,就是如何进行dll“注射”?即将我们的dll注射到要Hook的进程中去呢?

很简单哦,这里我们采用调用Windows提供给我们的一些现成的Hook来进行注射。举个例子,鼠标钩子,键盘钩子,大家都知道吧?我们可以给系统装一个鼠标钩子,然后所有响应到鼠标事件的进程,就会“自动”(其实是系统处理了)载入我们的dll然后设置相应的钩子函数。其实我们的目的只是需要让被注射进程载入我们的dll就可以了,我们可以再dll实例化的时候进行函数注射的,我们的这个鼠标钩子什么都不干的。





4简单的例子OneAddOne

讲了上面的原理,现在我们应该实战一下了。先不要考虑windows系统那些繁杂的函数,我们自己编写一个API函数来进行Hook与被Hook的练习吧,哈哈。



////////////////////

第一步,首先编写一个add.dll,很简单,这个dll只输出一个API函数,就是add啦。

新建一个win32 dll工程,

add.cpp的内容:



#i nclude "stdafx.h"



int WINAPI add(int a,int b){ file://千万别忘记声明WINAPI 否则调用的时候回产生声明错误哦!

return a+b;

}



BOOL APIENTRY DllMain( HANDLE hModule,

DWORD ul_reason_for_call,

LPVOID lpReserved

)

{

return TRUE;

}





然后别忘了在add.def里面输出函数:



LIBRARY Add

DESCRIPTION "ADD LA"

EXPORTS

add @1;



编译,ok,我们获得了add.dll



////////////////////

第二步,编写1+1主程序

新建一个基于对话框的工程One,

在 OnOK()里面调用add函数:

ConeDlg.h里面加入一些变量的声明:

….

Public:

HINSTANCE hAddDll;

typedef int (WINAPI*AddProc)(int a,int b);

AddProc add;





ConeDlg.cpp里进行调用:



void COneDlg::OnOK()

{

// TODO: Add extra validation here

if (hAddDll==NULL)

hAddDll=::LoadLibrary("add.dll");



add=(AddProc)::GetProcAddress(hAddDll,"add");



int a=1;

int b=2;

int c=add(a,b);

CString temp;

temp.Format("%d+%d=%d",a,b,c);

AfxMessageBox(temp);

}



OK,编译运行,正确的话就会显示1+2=3咯

////////////////////

第3步,要动手Hook咯,爽阿

新建一个MFC的dll工程,Hook

在Hook.dll工程里:



添加一个鼠标Hook MouseProc,鼠标hook什么也不做

LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)

{

LRESULT RetVal= CallNextHookEx(hhk,nCode,wParam,lParam);

return RetVal;

}



添加鼠标钩子的安装和卸载函数:

BOOL InstallHook()

{



hhk=::SetWindowsHookEx(WH_MOUSE,MouseProc,hinst,0);



….

return true;

}



void UninstallHook()

{

::UnhookWindowsHookEx(hhk);

}



再实例化中获得一些参数

BOOL CHookApp::InitInstance()

{

获得dll 实例,进程句柄

hinst=::AfxGetInstanceHandle();

DWORD dwPid=::GetCurrentProcessId();

hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);

调用注射函数

Inject();

return CWinApp::InitInstance();

}



好,最重要的注射函数:

void Inject()

{



if (m_bInjected==false)

{ 保证只调用1次

m_bInjected=true;



获取add.dll中的add()函数

HMODULE hmod=::LoadLibrary("add.dll");

add=(AddProc)::GetProcAddress(hmod,"add");

pfadd=(FARPROC)add;



if (pfadd==NULL)

{

AfxMessageBox("cannot locate add()");

}



// 将add()中的入口代码保存入OldCode[]

_asm

{

lea edi,OldCode

mov esi,pfadd

cld

movsd

movsb

}



NewCode[0]=0xe9;//实际上0xe9就相当于jmp指令

//获取Myadd()的相对地址

_asm

{

lea eax,Myadd

mov ebx,pfadd

sub eax,ebx

sub eax,5

mov dword ptr [NewCode+1],eax

}

//填充完毕,现在NewCode[]里的指令相当于Jmp Myadd

HookOn(); //可以开启钩子了

}

}



开启钩子的函数

void HookOn()

{

ASSERT(hProcess!=NULL);



DWORD dwTemp=0;

DWORD dwOldProtect;



//将内存保护模式改为可写,老模式保存入dwOldProtect

VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);

//将所属进程中add()的前5个字节改为Jmp Myadd

WriteProcessMemory(hProcess,pfadd,NewCode,5,0);

//将内存保护模式改回为dwOldProtect

VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);



bHook=true;

}



关闭钩子的函数

void HookOff()//将所属进程中add()的入口代码恢复

{

ASSERT(hProcess!=NULL);



DWORD dwTemp=0;

DWORD dwOldProtect;



VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);

WriteProcessMemory(hProcess,pfadd,OldCode,5,0);

VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);

bHook=false;

}



然后,写我们自己的Myadd()函数

int WINAPI Myadd(int a,int b)

{

//截获了对add()的调用,我们给a,b都加1

a=a+1;

b=b+1;



HookOff();//关掉Myadd()钩子防止死循环



int ret;

ret=add(a,b);



HookOn();//开启Myadd()钩子



return ret;

}



然后别忘记在hook.def里面输出

InstallHook

MouseProc

Myadd

UninstallHook

四个函数。




好到这里基本上大功告成咯



////////////////////



第4步,我们就可以修改前面的One的测试程序了



增加一个安装钩子的函数/按钮

void COneDlg::doHook()

{

hinst=LoadLibrary("hook.dll");

if(hinst==NULL)

{

AfxMessageBox("no hook.dll!");

return;

}

typedef BOOL (CALLBACK *inshook)();

inshook insthook;



insthook=::GetProcAddress(hinst,"InstallHook");

if(insthook==NULL)

{

AfxMessageBox("func not found!");

return;

}



DWORD pid=::GetCurrentProcessId();

BOOL ret=insthook();

}



别忘了退出时卸掉钩子

void COneDlg::OnCancel()

{

// TODO: Add extra cleanup here

typedef BOOL (CALLBACK *UnhookProc)();

UnhookProc UninstallHook;



UninstallHook=::GetProcAddress(hinst,"UninstallHook");

if(UninstallHook==NULL) UninstallHook();

if (hinst!=NULL)

{

::FreeLibrary(hinst);

}

if (hAddDll!=NULL)

{

::FreeLibrary(hAddDll);

}

CDialog::OnCancel();

}

hailongchang 2006-10-30
  • 打赏
  • 举报
回复
HOOK需要OS的支持,所以必须使用windows API 的SetWindowsHookEx等函数,而标准C 可以在linux等多种平台下编译运行,所以是不能完成这个任务的.

HOOK可以拦截消息,键盘,和鼠标等等.

f_acme(沧海一声笑) 说的是hook windows api的方法,也就是替换windows api函数的方法
cunsh 2006-10-30
  • 打赏
  • 举报
回复
mark
high2003376320 2006-10-30
  • 打赏
  • 举报
回复
太长了,那个程序.看得我要晕了
hailongchang 2006-10-30
  • 打赏
  • 举报
回复
linux下的就不叫hook了
f_acme 2006-10-30
  • 打赏
  • 举报
回复
来个linux下的例子怎么样,windows不是很懂哦
f_acme 2006-10-29
  • 打赏
  • 举报
回复
不是啊,很多需要拦截函数调用的啊
beginnow 2006-10-29
  • 打赏
  • 举报
回复
你所的是病毒罢
f_acme 2006-10-29
  • 打赏
  • 举报
回复
好像是用什么指针之类的东西,在要拦截函数的开始部分的几个字节改为一条跳转语句,跳到自己的函数去,可是不知怎么实现,具体不清楚,等高手啊。
beginnow 2006-10-29
  • 打赏
  • 举报
回复
拦截键盘或则是鼠标之类的消息需要根操作系统打交道当然需要系统函数,
拦截一个函数的调用? 怎莫做啊!在等等

70,021

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧