(为什么)Windows“Calc.exe”缺少WndProc?

时间:2014-12-03 11:35:38

标签: c vb.net windows winapi spy++

我正在摆弄wndprocs和WinSpy ++,我偶然发现了一个与calc.exe有关的奇怪事情。  它似乎缺少WndProc。

这是我的截图:我制作的测试程序,WinSpy ++窗口,显示N / A和罪魁祸首。

A test program I made, the Winspy window, showing N/A, and the culprit.

也许这个工具有点过时,但经验证据证明没有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

2 个答案:

答案 0 :(得分:4)

简而言之(关于您的代码):GetWindowLong()失败,因为您正在尝试读取目标进程地址空间中的地址。

<强>说明

GetWindowLong()返回0时,表示来自MSDN的错误

  

如果函数失败,则返回值为零。要获取扩展错误信息,请调用GetLastError。

检查Marshal.GetLastWin32Error(),您可能会看到错误代码为ERROR_ACCESS_DENIED(数值为0x5)。

为什么呢?因为GetWindowLong()正在尝试获取窗口过程的地址(或句柄)(不是在您的代码中,而是在目标进程中,理论上它甚至可能是默认窗口过程但我从未见过应用程序主窗口,不会处理至少几条消息)。您可以使用此技巧(但我从未尝试过!)来查看窗口是否正在使用默认程序(您有地址或不具有地址),我不知道......有人应该尝试。

现在想想WNDPROC是什么:

LRESULT (CALLBACK* WNDPROC) (HWND, UINT, WPARAM, LPARAM);

地址(在进程A中有效)在进程B中是不可调用的(根本没有任何意义)。 Windows DLL代码段是跨进程共享的(我假设,我没有检查,但在安全和性能之间的游戏中是合理的。)

此外CallWindowProc(NULL, ...)会将NULL理解为调用该窗口类(在HWND所有者上)的窗口过程的特殊值。来自MSDN:

  

...如果通过调用GetWindowLong函数获取此值...窗口或对话框过程的地址,或仅对CallWindowProc有意义的特殊内部值。

Microsoft Spy ++如何做到(也许WinSpy ++没有)?没有WinSpy ++源代码很难说。确保它不是那么容易,如GetWindowLong()正确的方式应该涉及CreateRemoteThread()LoadLibrary(),但Microsoft Spy ++和WinSpy ++源代码都不是可用(AFAIK)进一步检查......

<强>更新

WinSpy ++检查/调试与问题相关(你应该向开发人员发一张票,你的源代码可能因我上面解释的那样失败,你应该 - 总是< / strong> - 检查错误代码)但我们可能会寻找乐趣。

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.cGetRemoteWindowInfo.c。从GetRemoteInfo()中的WinSpy.c提取的代码:

GetClassInfoEx(0, spy_szClassName, &spy_WndClassEx);
GetRemoteWindowInfo(hwnd, &spy_WndClassEx, &spy_WndProc, spy_szPassword, 200);

现在在GetRemoteWindowInfo()我们看到了对GetClassInfoExProc的调用(在其他过程中注入):

pInjData->wndproc = (WNDPROC)pInjData->fnGetWindowLong(pInjData->hwnd, GWL_WNDPROC);
pInjData->fnGetClassInfoEx(pInjData->hInst,
    (LPTSTR)pInjData->szClassName, &pInjData->wcOutput);

如您所见(请遵循使用源代码)wcOutput是“类”标签中显示的内容,wndproc显示在“常规”标签中显示的内容。简单GetWindowLong()失败但GetClassInfoEx没有失败(但他们不一定检索相同的值,因为(如果我没有错)你在WNDCLASSEX中拥有的是你在{{1}注册的内容但是你使用RegisterClassEx获得的是你与GetWindowLong()挂钩的内容。

答案 1 :(得分:0)

你是对的。它没有WndProc(...)函数。它只是简单地使用DlgProc来处理对话事件。我现在就像我写的那样服务器/瘦客户端&#39; C / C ++中的代码捕获直接调用到Windows API函数,如WndProc(...)。任何Windows GUI功能 - BeginPaint(...)作为示例。我使用CALC.EXE作为测试,并且可执行文件在服务器上运行,而GUI调用被中继/返回到瘦客户端。只通过Vista测试了calc.exe版本。新版本有可能已被编程&#39;不同 - 意味着不使用Win32 SDK。但是,即使是MFC也只是Win32 SDK的一个shell,