原文地址:http://blog.sina.com.cn/s/blog_628821950100xmuc.html

原文对我的帮助极大,正是因为看了原文,我才学会了HOOK,鉴于原文的排版不是很好,

又没有原工程例子源码下载,因此我决定对其重新整理,文章后面附有我测试时的工程源码下载地址。

注:我测试的环境为Win7+VS2008+MFC

原文出处,好像是这篇:http://blog.csdn.net/glliuxueke/article/details/2702608      //后来才看到的

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

前言
       本文主要介绍了如何实现替换Windows上的API函数,实现Windows API Hook

(当然,对于socket的Hook只是其中的一种特例)。这种Hook API技术被广泛的采用在一些领域中,

如屏幕取词,个人防火墙等。这种API Hook技术并不是很新,但是涉及的领域比较宽广,

要想做好有一定的技术难度。本文是采集了不少达人的以前资料并结合自己的实验得出的心得体会,

在这里进行总结发表,希望能够给广大的读者提供参考,达到抛砖引玉的结果。

-------------------------------------------------------------------------------------------------------------------------------------------------------------

问题
       最近和同学讨论如何构建一个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章。

不过我使用的方法与其不近相同,应该相对比较简单易懂。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
原理

我们知道,系统函数都是以DLL封装起来的,应用程序应用到系统函数时,应首先把该DLL加载到当前的进程空间中,

调用的系统函数的入口地址,可以通过 GetProcAddress函数进行获取。当系统函数进行调用的时候,

首先把所必要的信息保存下来(包括参数和返回地址,等一些别的信息),然后就跳转到函数的入口地址,继续执行。

其实函数地址,就是系统函数“可执行代码”的开始地址。那么怎么才能让函数首先执行我们的函数呢?

呵呵,应该明白了吧,把开始的那段可执行代码替换为我们自己定制的一小段可执行代码,这样系统函数调用时,

不就按我们的意图乖乖行事了吗?其实,就这么简单。Very very简单。 :P

实际的说,就可以修改系统函数入口的地方,让他调转到我们的函数的入口点就行了。

采用汇编代码就能简单的实现Jmp XXXX, 其中XXXX就是要跳转的相对地址。

我们的做法是:把系统函数的入口地方的内容替换为一条Jmp指令,目的就是跳到我们的函数进行执行。

而Jmp后面要求的是相对偏移,也就是我们的函数入口地址到系统函数入口地址之间的差异,再减去我们这条指令的大小。

用公式表达如下:(1)int nDelta = UserFunAddr – SysFunAddr - (我们定制的这条指令的大小);(2)Jmp nDleta;

为了保持原程序的健壮性,我们的函数里做完必要的处理后,要回调原来的系统函数,然后返回。

所以调用原来系统函数之前必须先把原来修改的系统函数入口地方给恢复,否则,

系统函数地方被我们改成了Jmp XXXX就会又跳到我们的函数里,死循环了。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
那么说一下程序执行的过程。
       我们的dll“注射”入被hook的进程 -> 保存系统函数入口处的代码 -> 替换掉进程中的系统函数入口指向我们的函数 -> 当系统函数被

调用,立即跳转到我们的函数 -> 我们函数进行处理 -> 恢复系统函数入口的代码 -> 调用原来的系统函数 -> 再修改系统函数入口指向

我们的函数(为了下次hook)-> 返回。于是,一次完整的Hook就完成了。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        好,这个问题明白以后,讲一下下个问题,就是如何进行dll“注射”?即将我们的dll注射到要Hook的进程中去呢?

很简单哦,这里我们采用调用Windows提供给我们的一些现成的Hook来进行注射。举个例子,鼠标钩子,

键盘钩子大家都知道吧?我们可以给系统装一个鼠标钩子,然后所有响应到鼠标事件的进程,

就会“自动”(其实是系统处理了)载入我们的dll然后设置相应的钩子函数。其实我们的目的只是需要让被注射进程

载入我们的dll就可以了,我们可以再dll实例化的时候进行函数注射的,我们的这个鼠标钩子什么都不干的。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

简单的例子OneAddOne

讲了上面的原理,现在我们应该实战一下了。先不要考虑windows系统那些繁杂的函数,

我们自己编写一个API函数来进行Hook与被Hook的练习吧,哈哈。

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

dllmain.cpp的内容:

  1. //千万别忘记声明WINAPI,否则调用的时候回产生声明错误哦!
  2. int WINAPI add(int a,int b)
  3. {
  4. return a+b;
  5. }
  6. BOOL APIENTRY DllMain( HANDLE hModule,
  7. DWORD  ul_reason_for_call,
  8. LPVOID lpReserved
  9. )
  10. {
  11. return TRUE;
  12. }

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

然后别忘了在add.def里面输出函数add:
LIBRARY  Add
DESCRIPTION "ADD LA"
EXPORTS
 add  @1;

建完工程后,你会发现没有Add.def文件,这时我们自己新建一个Add.def文件,然后添加到工程中即可,

添加Add.def文件到工程后,我们还需要设置工程的属性,将Add.def添加到【项目】-->【Add属性】-->

【链接器】-->【输入】-->【模块定义文件】,如下图所示,不这样设置的话,我们添加的Add.def文件是

不起作用的哦。

设置好后,编译,ok,我们获得了Add.dll

-----------------------------------------------------------------------------------------------------------------------------------------------------

得到Add.dll后,我们可以用一个小工具【dll函数查看器】来打开我们的Add.dll文件,如果函数导出成功的话,我们就可以

在里面看到导出的函数名字了,如下图所示:

该工具下载地址:http://download.csdn.net/detail/friendan/6347455       //dll函数查看器

----------------------------------------------------------------------------------------------------------------------------------------------------------

有了dll文件后,接下来我们新建一个MFC对话框程序来调用该dll中导出的函数add,

程序界面即运行效果截图如下:

主要代码如下:

  1. //调用dll函数 add(int a,int b)
  2. void CCallAddDlg::OnBnClickedBtnCallAdd()
  3. {
  4. HINSTANCE hAddDll=NULL;
  5. typedef int (WINAPI*AddProc)(int a,int b);//函数原型定义
  6. AddProc add;
  7. if (hAddDll==NULL)
  8. {
  9. hAddDll=::LoadLibrary(_T("Add.dll"));//加载dll
  10. }
  11. add=(AddProc)::GetProcAddress(hAddDll,"add");//获取函数add地址
  12. int a=1;
  13. int b=2;
  14. int c=add(a,b);//调用函数
  15. CString tem;
  16. tem.Format(_T("%d+%d=%d"),a,b,c);
  17. AfxMessageBox(tem);
  18. }
 

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下来我们进行HOOK,即HOOK我们的Add.dll文件中的函数int add(int a,int b)

新建一个MFC的 dll工程,工程名为Hook,然后我们在Hook.cpp文件里面编写代码如下:

首先在头部声明如下变量:

  1. //变量定义
  2. //不同Instance共享的该变量
  3. #pragma data_seg("SHARED")
  4. static HHOOK  hhk=NULL; //鼠标钩子句柄
  5. static HINSTANCE hinst=NULL; //本dll的实例句柄 (hook.dll)
  6. #pragma data_seg()
  7. #pragma comment(linker, "/section:SHARED,rws")
  8. //以上的变量共享哦!
  9. CString temp; //用于显示错误的临时变量
  10. bool bHook=false; //是否Hook了函数
  11. bool m_bInjected=false; //是否对API进行了Hook
  12. BYTE OldCode[5]; //老的系统API入口代码
  13. BYTE NewCode[5]; //要跳转的API代码 (jmp xxxx)
  14. typedef int (WINAPI*AddProc)(int a,int b);//add.dll中的add函数定义
  15. AddProc add; //add.dll中的add函数
  16. HANDLE hProcess=NULL; //所处进程的句柄
  17. FARPROC pfadd;  //指向add函数的远指针
  18. DWORD dwPid;  //所处进程ID
  19. //end of 变量定义

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

编写鼠标钩子安装、卸载和处理函数:

  1. //鼠标钩子过程,什么也不做,目的是注入dll到程序中
  2. LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
  3. {
  4. return CallNextHookEx(hhk,nCode,wParam,lParam);
  5. }
  6. //鼠标钩子安装函数:
  7. BOOL InstallHook()
  8. {
  9. hhk=::SetWindowsHookEx(WH_MOUSE,MouseProc,hinst,0);
  10. return true;
  11. }
  12. //卸载鼠标钩子函数
  13. void UninstallHook()
  14. {
  15. ::UnhookWindowsHookEx(hhk);
  16. }
 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在dll实例化函数InitInstance()中,初始化变量和进行注入:

  1. //在dll实例化中获得一些参数
  2. BOOL CHookApp::InitInstance()
  3. {
  4. CWinApp::InitInstance();
  5. //获得dll 实例,进程句柄
  6. hinst=::AfxGetInstanceHandle();
  7. DWORD dwPid=::GetCurrentProcessId();
  8. hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
  9. //调用注射函数
  10. Inject();
  11. return TRUE;
  12. }
 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

编写注射函数,即HOOK函数Inject()了:

  1. //好,最重要的HOOK函数:
  2. void Inject()
  3. {
  4. if (m_bInjected==false)
  5. { //保证只调用1次
  6. m_bInjected=true;
  7. //获取add.dll中的add()函数
  8. HMODULE hmod=::LoadLibrary(_T("Add.dll"));
  9. add=(AddProc)::GetProcAddress(hmod,"add");
  10. pfadd=(FARPROC)add;
  11. if (pfadd==NULL)
  12. {
  13. AfxMessageBox(L"cannot locate add()");
  14. }
  15. // 将add()中的入口代码保存入OldCode[]
  16. _asm
  17. {
  18. lea edi,OldCode
  19. mov esi,pfadd
  20. cld
  21. movsd
  22. movsb
  23. }
  24. NewCode[0]=0xe9;//实际上0xe9就相当于jmp指令
  25. //获取Myadd()的相对地址
  26. _asm
  27. {
  28. lea eax,Myadd
  29. mov ebx,pfadd
  30. sub eax,ebx
  31. sub eax,5
  32. mov dword ptr [NewCode+1],eax
  33. }
  34. //填充完毕,现在NewCode[]里的指令相当于Jmp Myadd
  35. HookOn(); //可以开启钩子了
  36. }
  37. }
 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

编写HOOK开启和停止函数HookOn()和HookOff()

  1. //开启钩子的函数
  2. void HookOn()
  3. {
  4. ASSERT(hProcess!=NULL);
  5. DWORD dwTemp=0;
  6. DWORD dwOldProtect;
  7. //将内存保护模式改为可写,老模式保存入dwOldProtect
  8. VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);
  9. //将所属进程中add()的前5个字节改为Jmp Myadd
  10. WriteProcessMemory(hProcess,pfadd,NewCode,5,0);
  11. //将内存保护模式改回为dwOldProtect
  12. VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);
  13. bHook=true;
  14. }
  15. //关闭钩子的函数
  16. void HookOff()//将所属进程中add()的入口代码恢复
  17. {
  18. ASSERT(hProcess!=NULL);
  19. DWORD dwTemp=0;
  20. DWORD dwOldProtect;
  21. VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);
  22. WriteProcessMemory(hProcess,pfadd,OldCode,5,0);
  23. VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);
  24. bHook=false;
  25. }
 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

编写我们自己的Myadd函数()

  1. //然后,写我们自己的Myadd()函数
  2. int WINAPI Myadd(int a,int b)
  3. {
  4. //截获了对add()的调用,我们给a,b都加1
  5. a=a+1;
  6. b=b+1;
  7. HookOff();//关掉Myadd()钩子防止死循环
  8. int ret;
  9. ret=add(a,b);
  10. HookOn();//开启Myadd()钩子
  11. return ret;
  12. }

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

然后别忘记在hook.def里面导出我们的两个函数 :

InstallHook  
UninstallHook

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下来就可以进行HOOK的测试了,给前面的对话框程序,再添加两个按钮,一个用于安装钩子,另一个用于卸载钩子,

程序和运行效果截图如下:

//未HOOK之前

//HOOK之后

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

安装钩子和卸载钩子主要代码如下:

  1. HINSTANCE hinst=NULL;
  2. //安装鼠标钩子,进行HOOK
  3. void CCallAddDlg::OnBnClickedBtnStartHook()
  4. {
  5. typedef BOOL (CALLBACK *inshook)(); //函数原型定义
  6. inshook insthook;
  7. hinst=LoadLibrary(_T("Hook.dll"));//加载dll文件
  8. if(hinst==NULL)
  9. {
  10. AfxMessageBox(_T("no Hook.dll!"));
  11. return;
  12. }
  13. insthook=::GetProcAddress(hinst,"InstallHook");//获取函数地址
  14. if(insthook==NULL)
  15. {
  16. AfxMessageBox(_T("func not found!"));
  17. return;
  18. }
  19. insthook();//开始HOOK
  20. }
  21. //卸载鼠标钩子,停止HOOK
  22. void CCallAddDlg::OnBnClickedBtnStopHook()
  23. {
  24. if (hinst==NULL)
  25. {
  26. return;
  27. }
  28. typedef BOOL (CALLBACK *UnhookProc)(); //函数原型定义
  29. UnhookProc UninstallHook;
  30. UninstallHook=::GetProcAddress(hinst,"UninstallHook");//获取函数地址
  31. if(UninstallHook!=NULL)
  32. {
  33. UninstallHook();
  34. }
  35. if (hinst!=NULL)
  36. {
  37. ::FreeLibrary(hinst);
  38. }
  39. }
 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上就是之前我看的那篇文章的主要内容了,关于HOOK系统API,我会在其它的文章里面进行说明。

这里再说一下原文的缺点,我认为其有两个缺点:

1.停止HOOK时,没有恢复被HOOK函数的入口。

2.没有处理dll退出事件,没有在dll退出事件中恢复被HOOK函数入口。

以上两个缺点,很容易导致程序的崩溃,因此在我的例子程序中,都对它们进行了处理:

  1. //卸载鼠标钩子函数
  2. void UninstallHook()
  3. {
  4. if (hhk!=NULL)
  5. {
  6. ::UnhookWindowsHookEx(hhk);
  7. }
  8. HookOff();//记得恢复原函数入口
  9. }
  10. //dll退出时
  11. int CHookApp::ExitInstance()
  12. {
  13. HookOff();//记得恢复原函数入口
  14. return CWinApp::ExitInstance();
  15. }
 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上我这个例子工程的下载地址:hook dll文件中的函数add.zip

http://download.csdn.net/detail/friendan/6348209

友情提示:我在Debug模式运行程序时,HOOK会失败,在Release模式运行程序则HOOK成功。

windows hook (转)的更多相关文章

  1. windows hook 钩子

    windows  hook  钩子 场景: 1.打印机 Ctrl+P弹出支付窗口,付款成功后打印

  2. windows hook + pyhook3 + python win32api hook + C 键盘hook

    安装pyhook3见:https://www.cnblogs.com/lqerio/p/12096710.html 使用见:https://www.cnblogs.com/lqerio/p/12106 ...

  3. Windows Hook技术

    0x01 简介 有人称它为“钩子”,有人称它为“挂钩”技术.谈到钩子,很容易让人联想到在钓东西,比如鱼钩就用于钓鱼.编程技术的钩子也是在等待捕获系统中的某个消息或者动作.钩子的应用范围非常广泛,比如输 ...

  4. [Windows Hook] 屏蔽键盘按键

    //该例程为在系统级屏蔽一些系统键.如WIN.TAB.CAP.POWER.SLEEP.HOME等! //屏蔽组合键下面例程不适用!(比如CTRL+ESC需要在钩子函数中用(p.vkCode = VK_ ...

  5. windows Hook 消息分类

    调用SetWindowsHookEx的DLL的模块实例句柄,它可以经由DllMain入口的第一个参数得到.HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpf ...

  6. 电脑控制台灯(c# hook,显示室温,联网校正时间)

          突发奇想,于是便写了一个小程序用于控制台灯,这几天功能也在不断的完善中,目前基本已经完成.下面进行功能的简述的代码的分析. 整体设计包含下位机程序和上位机程序.下位机用的c语言,上位机用的 ...

  7. Hook入门

    Hook入门 2014-07-24 基本概念 Windows消息机制 Hook(钩子) 运行机制 核心函数 C# hook示例 基本概念[1] Windows消息机制[5] Windows操作系统是建 ...

  8. 在C#中使用全局鼠标、键盘Hook

    今天,有个同事问我,怎样在C#中使用全局钩子?以前写的全局钩子都是用unmanaged C或C++写个DLL来实现,可大家都知道,C#是基于.Net Framework的,是managed,怎么实现全 ...

  9. 如何在C#中使用全局鼠标、键盘Hook

    今天,有个同事问我,怎样在C#中使用全局钩子?以前写的全局钩子都是用unmanaged C或C++写个DLL来实现,可大家都知道,C#是基于.Net Framework的,是managed,怎么实现全 ...

随机推荐

  1. 每天学点Linux:七

    Shell编程 shell变量类型: 只有字符串型,变量可以被赋值,被修改,被引用. shell变量命名方法:第一个字符为字母,其余字符可以是字母,数字或下划线. 变量引用:在变量名前加$号,代表变量 ...

  2. 排序-java

    今天座右铭----每天的学习会让我们不断地进步! 往往面试中都会让我们用一种排序方法做一道排序题,下面我就罗列出快速排序.冒泡排序.插入排序.选择排序的java代码! 1.快速排序 public cl ...

  3. PLSQl远程连接oracle数据库

    PLSQL远程连接Oracle 10G 1.在安装ORACLE服务器的机器上搜索下列文件, ORACLE 服务器上的文件 oci.dll     ocijdbc10.dll     ociw32.dl ...

  4. 《Linux命令行与shell脚本编程大全》 第十四章 学习笔记

    第十四章:呈现数据 理解输入与输出 标准文件描述符 文件描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误 1.STDIN 代表标准输入.对于终端界面 ...

  5. javaScript操作select

    注意:Option中的O是要大写的,不然语法报错 1.动态创建select       function createSelect(){ var mySelect = document.createE ...

  6. This project references NuGet package(s) that are missing on this computer.

    Install Nuget. Right click on the solution and select "Enable NuGet Package Restore". Clic ...

  7. Oralce Exp 与 Imp 的使用方法

    1.完全:EXP  SYSTEM/SYSTEM@ORCL  FILE=C:\FULL.DMP  LOG=C:\FULL.DMP.LOG  FULL=Y  BUFFER=819200如果要执行完全导出, ...

  8. 在Windows7防火墙允许指定的端口

    在xp系统的时代,修改防火墙很方便,很简单.windows7或许是做得过于复杂了.当然所谓安全性也是相当于其他之前版本的系统更高了.为什么要打开端口,肯定是在windows7下启动了网络服务,需要开启 ...

  9. localhost简介、localhost与 127.0.0.1 及 本机IP 的区别

    localhost是什么意思? 相信有人会说是本地ip,曾有人说,用127.0.0.1比localhost好,可以减少一次解析. 看来这个入门问题还有人不清楚,其实这两者是有区别的. localhos ...

  10. Httpservlet cannot be resolved to a type

    这个问题与上个问题可以说是“错的类似”.解决方案:就是在Tomcat的lib目录下加入servlet-api.jar 即可.