C++ SetWindowsHookEx+;什么是CBT';不行?或者至少不是我认为应该的方式?
我有一个诊断程序,它使用C++ SetWindowsHookEx+;什么是CBT';不行?或者至少不是我认为应该的方式?,c++,windows,dll,keyboard-hook,setwindowshookex,C++,Windows,Dll,Keyboard Hook,Setwindowshookex,我有一个诊断程序,它使用SetWindowsHookEx和WH\u-KEYBOARD\u-LL在系统范围内扫描代码,我想扩展它以监控窗口焦点的变化,这是一种使用SetWindowsHookEx和基于计算机的训练CBT hookWH\u-CBT的可能性 对于WH\u KEYBOARD\u LLhook,我能够在我的过程中使用hook函数,它工作了,几乎在我桌面上的每个应用程序窗口中捕获按键。我的理解是,WH\u CBT实际上需要在一个单独的dll中,以便可以将它注入到其他进程中。所以我做了这个 我
SetWindowsHookEx
和WH\u-KEYBOARD\u-LL
在系统范围内扫描代码,我想扩展它以监控窗口焦点的变化,这是一种使用SetWindowsHookEx
和基于计算机的训练CBT hookWH\u-CBT
的可能性
对于WH\u KEYBOARD\u LL
hook,我能够在我的过程中使用hook函数,它工作了,几乎在我桌面上的每个应用程序窗口中捕获按键。我的理解是,WH\u CBT
实际上需要在一个单独的dll中,以便可以将它注入到其他进程中。所以我做了这个
我还意识到这会带来一个位不确定性要求——如果我的dll是64位的,我不能将它注入32位进程,反之亦然
无论如何,我在VS2008调试器中试用了它,果然看到了OutputDebugString
output(我的处理程序调用outputdugstring
)。但仅在Visual Studio和DebugView中—当我将焦点切换到DebugView时,DebugView将显示焦点更改字符串输出。当我切换回VS调试器时,VS输出窗口将显示焦点更改字符串输出
我想这可能是VS和DebugView之间的一个丑陋的交互,所以我尝试在没有调试器的情况下独自运行我的程序。同样,它将在DebugView中显示输出,但仅在切换到DebugView时显示。当我将焦点切换到Notepad++、SourceTree和六个其他应用程序时,DebugView中没有注册任何东西
我有点怀疑,所以我启动了process explorer并搜索了我的注入dll。果不其然,似乎只有一小部分进程可以获得dll。当我构建dll 32位时,Visual Studio、DebugView、procexp.exe
似乎都能获得dll,但我的机器中任何其他正在运行的32位进程都不能。当我构建64位dll时,explorer.exe
和procep64.exe
获取dll,但不获取我机器上的任何其他64位进程
有人能提出什么建议吗?有什么可能的解释吗?是否可能在某个地方获取日志事件,这可能解释为什么我的dll进入一个特定进程而不是另一个进程SetWindowsHookEx
使用GetLastError
报告错误\u成功
。下一步我可以去哪里
更新:
我已经上传了演示这一点的VisualStudio项目
我使用cmake,不幸的是cmake不会将32位和64位目标放在同一个sln中-因此64位.sln位于\u build64
,而32位.sln位于\u build32
。要明确的是,您不需要cmake来尝试这一点——只是我最初使用cmake来生成这些项目文件
这是我的主要.cpp
#include <iostream>
#include <iomanip>
#include <sstream>
#include "stdafx.h"
#include "km_inject.h"
using namespace std;
typedef pair<DWORD, string> LastErrorMessage;
LastErrorMessage GetLastErrorMessage()
{
DWORD code = GetLastError();
_com_error error(code);
LPCTSTR errorText = error.ErrorMessage();
return LastErrorMessage( code, string(errorText) );
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT __stdcall CALLBACK LowLevelKeyboardProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
KBDLLHOOKSTRUCT * hookobj = (KBDLLHOOKSTRUCT *)lParam;
DWORD vkCode = hookobj->vkCode;
DWORD scanCode = hookobj->scanCode;
DWORD flags = hookobj->flags;
DWORD messageTime = hookobj->time;
UINT vkCodeChar = MapVirtualKey( vkCode, MAPVK_VK_TO_CHAR );
#define BITFIELD(m) string m##_str = (flags & m)? #m : "NOT " #m
BITFIELD(LLKHF_EXTENDED);
BITFIELD(LLKHF_INJECTED);
BITFIELD(LLKHF_ALTDOWN);
BITFIELD(LLKHF_UP);
#undef BITFIELD
string windowMessageType;
#define KEYSTRING(m) case m: windowMessageType = #m; break
switch ( wParam )
{
KEYSTRING( WM_KEYDOWN );
KEYSTRING( WM_KEYUP );
KEYSTRING( WM_SYSKEYDOWN );
KEYSTRING( WM_SYSKEYUP );
default: windowMessageType = "UNKNOWN"; break;
};
#undef KEYSTRING
stringstream ss;
ss << left
<< setw(10) << messageTime << " "
<< setw(15) << windowMessageType << ": "
<< right
<< "VK=" << setw(3) << vkCode << " (0x" << hex << setw(3) << vkCode << dec << ") " << setw(2) << vkCodeChar << ", "
<< "SC=" << setw(3) << scanCode << " (0x" << hex << setw(3) << scanCode << dec << "), "
<< setw(20) << LLKHF_EXTENDED_str << ", "
<< setw(20) << LLKHF_INJECTED_str << ", "
<< setw(20) << LLKHF_ALTDOWN_str << ", "
<< setw(15) << LLKHF_UP_str << endl;
OutputDebugString( ss.str().c_str() );
return CallNextHookEx( 0, nCode, wParam, lParam );
}
int WINAPI WinMain(
__in HINSTANCE hInstance,
__in_opt HINSTANCE hPrevInstance,
__in_opt LPSTR lpCmdLine,
__in int nCmdShow )
{
OutputDebugString( "Beginning test...\n" );
// Set up main event loop for our application.
WNDCLASS windowClass = {};
windowClass.lpfnWndProc = WndProc;
char * windowClassName = "StainedGlassWindow";
windowClass.lpszClassName = windowClassName;
windowClass.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
if (!RegisterClass(&windowClass))
{
LastErrorMessage fullMessage = GetLastErrorMessage();
stringstream ss;
ss << "Failed to register window class: " << fullMessage.first << " \"" << fullMessage.second << "\"\n";
OutputDebugString( ss.str().c_str() );
return -1;
}
HWND mainWindow = CreateWindow(windowClassName, // class
"keylogger", // title
WS_OVERLAPPEDWINDOW | WS_VISIBLE , // 'style'
CW_USEDEFAULT, // x
CW_USEDEFAULT, // y
CW_USEDEFAULT, // width
CW_USEDEFAULT, // height
NULL, // parent hwnd - can be HWND_MESSAGE
NULL, // menu - use class menu
hInstance, // module handle
NULL); // extra param for WM_CREATE
if (!mainWindow)
{
LastErrorMessage fullMessage = GetLastErrorMessage();
stringstream ss;
ss << "Failed to create main window: " << fullMessage.first << " \"" << fullMessage.second << "\"\n";
OutputDebugString( ss.str().c_str() );
return -1;
}
// Get the name of the executable
char injectFileName[ MAX_PATH + 1 ];
{
int ret = GetModuleFileName( hInstance, injectFileName, MAX_PATH );
if ( ret == 0 || ret == MAX_PATH )
{
LastErrorMessage fullMessage = GetLastErrorMessage();
stringstream ss;
ss << "GetModuleFileName failed: " << fullMessage.first << " \"" << fullMessage.second << "\"\n";
OutputDebugString( ss.str().c_str() );
return -1;
}
char * sep = strrchr( injectFileName, '\\' );
if ( sep == NULL )
{
stringstream ss;
ss << "Couldn't find path separator in " << injectFileName << endl;
OutputDebugString( ss.str().c_str() );
return -1;
}
*sep = 0;
strcat_s( injectFileName, "\\km_inject.dll" );
}
// Get the module handle
HINSTANCE inject = LoadLibrary( injectFileName );
if ( NULL == inject )
{
LastErrorMessage fullMessage = GetLastErrorMessage();
stringstream ss;
ss << "Failed to load injector with LoadLibrary: " << fullMessage.first << " \"" << fullMessage.second << "\"\n";
OutputDebugString( ss.str().c_str() );
return -1;
}
#ifdef _WIN64
HOOKPROC LowLevelCBTProc = (HOOKPROC)GetProcAddress( inject, "LowLevelCBTProc" );
#else
HOOKPROC LowLevelCBTProc = (HOOKPROC)GetProcAddress( inject, "_LowLevelCBTProc@12" );
#endif
if ( !LowLevelCBTProc )
{
LastErrorMessage fullMessage = GetLastErrorMessage();
stringstream ss;
ss << "Failed to find LowLevelCBTProc function: " << fullMessage.first << " \"" << fullMessage.second << "\"\n";
OutputDebugString( ss.str().c_str() );
return -1;
}
// Install the keyboard and CBT handlers
if ( NULL == SetWindowsHookEx( WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, 0 ) )
{
LastErrorMessage fullMessage = GetLastErrorMessage();
stringstream ss;
ss << "Failed to set llkey hook: " << fullMessage.first << " \"" << fullMessage.second << "\"\n";
OutputDebugString( ss.str().c_str() );
return -1;
}
if ( NULL == SetWindowsHookEx( WH_CBT, LowLevelCBTProc, inject, 0 ) )
{
LastErrorMessage fullMessage = GetLastErrorMessage();
stringstream ss;
ss << "Failed to set cbt hook: " << fullMessage.first << " \"" << fullMessage.second << "\"\n";
OutputDebugString( ss.str().c_str() );
return -1;
}
BOOL bRet;
MSG msg;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
LastErrorMessage fullMessage = GetLastErrorMessage();
stringstream ss;
ss << "What on earth happened? errcode=" << fullMessage.first << ", \"" << fullMessage.second << "\"\n";
OutputDebugString( ss.str().c_str() );
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
OutputDebugString( "Bye, bye!\n" );
return 0;
}
km_.cpp:
#include <windows.h>
#include <utility>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;
extern "C" LRESULT __declspec(dllexport)__stdcall CALLBACK LowLevelCBTProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
#define HCBTCODE(m) case m: OutputDebugString( #m "\n" ); break;
switch ( nCode )
{
HCBTCODE( HCBT_ACTIVATE );
HCBTCODE( HCBT_CLICKSKIPPED );
HCBTCODE( HCBT_CREATEWND );
HCBTCODE( HCBT_DESTROYWND );
HCBTCODE( HCBT_KEYSKIPPED );
HCBTCODE( HCBT_MINMAX );
HCBTCODE( HCBT_MOVESIZE );
HCBTCODE( HCBT_QS );
HCBTCODE( HCBT_SETFOCUS );
HCBTCODE( HCBT_SYSCOMMAND );
default:
OutputDebugString( "HCBT_?\n" );
break;
}
return CallNextHookEx( 0, nCode, wParam, lParam );
}
extern "C" BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
//
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
//
break;
}
return TRUE;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
外部“C”LRESULT_uudeclspec(dllexport)uuu stdcall回调低级别cbtproc(
_在uu int nCode中,
_在WPARAM WPARAM,
_In_lparamlparam
)
{
#定义HCBTCODE(m)案例m:OutputDebugString(#m“\n”);break;
开关(nCode)
{
HCBT代码(HCBT_激活);
HCBT代码(HCBT_单击跳过);
HCBTCODE(HCBT_CREATEWND);
HCBT代码(HCBT_-Wnd);
HCBT代码(HCBT_键已断开);
HCBTCODE(HCBT_MINMAX);
HCBTCODE(HCBT_MOVESIZE);
HCBT代码(HCBT_QS);
HCBTCODE(HCBT_SETFOCUS);
HCBTCODE(HCBT_SYSCOMMAND);
违约:
OutputDebugString(“HCBT?\n”);
打破
}
返回CallNextHookEx(0,nCode,wParam,lParam);
}
外部“C”BOOL APIRENT DllMain(模块HMODULE,
德沃德·乌尔打电话的理由,
LPVOID lpReserved
)
{
开关(ul\u呼叫原因\u)
{
案例DLL\u进程\u附加:
//
打破
案例DLL\u线程\u连接:
打破
案例DLL\u线程\u分离:
打破
案例DLL\u进程\u分离:
//
打破
}
返回TRUE;
}
我很确定我知道这里发生了什么@500 InternalServerError提到,当他在注入的dll中有OutputDebugString()
时,它似乎挂起,无法安装。我想这也是发生在我身上的事情
OutputDebugString()
在Vista中发展出一种非常普通的趋势。特别是,Vista在HKEY\U LOCAL\U MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\debug Print filter
中引入了调试输出过滤器。我以前偶然发现过这一点,但在内核调试的环境中,这可能导致DbgPrint
/OutputDebugString
/printk
输出完全静音
按照这里的说明()我在调试输出中添加了一个完全允许的DEFAULT
过滤器,然后重新启动。现在,当我运行我的键盘记录器时,在我的键盘记录器之后启动的每个应用程序似乎都得到了注入的dll。它起作用了!DebugView现在可以看到我在键盘记录器之后启动的几乎所有应用程序的调试输出
我在想,根据@500 InternalServerError的经验,也许当Windows在Debug Print filter
中没有看到DEFAULT
过滤器时,这只是我的猜测,Windows没有使OutputDebugString
符号可用于链接,因此注入dll将失败(静默?)。已经链接到OutputDebugString
的应用程序(如DebugView本身、Visual Studio、process explorer和explorer.exe)将正常运行-我的注入dll将正确链接。不管怎样,这是我的猜测
谢谢大家的建议
更新:
好吧,我不再那么确定了。我返回并删除了DEFAULT
过滤器,我仍然可以看到我的钩子dll被加载到新的p中
#include <windows.h>
#include <utility>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;
extern "C" LRESULT __declspec(dllexport)__stdcall CALLBACK LowLevelCBTProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
#define HCBTCODE(m) case m: OutputDebugString( #m "\n" ); break;
switch ( nCode )
{
HCBTCODE( HCBT_ACTIVATE );
HCBTCODE( HCBT_CLICKSKIPPED );
HCBTCODE( HCBT_CREATEWND );
HCBTCODE( HCBT_DESTROYWND );
HCBTCODE( HCBT_KEYSKIPPED );
HCBTCODE( HCBT_MINMAX );
HCBTCODE( HCBT_MOVESIZE );
HCBTCODE( HCBT_QS );
HCBTCODE( HCBT_SETFOCUS );
HCBTCODE( HCBT_SYSCOMMAND );
default:
OutputDebugString( "HCBT_?\n" );
break;
}
return CallNextHookEx( 0, nCode, wParam, lParam );
}
extern "C" BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
//
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
//
break;
}
return TRUE;
}