I am trying to install global keyboard hook and on every DLL_PROCESS_ATTACH Notification try to subclass foreground window. I have separate exe and dll for both 64 bit and 32 bit application. I works ok if only one type (32 or 64 bit) exe and dll pair but not working for both at a time. I was not getting DLL_ATTACH_NOTIFICATION for 32 bit applications if I use 32 bit hook first and then 64 bit.Due to this i was unable to subclass 32 bit application.
sample code for DLL both for 32 and 64 bit with different name
`//#include "stdafx.h"
#include <tchar.h>
#include <process.h>
#pragma data_seg(".HOOKDATA")//Shared data among all instances.
HHOOK g_hKbdHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.HOOKDATA,RWS")//linker directive
#pragma data_seg(".HOOKDATA32")//Shared data among all instances.
HHOOK g_hKbdHook32 = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.HOOKDATA32,RWS")//linker directive
HINSTANCE g_hInstance = NULL;
HINSTANCE g_hInstance32 = NULL;
HWND hwndKeyFocused = NULL;
static HWND hwndOld;
static HWND hwndFocused;
static WNDPROC oldWindowProc;
BOOL APIENTRY
DllMain(
HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
DWORD dw;
int iRet;
HWND hwnd;
BOOL boIs32bit;
DWORD dwProcID;
HANDLE hProcess;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
dwProcID = GetCurrentProcessId();
hProcess = OpenProcess(
PROCESS_VM_READ |
PROCESS_VM_WRITE |
PROCESS_VM_OPERATION |
PROCESS_CREATE_THREAD |
PROCESS_QUERY_INFORMATION,
FALSE,
dwProcID
);
IsWow64Process(
hProcess,
&boIs32bit
);
if (TRUE == boIs32bit)
{
g_hInstance32 = (HINSTANCE)hModule;;
}
else
{
g_hInstance = (HINSTANCE)hModule;
}
hwnd = GetForegroundWindow();
oldWindowProc =(WNDPROC) SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)HackingWndProc);
dw = GetLastError();
hwndOld = hwnd;
break;
case DLL_PROCESS_DETACH:
SetWindowLongPtr(hwndOld, GWLP_WNDPROC, (LONG_PTR)oldWindowProc );
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
g_hKbdHook = NULL;
return TRUE;
}
BOOLEAN __stdcall
Init(
)
{
g_hKbdHook = NULL;
g_hKbdHook = SetWindowsHookEx(WH_KEYBOARD, ProcessKey, g_hInstance, NULL);
if(g_hKbdHook==NULL)
{
return FALSE;
}
return TRUE;
}
BOOLEAN __stdcall
Init32(
)
{
g_hKbdHook32 = NULL;
g_hKbdHook32 = SetWindowsHookEx(WH_KEYBOARD, ProcessKey32, g_hInstance32, NULL);
if(g_hKbdHook32 == NULL)
{
return FALSE;
}
return TRUE;
}
BOOLEAN __stdcall
DeInit(
)
{
UnhookWindowsHookEx(g_hKbdHook);
if (NULL != g_hKbdHook32)
{
UnhookWindowsHookEx(g_hKbdHook32);
}
return TRUE;
}
LRESULT CALLBACK
ProcessKey(
int ncode,
WPARAM wparam,
LPARAM lparam
)
{
return (CallNextHookEx(g_hKbdHook, ncode, wparam, lparam));//pass control to next hook in the hook chain.
}
LRESULT CALLBACK HackingWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return CallWindowProc(
oldWindowProc,
hWnd,
message,
wParam,
lParam
);
}
LRESULT CALLBACK
ProcessKey32(
int ncode,
WPARAM wparam,
LPARAM lparam
)
{
return (CallNextHookEx(g_hKbdHook32, ncode, wparam, lparam));//pass control to next hook in the hook chain.
}
enter code here
`
Related
I'm trying to use SetWindowsHookEx to install WH_CBT hook on global to inject the DLL into other process. Now I have the inject.exe, ExampleWindow.exe, inject.dll, let me show you the code.
1.code piece of inject.exe
#include <windows.h>
int main()
{
HHOOK hhook = NULL;
HMODULE dll = LoadLibrary(_T("D:\\projects\\CppDemon\\Release\\HookDemoDll.dll"));
HOOKPROC pfn = (HOOKPROC)GetProcAddress(dll, "MyCBTProc");
if (pfn == NULL)
{
printf("can not get MyCBTProc,press any key to exit\n");
getchar();
return 0;
}
printf("press any key to continue hook\n");
getchar();
hhook = SetWindowsHookEx(WH_CBT, pfn, dll, 0);
if (NULL == hhook)
{
printf("%d\n", GetLastError());
}
printf("press any key to unhook\n");
getchar();
UnhookWindowsHookEx(hhook);
return 0;
}
2.code piece of inject.dll
#ifdef __cplusplus
extern "C"
{
#endif
extern "C" __declspec(dllexport) LRESULT MyCBTProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam);
#ifdef __cplusplus
}
#endif
LRESULT MyCBTProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
DWORD pid = 0;
TCHAR buffer[128] = { 0 };
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
pid = GetCurrentProcessId();
_stprintf_s(buffer, 128, _T("[+] PID = %d"), pid);
OutputDebugString(buffer);
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
The ExampleWindow.exe is a test case for inject when the named window is created.
When I run inject.exe and everything goes well, the inject.dll has been loaded and the the debug message is output correct, and the PCHUNTER(this is an ARK tool) can detect the WH_CBT message hook in module inject.exe. Then, I run the ExampleWindow.exe, there also can output the debug message.
After this action, I refresh the message hook windows in PCHUNTER ,the WH_CBT hook message has disappeared from the control, and the inject.dll not injected in the ExampleWindow.exe at all.THis is not my expect result.
I don't know how this happens. I hope someone can help me find the cause of this problem.
I'm trying to hook a button click in a mfc window.And I used WM_COMMAND and WM_NOTIFY.For test, if the button is clicked, the hook must create a messagebox. The problem is that it doesn't react to button clicks.
Here's the code for reacting to WM_COMMAND and WM_NOTIFY:
LPMSG msg = (LPMSG)lParam;
switch( msg->message )
{
case WM_COMMAND:
MessageBox( NULL,"HOOK","YOOOO",MB_ICONEXCLAMATION );
break;
case WM_NOTIFY:
MessageBox( NULL,"HOOK","YOOOOO",MB_ICONEXCLAMATION );
break;
}
And here's the code to hole dll:
#include <Windows.h>
#include "FindingWindow.h"
#pragma comment( linker,"/SECTION:.SHARED,RWS" )
#pragma data_seg( ".SHARED" )
CaptureTextWindow* ctw;
HHOOK hook = 0;
HMODULE hInstance = 0;
HWND hWndServer = NULL;
#pragma data_seg()
static LRESULT CALLBACK msghook(int nCode, WPARAM wParam, LPARAM lParam);
__declspec(dllexport) BOOL clearMyHook(HWND hWnd);
BOOL APIENTRY DllMain( HINSTANCE hInst, DWORD ul_reason_for_call, LPVOID lpReserved )
{
switch( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
hInstance = hInst;
return TRUE;
case DLL_PROCESS_DETACH:
if(hWndServer != NULL)
clearMyHook(hWndServer);
return TRUE;
}
return TRUE;
}
__declspec(dllexport) BOOL WINAPI setMyHook(HWND hWnd)
{
if(hWndServer != NULL)
return FALSE;
hook = SetWindowsHookEx(
WH_CALLWNDPROC,
(HOOKPROC)msghook,
hInstance,
0);
if(hook != NULL)
{ /* success */
hWndServer = hWnd;
return TRUE;
} /* success */
return FALSE;
}
__declspec(dllexport) BOOL clearMyHook(HWND hWnd)
{
if(hWnd != hWndServer)
return FALSE;
BOOL unhooked = UnhookWindowsHookEx(hook);
if(unhooked)
hWndServer = NULL;
return unhooked;
}
static LRESULT CALLBACK msghook( int nCode, // hook code
WPARAM wParam , // message identifier
LPARAM lParam )
{
if( nCode < 0 )
{
CallNextHookEx( hook, nCode, wParam, lParam );
return 0;
}
LPMSG msg = (LPMSG)lParam;
switch( msg->message )
{
case WM_COMMAND:
MessageBox( NULL,"HOOK","YOOOO",MB_ICONEXCLAMATION );
break;
case WM_NOTIFY:
MessageBox( NULL,"HOOK","YOOOOO",MB_ICONEXCLAMATION );
break;
}
return CallNextHookEx( hook, nCode, wParam, lParam );
}
I used not only WM_COMMAND cause I thought mb it will work, but it didn't.Thanks for answer.
The lParam for a WH_CALLWNDPROC hook is of type CWPSTRUCT. Your CallWndProc should look like this:
// ...
const CWPSTRUCT& cwps = *(CWPSTRUCT*)lParam;
switch ( cwps.message ) {
case WM_COMMAND:
MessageBox( ... );
break;
// ...
I created a DLL to hook some events using a CBT hook. It seems to work only for the windows created by the process launching the hook...
My system is Windows 7 x64, but the behaviour is the same also on the x32.
This is the code (sorry but I'm no C++ expert):
#include "windows.h"
extern "C"
{
static LRESULT CALLBACK CbtProcCb(int nCode, WPARAM wParam, LPARAM lParam);
HINSTANCE g_hDll = NULL;
HWND g_hNotifyWin = NULL;
DWORD g_uNotifyMsg = NULL;
HHOOK g_hHook = NULL;
__declspec(dllexport) HHOOK SetCbtHook(HWND hWnd, LPCWSTR lStrMsg, DWORD threadId)
{
g_hNotifyWin = hWnd;
g_uNotifyMsg = RegisterWindowMessage(lStrMsg);
g_hHook = SetWindowsHookEx(WH_CBT, (HOOKPROC)CbtProcCb, g_hDll, threadId);
return g_hHook;
}
__declspec(dllexport) int UnsetCbtHook()
{
if ( !g_hHook )
return 0;
return UnhookWindowsHookEx(g_hHook);
}
}
static LRESULT CALLBACK CbtProcCb(int nCode, WPARAM wParam, LPARAM lParam)
{
SendNotifyMessage(g_hNotifyWin, g_uNotifyMsg, nCode, wParam); // Send nCode to check the received event
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if ( fdwReason == DLL_PROCESS_ATTACH )
g_hDll = hinstDLL;
return true;
}
Any hints?
When Windows installs globals hook, the DLL implementing the hook function is often loaded in other processes. In those processes, the g_hNotifyWin and g_uNotifyMsg global variables are NULL. That global variables are only not NULL in the context of the process in which the SetCbtHook call took place.
You must have a way to retrieve the correct value for g_hNotifyWin and 'g_uNotifyMsg', in an arbitrary process.
Add:
const char * g_pszClassName = "MyClassName";
const char * g_pszRegisteredMsg = "MyMessage";
to your DLL, and replace "MyClassName" by the class name of the g_hNotifyWin window. See the RegisterClass call in your EXE. Update also "MyMessage".
Then, use the following CbtProcCb function:
static LRESULT CALLBACK CbtProcCb(int nCode, WPARAM wParam, LPARAM lParam)
{
if ( nCode >= 0 ) {
if ( g_hNotifyWin == NULL ) g_hNotifyWin = FindWindow( g_pszClassName, NULL );
if ( g_uNotifyMsg == 0) g_uNotifyMsg = RegisterWindowMessage(g_pszRegisteredMsg);
if ( g_hNotifyWin && g_uNotifyMsg )
SendNotifyMessage(g_hNotifyWin, g_uNotifyMsg, nCode, wParam); // Send nCode to check the received event
}
return CallNextHookEx(NULL, nCode, wParam, lParam); // first arg useless see MSDN
}
EDIT:
As you noted in comment, same problem for g_uNotifyMsg See updated function. You could set up those variables in DllMain, indeed.
The first argument to CallNextHookEx may be NULL.
I guess your ThreadId is not zero, so hook is attached only to the calling process / thread respectively. Read documentation:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx
I've been writing a win32 wrapper class, and I've come across a problem: Because each instance of the class has a window, I've enclosed the this pointer in the user info space using SetWindowLongPtrW(), allowing me to call a message handler from the static WndProc function. This works fine: I can call the function. However, when I try to call another member function from the message handler, I get an access violation at 0x00000088
It does compile.
I posted quite a lot, because to be honest I'm not too sure where the problem originates from...
Please feel free to comment/criticize my code in general. Thanks for the help!
Here is the header:
#pragma once
#include <Windows.h>
#include "GlobalDefines.h"
#include "GraphicsWrapper.h"
#include "Keyboard.h"
namespace Startup
{
class GraphicsWrapper;
class WindowsWrapper
{
public:
WindowsWrapper(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
INT nCmdShow);
~WindowsWrapper();
void EnterMsgLoop(GraphicsWrapper* Gfx);
static LRESULT _stdcall WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
LRESULT _stdcall MessageHandler(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
WNDCLASSEX WndClass;
MSG Message;
RECT Desktop;
RECT Taskbar;
RECT WindowCoordinates;
LPSTR CommandLineArgs;
INT CmdShow;
HINSTANCE TheInstance;
HWND WindowHandle;
void InitializeWndClassEx();
void InitializeWindowHandleHWND();
void ShowWindowOnScreen();
bool GetScreenRect(RECT & Desktop);
bool GetTaskbarRect(RECT& rectTaskbar);
bool GetWindowCoords(RECT& WindowCoordinates);
int GetTaskbarSide();
enum TaskbarSides
{
Top,
Right,
Bottom,
Left
};
void SetFullScreen(bool Enable);
};
static IO::Keyboard * kbd;
}
And this is the relevant part of the implementation. I'll mark where the crash occurs.
void Startup::WindowsWrapper::InitializeWndClassEx()
{
WndClass.hIcon = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON) ) );
WndClass.hIconSm = LoadIcon(TheInstance,(MAKEINTRESOURCE(IDI_MAIN_ICON) ) );
WndClass.cbSize = sizeof(WNDCLASSEX);
WndClass.style = CS_HREDRAW | CS_VREDRAW;
WndClass.lpfnWndProc = WindowProc;
WndClass.hInstance = TheInstance;
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.lpszClassName = L"WindowClassName";
RegisterClassEx(&WndClass);
SetWindowLongPtrW(WindowHandle, GWLP_USERDATA, (long)this);
}
void Startup::WindowsWrapper::SetFullScreen(bool Enable)
{
long style = Enable ? WS_POPUP : WS_OVERLAPPED | WS_SYSMENU;
static RECT windowRect = {};
static bool needRect = true;
if (needRect)
{
GetWindowRect(WindowHandle, &windowRect);
needRect = false;
}
SetWindowLong(WindowHandle, GWL_STYLE, style);
if (Enable)
{
SetWindowPos(WindowHandle, HWND_TOPMOST,
0,0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SWP_SHOWWINDOW);
}
else
{
SetWindowPos(WindowHandle, 0,
windowRect.left,windowRect.top,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
SWP_SHOWWINDOW);
}
}
and
LRESULT CALLBACK Startup::WindowsWrapper::WindowProc
(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
WindowsWrapper* ourObjectPtr = NULL;
long thisObject = GetWindowLongW(hWnd, GWLP_USERDATA);
ourObjectPtr = (WindowsWrapper *)( (void*)thisObject);
long Result = ourObjectPtr->MessageHandler(hWnd, message, wParam, lParam);
RET(Result);
}
LRESULT _stdcall Startup::WindowsWrapper::MessageHandler
(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0); //Works fine here, but...
break;
case VK_SPACE:
this->SetFullScreen(false); //Crashes here w/ access violation
break;
case VK_SHIFT:
this->SetFullScreen(true); //Or here, w/ the same error.
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
This is the createWindowEx call. Thanks for your help, again.
void Startup::WindowsWrapper::InitializeWindowHandleHWND()
{
WindowHandle = CreateWindowEx(NULL,
L"WindowClassName",
L"WindowTitle"
WS_OVERLAPPED | WS_SYSMENU,
WindowCoordinates.left, WindowCoordinates.top,
WindowCoordinates.right, WindowCoordinates.bottom,
NULL, NULL, TheInstance,
CommandLineArgs);
}
I have some code from a custom dialog handler I wrote quite a while back, which might be of use to you.
Same principle applies for a window but switch the WM_INITDIALOG for WM_CREATE and also replace DWLP_USER with GWLP_USERDATA. The format of the callback is subtley different too. You should be able to salvage almost all of this function though.
LRESULT CALLBACK CDialog::DlgProc( HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
CDialog* pWindow;
if( msg == WM_INITDIALOG ) {
SetWindowLongPtr( hWndDlg, DWLP_USER, (LONG_PTR)lParam );
pWindow = reinterpret_cast<CDialog*>( lParam );
pWindow->m_hWnd = hWndDlg;
} else {
pWindow = reinterpret_cast<CDialog*>( (LPARAM)GetWindowLongPtr( hWndDlg, DWLP_USER ) );
}
if( pWindow != NULL ) {
LRESULT ret = pWindow->OnMessage( msg, wParam, lParam );
if( msg == WM_NCDESTROY ) pWindow->m_hWnd = NULL;
}
return FALSE;
}
I made hook for calculator and want to get messages which calculator receives. To do that, I set my own window procedure, but during unhooking if I use SetWindowLong(..) to recover the old window procedure program crushes.
DLL code:
#define EXPORT_API extern "C" __declspec(dllexport)
EXPORT_API void InstallHook();
EXPORT_API void UninstallHook();
#pragma data_seg("Shared")
HHOOK g_hHook = NULL;
WNDPROC g_OldWndProc = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")
HWND GetTargetWindowHwnd()
{
return ::FindWindowA(0, "Calculator");
}
// my new wnd procedure to catch messages
LRESULT CALLBACK NewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = 0;
switch(uMsg)
{
case WM_CLOSE:
{
MessageBoxA(0, "Here we are!", "", 0);
}
break;
default:
lResult = CallWindowProc(g_OldWndProc, hwnd, uMsg, wParam, lParam);
break;
}
lResult = CallWindowProc(g_OldWndProc, hwnd, uMsg, wParam, lParam);
return lResult;
}
// hook procedure
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *pMsg = (MSG *)lParam;
HWND hWnd = GetTargetWindowHwnd();
bool flagIn = false;
if( hWnd == pMsg->hwnd )
{// if messege was sent to my target window
if(g_OldWndProc == NULL)
{
// save the adress of old wnd procedure to recover it later
g_OldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);
// set my wnd procedure
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)NewWndProc);
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
EXPORT_API void InstallHook()
{
try
{
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hInstance, 0);
}
catch(...)
{
MessageBoxA(0, "Hook error", "Error", 0);
}
}
EXPORT_API void UninstallHook()
{
if(g_OldWndProc)
{
// recovering old wnd proc
HWND hWnd = GetTargetWindowHwnd();
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)g_OldWndProc);
g_OldWndProc = NULL;
}
if (g_hHook)
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInstance = (HINSTANCE) hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
EXE CODE:
void CHookTestDlg::OnBnClickedBtnInstall()
{
InstallHook();
}
void CHookTestDlg::OnBnClickedBtnUninstall()
{
UninstallHook();
}
If I don't use my wnd procedure it works normal. If I use SetWindowLong(..) to recover the old window procedure program crushes during unhook. What is wrong?
The problem is that you are setting the window proc on the target window from within the target process (calc), and in that case, it is succeeding. But when you call UninstallHook, that code runs in your own exe's process; and in that case, SetWindowLong will fail.
(Putting the hook values in shared memory won't help; SetWindowLong will still refuse to change the window proc across a process boundary - see MSDN for details.)
To get this to work, you would need to communicate to the hooked instance of the DLL and ask it to reset the wndproc from within that target process, and once that is done, then unhook the hook.
(atzz's advice on unhooking is also valid. Hooking windows that you don't own is generally best avoided.)
When unsubclassing, always check that the window was not subclassed by someone else after you. I.e. before restoring WindowProc, you should read it again and compare against expected value (NewWndProc). If it is different, you must not unload the DLL, because another subclasser has a pointer to your DLL code stored, and this pointer will become dangling as soon as your DLL is unloaded.