HOOK所有程序的MessageBox

让我学会HOOK的一篇文章:http://blog.sina.com.cn/s/blog_628821950100xmuc.html    //感激不尽呀


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

这篇文章用到的HOOK代码,跟我的前一篇文章【HOOK API入门之Hook自己程序的MessageBoxW】差不多,

因为这次HOOK的是系统所有用到MessageBox的程序,所以我们必须把代码写在dll文件中,

这里我用的是MFC DLL

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

熟悉API的都知道,系统中没有MessageBox,有的只是MessageBoxA和MessageBoxW,

因此HOOK MessageBox,其实是HOOK MessageBoxA和MessageBoxW。

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

//老规矩,先看看我们的程序及效果截图,后面再进行分析,文章后面附有VS2008源码下载

//程序主界面

HOOK所有程序的MessageBox_#pragma

//HOOK 程序本身的MessageBoxA

HOOK所有程序的MessageBox_加载_02

///HOOK 程序本身的MessageBoxW

HOOK所有程序的MessageBox_API_03

//启动HOOK后,切换到记事本的查找功能,其调用的MessageBoxW也被我们HOOK了

HOOK所有程序的MessageBox_加载_04

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

关于dll如何和主程序对话框进行通信,请看我的另一篇文章【低级鼠标钩子WH_MOUSE_LL】

文章地址:

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

下面说一下HOOK所有程序的MessageBox的思路。

首先必须明确我们的HOOK代码是写在dll文件中的,要HOOK一个程序的MessageBox,则必须

让其加载我们的dll文件,如何才能让一个程序加载我们的dll文件呢?这里我用的是鼠标钩子,

即我们安装一个全局的鼠标钩子WH_MOUSE,这样当任何一个程序响应鼠标消息时,其就会

加载我们的dll文件,按理说,窗口程序都会响应鼠标消息的,不然我们怎么能移动一个窗口呢。

当然,不响应鼠标消息的程序,如果你也想HOOK它,换其它类型的钩子即可。

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

下面说一下该dll文件的编写过程:

1.打开VS 2008,新建一个MFC DLL工程,dll类型我选择的是【带静态链接MFC的规则DLL(R)】

HOOK所有程序的MessageBox_API_05

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

2.在dll文件中编写安装鼠标钩子的函数StartHook(HWND hWnd)和卸载鼠标钩子的函数StopHook(),

这里说的只是个大概的过程,变量定义什么的,我就忽略了,具体请看原工程源码,

我们这里只说重点。

相关代码如下:

       
1. //鼠标钩子过程,目的是加载本dll到使用鼠标的程序  
2. //鼠标钩子的作用:当鼠标在某程序窗口中时,其就会加载我们这个dll  
3. LRESULT CALLBACK MouseProc(  
4. int nCode,      // hook code  
5. WPARAM wParam,  // message identifier  
6. LPARAM lParam   // mouse coordinates  
7.                            )  
8. {  
9. if (nCode==HC_ACTION)  
10.     {  
11. //将钩子所在窗口句柄发给主程序  
12. LPARAM)(((PMOUSEHOOKSTRUCT)lParam)->hwnd));  
13.     }  
14. return CallNextHookEx(hhk,nCode,wParam,lParam);  
15. }  
16.   
17.   
18. //安装钩子  
19. BOOL WINAPI StartHook(HWND hWnd)  
20. {  
21.     g_hWnd=hWnd;  
22.     hhk=::SetWindowsHookEx(WH_MOUSE,MouseProc,hInst,0);  
23. if (hhk==NULL)  
24.     {  
25. return FALSE;  
26.     }   
27. else  
28.     {  
29. return TRUE;  
30.     }  
31. }  
32.   
33. //卸载钩子  
34. VOID WINAPI StopHook()  
35. {  
36. //记得恢复原API入口哈  
37. //主程序调用该函数时,恢复的只是主程序原API的入口,  
38. //其它程序的API入口还没有被恢复,所以我们必须处理  
39. //dll退出过程,即在函数ExitInstance()中,调用恢复  
40. //API入口的函数HookOff(),只有这样,其它程序再次调用  
41. //原API时,才不会发生错误喔。  
42. //当我们HOOK所有程序的某个系统API时,千万要注意在  
43. //ExitInstance()中调用HookOff(),血的教训哈。  
44.   
45. if (hhk!=NULL)  
46.     {  
47.         UnhookWindowsHookEx(hhk);  
48.         FreeLibrary(hInst);  
49.     }  
50. }


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

3.导出我们的StartHook(HWND hWnd)和StopHook()函数,给外部程序调用,如果不导出,我们的主程序是调用不到

这两个函数的哦。如何导出呢?找到dll工程中的,后缀为def的文件,打开它,然后在里面写上要导出

的函数的名字即可,如:

; HookMessageBox.def : 声明 DLL 的模块参数。
 LIBRARY      "HookMessageBox"
 EXPORTS
 StartHook   //导出StartHook
 StopHook   //导出StopHook
     ; 此处可以是显式导出

//截图如下

HOOK所有程序的MessageBox_API_06

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

4.在MFC dll的入口函数InitInstance()编写代码,写什么代码好呢?

因为HOOK一个程序的API,我们必须知道它的进程句柄,既然程序加载我们的dll时,会

执行dll入口函数,我们就在这里获取该程序的进程句柄。

也因为我们要HOOK加载我们dll程序的API,因此我们就在dll入口处HOOK MessageBox,

即程序一记载我们的dll文件时,我们就HOOK其API。

主要代码如下:

1. //dll程序入口,当程序加载dll时,会执行InitInstance()  
2. BOOL CHookMessageBoxApp::InitInstance()  
3. {  
4.     CWinApp::InitInstance();  
5.   
6.     hInst=AfxGetInstanceHandle();  
7. DWORD dwPid=::GetCurrentProcessId();  
8.     hProcess=::OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);  
9.   
10. //开始注入,即HOOK  
11. return TRUE;


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

5.接下我们要特别处理dll的退出函数ExitInstance(),注意呀,我们一定要在dll的退出函数ExitInstance()里面,

写上恢复原API入口的代码或函数,不然程序再次调用该API时,会崩溃,因为dll退出后,假API函数也就不存在了。


1. //dll退出时  
2. int CHookMessageBoxApp::ExitInstance()  
3. {  
4.       
5. //dll退出时,记得恢复原API入口哈,血的教训呀。  
6. //当我们钩所有程序的API,且dll退出没有恢复原API入口时,  
7. //那么当被钩程序再次调用该API时,会发生错误,因为我们的  
8. //dll程序已经退出了,原来API入口被修改的前五个字节还是  
9. //指向我们dll中的自己定义的函数地址,现在dll退出,该地址  
10. //自然也就不存在了,程序调用该地址时,自然会发生崩溃了。  
11.   
12. return CWinApp::ExitInstance();  
13. }


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

6.在主程序中调用dll文件导出的两个函数StartHook(HWND hWnd)和StopHook(),

代码如下:

       
1. //安装钩子  
2. HINSTANCE g_hInst=NULL;  
3. void CHookTestDlg::OnBnClickedBtnStartHook()  
4. {  
5. "HookMessageBox.dll"));//加载dll文件  
6. if (g_hInst==NULL)  
7.     {  
8. "Load HookMessageBox.dll Failed"));  
9. return;  
10.     }  
11. typedef BOOL (WINAPI* StartHook)(HWND hWnd);//函数原型定义  
12.     StartHook Hook;  
13. "StartHook");//获取函数地址  
14. if (Hook==NULL)  
15.     {  
16. "GetFunction StartHook Failed"));  
17. return;  
18.     }  
19. if (Hook(m_hWnd))//调用函数  
20.     {  
21. "Hook Ok"));  
22.     }   
23. else  
24.     {  
25. "Hook Failed"));  
26.     }  
27. }  
28.   
29. //卸载钩子  
30. void CHookTestDlg::OnBnClickedBtnStopHook()  
31. {  
32. if (g_hInst==NULL)  
33.     {  
34. return;  
35.     }  
36. typedef VOID (WINAPI* StopHook)();//函数原型定义  
37.     StopHook UnHook;  
38. "StopHook");//获取函数地址  
39. if (UnHook==NULL)  
40.     {  
41. "GetFunction StopHook Failed"));  
42.         FreeLibrary(g_hInst);  
43.         g_hInst=NULL;  
44. return;  
45.     }  
46.   
47. //调用函数  
48.     FreeLibrary(g_hInst);  
49.     g_hInst=NULL;  
50. "StopHook ok"));  
51. }


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

以上就是HOOK系统所有程序MessageBox的大致流程了,前面说了HOOK的代码跟我写的前一篇文章的HOOK代码差别不大,

灵活应变一下即可,看不明白的盆友,请结合源码和文章来看,希望有助于你的理解。这里还是要特别强调一下,要在dll的

退出函数中恢复原API入口,千万别忘记哦。

本工程源码下载地址:Hook所有程序的MessageBox.zip


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

接下来贴一下本工程dll文件的主要代码:

       
1. #define  UM_WNDTITLE WM_USER+100 //自定义消息  
2.   
3. //全局共享变量  
4. #pragma data_seg(".Share")  
5. HWND g_hWnd=NULL;//主窗口句柄;  
6. HHOOK hhk=NULL; //鼠标钩子句柄;  
7. HINSTANCE hInst=NULL;//本dll实例句柄;  
8. #pragma data_seg()  
9. #pragma comment(linker, "/section:.Share,rws")  
10.   
11. HANDLE hProcess=NULL;  
12. BOOL bIsInjected=FALSE;  
13.   
14. //原函数定义  
15. typedef int (WINAPI *MsgBoxA)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);  
16. typedef int (WINAPI *MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);  
17. MsgBoxA oldMsgBoxA=NULL; //用于保存原函数地址  
18. MsgBoxW oldMsgBoxW=NULL; //用于保存原函数地址  
19. FARPROC pfMsgBoxA=NULL;//指向原函数地址的远指针  
20. FARPROC pfMsgBoxW=NULL;//指向原函数地址的远指针  
21. BYTE OldCodeA[5]; //老的系统API入口代码  
22. BYTE NewCodeA[5]; //要跳转的API代码 (jmp xxxx)  
23. BYTE OldCodeW[5]; //老的系统API入口代码  
24. BYTE NewCodeW[5]; //要跳转的API代码 (jmp xxxx)  
25.   
26. int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);  
27. int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);  
28.   
29.   
30. //开启钩子的函数  
31. void HookOn()   
32. {   
33.      ASSERT(hProcess!=NULL);  
34.   
35. DWORD dwTemp=0,dwOldProtect,dwRet=0,dwWrite;  
36.    
37. //修改原API入口前五个字节为jmp xxxxxxxx  
38. //Debug版本在我这里修改失败,运行Release版本成功  
39.      VirtualProtectEx(hProcess,pfMsgBoxA,5,PAGE_READWRITE,&dwOldProtect);   
40.      dwRet=WriteProcessMemory(hProcess,pfMsgBoxA,NewCodeA,5,&dwWrite);  
41. if (0==dwRet||0==dwWrite)  
42.      {  
43. "啊,写入失败");  
44.      }  
45.      VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&dwTemp);  
46.   
47. //修改原API入口前五个字节为jmp xxxxxxxx  
48. //Debug版本在我这里修改失败,运行Release版本成功  
49.      VirtualProtectEx(hProcess,pfMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);   
50.      dwRet=WriteProcessMemory(hProcess,pfMsgBoxW,NewCodeW,5,&dwWrite);  
51. if (0==dwRet||0==dwWrite)  
52.      {  
53. "啊,写入失败");  
54.      }  
55.      VirtualProtectEx(hProcess,pfMsgBoxW,5,dwOldProtect,&dwTemp);  
56. }  
57.   
58. //关闭钩子的函数  
59. void HookOff()//将所属进程中add()的入口代码恢复  
60. {   
61.      ASSERT(hProcess!=NULL);  
62.   
63. DWORD dwTemp=0,dwOldProtect=0,dwRet=0,dwWrite=0;  
64.   
65. //恢复原API入口  
66.      VirtualProtectEx(hProcess,pfMsgBoxA,5,PAGE_READWRITE,&dwOldProtect);   
67.      dwRet=WriteProcessMemory(hProcess,pfMsgBoxA,OldCodeA,5,&dwWrite);   
68. if (0==dwRet||0==dwWrite)  
69.      {  
70. "啊,写入失败");  
71.      }  
72.      VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&dwTemp);   
73.   
74. //恢复原API入口  
75.      VirtualProtectEx(hProcess,pfMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);   
76.      WriteProcessMemory(hProcess,pfMsgBoxW,OldCodeW,5,&dwWrite);   
77. if (0==dwRet||0==dwWrite)  
78.      {  
79. "啊,写入失败");  
80.      }  
81.      VirtualProtectEx(hProcess,pfMsgBoxW,5,dwOldProtect,&dwTemp);    
82. }  
83.   
84.   
85. void Inject()  
86. {  
87.    
88. if (!bIsInjected)  
89.  {   
90. //保证只调用1次  
91.   
92. //获取函数  
93. HMODULE hmod=::LoadLibrary(_T("User32.dll"));  
94. "MessageBoxA");  
95.   pfMsgBoxA=(FARPROC)oldMsgBoxA;  
96. "MessageBoxW");  
97.   pfMsgBoxW=(FARPROC)oldMsgBoxW;  
98.     
99. if (pfMsgBoxA==NULL)  
100.   {  
101. "cannot get MessageBoxA()"),_T("error"),0);  
102. return;  
103.   }  
104. if (pfMsgBoxW==NULL)  
105.   {  
106. "cannot get MessageBoxW()"),_T("error"),0);  
107. return;  
108.   }  
109.   
110. // 将原API中的入口代码保存入OldCodeA[],OldCodeW[]  
111.   _asm   
112.   {   
113.    lea edi,OldCodeA   
114.    mov esi,pfMsgBoxA  
115.    cld   
116.    movsd   
117.    movsb   
118.   }  
119.   _asm   
120.   {   
121.       lea edi,OldCodeW   
122.           mov esi,pfMsgBoxW  
123.           cld   
124.           movsd   
125.           movsb   
126.   }  
127.   
128. //实际上0xe9就相当于jmp指令  
129. //实际上0xe9就相当于jmp指令  
130.   
131. //获取我们的API的地址  
132.   _asm   
133.   {   
134.       lea eax,MyMessageBoxA  
135.           mov ebx,pfMsgBoxA  
136.           sub eax,ebx   
137.           sub eax,5   
138.           mov dword ptr [NewCodeA+1],eax   
139.   }   
140.   _asm   
141.   {   
142.    lea eax,MyMessageBoxW  
143.    mov ebx,pfMsgBoxW  
144.    sub eax,ebx   
145.    sub eax,5   
146.    mov dword ptr [NewCodeW+1],eax   
147.   }   
148.    
149. //填充完毕,现在NewCode[]里的指令相当于Jmp Myadd  
150. //可以开启钩子了  
151.  }  
152. }  
153.   
154. //我们的假API函数MyMessageBoxA  
155. int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType)  
156. {  
157. int nRet=0;  
158.   
159. //调用原函数之前,记得先恢复HOOK呀,不然是调用不到的  
160. //如果不恢复HOOK,就调用原函数,会造成死循环  
161. //毕竟调用的还是我们的函数,从而造成堆栈溢出,程序崩溃。  
162.   
163. "哈哈,MessageBoxA被HOOK了吧",lpCaption,uType);  
164.     nRet=::MessageBoxA(hWnd,lpText,lpCaption,uType);  
165. //调用完原函数后,记得继续开启HOOK,不然下次会HOOK不到哦。   
166.   
167. return nRet;  
168. }  
169.   
170. //我们的假API函数MyMessageBoxW  
171. int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)  
172. {  
173. int nRet=0;  
174.   
175. //调用原函数之前,记得先恢复HOOK呀,不然是调用不到的  
176. //如果不恢复HOOK,就调用原函数,会造成死循环  
177. //毕竟调用的还是我们的函数,从而造成堆栈溢出,程序崩溃。  
178.   
179. "哈哈,MessageBoxW被HOOK了吧"),lpCaption,uType);  
180.     nRet=::MessageBoxW(hWnd,lpText,lpCaption,uType);  
181. //调用完原函数后,记得继续开启HOOK,不然下次会HOOK不到哦。   
182.   
183. return nRet;  
184. }  
185.   
186. // CHookMessageBoxApp  
187.   
188. BEGIN_MESSAGE_MAP(CHookMessageBoxApp, CWinApp)  
189. END_MESSAGE_MAP()  
190.   
191.   
192. // CHookMessageBoxApp 构造  
193.   
194. CHookMessageBoxApp::CHookMessageBoxApp()  
195. {  
196.       
197. }  
198.   
199.   
200. // 唯一的一个 CHookMessageBoxApp 对象  
201.   
202. CHookMessageBoxApp theApp;  
203.   
204. //dll程序入口,当程序加载dll时,会执行InitInstance()  
205. BOOL CHookMessageBoxApp::InitInstance()  
206. {  
207.     CWinApp::InitInstance();  
208.   
209.     hInst=AfxGetInstanceHandle();  
210. DWORD dwPid=::GetCurrentProcessId();  
211.     hProcess=::OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);  
212.   
213. //开始注入,即HOOK  
214. return TRUE;  
215. }  
216.   
217. //鼠标钩子过程,目的是加载本dll到使用鼠标的程序  
218. //鼠标钩子的作用:当鼠标在某程序窗口中时,其就会加载我们这个dll  
219. LRESULT CALLBACK MouseProc(  
220. int nCode,      // hook code  
221. WPARAM wParam,  // message identifier  
222. LPARAM lParam   // mouse coordinates  
223.                            )  
224. {  
225. if (nCode==HC_ACTION)  
226.     {  
227. //将钩子所在窗口句柄发给主程序  
228. LPARAM)(((PMOUSEHOOKSTRUCT)lParam)->hwnd));  
229.     }  
230. return CallNextHookEx(hhk,nCode,wParam,lParam);  
231. }  
232.   
233.   
234. //安装钩子  
235. BOOL WINAPI StartHook(HWND hWnd)  
236. {  
237.     g_hWnd=hWnd;  
238.     hhk=::SetWindowsHookEx(WH_MOUSE,MouseProc,hInst,0);  
239. if (hhk==NULL)  
240.     {  
241. return FALSE;  
242.     }   
243. else  
244.     {  
245. return TRUE;  
246.     }  
247. }  
248.   
249. //卸载钩子  
250. VOID WINAPI StopHook()  
251. {  
252. //记得恢复原API入口哈  
253. //主程序调用该函数时,恢复的只是主程序原API的入口,  
254. //其它程序的API入口还没有被恢复,所以我们必须处理  
255. //dll退出过程,即在函数ExitInstance()中,调用恢复  
256. //API入口的函数HookOff(),只有这样,其它程序再次调用  
257. //原API时,才不会发生错误喔。  
258. //当我们HOOK所有程序的某个系统API时,千万要注意在  
259. //ExitInstance()中调用HookOff(),血的教训哈。  
260.   
261. if (hhk!=NULL)  
262.     {  
263.         UnhookWindowsHookEx(hhk);  
264.         FreeLibrary(hInst);  
265.     }  
266. }  
267.   
268. //dll退出时  
269. int CHookMessageBoxApp::ExitInstance()  
270. {  
271.       
272. //dll退出时,记得恢复原API入口哈,血的教训呀。  
273. //当我们钩所有程序的API,且dll退出没有恢复原API入口时,  
274. //那么当被钩程序再次调用该API时,会发生错误,因为我们的  
275. //dll程序已经退出了,原来API入口被修改的前五个字节还是  
276. //指向我们dll中的自己定义的函数地址,现在dll退出,该地址  
277. //自然也就不存在了,程序调用该地址时,自然会发生崩溃了。  
278.   
279. return CWinApp::ExitInstance();  
280. }


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


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

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