我不喜欢wndprocs和WinSpy++,却偶然发现calc.exe有点奇怪。
它似乎缺少WndProc。
这是我的屏幕截图:我制作的测试程序,WinSpy++窗口(显示N/A)和元凶。
也许该工具有些过时了,但是经验证据证明那里没有WndProc。
我不知道这是否是设计使然(这很奇怪),或者我是否缺少某些东西...
这里是引用代码:
Function FindWindow(title As String) As IntPtr
Return AutoIt.AutoItX.WinGetHandle(title)
End Function
Function GetWindowProc(handle As IntPtr) As IntPtr
Return GetWindowLong(handle, WindowLongFlags.GWL_WNDPROC)
End Function
最佳答案
简而言之(关于您的代码):GetWindowLong()
失败,因为您试图读取目标进程地址空间中的地址。
EXPLANATION
当GetWindowLong()
返回0时,表示存在来自MSDN的错误:
检查Marshal.GetLastWin32Error()
,您可能会看到错误代码是ERROR_ACCESS_DENIED
(数字值为0x5)。
为什么?因为GetWindowLong()
试图获取窗口过程的地址(或句柄)(不在您的代码中,而是在目标过程中,理论上它甚至可能是默认窗口过程,但我从未见过不依赖于此的应用程序主窗口最少的消息)。您可以使用此技巧(但我从未尝试过!)来查看窗口是否正在使用默认过程(您是否有地址),我不知道...应该尝试。
现在想想WNDPROC
是什么:
LRESULT (CALLBACK* WNDPROC) (HWND, UINT, WPARAM, LPARAM);
地址(在过程A中有效)在过程B中(在此毫无意义)是不可调用的。 Windows DLLs代码段跨进程共享(我假设,我没有检查,但是在安全性和性能之间是合理的)。
此外,
CallWindowProc(NULL, ...)
将理解NULL
作为调用该窗口类的窗口过程的特殊值(在HWND
所有者上)。从MSDN:Microsoft Spy++是如何做到的(也许WinSpy++没有)?没有WinSpy++源代码很难说。当然,这不是像
GetWindowLong()
这样简单的方法,正确的方法应该包含CreateRemoteThread()
并从中进行LoadLibrary()
的处理,但是Microsoft Spy++和WinSpy++源代码都不可用(AFAIK)进行进一步检查...更新
WinSpy++检查/调试与问题完全无关(您应向开发人员发布票证,您的源代码可能因上述原因而失败,您应该-始终-检查错误代码),但我们可能会采取以下措施:寻找乐趣。
在
InjectThread.c
中,我们看到它使用WriteProcessMemory
+ CreateRemoteThread
然后ReadProcessMemory
读回数据(未省略相关代码):// Write a copy of our injection thread into the remote process
WriteProcessMemory(hProcess, pdwRemoteCode, lpCode, cbCodeSize, &dwWritten);
// Write a copy of the INJTHREAD to the remote process. This structure
// MUST start on a 32bit boundary
pRemoteData = (void *)((BYTE *)pdwRemoteCode + ((cbCodeSize + 4) & ~ 3));
// Put DATA in the remote thread's memory block
WriteProcessMemory(hProcess, pRemoteData, lpData, cbDataSize, &dwWritten);
hRemoteThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)pdwRemoteCode, pRemoteData, 0, &dwRemoteThreadId);
// Wait for the thread to terminate
WaitForSingleObject(hRemoteThread, INFINITE);
// Read the user-structure back again
if(!ReadProcessMemory(hProcess, pRemoteData, lpData, cbDataSize, &dwRead))
{
//an error occurred
}
“常规”选项卡和“类”选项卡中的窗口过程有所不同(在“类”选项卡中它正确显示一个值)。从
DisplayClassInfo.c
://window procedure
if(spy_WndProc == 0)
{
wsprintf(ach, _T("N/A"));
}
else
{
wsprintf(ach, szHexFmt, spy_WndProc);
if(spy_WndProc != spy_WndClassEx.lpfnWndProc)
lstrcat(ach, _T(" (Subclassed)"));
}
//class window procedure
if(spy_WndClassEx.lpfnWndProc == 0)
wsprintf(ach, _T("N/A"));
else
wsprintf(ach, szHexFmt, spy_WndClassEx.lpfnWndProc);
如您所见,它们是不同的值(以不同的方式获得)。填充
spy_WndProc
的代码在WinSpy.c
和GetRemoteWindowInfo.c
中。从GetRemoteInfo()
中的WinSpy.c
中提取的代码:GetClassInfoEx(0, spy_szClassName, &spy_WndClassEx);
GetRemoteWindowInfo(hwnd, &spy_WndClassEx, &spy_WndProc, spy_szPassword, 200);
现在在
GetRemoteWindowInfo()
中,我们看到了对GetClassInfoExProc
的调用(在另一个过程中注入(inject)):pInjData->wndproc = (WNDPROC)pInjData->fnGetWindowLong(pInjData->hwnd, GWL_WNDPROC);
pInjData->fnGetClassInfoEx(pInjData->hInst,
(LPTSTR)pInjData->szClassName, &pInjData->wcOutput);
如您所见(请遵循使用源代码),
wcOutput
是“类”选项卡中显示的内容,wndproc
是“常规”选项卡中显示的内容。只是GetWindowLong()
失败了,但是GetClassInfoEx
却没有(但是它们不一定检索相同的值,因为(如果我没错的话),您在WNDCLASSEX
中拥有的就是您在RegisterClassEx
中注册的内容,但是在GetWindowLong()
中获得的却是您与SetWindowLong()
挂钩的内容。