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.
Related
I am currently attempting to use the Windows clipboard and its notifications in my application. Specifically, I am attempting to subscribe to the WM_CLIPBOARDUPDATE window message by using the AddClipboardFormatListener() function. Previously, I had been using the SetClipboardViewer() function in order to add my window directly into the clipboard viewer chain. This had worked just fine, and I had received the relevant messages WM_DRAWCLIPBOARD and WM_DESTROYCLIPBOARD when expected. However, I would like to avoid continuing to use the clipboard chain because of how volatile it can be.
My understanding was that I would be perfectly able to receive WM_CLIPBOARDUPDATE after calling AddClipboardFormatListener(). Is there another step here that I am missing? What do I need to do to make sure that I receive this message properly? As it stands currently, I am not receiving it when performing a copy operation.
Here is an abridged example of what my code looks like:
WNDPROC override:
LRESULT CALLBACK ClipboardService::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch ( pMsg->message )
{
case WM_DRAWCLIPBOARD:
// Handle clipboard available event and forward message
break;
case WM_CLIPBOARDUPDATE:
// This is never triggered
break;
case WM_DESTROYCLIPBOARD:
// Handle clipboard cleared event and forward message
break;
}
return ::CallNextHookEx( g_Hook, nCode, wParam, lParam );
}
Called by Constructor:
HRESULT ClipboardService::SetOrRefreshWindowsHook()
{
HRESULT hr = S_OK;
try
{
if (!m_bHookSet)
{
g_hwndCurrent = ::CreateWindowEx(0, "Message", "ClipboardMessageWindow", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
m_dwThreadID = ::GetWindowThreadProcessId(g_hwndCurrent, &m_dwProcessID);
_Module.Lock();
SetLastError(0);
g_Hook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, 0, m_dwThreadID);
//g_hwndNext = ::SetClipboardViewer(g_hwndCurrent); old way to subscribe
// This is what I expect should subscribe me to WM_CLIPBOARDUPDATE messages
if (!::AddClipboardFormatListener(g_hwndCurrent))
hr_exit(E_UNEXPECTED);
DWORD dwLastError = ::GetLastError();
g_This = this;
m_bHookSet = true;
}
}
catch (...)
{
hr_exit(E_UNEXPECTED);
}
wrapup:
return hr;
}
This is a COM interface that is called by a .NET wrapper, but I don't think that either of those two things are relevant to my problem in this case (figured I would add just in case).
You should not be using SetWindowsHookEx(WH_CALLWNDPROC) to receive messages to your own window. Use RegisterClass/Ex() instead to register your own custom window class that has your WndProc assigned to it, and then CreateWindowEx() can create an instance of that window class. No hooking needed.
HINSTANCE g_hThisInst = NULL;
HWND g_hwndCurrent = NULL;
//HWND g_hwndNext = NULL;
bool g_AddedListener = false;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
g_hThisInst = hinstDLL;
return TRUE;
}
LRESULT CALLBACK ClipboardService::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
//g_hwndNext = ::SetClipboardViewer(hwnd);
g_AddedListener = ::AddClipboardFormatListener(hwnd);
return g_AddedListener ? 0 : -1;
case WM_DESTROY:
/*
ChangeClipboardChain(hwnd, g_hwndNext);
g_hwndNext = NULL;
*/
if (g_AddedListener)
{
RemoveClipboardFormatListener(hwnd);
g_AddedListener = false;
}
return 0;
/*
case WM_CHANGECBCHAIN:
if (g_hwndNext == (HWND)wParam)
g_hwndNext = (HWND)lParam;
else if (g_hwndNext)
SendMessage(g_hwndNext, uMsg, wParam, lParam);
break;
case WM_DRAWCLIPBOARD:
// Handle clipboard available event
if (g_hwndNext)
SendMessage(g_hwndNext, uMsg, wParam, lParam);
break;
*/
case WM_CLIPBOARDUPDATE:
// Handle clipboard updated event
return 0;
case WM_DESTROYCLIPBOARD:
// Handle clipboard cleared event and forward message
break;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
HRESULT ClipboardService::SetOrRefreshWindowsHook()
{
try
{
if (!g_hwndCurrent)
{
WNDCLASS wndClass = {};
wndClass.lpfnWndProc = &ClipboardService::WndProc;
wndClass.hInstance = g_hThisInst;
wndClass.lpszClassName = TEXT("ClipboardMessageWindow");
if (!::RegisterClass(&wndClass))
{
DWORD dwLastError = ::GetLastError();
if (dwLastError != ERROR_CLASS_ALREADY_EXISTS)
return HRESULT_FROM_WIN32(dwLastError);
}
g_hwndCurrent = ::CreateWindowEx(0, wndClass.lpszClassName, "", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, g_hThisInst, NULL);
if (!g_hwndCurrent)
{
DWORD dwLastError = ::GetLastError();
return HRESULT_FROM_WIN32(dwLastError);
}
g_This = this;
}
}
catch (...)
{
return E_UNEXPECTED;
}
return S_OK;
}
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 working in an IDE which creates a hwnd and its respective WndProc LRESULT CALLBACK. I need to change the WndProc to a custom one.
I've read that SetWindowLong would do the job, but I can't find any working example. For example:
HWND hwnd; //My window
SetWindowLong(hwnd, GWL_WNDPROC, myNewWndProc);
The third parameter for SetWindowLong is a Long as the name of the function names it. How can I make a reference from my WndProc function to a Long?
My WndProc:
LRESULT CALLBACK WndProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
msg_dev(toString(uMsg));
switch(uMsg){
case WM_MOUSEMOVE:
SetCursor(LoadCursor(NULL, IDC_HAND));
break;
case WM_LBUTTONDOWN:
msg_dev("Button down!");
break;
default:
DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
};
You need to use something like this:
WNDPROC prevWndProc;
...
prevWndProc = (WNDPROC) SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);
...
LRESULT CALLBACK myNewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
msg_dev(toString(uMsg));
switch(uMsg)
{
case WM_MOUSEMOVE:
SetCursor(LoadCursor(NULL, IDC_HAND));
break;
case WM_LBUTTONDOWN:
msg_dev("Button down!");
break;
}
return CallWindowProc(prevWndProc, hwnd, uMsg, wParam, lParam);
}
See this article:
When you subclass a window, it's the original window procedure of the window you subclass you have to call when you want to call the original window procedure
That being said, you should use SetWindowSubclass() instead of SetWindowLongPtr(). Let it handle this for you. See this article for more details:
Safer subclassing
For example:
#define MY_SUBCLASS_ID 1
SetWindowSubclass(hwnd, &mySubClassProc, MY_SUBCLASS_ID, 0);
...
LRESULT CALLBACK mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
msg_dev(toString(uMsg));
switch(uMsg)
{
case WM_MOUSEMOVE:
SetCursor(LoadCursor(NULL, IDC_HAND));
break;
case WM_LBUTTONDOWN:
msg_dev("Button down!");
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &mySubClassProc, uIdSubclass);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
A simple cast does the job.
SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);
Otherwise It would be incompatible types: LRESULT and LONG.
The MSDN documentation for SetWindowLong() states that GWL_WNDPROC
Sets a new address for the window procedure.
This means that your third parameter should be a pointer to a function. Your SetWindowLong() call should therefore look like this:
SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);
Note also the Remarks section that states:
An application must pass any messages not processed by the new window procedure to the previous window procedure by calling CallWindowProc.
You can use setWindowLong to address your problem.
setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure);
However you would be setting the window procedure twice. Once with the IDE default and then to yours. What you need to dobis set the window procedure when the window is being REGISTERED.
#include <windows.h>
void registerWindow();
void createWindow();
void messageLoop();
int main()
{
registerWindow();
createWindow();
messageLoop();
}
LRESULT CALLBACK myWindowProcedure(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
return DefWindowProc(hwnd,msg,wparam,lparam);
}
void registerWindow()
{
/** This is the important part.
* Find this part in your code.
* Set WNDCLASS::lpfnWndProc to what ever
* window procedure you want.
**/
WNDCLASS wc = {};
wc.lpfnWndProc = myWindowProcedure;
wc.hInstance = hInstance;
wc.lpszClassName = "CLASS NAME";
RegisterClass(&wc);
// WARNING: Your app will crash at runtime if the
// windows procedure is "NOT PROPER"
}
void createWindow()
{
auto hwnd = CreateWindowEx(
0, // Optional window styles.
"CLASS NAME", // Window class
"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
HINSTANCE(), // Instance handle
NULL // Additional application data
);
ShowWindow(hwnd, nCmdShow
}
void messageLoop()
{
MSG msg;
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
You have to use SetWindowLongPtr (which on 32-bit is a macro but a separate function on 64-bit) to ensure compatibility with both 32- and 64-bit systems.
Syntax would be as follows:
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)&myNewWndProc);
Note SetWindowLongPtr is used instead of SetWindowLong, and GWLP_WNDPROC is used as the nIndex constant.
Well I tried different solution to my problem but It just doesn't work.
I call SetWindowsHookExA and then when I press a key the messagebox is not shown. What to do?
this is my code (this is a DLL which is loaded by another DLL which is loaded by the program):
#include <Windows.h>
HINSTANCE gl_hThisInstance = NULL;
HHOOK hHook = NULL;
LRESULT CALLBACK KeyHit(int code,WPARAM wParam,LPARAM lParam);
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
gl_hThisInstance = (HINSTANCE)hModule;
hHook = SetWindowsHookExA(
WH_KEYBOARD,
KeyHit,
//(HWND)gl_hThisInstance//not working
0,//not working
//(DWORD)gl_hThisInstance//not working
//GetCurrentThreadId()//even not working with this
0//not working
);
break;
}
return TRUE;
}
LRESULT CALLBACK KeyHit(int code,WPARAM wParam,LPARAM lParam)
{
MessageBox(0,"PRESSED","PRESSED",0);
return CallNextHookEx(hHook,code,wParam,lParam);
}
I got issues with the hooking before. Not really an issue, but the way I did it was not supposed to.
First of all, you should have 2 exported functions from the DLL, SetHook and RemoveHook. The SetHook function will call the SetWindowsHookEx() from there. If you ever try to call the SetWindowsHookEx() from within a thread or DLLMain of your DLL, the function returns no errors, but the callback function will never be called. It took me sometimes to figure it out.
Posted here is my working code to catch WH_GETMESSAGE, you can reference from here.
Here is my working exported SetHook() function from the DLL.
bool __declspec(dllexport) __stdcall SetHook(DWORD myWnd)
{
mySavedHook = SetWindowsHookEx(WH_GETMESSAGE,
GetMsgProc,
infoContainer.DllHModule,
myWnd);
int errorID = GetLastError();
if (errorID != 0)
{
MessageBoxA(NULL, "Failed to implement hook", "Failed", 0);
MessageBoxA(NULL, to_string(errorID).c_str(), "Error ID", 0);
return false;
}
else
{
return true;
}
}
infoContainer.DllHModule: instance of the DLL, which is the first parameter of DllMain().
myWnd: is my Thread ID (not Process ID) - Get it from GetWindowThreadProcessId(window_handle, NULL). To implement global hook, use 0 as myWnd.
And here is my callback function.
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0)
{
LPMSG msg = (LPMSG)lParam;
if (msg->message == WM_CALLFUNCTION)
{
MessageBoxA(NULL, "Receive WM_CALLFUNTION", "Good news", 0);
}
}
//Doesn't matter, just call this function and return it.
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
The callback function must have the CALLBACK keyword to work.
From the external application, call the SetHook() from the DLL and use the Thread ID as its param.
I'm writing up a quick tool in C# thats meant to sort of be a virtual keyboard. At the moment I am using SendKeys. I want to know if keyloggers would capture the keys so I found this code but i don't have mfc installed so i cant compile nor run it
How might i key if SendKeys is being logged by keyloggers or how do I get the code (snippet below) running in a single exe to test my code with?
#include <Windows.h>
static UINT uMsg = 0;
static HWND hWndMain = 0;
static HHOOK hKeyHook = NULL ;
#pragma data_seg()
HINSTANCE hInstance = 0;
HOOKPROC lpfnHookProc = 0;
LRESULT __stdcall KeyboardFunc (int nCode, WPARAM wParam, LPARAM lParam)
{
BOOL bPassToChain;
char szDebug [100];
// Check for exception cases...
if (nCode < 0)
return (CallNextHookEx (hKeyHook, nCode, wParam, lParam));
if (nCode == HC_NOREMOVE)
return (CallNextHookEx (hKeyHook, nCode, wParam, lParam));
switch (wParam)
{
case VK_F2:
wsprintf (szDebug, "F2 key message, lparam = 0x%X\n", lParam);
OutputDebugString (szDebug);
// only send on keydown, not keyup (autorepeat)
if (HIWORD (lParam) & 0xC000)
{
OutputDebugString ("F2 Keyup\n");
}
else
{
wsprintf (szDebug, "Sending F2 keydown message %d to hwnd 0x%X\n",
uMsg, hWndMain);
OutputDebugString (szDebug);
PostMessage (hWndMain, uMsg, 0, 0);
}
bPassToChain = FALSE;
break;
default :
bPassToChain = TRUE ;
break ;
}
if (bPassToChain)
return (CallNextHookEx (hKeyHook, nCode, wParam, lParam));
else
return TRUE ; // We have processed this key
}
BOOL __stdcall InstallExampleKeyboardHook (HWND hWnd, UINT uMyMsg)
{
hWndMain = hWnd ;
uMsg = uMyMsg;
lpfnHookProc = (HOOKPROC) KeyboardFunc ;
hKeyHook = SetWindowsHookEx (WH_KEYBOARD, lpfnHookProc, hInstance, NULL);
if (hKeyHook)
return TRUE ;
else
return FALSE ;
}
It's nothing about MFC. Straight native c++ code. If you have Visual Studio, you should be able to make a quick console c++ app, copy and paste that code in, and compile and test. If not, go and get the free but big Windows SDK. Less friendly then VS, but there's a compiler, so should get you in the right direction.
Low level hooks are not quite debugger friendly, so you might need to trace or log out some helpful statements.
Edited: to get the module handle, you will need something like this:
HookModule = LoadLibrary(ModulePath);
HookProc HookFunction = GetProcAddress(HookModule, "GetMessageCallBack");
GetMessageHookHandle = SetWindowsHookEx(HookType.WH_GETMESSAGE, HookFunction, HookModule, 0);