我创建了一个简单的Unicode窗口,然后按下键盘上的一个键,看看WM_CHAR消息的wParam值是多少,它给了我预期的字符的Unicode代码点,我按了"S"键和我的键盘布局设置为阿拉伯语(所以阿拉伯语字符是'س').
现在,我还在Spy ++中捕获了窗口消息,但是我注意到它给了我一个错误的wParam值,它实际上给了我Windows中的字符代码值:阿拉伯语代码页!
这是结果的截图:
这是源代码:
#define UNICODE #include#include LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_CLOSE: DestroyWindow(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_CHAR: char str[256]; sprintf(str, "0x%.4x", wParam); MessageBoxA(NULL, str, "", 0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); wc.lpszMenuName = NULL; wc.lpszClassName = L"WinClass"; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); RegisterClassEx(&wc); HWND hWnd = CreateWindowEx(0, L"WinClass", L"My Title", WS_OVERLAPPEDWINDOW, 261, 172, 594, 384, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); MSG msg; while(GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
Hans Passant.. 8
Spy ++的工作方式有点公开,您可以轻松判断何时运行Dumpbin.exe /imports
.exe文件.对于spyxx_amd64.exe(64位版本),最相关的条目是:
SPYXXHK_AMD64.DLL ... 3 SpyxxCallWndRetProc 2 SpyxxCallWndProc 4 SpyxxGetMsgProc USER32.dll ... 320 SetWindowsHookExW 31F SetWindowsHookExA
换句话说,它使用SetWindowsHookEx()来设置3个挂钩,WH_CALLWNDPROC,WH_CALLWNDPROCRET和WH_GETMESSAGE.Spyxxhk_amd64.dll是注入每个进程的DLL,它包含钩子回调.
请注意,它使用了 SetWindowsHookEx()的Unicode和Ansi版本.可以轻松解释结果的一种方法是在Unicode窗口上使用Ansi版本(SetWindowsHookExA).这样的钩子只能观察WM_CHAR消息的Ansi版本.
请记住,当您在桌面上运行包含Unicode和Ansi窗口的进程时,Spy ++有一个不可能解决的问题,这种情况并不罕见,它无法让所有人都满意.最简单的假设是它只是将SetWindowsHookExA作为最小公分母.
实际上证明 Spy ++得到了这个错误是很难做到的,而且我已经相当长一段时间了.任何试图在使用它时捕获Spy ++安装钩子的尝试都是一个破坏,钩子在程序启动时很早就安装好了.我最终发现的调试技术:
我首先NtUserSetWindowHookEx
通过反汇编SetWindowsHookExW的代码来找到本机api入口点.在我的机器(Win8.1)上,它位于0x00007FFECC3BA970.
启动VS提升,需要启动Spy ++.
文件+打开+项目/解决方案,然后选择Spyxx_amd64.exe.
Debug + Step Into.这将启动程序并在AfxWinMain中找到执行点(Spy ++是使用MFC库编写的).
Debug + Windows + Disassembly,将0x00007FFECC3BA970粘贴到地址框中
在此地址处设置断点.按F5
有两个错误命中,MFC内部使用SetWindowsHookExW().但随后它点亮了,三个点击都看起来像这样:
user32.dll!NtUserSetWindowsHookEx() user32.dll!_SetWindowsHookEx() + 0x5b bytes user32.dll!SetWindowsHookExAW() + 0x5b bytes user32.dll!SetWindowsHookExA() + 0x11 bytes spyxx_amd64.exe!SetMsgHook() + 0x6a bytes spyxx_amd64.exe!HookMain() + 0x470 bytes msvcr120.dll!_callthreadstart() Line 257 C msvcr120.dll!_threadstart(void * ptd) Line 237 + 0x5 bytes C kernel32.dll!BaseThreadInitThunk() + 0xd bytes ntdll.dll!RtlUserThreadStart() + 0x34 bytes
哪个是证明,你可以看到被调用的SetWindowsHookExA().Ansi版本,Spy ++只能显示WM_CHAR消息的Ansi版本.
Spy ++的工作方式有点公开,您可以轻松判断何时运行Dumpbin.exe /imports
.exe文件.对于spyxx_amd64.exe(64位版本),最相关的条目是:
SPYXXHK_AMD64.DLL ... 3 SpyxxCallWndRetProc 2 SpyxxCallWndProc 4 SpyxxGetMsgProc USER32.dll ... 320 SetWindowsHookExW 31F SetWindowsHookExA
换句话说,它使用SetWindowsHookEx()来设置3个挂钩,WH_CALLWNDPROC,WH_CALLWNDPROCRET和WH_GETMESSAGE.Spyxxhk_amd64.dll是注入每个进程的DLL,它包含钩子回调.
请注意,它使用了 SetWindowsHookEx()的Unicode和Ansi版本.可以轻松解释结果的一种方法是在Unicode窗口上使用Ansi版本(SetWindowsHookExA).这样的钩子只能观察WM_CHAR消息的Ansi版本.
请记住,当您在桌面上运行包含Unicode和Ansi窗口的进程时,Spy ++有一个不可能解决的问题,这种情况并不罕见,它无法让所有人都满意.最简单的假设是它只是将SetWindowsHookExA作为最小公分母.
实际上证明 Spy ++得到了这个错误是很难做到的,而且我已经相当长一段时间了.任何试图在使用它时捕获Spy ++安装钩子的尝试都是一个破坏,钩子在程序启动时很早就安装好了.我最终发现的调试技术:
我首先NtUserSetWindowHookEx
通过反汇编SetWindowsHookExW的代码来找到本机api入口点.在我的机器(Win8.1)上,它位于0x00007FFECC3BA970.
启动VS提升,需要启动Spy ++.
文件+打开+项目/解决方案,然后选择Spyxx_amd64.exe.
Debug + Step Into.这将启动程序并在AfxWinMain中找到执行点(Spy ++是使用MFC库编写的).
Debug + Windows + Disassembly,将0x00007FFECC3BA970粘贴到地址框中
在此地址处设置断点.按F5
有两个错误命中,MFC内部使用SetWindowsHookExW().但随后它点亮了,三个点击都看起来像这样:
user32.dll!NtUserSetWindowsHookEx() user32.dll!_SetWindowsHookEx() + 0x5b bytes user32.dll!SetWindowsHookExAW() + 0x5b bytes user32.dll!SetWindowsHookExA() + 0x11 bytes spyxx_amd64.exe!SetMsgHook() + 0x6a bytes spyxx_amd64.exe!HookMain() + 0x470 bytes msvcr120.dll!_callthreadstart() Line 257 C msvcr120.dll!_threadstart(void * ptd) Line 237 + 0x5 bytes C kernel32.dll!BaseThreadInitThunk() + 0xd bytes ntdll.dll!RtlUserThreadStart() + 0x34 bytes
哪个是证明,你可以看到被调用的SetWindowsHookExA().Ansi版本,Spy ++只能显示WM_CHAR消息的Ansi版本.