说到HOOK,我看了很多的资料和教程,无奈就是学不会HOOK,不懂是我的理解能力差,还是你们说的

不够明白,直到我看了以下这篇文章,终于学会了HOOK:

http://blog.sina.com.cn/s/blog_628821950100xmuc.html    //感谢文章作者的分享,让我学会了HOOK

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

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

既然窝已经入门了HOOK,窝会写几篇关于HOOK的文章,让同样想入门HOOK,却难以入门的童鞋

有个参考,这篇是第一篇,希望帮助到有此需要的盆友,我测试的环境都是:Win7+VS2008+MFC

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

第一篇说的是HOOK自己程序的MessageBoxW,诚然HOOK自己程序用到的API在实际应用中没有什么

大的用处,不过我认为对于我们理解HOOK却有莫大的帮助,因此我的HOOK文章就从HOOK自己的程序

开始,文章后面附有本例子程序的VS2008源码下载地址。

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

//先看下我写的例子程序及运行效果截图,后面再对其进行分析

//未钩MessageBoxW前

//钩MessageBoxW后

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

根据我的理解,先说一下HOOK API的一般思路和步骤。

HOOK API的思路就是修改原API的入口,使其跳转到我们的假API入口,

然后执行我们的假API函数,为什么说是假API函数呢?

因为我们的假API,除了函数名称和真实API的名称不一样之外,其它都是相同的,即

它们的函数参数和返回值和调用形式都是一样的。

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

HOOK API的一般步骤:

1.定义假API函数

注意假API函数,除了函数名称和真实API不一样之外,其它的都要跟真实API的定义相同,如参数类型和返回值、调用形式等。

如我们可以这样定义假的MessageBoxW:

  1. int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
  2. {
  3. //定义假API时,具体的函数体代码暂时可不写...
  4. return 0;
  5. }

至于我们如何知道真实API的原型定义呢?很简单,查看MSDN即可,或者你也可以在VS2008开发环境中,

写下该真实API,然后选中该API,再点鼠标右键,选择【转到定义】,就可以看到其原型声明了。

...........................................................................................................................................................................

2.定义API函数类型

因为我们要动态获取原API函数的地址,获取到后,我们要将其保存起来,保存在哪里呢?

这就是定义API函数类型的原因了,有了API函数类型的定义后,我们就可以用其定义一个变量来 保存获取到的

真实API函数的地址了,例如,我定义的MessageBoxW函数类型的语句如下:

  1. //原函数类型定义
  2. typedef int (WINAPI* MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);

注意,原函数类型定义中的函数返回值和参数类型要跟MessageBoxW的定义相同。

上面就是定义了API函数类型MsgBoxW,这跟我们常见的int、char、float等类型,用法是一样的,

实际上,我们完全可以把MsgBoxW当成int类型来使用,这样一说,API函数类型也不过如此嘛。

有了API函数类型MsgBoxW,我们就可以用其来定义变量,从而为保存原API地址做准备了。

如:

  1. MsgBoxW OldMsgBoxW=NULL;//指向原函数的指针

有了API函数类型后,我们还需要一个远指针类型的变量,因为系统API是在dll文件中的,更多关于

远指针的信息,我现在也不是很清楚,有兴趣的盆友,不妨百度一下。系统已经给我们定义好了

远指针类型:FARPROC,我们直接拿来用即可,如:

  1. FARPROC pfOldMsgBoxW;  //指向函数的远指针

..................................................................................................................................................................................................................

3.获取API函数入口

有了保存原API函数地址的变量OldMsgBox和指向原API函数的远指针,我们就可以获取真实API的地址了。

如:

  1. //获取原API入口地址
  2. HMODULE hmod=::LoadLibrary(_T("User32.dll"));
  3. OldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");
  4. pfOldMsgBoxW=(FARPROC)OldMsgBoxW;

注意,上面我们将原API地址OldMsgBoxW强制转换成了远指针pfOldMsgBoxW,至于为什么要强制转换,

我现在也不是很清楚,想知道的童鞋,可以百度一下。

................................................................................................................................................................................................................

4.保存原API入口的前5个字节

保存的目的是为了恢复用的,毕竟我们HOOK了API后,还需要调用真实的API嘛,

如何保存一个函数入口的前5个字节呢?这里用到了汇编代码,至于不用汇编可以吗?我想是可以的。

有空时,我再试一下,不用汇编是否能保存一个API入口的前5个字节,这里就先用别人写的汇编代码吧,

代码如下:

  1. // 将原API的入口前5个字节代码保存到OldCode[]
  2. BYTE OldCode[5];
  3. _asm
  4. {
  5. lea edi,OldCode      //获取OldCode数组的地址,放到edi
  6. mov esi,pfOldMsgBoxW //获取原API入口地址,放到esi
  7. cld    //方向标志位,为以下两条指令做准备
  8. movsd //复制原API入口前4个字节到OldCode数组
  9. movsb //复制原API入口第5个字节到OldCode数组
  10. }

....................................................................................................................................................................................................................

5.获取新入口的前5个字节

因为我们修改真实API入口的前5个字节,使其跳转到我们假API函数的入口地址,即改成Jmp xxxxxxxx,其中xxxxxxxx就是我们

假API的入口地址,那么我们如何得到该地址呢?

前人已经总结出了一条公式:

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

Jmp nAddr; //我们要获取的就是nAddr的值

既然已经有了公式,我们拿来用即可,UserFunAddr相当于我们的假API,SysFunAddr相当于真实API,这里指令的大小为5,

实现以上公式的汇编代码如下:

  1. //获取MyMessageBoxW的相对地址,为Jmp做准备
  2. //int nAddr= UserFunAddr – SysFunAddr - (我们定制的这条指令的大小);
  3. //Jmp nAddr;
  4. //(我们定制的这条指令的大小), 这里是5,5个字节嘛
  5. BYTE NewCode[5];
  6. _asm
  7. {
  8. lea eax,MyMessageBoxW //获取我们的MyMessageBoxW函数地址
  9. mov ebx,pfOldMsgBoxW  //原系统API函数地址
  10. sub eax,ebx           //int nAddr= UserFunAddr – SysFunAddr
  11. sub eax,5             //nAddr=nAddr-5
  12. mov dword ptr [NewCode+1],eax //将算出的地址nAddr保存到NewCode后面4个字节
  13. //注:一个函数地址占4个字节
  14. }

......................................................................................................................................................................................................................

6.修改真实API入口的前5个字节

前面我们已经保存了真实API入口的前5个字节,也已经计算出了新入口的前5个字节,

可谓万事俱备,只欠东风,现在可以修改真实API入口的前5个字节了。

代码如下:

  1. //填充完毕,现在NewCode[]里的指令相当于Jmp MyMessageBoxW
  2. //既然已经获取到了Jmp MyMessageBoxW
  3. //现在该是将Jmp MyMessageBoxW写入原API入口前5个字节的时候了
  4. //知道为什么是5个字节吗?
  5. //Jmp指令相当于0xe9,占一个字节的内存空间
  6. //MyMessageBoxW是一个地址,其实是一个整数,占4个字节的内存空间
  7. //int n=0x123;   n占4个字节和MyMessageBoxW占4个字节是一样的
  8. //1+4=5,知道为什么是5个字节了吧
  9. HookOn();

修改API入口前5个字节的HookOn()函数具体代码如下:

  1. //开启钩子的函数
  2. void HookOn()
  3. {
  4. ASSERT(hProcess!=NULL);
  5. DWORD dwTemp=0;
  6. DWORD dwOldProtect;
  7. //修改API函数入口前5个字节为jmp xxxxxx
  8. VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
  9. WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0);
  10. VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
  11. }

以上修改API函数入口前5个字节,用到了两个系统API函数,百度百科里面对它们有详解,在此就不说了。

注:hProcess是进程句柄,我们可以这样获取它:

DWORD dwPid=::GetCurrentProcessId();
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);

..........................................................................................................................................................................................................

7.恢复API函数入口前5个字节

有修改,就有恢复嘛,恢复函数代码如下:

  1. //关闭钩子的函数
  2. void HookOff()
  3. {
  4. ASSERT(hProcess!=NULL);
  5. DWORD dwTemp=0;
  6. DWORD dwOldProtect;
  7. //恢复API函数入口前5个字节
  8. VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
  9. WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,5,0);
  10. VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
  11. }

注:hProcess是进程句柄,我们可以这样获取它:

DWORD dwPid=::GetCurrentProcessId();
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以上就是HOOK API的大致流程了,看不明白的盆友,不妨再看下我写的源码,文章结合源码,

希望有助于你们的理解。

源码下载地址:Hook自己程序的MessageBoxW.zip

学会了HOOK自己的程序,下篇我们再接再厉,HOOK系统所有程序的MessageBoxA和MessageBoxW

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

下面贴一下以上这个程序的主要代码,算是有个整体印象吧:

  1. //原函数类型定义
  2. typedef int (WINAPI* MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);
  3. MsgBoxW OldMsgBoxW=NULL;//指向原函数的指针
  4. FARPROC pfOldMsgBoxW;  //指向函数的远指针
  5. BYTE OldCode[5]; //原系统API入口代码
  6. BYTE NewCode[5]; //原系统API新的入口代码 (jmp xxxxxxxx)
  7. HANDLE hProcess=NULL;//本程序进程句柄
  8. HINSTANCE hInst=NULL;//API所在的dll文件句柄
  9. void HookOn();
  10. void HookOff();
  11. int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
  12. {
  13. TRACE(lpText);
  14. HookOff();//调用原函数之前,记得先恢复HOOK呀,不然是调用不到的
  15. //如果不恢复HOOK,就调用原函数,会造成死循环
  16. //毕竟调用的还是我们的函数,从而造成堆栈溢出,程序崩溃。
  17. int nRet=::MessageBoxW(hWnd,_T("哈哈,MessageBoxW被HOOK了"),lpCaption,uType);
  18. HookOn();//调用完原函数后,记得继续开启HOOK,不然下次会HOOK不到。
  19. return nRet;
  20. }
  21. //开启钩子的函数
  22. void HookOn()
  23. {
  24. ASSERT(hProcess!=NULL);
  25. DWORD dwTemp=0;
  26. DWORD dwOldProtect;
  27. //修改API函数入口前5个字节为jmp xxxxxx
  28. VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
  29. WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0);
  30. VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
  31. }
  32. //关闭钩子的函数
  33. void HookOff()
  34. {
  35. ASSERT(hProcess!=NULL);
  36. DWORD dwTemp=0;
  37. DWORD dwOldProtect;
  38. //恢复API函数入口前5个字节
  39. VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
  40. WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,5,0);
  41. VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
  42. }
  43. //获取API函数入口前5个字节
  44. //旧入口前5个字节保存在前面定义的字节数组BYTE OldCode[5]
  45. //新入口前5个字节保存在前面定义的字节数组BYTE NewCode[5]
  46. void GetApiEntrance()
  47. {
  48. //获取原API入口地址
  49. HMODULE hmod=::LoadLibrary(_T("User32.dll"));
  50. OldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");
  51. pfOldMsgBoxW=(FARPROC)OldMsgBoxW;
  52. if (pfOldMsgBoxW==NULL)
  53. {
  54. MessageBox(NULL,_T("获取原API入口地址出错"),_T("error!"),0);
  55. return;
  56. }
  57. // 将原API的入口前5个字节代码保存到OldCode[]
  58. _asm
  59. {
  60. lea edi,OldCode      //获取OldCode数组的地址,放到edi
  61. mov esi,pfOldMsgBoxW //获取原API入口地址,放到esi
  62. cld    //方向标志位,为以下两条指令做准备
  63. movsd //复制原API入口前4个字节到OldCode数组
  64. movsb //复制原API入口第5个字节到OldCode数组
  65. }
  66. NewCode[0]=0xe9;//实际上0xe9就相当于jmp指令
  67. //获取MyMessageBoxW的相对地址,为Jmp做准备
  68. //int nAddr= UserFunAddr – SysFunAddr - (我们定制的这条指令的大小);
  69. //Jmp nAddr;
  70. //(我们定制的这条指令的大小), 这里是5,5个字节嘛
  71. BYTE NewCode[5];
  72. _asm
  73. {
  74. lea eax,MyMessageBoxW //获取我们的MyMessageBoxW函数地址
  75. mov ebx,pfOldMsgBoxW  //原系统API函数地址
  76. sub eax,ebx           //int nAddr= UserFunAddr – SysFunAddr
  77. sub eax,5             //nAddr=nAddr-5
  78. mov dword ptr [NewCode+1],eax //将算出的地址nAddr保存到NewCode后面4个字节
  79. //注:一个函数地址占4个字节
  80. }
  81. //填充完毕,现在NewCode[]里的指令相当于Jmp MyMessageBoxW
  82. //既然已经获取到了Jmp MyMessageBoxW
  83. //现在该是将Jmp MyMessageBoxW写入原API入口前5个字节的时候了
  84. //知道为什么是5个字节吗?
  85. //Jmp指令相当于0xe9,占一个字节的内存空间
  86. //MyMessageBoxW是一个地址,其实是一个整数,占4个字节的内存空间
  87. //int n=0x123;   n占4个字节和MyMessageBoxW占4个字节是一样的
  88. //1+4=5,知道为什么是5个字节了吧
  89. HookOn();
  90. }
  91. //开始Hook MessageBoxW
  92. void CHookMsgBoxDlg::OnBnClickedBtnStartHook()
  93. {
  94. DWORD dwPid=::GetCurrentProcessId();
  95. hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
  96. GetApiEntrance();
  97. SetDlgItemText(IDC_STATIC_INFO,_T("Hook已启动"));
  98. }
  99. //调用MessageBoxW
  100. void CHookMsgBoxDlg::OnBnClickedBtnCallMsgBox()
  101. {
  102. ::MessageBoxW(m_hWnd,_T("这是正常的MessageBoxW"),_T("Hello"),0);
  103. }
  104. //停止Hook MessageBoxW
  105. void CHookMsgBoxDlg::OnBnClickedBtnStopHook()
  106. {
  107. HookOff();
  108. SetDlgItemText(IDC_STATIC_INFO,_T("Hook未启动"));
  109. }

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

您的十分满意是我追求的宗旨。

您的一点建议是我后续的动力。

https://blog.csdn.net/friendan/article/details/12222651

HOOK API入门之Hook自己程序的MessageBoxW(简单入门)的更多相关文章

  1. HOOK API(二)—— HOOK自己程序的 MessageBox

    HOOK API(二) —— HOOK自己程序的 MessageBox 0x00 前言 以下将给出一个简单的例子,作为HOOK API的入门.这里是HOOK 自己程序的MessageBox,即将自己程 ...

  2. HOOK API(二) —— HOOK自己程序的 MessageBox

    转载来源:https://www.cnblogs.com/hookjc/ 0x00 前言 以下将给出一个简单的例子,作为HOOK API的入门.这里是HOOK 自己程序的MessageBox,即将自己 ...

  3. HOOK API(三)—— HOOK 所有程序的 MessageBox

    HOOK API(三) —— HOOK 所有程序的 MessageBox 0x00 前言 本实例要实现HOOK MessageBox,包括MessageBoxA和MessageBoxW,其实现细节与H ...

  4. HOOK API(三) —— HOOK 所有程序的 MessageBox

    转载来源:https://www.cnblogs.com/hookjc/ 0x00 前言 本实例要实现HOOK MessageBox,包括MessageBoxA和MessageBoxW,其实现细节与H ...

  5. 汇编 -- Hook API (MessageBoxW)

    说到HOOK.我看了非常多的资料和教程.无奈就是学不会HOOK.不懂是我的理解能力差.还是你们说的 不够明确,直到我看了下面这篇文章,最终学会了HOOK: http://blog.sina.com.c ...

  6. 汇编Ring 3下实现 HOOK API

    [文章标题]汇编ring3下实现HOOK API [文章作者]nohacks(非安全,hacker0058) [作者主页]hacker0058.ys168.com [文章出处]看雪论坛(bbs.ped ...

  7. HOOK API(四)—— 进程防终止

    HOOK API(四) —— 进程防终止 0x00        前言 这算是一个实战吧,做的一个应用需要实现进程的防终止保护,查了相关资料后决定用HOOK API的方式实现.起初学习HOOK API ...

  8. HOOK API (一)——HOOK基础+一个鼠标钩子实例

    HOOK API (一)——HOOK基础+一个鼠标钩子实例 0x00 起因 最近在做毕业设计,有一个功能是需要实现对剪切板的监控和进程的防终止保护.原本想从内核层实现,但没有头绪.最后决定从调用层入手 ...

  9. HOOK API(四) —— 进程防终止

    0x00        前言 这算是一个实战吧,做的一个应用需要实现进程的防终止保护,查了相关资料后决定用HOOK API的方式实现.起初学习HOOK API的起因是因为要实现对剪切板的监控,后来面对 ...

随机推荐

  1. 29.局部和全局重载new delete

    #include <iostream> #include <Windows.h> using namespace std; //全局内存管理,统计释放内存,分配内存 //重载全 ...

  2. jQuery自定义插件规范

    <ul class="list"> <li>导航列表 <ul class="nav"> <li>导航列表1< ...

  3. 蚂蚁金服入股36Kr给我的一点警示:应该相信自己的理性分析,不能盲目迷信权威

    最近3年,关注互联网和创业投资比较多,每周都会关注下本周发生的创业投融资大事件. 我注意到,一些自媒体作者经常会发布一些有"前瞻性"的文章,比如"美团大众要合并了&quo ...

  4. NuGet 使用及dll管理

    NuGet学习笔记(1)——初识NuGet及快速安装使用 作者: 懒惰的肥兔  来源: 博客园  发布时间: 2012-05-20 21:33  阅读: 53168 次  推荐: 33   原文链接  ...

  5. eclipse git冲突解决

    1.工程->Team->同步: 2.从远程pull至本地,就会出现如下内容: 3.使用Merge Tool,执行第二项 4.再手动修改 4.修改后的文件需要添加到Git index中去: ...

  6. CODEVS——T1332 上白泽慧音 || 洛谷——P1726 上白泽慧音

    http://codevs.cn/problem/1332/|| https://www.luogu.org/problem/show?pid=1726#sub  时间限制: 1 s  空间限制: 1 ...

  7. Cronolog 分割 Tomcat8 Catalina.out日志 (转)

    默认情况下,tomcat的catalina.out日志文件是没有像其它日志一样,按日期进行分割,而是全部输出全部写入到一个catalina.out,这样日积月累就会造成.out日志越来越大,给管理造成 ...

  8. 洛谷 P2069 松鼠吃果子

    P2069 松鼠吃果子 题目描述 有N个一种松鼠喜欢吃的果子由下向上串排成一列,并标号1,2,...N.一只松鼠从最下果子开始向上跳,并且第i次跳可以一次跳过i*i*i除以5的余数+1个果子(=i*i ...

  9. 在线java反编译服务

    大家是否遇到过有java class文件,却没有java源码的苦恼.近期findmaven.net提供了在线java反编译服务http://www.findmaven.net/decompile_cn ...

  10. 程序中图片透明 函数(使用SetBkColor API函数)

    void DrawTransparentBitmap(HDC  hdc,  HBITMAP  hBitmap,  short  xStart,  short  yStart,  COLORREF  c ...