HOOK API入门之Hook自己程序的MessageBoxW

原文地址:http://blog.csdn.net/friendan/article/details/12222651

说到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:

[cpp]  view plain  copy
 print ?
  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函数类型的语句如下:

[cpp]  view plain  copy
 print ?
  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地址做准备了。

如:

[cpp]  view plain  copy
 print ?
  1. MsgBoxW OldMsgBoxW=NULL;//指向原函数的指针  

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

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

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

[cpp]  view plain  copy
 print ?
  1. FARPROC pfOldMsgBoxW;  //指向函数的远指针  

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

3.获取API函数入口

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

如:

[cpp]  view plain  copy
 print ?
  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个字节,这里就先用别人写的汇编代码吧,

代码如下:

[cpp]  view plain  copy
 print ?
  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,

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

[cpp]  view plain  copy
 print ?
  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个字节了。

代码如下:

[cpp]  view plain  copy
 print ?
  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()函数具体代码如下:

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

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

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

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

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


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

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

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


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

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

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

希望有助于你们的理解。

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

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

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

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

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


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值