说到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. C# json 总结

    json格式字符串转换为实体类,大括号 {} 表示对象,[] 数组表示列表. json文件读取到内存中就是字符串,.NET操作json就是生成与解析json字符串. 添加引用:using Newton ...

  2. vue中v-for的用法以及参数的作用

    <template> <div> <div class="content clearfix" v-on:click="goorderingD ...

  3. css中的!important作用

    css中的!important作用 一.总结 1.!important:是hack, 2.!important作用:让浏览器首选执行这个语句,当对同一个对象设置了多个同类型的属性的时候,首选执行这一个 ...

  4. nuxt按需引入 element-UI、自定义主题色(终极按需引入)

    首先你要知道 nuxt.js怎么引入第三方插件 : 不多BB. 一.按需引入element-UI 第一步:安装 babel-plugin-component: npm install babel-pl ...

  5. python基础--数值类型和序列类型

    Python中数值类型:int(整数),float(浮点数),True/False(布尔值,首字母必须大写) int:1    #任意整数 float:2.3   #小数 python赋值: a = ...

  6. Vijos 1164 曹冲养猪(中国剩余定理)

    P1164曹冲养猪 Accepted 标签:三国争霸[显示标签] 描写叙述 自从曹冲搞定了大象以后,曹操就開始捉摸让儿子干些事业,于是派他到中原养猪场养猪,但是曹冲满不高兴.于是在工作中马马虎虎,有一 ...

  7. Dcloud课程4 如何进行APP接口开发

    Dcloud课程4 如何进行APP接口开发 一.总结 一句话总结:通过json或者xml. 1.APP如何进行通信? 通过在地址上接参数指明传递的数据的类型.而数据传递的类型一般是XML和json. ...

  8. 好记性不如烂笔头——datagridview相关

    DataGridViewTextBoxColumn dgv_IDY = new DataGridViewTextBoxColumn(); dgv_IDY.Visible = false; dgv_ID ...

  9. (转)bat批处理的注释语句

    在批处理中,段注释有一种比较常用的方法: goto start = 可以是多行文本,可以是命令 = 可以包含重定向符号和其他特殊字符 = 只要不包含 :start 这一行,就都是注释 :start 另 ...

  10. jQuery知识点汇总

    $(this) 当前 HTML 元素 $("p") 所有 <p> 元素 $("p.intro") 所有 class="intro" ...