本文主要是介绍Windows HOOK API (二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
资料来源:http://blog.sina.com.cn/s/blog_628821950100xmuc.html
HooK MessageBox消息:http://blog.csdn.net/friendan/article/details/12222651
前言
本文主要介绍了如何实现替换Windows上的API函数,实现Windows API Hook(当然,对于socket的Hook只是其中的一种特例)。这种Hook API技术被广泛的采用在一些领域中,如屏幕取词,个人防火墙等。
这种API Hook技术并不是很新,但是涉及的领域比较宽广,要想做好有一定的技术难度。本文是采集了不少达人的以前资料并结合自己的实验得出的心得体会,在这里进行总结发表,希望能够给广大的读者提供参考,达到抛砖引玉的结果。
2问题
最近和同学讨论如何构建一个Windows上的简单的个人防火墙。后来讨论涉及到了如何让进程关联套接字端口,替换windows API,屏幕取词等技术。
其中主要的问题有:
1) 采用何种机制来截获socket的调用?
一般来说,实现截获socket的方法有很多很多,最基本的,可以写驱动,驱动也有很多种,TDI驱动, NDIS驱动,Mini port驱动…。由于我使用的是Win2000系统,所以截获socket也可以用Windows SPI来进行。另外一种就是Windows API Hook技术。
由于我没什么硬件基础,不会写驱动,所以第一种方法没有考虑,而用SPI相对比较简单。但是后来觉得Windows API Hook适应面更广,而且觉得自己动手能学到不少东西,就决定用Windows API Hook来尝试做socket Hook.
2) API Hook的实现方法?
实际上就是对系统函数的替换,当然实现替换的方法大概不下5,6种吧,可以参考《Windows核心编程》第22章。不过我使用的方法与其不近相同,应该相对比较简单易懂。
3原理
我们知道,系统函数都是以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的内容:
#include "stdafx.h"
int WINAPI add(int a,int b){
}
BOOL APIENTRY DllMain( HANDLE hModule,
{
}
然后别忘了在add.def里面输出函数:
LIBRARY
DESCRIPTION
EXPORTS
编译,ok,我们获得了add.dll
第二步,编写1+1主程序
新建一个基于对话框的工程One,
在 OnOK()里面调用add函数:
ConeDlg.h里面加入一些变量的声明:
….
Public:
…
ConeDlg.cpp里进行调用:
void COneDlg::OnOK()
{
}
OK,编译运行,正确的话就会显示1+2=3咯
第3步,要动手Hook咯,爽阿
新建一个MFC的 dll工程,Hook
在Hook.dll工程里:
添加一个鼠标Hook MouseProc,鼠标hook什么也不做
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
}
添加鼠标钩子的安装和卸载函数:
BOOL InstallHook()
{
}
void UninstallHook()
{
}
再实例化中获得一些参数
BOOL CHookApp::InitInstance()
{
hinst=::AfxGetInstanceHandle();
//调用注射函数
}
好,最重要的注射函数:
void Inject()
{
}
开启钩子的函数
void HookOn()
{
}
关闭钩子的函数
void HookOff()//将所属进程中add()的入口代码恢复
{
}
然后,写我们自己的Myadd()函数
int WINAPI Myadd(int a,int b)
{
}
然后别忘记在hook.def里面输出
InstallHook
四个函数。
(全部的程序会贴在最后面的)
好到这里基本上大功告成咯
第4步,我们就可以修改前面的One的测试程序了
增加一个安装钩子的函数/按钮
void COneDlg::doHook()
{
BOOL ret=insthook();
}
别忘了退出时卸掉钩子
void COneDlg::OnCancel()
{
}
好了,大功告成咯,现在运行一下One,
先点Ok,测试1+2=3 没问题,
然后点Hook,安装钩子,,显示installhook ok,然后再点一下ok,
哈哈,发现结果1+2=5 !!!???
Hook成功啦!
附上Hook.cpp的原码
/
Hook.cpp
// Hook.cpp : Defines the initialization routines for the DLL.
//
#include "stdafx.h"
#include "Hook.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CHookApp
BEGIN_MESSAGE_MAP(CHookApp, CWinApp)
END_MESSAGE_MAP()
/
// CHookApp construction
CHookApp::CHookApp()
{
}
/
// The one and only CHookApp object
CHookApp theApp;
//变量定义
//不同Instance共享的该变量
#pragma data_seg("SHARED")
static
static
#pragma data_seg()
#pragma comment(linker, "/section:SHARED,rws")
//以上的变量共享哦!
CString temp;
bool bHook=false;
bool m_bInjected=false;
BYTE OldCode[5];
BYTE NewCode[5];
typedef int (WINAPI*AddProc)(int a,int b);//add.dll中的add函数定义
AddProc add;
HANDLE hProcess=NULL;
FARPROC pfadd;
DWORD dwPid;
//end of 变量定义
//函数定义
void HookOn();
void HookOff();
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam); //鼠标钩子函数
void Inject();
int WINAPI Myadd(int a,int b);
BOOL InstallHook();
void UninstallHook();
//end of 函数定义
BOOL CHookApp::InitInstance()
{
}
int CHookApp::ExitInstance()
{
}
BOOL InstallHook()
{
}
void UninstallHook()
{
}
void Inject()
{
}
int WINAPI Myadd(int a,int b)
{
}
LRESULT CALLBACK MouseProc(
{
}
void HookOn()
{
}
void HookOff()//将所属进程中add()的入口代码恢复
{
}
//
hook.def
LIBRARY
DESCRIPTION
EXPORTS
5 实战Hook Win Socket API
如果对于上面这章的的理解没有什么问题的化,我想对于windows API的Hook应该也就是按部就班的做就可以了,不会有太大的问题,只要足够细心耐心,善于处理发现一些细节问题就行了。
当然,为了使我们的程序使用起来更灵活方便,必须要改进我们的程序结构,因为我们要Hook很多的API,不可能为每个API制定一套注射、截获的函数,
所以,我设计了一个类来储存某个API的一些信息:
class CHookFuncInfo
{
public:
public:
入口处的代码
};
对某API函数的注射都可以用Inject()来处理
void Inject(CHookFuncInfo* phinfo);
开/关某个API可以用HookOn/HookOff来处理
void HookOff(CHookFuncInfo * phinfo);
void HookOn(CHookFuncInfo * phinfo);
需要注意的是,在汇编代码里,不可以直接对对象的指针进行调用
即 mov eax, phinfo->fpFunc;诸如此类的代码是不被允许的。
所以我们要加入新的变量,处理完毕,将变量值传回给传入的对象指针:
void Inject(CHookFuncInfo * phinfo)
{
……
……
}
举个例子:
CHookFuncInfo
//创建一个sendto函数的API钩子
然后注射-〉开启钩子-〉…
下面是我定义的MySendto()的代码:
int WINAPI MySendto(
{
//将一些sendto()的数据通过字符串方式传递给
//名为”XsockSpy”的窗体。
//XsockSpy窗体只需要响应对WM_COPYDATA的处理和显示就OK了,很简单。
};
由于时间关系,截获socket的代码我仅仅实现了sendto()这个函数的截获,而且代码可能也比较临乱吧,大家见谅了!
这篇关于Windows HOOK API (二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!