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 程序本身的MessageBoxA
///HOOK 程序本身的MessageBoxW
//启动HOOK后,切换到记事本的查找功能,其调用的MessageBoxW也被我们HOOK了
-------------------------------------------------------------------------------------------------------------------------------------
关于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)】
---------------------------------------------------------------------------------------------------------------------------------------------------
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
; 此处可以是显式导出
//截图如下
--------------------------------------------------------------------------------------------------------------------------------------------------------------
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. }
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
您的十分满意是我追求的宗旨。
您的一点建议是我后续的动力。