簡體   English   中英

SetWindowsHookEx + WH_CBT不起作用? 或者至少不是我認為應該的方式?

[英]SetWindowsHookEx + WH_CBT doesn't work? Or at least not the way I think it should?

我有診斷程序使用SetWindowsHookExWH_KEYBOARD_LL掃描系統范圍的代碼我想擴展它以監視窗口焦點的變化,這是使用SetWindowsHookEx和基於計算機的訓練CBT鈎子WH_CBT

對於WH_KEYBOARD_LL鈎子,我能夠將鈎子函數放在我的進程中並且它可以工作,在桌面上的幾乎每個應用程序窗口中捕獲按鍵。 我的理解是WH_CBT實際上需要在一個單獨的dll中,以便它可以注入其他進程。 所以我做到了這一點。

我也知道這有點需要 - 如果我的dll是64位,我不能將它注入32位進程,反之亦然。

無論如何,我在VS2008調試器中試了一下,果然,我看到了OutputDebugString輸出(我的處理程序調用了OutputDebugString )。 但只有在Visual Studio和DebugView中 - 當我將焦點切換到DebugView時,DebugView將顯示焦點更改字符串輸出。 當我切換回VS調試器時,VS輸出窗口將顯示焦點更改字符串輸出。

我認為這可能是VS和DebugView之間的丑陋交互,所以我嘗試自己運行我的程序,沒有調試器。 同樣,它將在DebugView中顯示輸出,但僅在切換到DebugView時顯示。 當我將焦點切換到Notepad ++,SourceTree和其他六個應用程序時,DebugView中沒有注冊任何內容。

我有點懷疑所以我啟動了進程資源管理器並搜索了我的注入dll。 果然,只有少數幾個進程似乎得到了dll。 當我構建32位DLL,Visual Studio,DebugView, procexp.exe似乎都得到了dll,但不是我機器中任何其他正在運行的32位進程。 當我構建64位DLL時, explorer.exeprocexp64.exe獲取dll,但不是我機器上的任何其他64位進程。

誰能提出任何建議? 任何可能的解釋? 是否有可能在某處獲取日志記錄事件,這可能解釋了為什么我的dll進入一個特定進程但不進入另一個進程? SetWindowsHookEx使用GetLastError報告ERROR_SUCCESS 我在哪里可以看下一個?

更新:

我已經上傳了展示這一點的視覺工作室項目。

https://dl.dropboxusercontent.com/u/7059499/keylog.zip

我使用cmake,不幸的是cmake不會將32位和64位目標放在同一個sln中 - 所以64位.sln位於_build64 ,而32位.sln位於_build32 為了清楚起見,你不需要cmake來試試這個 - 這只是我用cmake來生成這些項目文件。

這是我的main.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;
}

這是我為此創建的dll,km_inject.cpp / .h

km_inject.h:

#ifndef INCLUDED_keyloggermini_km_inject_h
#define INCLUDED_keyloggermini_km_inject_h

#if defined(__cplusplus__)
extern "C" {
#endif

LRESULT __declspec(dllimport)__stdcall CALLBACK LowLevelCBTProc(
  _In_  int nCode,
  _In_  WPARAM wParam,
  _In_  LPARAM lParam
);

#if defined(__cplusplus__)
};
#endif


#endif

km_inject.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;
}

我很確定我知道這里發生了什么。 @ 500-InternalServerError提到當他在注入的dll中有OutputDebugString()時,它似乎掛起並且沒有安裝。 我想這也是我發生的事情。

OutputDebugString()開發了一個非常平均的Vista連勝。 特別是,Vista在HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Debug Print Filter引入了調試輸出過濾HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Debug Print Filter 我之前偶然發現了這一點,但是在內核調試的上下文中,這可能是完全消除DbgPrint / OutputDebugString / printk輸出的原因。

根據這里的說明( http://blogs.msdn.com/b/doronh/archive/2006/11/14/where-did-my-debug-output-go-in-vista.aspx )我完全添加了一個允許DEFAULT過濾到我的調試輸出,然后重新啟動。 現在,當我運行我的鍵盤記錄器時,在我的鍵盤記錄器之后啟動的每個應用程序似乎都會獲得注入的dll。 有用! DebugView現在可以看到我在鍵盤記錄之后開始的幾乎所有應用程序的調試輸出。

我認為根據@ 500-InternalServerError的經驗,也許當Windows在 Debug Print Filter中沒有看到 DEFAULT過濾 Debug Print Filter ,這只是我的猜測,Windows不會使 OutputDebugString符號可用於鏈接和注入dll會失敗(默默地?)。 已經鏈接到 OutputDebugString應用程序 - 比如DebugView本身,Visual Studio,進程資源管理器,以及顯然是explorer.exe,都可以 - 我的注入dll將正確鏈接。 無論如何,這是我的猜測。

謝謝大家的建議。

更新:

好的,我不再那么肯定了。 我回去並刪除了DEFAULT過濾器,我仍然可以看到我的hook dll被加載到新進程中。 那么這里發生了什么? 我真的不知道。 濫用進程資源管理器? 如果您沒有使用管理員權限啟動進程資源管理器,它將無法搜索特定dll的所有進程。 但即便如此,它應該沒有問題發現我開始的十幾個或標准的非管理進程。

我不能再重現這個問題了。 如果有人感興趣,示例代碼仍可在上面的鏈接中找到。 顯然,我不會將此標記為答案,因為問題只是“消失了”。 如果問題再次出現,我會更新。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM