【发布时间】:2014-01-03 19:01:15
【问题描述】:
我的环境详情:
- 操作系统:Windows 7 Enterprise Service Pack 1(64 位操作系统)
- 编译器:Microsoft Visual Studio 2005(Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86)
- 我的程序 main.exe 和 hook.dll 是 32 位的
- Internet Explorer (iexplore.exe) 是 64 位
- Chrome (chrome.exe) 是 32 位
我编写了一个名为 main.exe 的 C++ 程序,它进行以下调用:
HOOKPROC callback = (HOOKPROC) GetProcAddress(dll, "keyboardHook");
HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, callback, dll, NULL);
keyboardHook 函数在名为 hook.dll 的 DLL 中定义。此函数记录控制台上的每次击键以及名为 out.txt 的文件。
我进行以下实验。
- 在命令提示符下执行 main.exe。
- 将 Internet Explorer(64 位)设为我的活动窗口,然后按 A、B 和 C。
- 将 Chrome(32 位)设为我的活动窗口,然后按 X、Y 和 Z。
- 使命令提示符成为我的活动窗口并按 Ctrl + C 杀死 main.exe。
我在控制台上只看到以下输出。
C:\lab\keyhook>main,exe
hinstDLL: 10000000; fdwReason: 1; lpvReserved: 00000000
); scan code: 28; transition state: 1
nCode: 0; wParam: 65 (A); scan code: 30; transition state: 0
nCode: 0; wParam: 65 (A); scan code: 30; transition state: 1
nCode: 0; wParam: 66 (B); scan code: 48; transition state: 0
nCode: 0; wParam: 66 (B); scan code: 48; transition state: 1
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 0
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 1
nCode: 0; wParam: 17 (◄); scan code: 29; transition state: 0
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 0
hinstDLL: 10000000; fdwReason: 2; lpvReserved: 00000000
hinstDLL: 10000000; fdwReason: 0; lpvReserved: 00000001
而且我在要登录的文件中只看到以下输出。
C:\lab\keyhook>type out.txt
); scan code: 28; transition state: 1
nCode: 0; wParam: 65 (A); scan code: 30; transition state: 0
nCode: 0; wParam: 65 (A); scan code: 30; transition state: 1
nCode: 0; wParam: 66 (B); scan code: 48; transition state: 0
nCode: 0; wParam: 66 (B); scan code: 48; transition state: 1
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 0
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 1
nCode: 0; wParam: 17 (◄); scan code: 29; transition state: 0
nCode: 0; wParam: 67 (C); scan code: 46; transition state: 0
您可以看到在 32 位 Chrome 上进行的击键在控制台或日志文件中都没有被捕获。
为什么我用 32 位 C++ 编译器编译的 32 位程序可以成功捕获 64 位 iexplore.exe 上的击键,但无法捕获 32 位 chrome.exe 上的击键?
MSDN 上的SetWindowsHookEx documentation 似乎表明我的 32 位程序应该能够捕获仅在其他 32 位程序上进行的击键。
SetWindowsHookEx 可用于将 DLL 注入另一个进程。 32位DLL不能注入64位进程,64位DLL不能注入32位进程。如果应用程序需要在其他进程中使用钩子,则需要 32 位应用程序调用 SetWindowsHookEx 将 32 位 DLL 注入 32 位进程,64 位应用程序调用 SetWindowsHookEx 将 64 位DLL 转换为 64 位进程。 32 位和 64 位 DLL 必须具有不同的名称。
因为钩子在应用程序的上下文中运行,它们必须与应用程序的“位数”相匹配。如果 32 位应用程序在 64 位 Windows 上安装全局挂钩,则 32 位挂钩将被注入到每个 32 位进程中(通常的安全边界适用)。在 64 位进程中,线程仍被标记为“已挂钩”。但是,由于 32 位应用程序必须运行钩子代码,系统会在钩子应用程序的上下文中执行钩子;具体来说,在调用 SetWindowsHookEx 的线程上。这意味着挂钩应用程序必须继续发送消息,否则可能会阻止 64 位进程的正常运行。
这是调用SetWindowsHookEx的主程序的完整代码。
// Filename: main.cc
#include <iostream>
#include <windows.h>
int main(int argc, char **argv)
{
HMODULE dll = LoadLibrary("hook.dll");
if (dll == NULL) {
std::cerr << "LoadLibrary error " << GetLastError() << std::endl;
return 1;
}
HOOKPROC callback = (HOOKPROC) GetProcAddress(dll, "keyboardHook");
if (callback == NULL) {
std::cerr << "GetProcAddress error " << GetLastError() << std::endl;
return 1;
}
HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, callback, dll, NULL);
if (hook == NULL) {
std::cerr << "SetWindowsHookEx error " << GetLastError() << std::endl;
return 1;
}
MSG messages;
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
UnhookWindowsHookEx(hook);
}
这是 DLL 代码。
// Filename: hook.cc
#include <iostream>
#include <fstream>
#include <windows.h>
extern "C" __declspec(dllexport)
LRESULT keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
std::ofstream outputTxt("out.txt", std::ofstream::out | std::ofstream::app);
outputTxt << "nCode: " << nCode << "; wParam: " << wParam
<<" (" << char(wParam) << "); scan code: "
<< ((lParam & 0xFF0000) >> 16)
<< "; transition state: " << ((lParam & 0x80000000) >> 31)
<< std::endl;
outputTxt.close();
std::cout << "nCode: " << nCode << "; wParam: " << wParam
<<" (" << char(wParam) << "); scan code: "
<< ((lParam & 0xFF0000) >> 16)
<< "; transition state: " << ((lParam & 0x80000000) >> 31)
<< std::endl;
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
std::cout << "hinstDLL: " << hinstDLL
<< "; fdwReason: " << fdwReason
<< "; lpvReserved: " << lpvReserved << std::endl;
return TRUE;
}
这就是我编译这个项目的方式:
vcvars32.bat
cl /LD hook.cc /link user32.lib
cl main.cc /link user32.lib
这就是流程的样子。看到 chrome.exe 和 main.exe 是 32 位进程,而 iexplore.exe 的实例是 64 位进程。
您能解释一下为什么我的观察结果与 MSDN 文档不符吗?
【问题讨论】:
-
如果你挂接的程序没有控制台,你不能用 32 位 DLL 挂接一个 64 位进程并且你看不到 std::cout 输出。你成功地钩住了你自己的程序。
-
@HansPassant 是的!这就是我实验中的问题。当我使用
"C:\\out.txt"作为输出文件路径时,我可以看到在 32 位和 64 位进程上的击键。我将使用固定代码添加答案。感谢您的帮助!