SetWindowsHookEx + WH_CBT не работает? Или, по крайней мере, не так, как мне кажется?

У меня есть диагностическая программа, которая использует SetWindowsHookEx а также WH_KEYBOARD_LL для сканирования кодов в масштабе всей системы, я хотел бы расширить его, чтобы отслеживать изменения фокуса окна, что возможно при использовании SetWindowsHookEx и компьютерный обучающий крючок CBT WH_CBT,

Для WH_KEYBOARD_LL hook, я смог включить функцию hook в мой процесс, и он работал, фиксируя нажатия клавиш практически в каждом окне приложения на моем рабочем столе. Я понимаю, что 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. Когда я собираю DLL 32-разрядный, Visual Studio, DebugView, procexp.exe Кажется, что все получают DLL, но НЕ другие 32-битные процессы, запущенные на моей машине. Когда я собираю dll 64-битный, explorer.exe а также procexp64.exe получить DLL, но не любой из других 64-битных процессов на моей машине.

Кто-нибудь может предложить что-нибудь? Любые возможные объяснения? Можно ли где-нибудь получить запись событий, что может объяснить, почему моя DLL входит в один конкретный процесс, а не в другой? SetWindowsHookEx отчеты ERROR_SUCCESS с GetLastError, Где я могу посмотреть дальше?

ОБНОВИТЬ:

Я загрузил проекты визуальной студии, которые демонстрируют это.

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

3

Решение

Я уверен, что знаю, что здесь происходит. @ 500-InternalServerError упомянул это, когда у него есть OutputDebugString() в его введенной dll, кажется, зависает и не устанавливается. Я думаю, что это то, что происходит со мной тоже.

OutputDebugString() разработал очень плохую серию с Vista. В частности, Vista представила фильтр вывода отладки в 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 не видит DEFAULT фильтровать Debug Print Filterи это только мое предположение, что Windows не делает OutputDebugString символ, доступный для связывания и, таким образом, вставки DLL не удастся (молча?). Приложения, которые уже ссылаются на OutputDebugString — Как и сам DebugView, Visual Studio, проводник процессов и, по-видимому, explorer.exe, будут в порядке — моя DLL-библиотека для инъекций будет работать правильно. Во всяком случае, это мое предположение.

Спасибо всем за предложения.

ОБНОВИТЬ:

Хорошо, я больше не уверен в этом. Я вернулся и удалил DEFAULT фильтр, и я все еще могу видеть, как мой крючок DLL загружается в новые процессы. Так что здесь происходит? Я действительно не знаю. Неправильное использование Process Explorer? Если вы не запустите проводник процессов с правами администратора, он не сможет найти все процессы для определенной библиотеки DLL. Но даже тогда не должно было быть проблем с обнаружением дюжины или стандартных процессов без прав администратора, которые я начал.

Я больше не могу воспроизвести проблему. Если кому-то интересно, пример кода по-прежнему доступен по ссылке выше. Очевидно, я не собираюсь отмечать это как ответ, потому что проблема просто «ушла». Я обновлю это, если проблема вернется.

0

Другие решения


По вопросам рекламы [email protected]