请教一个64位WIN7下进行COM HOOK的问题。

贪食蛇男 2011-11-22 02:46:06
我HOOK了 IFileOperation 的 CopyItems MoveItems PerformOperations 这些操作,在32位WIN7上运行良好,但在64位WIN7上完全不行。
于是我写了一个非常小的DEMO工程,代码如下:

#include <windows.h>
#include <stdio.h>

size_t WINAPI HookVtbl(void* pObject, size_t classIdx, size_t methodIdx, size_t newMethod)
{
size_t** vtbl = (size_t**)pObject;
DWORD oldProtect = 0;
size_t oldMethod = vtbl[classIdx][methodIdx];
VirtualProtect(vtbl[classIdx] + sizeof(size_t*) * methodIdx, sizeof(size_t*), PAGE_READWRITE, &oldProtect);
vtbl[classIdx][methodIdx] = newMethod;
VirtualProtect(vtbl[classIdx] + sizeof(size_t*) * methodIdx, sizeof(size_t*), oldProtect, &oldProtect);
return oldMethod;
}

class A
{
public:
virtual void printa()
{
printf("print in A\n");
}

virtual void print(int i, int j, int k)
{
printf("print in A\n");
}

virtual void print2(int i, int j, int k)
{
printf("print2 in A\n");
}
};

void WINAPI print(int i, int j, int k)
{
printf("print in global name space %d %d %d\n", i, j, k);
}

void WINAPI print2(int i, int j, int k)
{
printf("print2 in global name space %d %d %d\n", i, j, k);
}

int main()
{
printf("sizeof(int) = %d\n", sizeof(int));
printf("sizeof(size_t) = %d\n", sizeof(size_t));
A* a = new A();
int old = NULL;
size_t oldSizeT = NULL;
size_t oldSize2 = NULL;
oldSizeT = HookVtbl(a, 0, 1, (size_t)print);
oldSize2 = HookVtbl(a, 0, 2, (size_t)print2);
a->print(1, 2 ,3);
a->print2(1, 2 ,3);
HookVtbl(a, 0, 1, oldSizeT);
HookVtbl(a, 0, 2, oldSize2);
a->print(1, 2 ,3);
a->print2(1, 2 ,3);
}



分别编译成32位和64位,然后拿到64位机上测试,得出结果如下:

C:\Users\hiroyukki>C:\Users\hiroyukki\Desktop\vtb.exe
sizeof(int) = 4
sizeof(size_t) = 4
print in global name space 1 2 3
print2 in global name space 1 2 3
print in A
print2 in A

C:\Users\hiroyukki>C:\Users\hiroyukki\Desktop\vtb64.exe
sizeof(int) = 4
sizeof(size_t) = 8
print in global name space 3792736 1 2
print2 in global name space 3792736 1 2
print in A
print2 in A

可以注意到,64位的时候,HOOK用的函数参数不太对劲……。
然后我把全局函数在64位时候增加参数,则运行正常了。代码如下:

#include <windows.h>
#include <stdio.h>

size_t WINAPI HookVtbl(void* pObject, size_t classIdx, size_t methodIdx, size_t newMethod)
{
size_t** vtbl = (size_t**)pObject;
DWORD oldProtect = 0;
size_t oldMethod = vtbl[classIdx][methodIdx];
VirtualProtect(vtbl[classIdx] + sizeof(size_t*) * methodIdx, sizeof(size_t*), PAGE_READWRITE, &oldProtect);
vtbl[classIdx][methodIdx] = newMethod;
VirtualProtect(vtbl[classIdx] + sizeof(size_t*) * methodIdx, sizeof(size_t*), oldProtect, &oldProtect);
return oldMethod;
}

class A
{
public:
virtual void printa()
{
printf("print in A\n");
}

virtual void print(int i, int j, int k)
{
printf("print in A\n");
}

virtual void print2(int i, int j, int k)
{
printf("print2 in A\n");
}
};

void WINAPI print(
#ifdef _WIN64
int t,
#endif
int i, int j, int k)
{
printf("print in global name space %d %d %d\n", i, j, k);
}

void WINAPI print2(
#ifdef _WIN64
int t,
#endif
int i, int j, int k)
{
printf("print2 in global name space %d %d %d\n", i, j, k);
}

int main()
{
printf("sizeof(int) = %d\n", sizeof(int));
printf("sizeof(size_t) = %d\n", sizeof(size_t));
A* a = new A();
int old = NULL;
size_t oldSizeT = NULL;
size_t oldSize2 = NULL;
oldSizeT = HookVtbl(a, 0, 1, (size_t)print);
oldSize2 = HookVtbl(a, 0, 2, (size_t)print2);
a->print(1, 2 ,3);
a->print2(1, 2 ,3);
HookVtbl(a, 0, 1, oldSizeT);
HookVtbl(a, 0, 2, oldSize2);
a->print(1, 2 ,3);
a->print2(1, 2 ,3);
}



则输出“看起来”是正常的:
C:\Users\hiroyukki>C:\Users\hiroyukki\Desktop\vtb.exe
sizeof(int) = 4
sizeof(size_t) = 4
print in global name space 1 2 3
print2 in global name space 1 2 3
print in A
print2 in A

C:\Users\hiroyukki>C:\Users\hiroyukki\Desktop\vtb64.exe
sizeof(int) = 4
sizeof(size_t) = 8
print in global name space 1 2 3
print2 in global name space 1 2 3
print in A
print2 in A
...全文
1132 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhangmengvv 2013-10-04
  • 打赏
  • 举报
回复
引用 5 楼 hiroyukki 的回复:
明白,我的题目和内容有点不搭调。 我做COM HOOK的时候是那么做的,我COM HOOK时候的错误和这个不一样,并且我也已经查出来我做COM HOOK时错在哪里了。 我是做 IFileOperation 移动/复制文件的监控,结果在WIN7 64位上把函数地址当做一个DWORD处理,发生了截断造成的崩溃,和上面我说的没关系。 谢谢你三楼的精彩回复,即是说,64位上调用约定不再是参数全部入栈了…… [Quote=引用 4 楼 lactoferrin 的回复:] 你这个不是COM hook,32位的COM是stdcall修饰的thiscall,this放在栈中,因此替代函数也得补一个参数 [/Quote]
楼主好,我现在也遇到了和你相似的问题,在32位win7上hook CopyItems_Index成功,在64位上却没有反应,
贪食蛇男 2011-11-23
  • 打赏
  • 举报
回复
经过多次使用 ida32 ida64 windbg 对 _cdecl _thiscall _stdcall 的调用方式的分析,印证了列宁大牛的话。
我的实验中一个很有代表性的例子如下,希望以后有需要的朋友能看到它:


#include <stdio.h>
#include <windows.h>

size_t WINAPI HookVtbl(void* pObject, size_t classIdx, size_t methodIdx, size_t newMethod)
{
size_t** vtbl = (size_t**)pObject;
DWORD oldProtect = 0;
size_t oldMethod = vtbl[classIdx][methodIdx];
VirtualProtect(vtbl[classIdx] + sizeof(size_t*) * methodIdx, sizeof(size_t*), PAGE_READWRITE, &oldProtect);
vtbl[classIdx][methodIdx] = newMethod;
VirtualProtect(vtbl[classIdx] + sizeof(size_t*) * methodIdx, sizeof(size_t*), oldProtect, &oldProtect);
return oldMethod;
}

class A
{
public:
A(int i)
{
m_i = i;
}

virtual void print(int a, int b, int c)
{
printf("in A: %d %d %d %d\n", a, b, c, m_i);
}

int m_i;
};

void WINAPI print(
#ifdef _WIN64
A* pa,
#endif
int a, int b, int c)
{
#ifndef _WIN64
A* pa = NULL;
_asm mov pa, ecx;
#endif
printf("global print: %d %d %d %d\n", a, b, c, pa->m_i);
}

int main()
{
A* a = new A(56);
a->print(12, 34, 78);
size_t old = HookVtbl(a, 0, 0, (size_t)print);
a->print(12, 34, 78);
HookVtbl(a, 0, 0, old);
a->print(12, 34, 78);
delete a;
}

贪食蛇男 2011-11-22
  • 打赏
  • 举报
回复
列宁,看来我指明了让你来回答这个问题是对的,多谢了,这就结贴。
mLee79 2011-11-22
  • 打赏
  • 举报
回复
M$VS 按上面一行的 win64规范办, *nix 按下面的 elf64规范办... 我还专门写了个给yasm用的宏定义, 写的汇编代码可以在 win64 elf64 下都可以用, 不过就只能最多传递4个参数了... M$就稀饭折腾, 看这win64调用约定就没elf64约定靠谱,高效 ....
healer_kx 2011-11-22
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 mlee79 的回复:]

win64 下用 rcx , rdx , r8 , r9 传递前4个参数, 多余的参数由堆栈传递, 并且会留下这4个参数的位置, 浮点数用 xmm0 ... 寄存器传递 ...
elf64 下用 rdi , rsi , rdx , rcx , r8 , r9 传递前6个参数...
[/Quote]
你是说VS的编译器是这么处理的?
mLee79 2011-11-22
  • 打赏
  • 举报
回复
win64 下用 rcx , rdx , r8 , r9 传递前4个参数, 多余的参数由堆栈传递, 并且会留下这4个参数的位置, 浮点数用 xmm0 ... 寄存器传递 ...
elf64 下用 rdi , rsi , rdx , rcx , r8 , r9 传递前6个参数...
healer_kx 2011-11-22
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 lactoferrin 的回复:]

只说你的程序
32位virtual void print(int i, int j, int k)的this放在ecx,i,j,k在栈,WINAPI是__stdcall,ijk也在栈并且顺序一样,因此用void WINAPI print(int i, int j, int k)替代 virtual void print(int i, int j, int k)时,ijk位置一样

64位的……
[/Quote]
学习了。
nightkids_008 2011-11-22
  • 打赏
  • 举报
回复
列宁 一如既往的牛X
cocoabird 2011-11-22
  • 打赏
  • 举报
回复
3楼牛,学习
贪食蛇男 2011-11-22
  • 打赏
  • 举报
回复
明白,我的题目和内容有点不搭调。
我做COM HOOK的时候是那么做的,我COM HOOK时候的错误和这个不一样,并且我也已经查出来我做COM HOOK时错在哪里了。
我是做 IFileOperation 移动/复制文件的监控,结果在WIN7 64位上把函数地址当做一个DWORD处理,发生了截断造成的崩溃,和上面我说的没关系。
谢谢你三楼的精彩回复,即是说,64位上调用约定不再是参数全部入栈了……
[Quote=引用 4 楼 lactoferrin 的回复:]

你这个不是COM hook,32位的COM是stdcall修饰的thiscall,this放在栈中,因此替代函数也得补一个参数
[/Quote]
Lactoferrin 2011-11-22
  • 打赏
  • 举报
回复
你这个不是COM hook,32位的COM是stdcall修饰的thiscall,this放在栈中,因此替代函数也得补一个参数
Lactoferrin 2011-11-22
  • 打赏
  • 举报
回复
只说你的程序
32位virtual void print(int i, int j, int k)的this放在ecx,i,j,k在栈,WINAPI是__stdcall,ijk也在栈并且顺序一样,因此用void WINAPI print(int i, int j, int k)替代 virtual void print(int i, int j, int k)时,ijk位置一样

64位的virtual void print(int i, int j, int k)的this放在rcx,i在edx,j在r8d,k在r9d,而
void WINAPI print(int i, int j, int k) 的i在ecx,j在edx,k在r8d,顺序就不对,因此前面补一个t时,t正好占了this的位置,后面的才对齐
贪食蛇男 2011-11-22
  • 打赏
  • 举报
回复
看起来64位机调用约定和32位大相迥异,有没有做过64位COM HOOK的高手来给我解下惑。

64,642

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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