I have a DLL that I use to hook the Notepad and catch keystrokes. All works well, but I'd like to have access to the KeyboardHook Callback from within my exe. So that everytime the Callback in the .dll is triggered, it passes it's values to the .exe Is this possible?
** HOOK.dll (hookDll.cpp) **
#include "windows.h"
#pragma data_seg (".SHARED")
HHOOK keyboardHook = 0;
HINSTANCE g_hInstance = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.SHARED,RWS")
extern "C" __declspec(dllexport) LRESULT CALLBACK KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if(0 > nCode)
return CallNextHookEx(keyboardHook, nCode, wParam, lParam);
{
MessageBox(NULL, L"Got Keyboard Event !", L"Event", 0);
}
return CallNextHookEx(keyboardHook, nCode, wParam, lParam);
}
extern "C" __declspec(dllexport) bool InstallKeyboardHook(unsigned long threadID)
{
keyboardHook = SetWindowsHookEx(WH_KEYBOARD, &KeyboardHook, g_hInstance, threadID);
return true;
}
extern "C" __declspec(dllexport) bool UnInstallKeyboardHook()
{
UnhookWindowsHookEx(keyboardHook);
return true;
}
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;
}
** Hook.exe (hook.h) **
#pragma once
#include "afxwin.h"
typedef bool(*InstallHook)(unsigned long);
typedef bool(*UnInstallHook)();
typedef LRESULT (*KeyboardHook)(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MyCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
//Id like this to get called whenever the CALLBACK in the dll is called
MessageBox(NULL, L"test", L"test", 0);
return NULL;
}
class CTrayMeApp : public CWinApp
{
public:
CTrayMeApp(){};
~CTrayMeApp(){};
virtual BOOL InitInstance();
};
extern CTrayMeApp theApp;
** Hook.exe (hook.cpp) **
#include "hook.h"
CTrayMeApp theApp;
BOOL CTrayMeApp::InitInstance()
{
BOOL bReturn = FALSE;
CWinApp::InitInstance();
HMODULE hModule = LoadLibrary(_T("Hook.dll"));
//Get the function address which installs the keyboard events filter.
InstallHook fpKeyboardHook = (InstallHook) GetProcAddress(hModule,"InstallKeyboardHook");
//Install the Keyboard Event Hook (Filter).
fpKeyboardHook(GetTargetThreadIdFromWindow("Notepad", "Untitled - Notepad"));
//Install the Callback
KeyboardHook cpKeyboardHook = (KeyboardHook) GetProcAddress(hModule,"_KeyboardHook#12");
//OK, are callback is here, now how do we make use of it?
KeyboardHook(MyCallback); //of course this won't work, but it gives an idea of what I'm trying to do.
FreeLibrary(hModule);
Sleep(10000); //give us 10 seconds to test it out
return bReturn;
}
Inside your DLL:
LRESULT CALLBACK (*myCallbackRef)(int nCode, WPARAM wParam, LPARAM lParam);
extern "C" __declspec(dllexport) void InstallMyHook(LRESULT CALLBACK (*MyCallback)(int nCode, WPARAM wParam, LPARAM lParam))
{
myCallbackRef = MyCallback;
}
Use myCallbackRef with parameters inside KeyboardHook.
In your program
Call
InstallMyHook(MyCallBack);
Related
I'm currently hooking window activations with the following code:
extern "C" __declspec(dllexport) LRESULT CALLBACK CBTProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
if (nCode < 0) return CallNextHookEx(nullptr, nCode, wParam, lParam);
HWND hwnd = reinterpret_cast<HWND>(wParam);
switch (nCode)
{
case HCBT_ACTIVATE: // The system is about to activate a window.
{
return 0; // 0 - Allow 1 - Deny
}
}
return 0;
From the docs:
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644977(v=vs.85)
lParam
Specifies a long pointer to a CBTACTIVATESTRUCT structure containing the handle to the active window and specifies whether the activation is changing because of a mouse click.
How I could interpret the value of lParam and distinguish it?
As the documentation says, for HCBT_ACTIVATE the lParam specifies a pointer to a CBTACTIVATESTRUCT, so simply typecast it accordingly, just like you are doing with the wParam, eg:
extern "C" __declspec(dllexport) LRESULT CALLBACK CBTProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
if (nCode < 0) return CallNextHookEx(nullptr, nCode, wParam, lParam);
switch (nCode)
{
case HCBT_ACTIVATE: // The system is about to activate a window.
{
HWND hwnd = reinterpret_cast<HWND>(wParam);
CBTACTIVATESTRUCT* cbt = reinterpret_cast<CBTACTIVATESTRUCT*>(lParam);
// use hwnd, cbt->fMouse, and cbt->hWndActive as needed...
return 0; // 0 - Allow 1 - Deny
}
}
return 0;
}
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 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
`
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 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.