王艳平的windows程序设计,挂钩API的例子为什么会失败?

liao0726 2011-04-07 04:18:40
在调试版下挂钩,书上也是讲了,如果想在发行版也能挂钩了话,那就把属性页改成读写,

下面是更改代码:

DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(lpAddr, &mbi, sizeof(mbi));
VirtualProtect(lpAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);
// 修改IAT表项,使其指向我们自定义的函数,
// 相当于语句“*lpAddr = (DWORD)MyMessageBoxA;”
DWORD* lpNewProc = (DWORD*)MyMessageBoxA;
::WriteProcessMemory(GetCurrentProcess(), lpAddr, &lpNewProc, sizeof(DWORD), NULL);
// 恢复页的保护属性
VirtualProtect(lpAddr, sizeof(DWORD), dwOldProtect, 0);

可是上面的代码加上去了,在Release版本下还是不行,谁知道是什么原因啊


问题补充 2011-04-07 13:34
没人知道吗?csdn我又没分,不然问问怎么会跟CSDN比,人家是专业的,问问那么杂,全部的代码在这。谁知道而且愿意回答的话,在调试和发行上运行一下吧,调试里可以,发行版就不行了,而且我还判断了下,发行版也确实是修改了IAT,IAT里挂钩函数前后的地址不一样,但就是不行,也没有哪个函数执行失败,可为什么不行呢?

全部代码:

#include <windows.h>
#include <stdio.h>
// 保存MessageBoxA函数的真实地址
PROC OrgAddr = (PROC)MessageBoxA;
// 定义MessageBoxA函数原型
typedef int (WINAPI *PFNMESSAGEBOX)(HWND, LPCSTR, LPCSTR, UINT uType);
// 挂钩指定模块hMod对MessageBoxA的调用
BOOL SetHook(HMODULE hMod,PROC OrgAddr,DWORD *NewAddr,char *DllName);
int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{ return ((PFNMESSAGEBOX)OrgAddr)(hWnd, "新函数", "09HookDemo", uType);}
void main()
{
// 调用原API函数
::MessageBox(NULL, "原函数", "09HookDemo", 0);
// 挂钩后再调用
SetHook(::GetModuleHandle(NULL),OrgAddr,(DWORD *)MyMessageBoxA,"user32.dll");
::MessageBox(NULL, "原函数", "09HookDemo", 0);
}
// 用于替换MessageBoxA的自定义函数
BOOL SetHook(HMODULE hMod,PROC OrgAddr,DWORD *NewAddr,char *DllName)
{ IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)hMod;
IMAGE_OPTIONAL_HEADER * pOptHeader =
(IMAGE_OPTIONAL_HEADER *)((BYTE*)hMod + pDosHeader->e_lfanew + 24);
IMAGE_IMPORT_DESCRIPTOR* pImportDesc = (IMAGE_IMPORT_DESCRIPTOR*)
((BYTE*)hMod + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
// 在导入表中查找user32.dll模块。因为MessageBoxA函数从user32.dll模块导出
while(pImportDesc->FirstThunk)
{ char* pszDllName = (char*)((BYTE*)hMod + pImportDesc->Name);
if(lstrcmpiA(pszDllName,DllName) == 0)
{ break; }
pImportDesc++;
}
if(pImportDesc->FirstThunk)
{ // 一个IMAGE_THUNK_DATA结构就是一个双字,它指定了一个导入函数
// 调入地址表其实是IMAGE_THUNK_DATA结构的数组,也就是DWORD数组
IMAGE_THUNK_DATA* pThunk = (IMAGE_THUNK_DATA*)
((BYTE*)hMod + pImportDesc->FirstThunk);
while(pThunk->u1.Function)
{ // lpAddr指向的内存保存了函数的地址
DWORD* lpAddr = (DWORD*)&(pThunk->u1.Function);
if(*lpAddr == (DWORD)OrgAddr)
{
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(lpAddr, &mbi, sizeof(mbi));
VirtualProtect(lpAddr, sizeof(DWORD), PAGE_READWRITE , &dwOldProtect);
//修改IAT表项,使其指向我们自定义的函数,
//相当于语句“*lpAddr = (DWORD)MyMessageBoxA;”
DWORD* lpNewProc = (DWORD *)MyMessageBoxA;
::WriteProcessMemory(GetCurrentProcess(), lpAddr, &NewAddr, sizeof(DWORD),NULL);
VirtualProtect(lpAddr, sizeof(DWORD), dwOldProtect, 0);
return TRUE;
}
pThunk++;
}
}
return FALSE;
}
...全文
181 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
liao0726 2011-04-08
  • 打赏
  • 举报
回复
我把那个教程贴出来吧:
3.HOOK API 的实现
定位导入表之后即可定位导入地址表。为了截获 API 调用,只要用自定义函数的地址覆
盖掉导入地址表中真实的 API函数地址即可。
下面是挂钩 MessageBoxA 函数的例子(09HookDemo 工程)。这个例子用自定义函数
MyMessageBoxA 取代了 API 函数 MessageBoxA,使得主模块中对 MessageBoxA 的调用都变
成了对自定义函数 MyMessageBoxA的调用。具体代码如下。
#include <windows.h>
#include <stdio.h>
// 挂钩指定模块hMod对MessageBoxA的调用
BOOL SetHook(HMODULE hMod);
// 定义MessageBoxA函数原型
typedef int (WINAPI *PFNMESSAGEBOX)(HWND, LPCSTR, LPCSTR, UINT uType);
// 保存MessageBoxA函数的真实地址
PROC g_orgProc = (PROC)MessageBoxA;
void main()
{ // 调用原API函数
::MessageBox(NULL, "原函数", "09HookDemo", 0);
// 挂钩后再调用
SetHook(::GetModuleHandle(NULL));
::MessageBox(NULL, "原函数", "09HookDemo", 0);
}
// 用于替换MessageBoxA的自定义函数
int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{ return ((PFNMESSAGEBOX)g_orgProc)(hWnd, "新函数", "09HookDemo", uType);}
BOOL SetHook(HMODULE hMod)
{ IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)hMod;
IMAGE_OPTIONAL_HEADER * pOptHeader =
(IMAGE_OPTIONAL_HEADER *)((BYTE*)hMod + pDosHeader->e_lfanew + 24);
IMAGE_IMPORT_DESCRIPTOR* pImportDesc = (IMAGE_IMPORT_DESCRIPTOR*)
((BYTE*)hMod + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
// 在导入表中查找user32.dll模块。因为MessageBoxA函数从user32.dll模块导出
while(pImportDesc->FirstThunk)
{ char* pszDllName = (char*)((BYTE*)hMod + pImportDesc->Name);
if(lstrcmpiA(pszDllName, "user32.dll") == 0)
{ break; }
pImportDesc++;
}
if(pImportDesc->FirstThunk)
{ // 一个IMAGE_THUNK_DATA结构就是一个双字,它指定了一个导入函数
// 调入地址表其实是IMAGE_THUNK_DATA结构的数组,也就是DWORD数组
IMAGE_THUNK_DATA* pThunk = (IMAGE_THUNK_DATA*)
((BYTE*)hMod + pImportDesc->FirstThunk);
while(pThunk->u1.Function)
{ // lpAddr指向的内存保存了函数的地址
DWORD* lpAddr = (DWORD*)&(pThunk->u1.Function);
if(*lpAddr == (DWORD)g_orgProc)
{
// 修改IAT表项,使其指向我们自定义的函数,
// 相当于语句“*lpAddr = (DWORD)MyMessageBoxA;”
DWORD* lpNewProc = (DWORD*)MyMessageBoxA;
::WriteProcessMemory(GetCurrentProcess(), lpAddr, &lpNewProc, sizeof(DWORD), NULL);
return TRUE;
}
pThunk++;
}
}
return FALSE;
}
}
运行这个程序,在 SetHook 函数执行前后,弹出的对话框是不相同的。执行前调用的是真
实的 MessageBoxA 函数,而执行后调用的是程序自定义的 MyMessageBoxA 函数。原因是
SetHook 函数修改了记录 MessageBoxA地址的导入地址表项。
if(*lpAddr == (DWORD)g_orgProc)
{ // 修改IAT表项,使其指向我们自定义的函数,相当于“*lpAddr = (DWORD)MyMessageBoxA;”
DWORD* lpNewProc = (DWORD*)MyMessageBoxA;
::WriteProcessMemory(::GetCurrentProcess(), lpAddr, &lpNewProc, sizeof(DWORD), NULL);
return TRUE;
}
事实上,这样的代码在 Debug 版本下运行是没有问题的,但是如果运行 Release 版本,程
序对 WriteProcessMemory 函数的调用将会失败,因为此时 lpAddr 指向的内存仅是可读的。要
想写这块内存,必须调用 VirtualProtect 函数改变内存地址所在页的页属性,将它改为可写,
如下代码所示。
// 修改页的保护属性
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(lpAddr, &mbi, sizeof(mbi));
VirtualProtect(lpAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);
// 写内存
:WriteProcessMemory(::GetCurrentProcess(),lpAddr, &lpNewProc, sizeof(DWORD), NULL);
// 恢复页的保护属性
VirtualProtect(lpAddr, sizeof(DWORD), dwOldProtect, 0);


不是运行错了,而是后面讲到的,我按照所讲的方法,在Release版本下没有HOOK 掉API函数
我在其它机子也试过了,XP的,同样的结果,我不由得怀疑这个教程的真实性。
开玩笑我怎么敢怀疑呢?但真的不知道怎么回事啊!
liao0726 2011-04-08
  • 打赏
  • 举报
回复
OK了,看来现在我还得努力啊!,汇编没接触过。
[Quote=引用 24 楼 lactoferrin 的回复:]
直接给你看main(你原来的)的汇编,一看就知道
00401140 56 push esi
00401141 8b35f4204100 mov esi,dword ptr [ConsoleTest+0x120f4 (004120f4)]
00401147 6a00 push 0
00401149 680c514100 push offset ConsoleTest+0x1510c (004……
[/Quote]
Lactoferrin 2011-04-08
  • 打赏
  • 举报
回复
直接给你看main(你原来的)的汇编,一看就知道
00401140 56 push esi
00401141 8b35f4204100 mov esi,dword ptr [ConsoleTest+0x120f4 (004120f4)]
00401147 6a00 push 0
00401149 680c514100 push offset ConsoleTest+0x1510c (0041510c)
0040114e 6804514100 push offset ConsoleTest+0x15104 (00415104)
00401153 6a00 push 0
00401155 ffd6 call esi
00401157 6a00 push 0
00401159 ff1508204100 call dword ptr [ConsoleTest+0x12008 (00412008)]
0040115f 50 push eax
00401160 e83b000000 call ConsoleTest+0x11a0 (004011a0)
00401165 83c404 add esp,4
00401168 6a00 push 0
0040116a 680c514100 push offset ConsoleTest+0x1510c (0041510c)
0040116f 6804514100 push offset ConsoleTest+0x15104 (00415104)
00401174 6a00 push 0
00401176 ffd6 call esi
00401178 5e pop esi
00401179 c3 ret
liao0726 2011-04-08
  • 打赏
  • 举报
回复
算了,还是有一点收获。尽管更增加了我的疑惑。
就这样将就着用吧。不过还是谢谢大家,
liao0726 2011-04-08
  • 打赏
  • 举报
回复
列宁大哥,训练你耐心的时候来到了,我还是不懂,MessageBox SetHook MessageBox不行,
而SetHook MessageBox MessageBox就可以,实在想不通,你说的那个__declspec(dllimport)函数是什么。如果这样的话,那没HOOK API 之前,就不能调用MessageBox了吗?这怎么能行。

[Quote=引用 21 楼 lactoferrin 的回复:]
你想都release版了编译器还好意思每次调用__declspec(dllimport)的函数都从内存中读地址吗?
[/Quote]
Lactoferrin 2011-04-08
  • 打赏
  • 举报
回复
你想都release版了编译器还好意思每次调用__declspec(dllimport)的函数都从内存中读地址吗?
liao0726 2011-04-08
  • 打赏
  • 举报
回复
咦!放到后面就没问题了,这是怎么回事啊,你可得跟我好好解释解释
[Quote=引用 19 楼 lactoferrin 的回复:]
你把release的改成
void main()
{ // 调用原API函数
SetHook(::GetModuleHandle(NULL));
::MessageBoxA(NULL, "原函数", "09HookDemo", 0);

::MessageBoxA(NULL, "原函数", "09HookDemo", 0);
}
就全部是新函数了,……
[/Quote]
Lactoferrin 2011-04-08
  • 打赏
  • 举报
回复
你把release的改成
void main()
{ // 调用原API函数
SetHook(::GetModuleHandle(NULL));
::MessageBoxA(NULL, "原函数", "09HookDemo", 0);

::MessageBoxA(NULL, "原函数", "09HookDemo", 0);
}
就全部是新函数了,看你能不能猜出来原因
zhlandwufei 2011-04-08
  • 打赏
  • 举报
回复
SetWindowsHookEx
c_losed 2011-04-07
  • 打赏
  • 举报
回复
#pragma comment(lib, "User32.lib")

你的代码加上这个就可以静态编译了
编译执行正常
liao0726 2011-04-07
  • 打赏
  • 举报
回复
今天不能选了,明天再来看。
c_losed 2011-04-07
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 liao0726 的回复:]

Debug 和Release下都没问题吗?
如果是的话,把工程项目给我,我再加点分,就选你了。
[/Quote]
都没问题。。。
代码还是你的代码
我只是Release 执行了一下
不过ms不能静态编译
liao0726 2011-04-07
  • 打赏
  • 举报
回复
Debug 和Release下都没问题吗?
如果是的话,把工程项目给我,我再加点分,就选你了。
c_losed 2011-04-07
  • 打赏
  • 举报
回复
XP SP2 测试代码没问题。。。
c_losed 2011-04-07
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 liao0726 的回复:]

怎么了,还在吗?热心肠的家伙,你可是我的救命稻草啊。一定要给我解决这个问题啊,我可以加分。
[/Quote]
在 机器出了点问题
是09HookDemo吧 Debug中
arong1234 2011-04-07
  • 打赏
  • 举报
回复
操作系统一样么?如果操作系统不一样也不可比[Quote=引用 8 楼 liao0726 的回复:]
这个代码跟原来的不一样,为了方便,被我给弄成一个函数了,不过原原本本的代码我也试过,但也一样。你试一试吧!
第九章,第三个例子,HOOK API 实现
书上自己也说了在Release版本不行,给出了解决方法,可是我按照书上那个方法,还是不行
[/Quote]
liao0726 2011-04-07
  • 打赏
  • 举报
回复
怎么了,还在吗?热心肠的家伙,你可是我的救命稻草啊。一定要给我解决这个问题啊,我可以加分。
liao0726 2011-04-07
  • 打赏
  • 举报
回复
这个代码跟原来的不一样,为了方便,被我给弄成一个函数了,不过原原本本的代码我也试过,但也一样。你试一试吧!
第九章,第三个例子,HOOK API 实现
书上自己也说了在Release版本不行,给出了解决方法,可是我按照书上那个方法,还是不行
c_losed 2011-04-07
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 liao0726 的回复:]

那你把上面例子复制过去,试一下,在调试版本没有问题,可在Release发行版就不行了,你去试一下,看可以吗?如果可以那就奇怪了,难道还因为机子而定!
[/Quote]
第九章的么?我这有源码 你说下是哪个工程 我调下
liao0726 2011-04-07
  • 打赏
  • 举报
回复
那你把上面例子复制过去,试一下,在调试版本没有问题,可在Release发行版就不行了,你去试一下,看可以吗?如果可以那就奇怪了,难道还因为机子而定!
加载更多回复(5)

3,881

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 其它技术问题
社区管理员
  • 其它技术问题社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧