I want to hook my KeyboardProc from dll into my created window using dll injection. I want to see message boxes when pressing keys with focused injected window, but my code is not working properly.
Injected window code:
#include <windows.h>
#include <iostream>
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (lParam)
{
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgs, int nCmdShow)
{
HWND hwnd;
LPCTSTR className = L"WNDCLASS";
LPCTSTR windowName = L"Window";
WNDCLASSEX wcex;
memset(&wcex, 0, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.hInstance = hInstance;
wcex.lpszClassName = className;
wcex.style = CS_DBLCLKS;
wcex.lpfnWndProc = WndProc;
wcex.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
if (!RegisterClassEx(&wcex))
{
return -1;
}
hwnd = CreateWindowEx(NULL, className, windowName, WS_OVERLAPPEDWINDOW, 0, 0, 500, 500, NULL, NULL, hInstance, NULL);
if (!hwnd)
{
return -2;
}
MSG msg;
ShowWindow(hwnd, SW_NORMAL);
UpdateWindow(hwnd);
while (GetMessage(&msg, hwnd, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return
msg.lParam;
}
dll code:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <tchar.h>
HHOOK hhkKb;
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (wParam == WM_KEYDOWN)
{
MessageBox(0, L"DOWN", L"keyboard key down in dll", MB_ICONINFORMATION);
}
else if (wParam == WM_KEYUP)
{
MessageBox(0, L"UP", L"keyboard key up in dll", MB_ICONINFORMATION);
}
return
CallNextHookEx(hhkKb, nCode, wParam, lParam);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
HWND windowHandle = FindWindow(NULL, L"Window");
if (windowHandle == NULL)
{
MessageBox(NULL, L"Error", L"Handle is null", MB_ICONERROR);
return TRUE;
}
DWORD threadId = GetWindowThreadProcessId(windowHandle, NULL);
hhkKb = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hModule, threadId);
MessageBox(NULL, L"Success", L"Sucessfully injected dll", MB_ICONINFORMATION); //shows that message
break;
}
case DLL_PROCESS_DETACH:
{
UnhookWindowsHookEx(hhkKb);
break;
}
}
return TRUE;
}
I see "Sucessfully injected dll" message, but when I'm pressing keys into injected window, the KeyboardProc is not called, what I'm doing wrong?
using dll injection
unclear how you do this dll injection, but based on your previous question can assume that you manually inject your dll by CreateRemoteThread to LoadLibraryA. and in any case call SetWindowsHookEx from dll entry point is error by sense.
formally if thread, which call SetWindowsHookEx, exit - hooks will be automatically removed. so can say that on exit thread indirect call UnhookWindowsHookEx. don't sure are this clear documented, but can be view in next simply test
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
DbgPrint("%x>KeyboardProc(%x)\n", GetCurrentThreadId(), wParam);
return CallNextHookEx(0, nCode, wParam, lParam);
}
ULONG HookThread(PVOID threadId)
{
DbgPrint("%x>HookThread(%x)\n", GetCurrentThreadId(), (ULONG)(ULONG_PTR)threadId);
if (HHOOK hhk = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, (HINSTANCE)&__ImageBase, (ULONG)(ULONG_PTR)threadId))
{
Sleep(10000);//10 sec
//MessageBoxW(0,0,L"Close Me", MB_ICONWARNING);
}
return 0;
}
void test()
{
if (HANDLE hThread = CreateThread(0, 0, HookThread, (PVOID)(ULONG_PTR)GetCurrentThreadId(), 0,0))
{
CloseHandle(hThread);
MessageBoxW(0,0,L"Press Buttons here", MB_ICONINFORMATION);
}
}
first several seconds (or until you close MessageBoxW L"Close Me" if chose this variant) you can view dbgprint from KeyboardProc when you press buttons on first messagebox. after HookThread exit - no more KeyboardProc will be called.
so if you inject dll via CreateRemoteThread - this thread call LoadLibrary, then SetWindowsHookEx will be called in this thread, and finally thread just exit - and this remove effect of call SetWindowsHookEx - hook will be remove.
however if we use SetWindowsHookEx we not need manually inject dll to process. visa versa - this api special design for automatically inject dll to remote process. and of course you must not call SetWindowsHookEx from dll entry point - this is nonsense. you need call SetWindowsHookEx from remote process - as result your dll and will be injected to target process. reread Installing and Releasing Hook Procedures
Related
I want to break from the windows message loop. Just like C++ how to break message loop in windows hook . I came up with one solution, which works fine for Window Desktop Application, but fails for Console Application. How can this happen?
EDIT: I upload my codes to https://github.com/hellohawaii3/Experiment , clone it and then you can reproduce my problem quickly. Thanks!
1.Why I want to do so?
I am writing a console application. First, users are asked to set some options which determine app's behaviors. Then, the app start the message loop and monitor the input of the keyboard/mouse. Users may want to change the setting, so I want to enable users to press some hotkey, quit from the message loop and go back to the beginning of the application.
If you know any way to implement this function without worrying about breaking from the message loop, please tell me! However, I also want to know why my solution fails for console app and works well for desktop app. Thanks for your help!
2.What have I tried.
I use a bool variable 'recieve_quit'. When certain key is pressed on the keyboard, the variable is setting to True by the hook callback function. For every loop getting message, check the variable 'recieve_quit' first and quit when the variable is False.
3.Result of my experiment
For Console APP, The variable 'recieve_quit' is set correctly when certain key is pressed, however, the message loop continues.
For Windows Desktop APP with GUI, I can quit from the message loop as expected.
4.Experiment settings
I am using VS2019, C++, windows 10 home 1909.
I use dll inject to set hook for my console app.
5.My code
I provide toy example codes here, most of which is generated by Visual Studio automatically, do not bother if you think my codes are too loog.
(a)My console app
Console:
// Console.cpp
#include <iostream>
#include "dll_func.h"
#include <windows.h>
int main()
{
MSG msg;
HHOOK hhook_tmp2 = SetWindowsHookEx(WH_KEYBOARD_LL, HandleKeyboardEvent, hDllModule, 0);
while (recieve_quit == false)
{
if (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
MessageBox(NULL, TEXT("APP QUIT"), TEXT(" "), MB_OK);
}
(b)my dll containing hook function
My dll_func.h file, following the doc https://learn.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-160
#pragma once
#ifdef DLL1_EXPORTS
#define DLLFUNC_API __declspec(dllexport)
#else
#define DLLFUNC_API __declspec(dllimport)
#endif
#include "framework.h"
extern "C" DLLFUNC_API bool recieve_quit;
extern "C" DLLFUNC_API LRESULT CALLBACK HandleKeyboardEvent(int nCode, WPARAM wParam, LPARAM lParam);// refer to https://stackoverflow.com/a/60913531/9758790
extern "C" DLLFUNC_API HMODULE hDllModule;//or whatever name you like
My dll_func.cpp file, containing the definition of hook function.
#include "pch.h"
#include <windows.h>
#include "dll_func.h"
bool recieve_quit = false;
LRESULT CALLBACK HandleKeyboardEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
if (wParam == WM_KEYDOWN) {
if (p->vkCode == VK_F8) {
recieve_quit = true;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
The dllmain.cpp is also modified a little after it is created by Visual Studio as suggested by https://stackoverflow.com/a/60913531/9758790 to get the hinstance of the dll for dll injection.
// dllmain.cpp
#include "pch.h"
#include "dll_func.h"
// refer to https://stackoverflow.com/a/60913531/9758790
HMODULE hDllModule;//or whatever name you like
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
hDllModule = hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
(c) My Desktop application.
I select 'Creating Windows Desktop Application' when created the solution with Visual Studio. Most of the codes is generated automatically by VS. The codes I added and modifed is surrounded by ***** in the codes.
// GUI.cpp
//
#include "framework.h"
#include "GUI.h"
#define MAX_LOADSTRING 100
HINSTANCE hInst;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
// *********************** ADDED BY ME! ***********************
// same as dll_func.cpp
bool recieve_quit = false;
LRESULT CALLBACK HandleKeyboardEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
//FILE* file;
//fopen_s(&file, "./temp0210222.txt", "a+");
//fprintf(file, "Function keyboard_hook called.n");
//fclose(file);
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam;
if (wParam == WM_KEYDOWN) {
if (p->vkCode == VK_F8) {
recieve_quit = true;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
// *********************** END OF MY CODES ***********************
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// *********************** ADDED BY ME! ***********************
HHOOK hhook_tmp2 = SetWindowsHookEx(WH_KEYBOARD_LL, HandleKeyboardEvent, hInst, 0);
// *********************** END OF MY CODES ***********************
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_GUI, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GUI));
MSG msg;
// main message loop:
// *********************** MODIFIED BY ME! ***********************
//while (GetMessage(&msg, nullptr, 0, 0))
//{
// if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
// {
// TranslateMessage(&msg);
// DispatchMessage(&msg);
// }
//}
while (recieve_quit == false)
{
if (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
MessageBox(NULL, TEXT("APP QUIT"), TEXT(" "), MB_OK);
// *********************** END OF MODIFICATION ***********************
return (int) msg.wParam;
}
//
// Function: MyRegisterClass()
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GUI));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_GUI);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// Function: InitInstance(HINSTANCE, int)
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// Function: WndProc(HWND, UINT, WPARAM, LPARAM)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// "About"
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
(d)Tips for reproducing my results
For Console APP: Build a new console application solution named Console, and copy my codes to replace Console.cpp. Add a new project to this solution, select Create new project -> DLL, name it as Dll1. Create dll_func.h, dll_func.c, and copy my codes in. Do not forget to modify dllmain.cpp, too. Set the path of AdditionalIncludeDirectories, add dll1.lib to AdditionalDependencies. Set the path to dll and lib files too.
For Windows Desktop APP: Build a now Windows Desktop App solution, name it as GUI. Copy my codes in GUI.cpp and simply run it. Press key F8, the app will quit and pop up a message box as expected.
This post explains why console apps dont receive keyboard messages and how they can handle them:
does not go inside the Windows GetMessage loop on console application
In your console case execution enters GetMessage and never exits it. The hook receives a notification and correctly sets recieve_quit. Since the execution never exits GetMessage, recieve_quit is not checked.
This answer is about handling getting keypresses in a console app:
https://stackoverflow.com/a/6479673/4240951
In general - add a hidden window to your console app or check GetAsyncState of the needed key.
There is no need to set hooks when you have a message loop. You may check keypresses as follows:
while (recieve_quit == false)
{
if (GetMessage(&msg, nullptr, 0, 0)) {
if (msg.message == WM_KEYDOWN && msg.wParam == VK_F8)
recieve_quit = true;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
I have windows application with this source code
#include <Windows.h>
#include <thread>
#include <chrono>
int WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int cmdShow)
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WinProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_QUERYENDSESSION:
MessageBox(NULL, "Triggered?", "Message", 0);
AbortSystemShutdown(NULL);
return 0;
default:
return DefWindowProc(hWnd, Message, wParam, lParam);
}
return 0;
}
I need to know when the system is shutting down and prevent it, or at least send a message to the user.
It doesn't seem that my application is receiving the WM_QUERYENDSESSION message.
I also tried to use ShutdownBlockReasonCreate() but I don't have a HWND for a window.
How should I do this?
As stated in the reference for WM_QUERYENDSESSION:
A window receives this message through its WindowProc function.
You have a WindowProc but you are missing a window. A WindowProc must be associated with a window, otherwise it is not known to Windows. To associate a WindowProc with a window, you can call RegisterClassEx followed by CreateWindowEx. Specify the name of your newly created window class in the call to CreateWindowEx.
The window must be a top-level window. It can be invisible, but in this case the following applies (from Application Shutdown Changes in Windows Vista):
Also note that if your application has no visible top-level windows,
it must use this API [ShutdownBlockReasonCreate()] if it needs to successfully block shutdown. Such
applications will automatically be terminated if they block shutdown
without using the API.
Note that a message-only window will not receive WM_QUERYENDSESSION.
Working example:
#include <windows.h>
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );
int APIENTRY wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow )
{
WNDCLASSEXW wx = { sizeof(wx) }; // set cbSize member and zero-initialize all other
wx.lpfnWndProc = WndProc;
wx.hInstance = hInstance;
wx.lpszClassName = L"MyWindowClass";
if( ! RegisterClassExW( &wx ) )
return 1; // TODO: improve error handling
HWND hWnd = CreateWindowExW( 0, wx.lpszClassName, L"My Application", 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL );
if( ! hWnd )
return 2; // TODO: improve error handling
MSG msg;
while( GetMessage( &msg, nullptr, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return static_cast<int>( msg.wParam );
}
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch( message )
{
case WM_QUERYENDSESSION:
{
// Try to block shutdown.
ShutdownBlockReasonCreate( hWnd, L"I don't want to sleep (yet)!" );
return FALSE;
}
case WM_ENDSESSION:
{
// TODO: Always handle this message because shutdown can be forced
// even if we return FALSE from WM_QUERYENDSESSION!
return 0;
}
default:
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
return 0;
}
Further read:
Application Shutdown Changes in Windows Vista
Restart Manager - Guidelines for Applications
Shutting Down
I have super simple WinApi program. Close button does not destroy process of the program. What should be added?
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hINSTANCE;
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpstr,
int nCmdShow) {
// VARIABLES
TCHAR className[] = _T("win32api");
TCHAR windowName[] = _T("Protected Window");
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_INFORMATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = className;
wcex.hIconSm = LoadIcon(NULL, IDI_SHIELD);
if (!RegisterClassEx(&wcex)) {
MessageBox(NULL, _T("Cannot register window"), _T("Error"), MB_OK | MB_ICONERROR);
return 0;
}
HWND hWnd = CreateWindow(className,
windowName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
300,
NULL,
NULL,
hInstance,
NULL);
if (!hWnd) {
MessageBox(hWnd, _T("Call to register windows isn't working"), _T("Error"), NULL);
return 1;
}
hINSTANCE = hInstance;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, hWnd, 0, 0) != 0 || GetMessage(&msg, hWnd, 0, 0) != -1) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.lParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uInt, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
HDC hdc;
switch (uInt) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 10, 20, _T("Text sample right here boyz."), _tcsclen(_T("Text sample right here boyz.")));
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uInt, wParam, lParam);
break;
}
return 0;
}
If I add one more case
case WM_CLOSE:
PostQuitMessage(0);
break;
It doesnt close at all (pressing close button is not doing anything). Any other tips are very appreciated.
Your message loop is wrong, for two reasons:
you are calling GetMessage() twice. Don't do that! Think of what happens if you do. If the GetMessage() call on the left-hand side of || were to detect WM_QUIT (which it can't, see further below), it would return 0. Which would cause || to call the GetMessage() on the right-hand side, ignoring the WM_QUIT from the previous call and blocking the loop until a new message arrives later. You should call GetMessage() only once per loop iteration, and then act on the return value as needed:
BOOL bRet;
do
{
bRet = GetMessage(&msg, hWnd, 0, 0);
if (bRet == 0) break;
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
while (1);
However, you are also filtering messages by HWND (don't do that!), and so it will only return messages that are posted to the specified HWND via PostMessage(). PostQuitMessage() posts its WM_QUIT message to the input queue of the calling thread itself, not to the HWND. So the filtering will never see the WM_QUIT message. In order for WM_QUIT to break the loop, you need to stop filtering by HWND altogether, or at least make an unfiltered call periodically.
Your message loop should look like this instead. This is the standard message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Note in this case, there is no need to handle -1 from GetMessage()!.
To close the app, you need to destroy the window. Typically that's done in one of two ways:
Have your WM_CLOSE handler call DestroyWindow.
Have the DefWindowProc handle the message after you do your thing.
Your WndProc is a little out of the ordinary. I usually see them written as:
{
switch(msg)
{
case WM_CLOSE:
// prompt user, clean stuff up, etc.
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
// Other cases here. Most will fall out of the switch so that
// DefWindowProc can handle them (for other system notifications).
// Only return from the case if you really don't want anybody else
// to handle the message.
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
For specifics on WM_CLOSE, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms632617(v=vs.85).aspx, which specifically says that you must call DestroyWindow or let DefWindowProc do it.
I am using this lib http://code.google.com/p/injex/ to inject a dll that contains a keyboard hook function. The dll is simple, just create a thread and use the 'setWindowsHookEx' function.
The injection was always successful, but the keyboard hook didn't work for all the cases.
I wrote a simple 'hello world' win32 app, and use injex.exe to inject the keyboard hook dll into it. All works as expected.
If I choose a complex program to inject (A game written in delphi), the dll was successfully injected(I can see the injected main form), but it failed to hook the keyboard.
So my question is:
Is the failure of keyboard hook because of the injection method, or the keyboard hook function?
Is it possible that a dll can be injected, but its functionalities are not guaranteed?
So any advices are welcome.
Here is the dll code:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
//GUI==============================================================================
#define MYMENU_EXIT (WM_APP + 101)
#define MYMENU_MESSAGEBOX (WM_APP + 102)
HINSTANCE inj_hModule; //Injected Modules Handle
HWND prnt_hWnd; //Parent Window Handle
//WndProc for the new window
LRESULT CALLBACK DLLWindowProc (HWND, UINT, WPARAM, LPARAM);
//Register our windows Class
BOOL RegisterDLLWindowClass(wchar_t szClassName[])
{
WNDCLASSEX wc;
wc.hInstance = inj_hModule;
wc.lpszClassName = (LPCWSTR)L"InjectedDLLWindowClass";
wc.lpszClassName = (LPCWSTR)szClassName;
wc.lpfnWndProc = DLLWindowProc;
wc.style = CS_DBLCLKS;
wc.cbSize = sizeof (WNDCLASSEX);
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.lpszMenuName = NULL;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
if (!RegisterClassEx (&wc))
return 0;
}
//Creating our windows Menu
HMENU CreateDLLWindowMenu()
{
HMENU hMenu;
hMenu = CreateMenu();
HMENU hMenuPopup;
if(hMenu==NULL)
return FALSE;
hMenuPopup = CreatePopupMenu();
AppendMenu (hMenuPopup, MF_STRING, MYMENU_EXIT, TEXT("Exit"));
AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("File"));
hMenuPopup = CreatePopupMenu();
AppendMenu (hMenuPopup, MF_STRING,MYMENU_MESSAGEBOX, TEXT("MessageBox"));
AppendMenu (hMenu, MF_POPUP, (UINT_PTR) hMenuPopup, TEXT("Test"));
return hMenu;
}
//Our new windows proc
LRESULT CALLBACK DLLWindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_KEYDOWN:
if(wParam == VK_HOME) {
//TextOut(hdc,15, 15, keydown, _tcslen(keydown));
// etc.
MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Win32 Guided Tour"), NULL);
}
break;
case WM_COMMAND:
switch(wParam)
{
case MYMENU_EXIT:
SendMessage(hwnd, WM_CLOSE, 0, 0);
break;
case MYMENU_MESSAGEBOX:
MessageBox(hwnd, L"Test", L"MessageBox",MB_OK);
break;
}
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
//HOOK LIB=====================================================================================================
// This will be called when keys are pressed.
HHOOK g_kbHook=NULL;
LRESULT CALLBACK KeyboardHook(int nCode,WPARAM wParam,LPARAM lParam)
{
KBDLLHOOKSTRUCT* key;
if(wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN){
key = (KBDLLHOOKSTRUCT*)lParam;
if(key->vkCode == VkKeyScan('a')){
//odprintf("You pressed 'a'");
MessageBox(NULL, _T("Pressed a"), _T("Win32 Guided Tour"), NULL);
}
if(key->vkCode == VK_F1){
MessageBox(NULL, _T("Pressed F1"), _T("Win32 Guided Tour"), NULL);
//odprintf("You pressed F1");
}
}
return 0;
}
LRESULT CALLBACK _KeyboardHookProc(int nCode,WPARAM wParam,LPARAM lParam){
//g_kbHookFunction(nCode,wParam,lParam);
KeyboardHook(nCode,wParam,lParam);
return CallNextHookEx(g_kbHook,nCode,wParam,lParam);
};
//ENTRY POINT================================================================================================
//our start function, start everything here
DWORD WINAPI start(LPVOID lpParam){
// START YOUR CODE HERE...
// Lay some hooks, season libraly with pwnsauce.
// Do whatever you want here, this is your thread to play with.
// NOTE: This is used as the starting function in most examples.
// STOP HERE.
MSG messages;
wchar_t *pString = reinterpret_cast<wchar_t * > (lpParam);
HMENU hMenu = CreateDLLWindowMenu();
RegisterDLLWindowClass(L"InjectedDLLWindowClass");
prnt_hWnd = FindWindow(L"Window Injected Into ClassName", L"Window Injected Into Caption");
HWND hwnd = CreateWindowEx (0, L"InjectedDLLWindowClass", L"mywindow", WS_EX_PALETTEWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, prnt_hWnd, hMenu,inj_hModule, NULL );
ShowWindow (hwnd, SW_SHOWNORMAL);
g_kbHook = SetWindowsHookEx(WH_KEYBOARD_LL,_KeyboardHookProc,GetModuleHandle(NULL),0);
if (!g_kbHook) {
}
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return 1;
g_kbHook = NULL;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
// We create a new thread to do our bidding in, this way we don't hold the loader lock.
// We close the handle to the thread for various reasons.
inj_hModule = hModule;
CloseHandle(CreateThread(NULL, 0, start, (LPVOID)hModule, 0, NULL));
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
If the process you're trying to hook already installed its own keyboard hook, that hook may not be calling CallNextHookEx() to pass the message along to your hook. See the description at MSDN (note the explanation in the Return Value section). If that is indeed the cause of your problem, your only option may be to install your hook before the process installs its own hook.
Summary
I am creating a simple application that allows the user to select a process that contains a top-level window. The user first types the path of a native DLL (not a managed DLL). Then the user types the name of the method that will be called inside the hook procedure. The method must not return a value and must be parameter-less. Then their is a button that does the hooking.
The Problem
I am able to retrieve the module handle of the library that does the hooking. In addition, I am also able to get the module handle library the user wants to use that contains the method that he/she wants to hook. Plus, I am able to receive the procedures address both the method that user wants to hook and etc.
In other words their is not on invalid handle being returned. Other than the handle to the hook (HHOOK)
But SetWindowsHookEx returns a NULL HHOOK and GetLastError returns error code 126 (ERROR_NO_MOD_FOUND).
Possible Theories On Why I Get ERROR_MOD_NOT_FOUND
There is a limitation on the number of global hooks because I read somewhere with someone who has had that same error when hooking.
I am not getting the correct module handle of the DLL that will do the hooking. But then again I have tried many different method to get the libraries HMODULE/HINSTANCE but all fail with the same error.
WinHooker.cpp
#include "winhooker.h"
HMODULE GetCurrentModule()
{
HMODULE hModule = NULL;
GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCTSTR)GetCurrentModule,
&hModule);
return hModule;
}
LRESULT (WINAPI hookProc)(int nCode, WPARAM wParam, LPARAM lParam);
void recievedCallback(WIN32Hooker* hooker);
WIN32Hooker* current;
WIN32Hooker::WIN32Hooker(HINSTANCE* hInstance, Callback callback)
{
if (!hInstance)
{
HMODULE hModule = GetCurrentModule();
hInstance = &hModule;
}
this->callback = callback;
this->hInstance = hInstance;
return;
}
void WIN32Hooker::Hook(DWORD threadToHook)
{
recievedCallback(this);
this->hhk = SetWindowsHookEx(WH_CALLWNDPROC, hookProc, *this->hInstance, threadToHook);
DWORD errorCode = GetLastError(); // 126 ERROR_MOD_NOT_FOUND
return;
}
void WIN32Hooker::NextHook(int nCode, WPARAM wParam, LPARAM lParam)
{
callback();
CallNextHookEx(this->hhk, nCode, wParam, lParam);
return;
}
void WIN32Hooker::Free()
{
UnhookWindowsHookEx(this->hhk);
return;
}
LRESULT (WINAPI hookProc)(int nCode, WPARAM wParam, LPARAM lParam)
{
current->NextHook(nCode, wParam, lParam);
return 0;
}
void recievedCallback(WIN32Hooker* hooker)
{
current = hooker;
}
extern "C" WIN32Hooker* hookerMalloc(HINSTANCE* hInstance, Callback callback)
{
return new WIN32Hooker(hInstance, callback);
}
Test.cpp
#include <Windows.h>
extern "C" void sendMessage(void)
{
MessageBox(NULL, L"Test", L"Test", MB_ICONINFORMATION);
}
Main Code
// Window Hooker Bytes.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "Window Hooker Bytes.h"
#include "processes.h"
#include "button.h"
#include "edit.h"
#include "listbox.h"
#include "/Users/FatalisProgrammer/Documents/Visual Studio 2010/Projects/Window Hooker Bytes/WindowHookerLib/winhooker.h"
#define MAX_LOADSTRING 100
using namespace Processes;
using namespace Controls;
void Delete(); // Delete proto-type
void windowListCallback(map<HWND, wstring>* list); // Callback proto-type
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
Button* hookButton;
Edit* dllPathEdit;
Edit* methodNameEdit;
ListBox* windowList;
ProcessEnumerator* processEnumerator;
map<HWND, wstring> mapList;
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_WINDOWHOOKERBYTES, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWHOOKERBYTES));
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage are only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWHOOKERBYTES));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WINDOWHOOKERBYTES);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU, CW_USEDEFAULT, 0, 800 / 2, 480 / 2, NULL, NULL, hInstance, NULL);
hookButton = new Button(hWnd, 120, L"Hook Method From DLL!", 5, (480 / 2) - 24 - 24 - 24 - 6, (800 / 2) - 15, 24);
hookButton->show(hInst);
dllPathEdit = new Edit(hWnd, 169, 5, 5, (800 / 2) - 15, 24);
dllPathEdit->show(hInst);
dllPathEdit->setWatermarkText(L"Enter Path Of A Native DLL.");
methodNameEdit = new Edit(hWnd, 256, 5, (5 * 2) + 24, (800 / 2) - 15, 24);
methodNameEdit->show(hInst);
methodNameEdit->setWatermarkText(L"Enter Method (Must Return Void And Be Parameterless)");
methodNameEdit->setFont(L"Times", 16);
dllPathEdit->setFont(L"Times", 16);
hookButton->setFont(L"Times", 16);
windowList = new ListBox(hWnd, 333, 5, (5 * 8) + 24, (800 / 2) - 15, (124 / 2) + 24);
windowList->show(hInst);
windowList->setFont(L"Times", 16);
hookButton->setUACShield();
if (!hWnd)
{
return FALSE;
}
processEnumerator = new ProcessEnumerator();
processEnumerator->enumerateProcesses(windowListCallback);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
if (wmId == hookButton->getID())
{
HMODULE libraryModule = LoadLibrary(L"WindowHookerLib.dll");
typedef WIN32Hooker* (*HookerMalloc)(HINSTANCE* hInstance, Callback callback);
HookerMalloc hookerMalloc = (HookerMalloc)GetProcAddress(libraryModule, "hookerMalloc");
DWORD errorCode = GetLastError();
HMODULE libraryToHookModule = LoadLibrary(dllPathEdit->getText());
if (!libraryToHookModule || !libraryModule)
{
MessageBox(hWnd, L"Error: Library That Contains The Method To Hook Or The WindowHookerLib.dll Does Not Exist!\nMake Sure WindowHookerLib.dll Is In The Current Working Directory!", L"Error In Application!", MB_ICONERROR);
}
else
{
typedef void (__stdcall *Method)(void);
char* methodName = new char[wcslen(methodNameEdit->getText()) * 2];
wcstombs(methodName, methodNameEdit->getText(), wcslen(methodNameEdit->getText()) * 2);
Method method = (Method)GetProcAddress(libraryToHookModule, methodName);
if (!hookerMalloc || !method)
{
MessageBox(hWnd, L"Error: The Method To Hook Does Not Exist Or The Method To Initiate The Hooker Is Not Available!", L"Error In Application", MB_ICONERROR);
}
else
{
WIN32Hooker* hooker = hookerMalloc(NULL, method);
DWORD pID;
int selectedItemIndex = windowList->getSelectedIndex();
vector<HWND> v;
for(map<HWND,wstring>::iterator it = mapList.begin(); it != mapList.end(); ++it)
{
v.push_back(it->first);
}
GetWindowThreadProcessId(v[selectedItemIndex], &pID);
if (pID >= 0)
{
hooker->Hook(pID);
}
else
{
MessageBox(hWnd, L"Error Could Not Retrieve Process ID!", L"Error In Application", MB_ICONERROR);
}
}
delete methodName;
}
}
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
Delete();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
void Delete()
{
delete dllPathEdit;
delete hookButton;
delete methodNameEdit;
delete windowList;
processEnumerator->free();
delete processEnumerator;
return;
}
void windowListCallback(map<HWND, wstring>* list)
{
mapList = *list;
vector<wstring> v;
for(map<HWND,wstring>::iterator it = mapList.begin(); it != mapList.end(); ++it)
{
v.push_back(it->second);
}
for each(wstring item in v)
{
windowList->addItem(item);
}
return;
}
Conclusion
Any help would be appreciated. If I am doing something wrong, feel free to point it out. This project is for learning purposes, so tell me what needs to be fixed, or any tips would be great.
In this code:
HMODULE hModule = GetCurrentModule();
hInstance = &hModule;
you are assigning the address of a local variable to hInstance. When this is dereferenced in the call to SetWindowsHookEx you'll get a bogus value.
Throughout the code, don't use a pointer to an HINSTANCE, just use a plain HINSTANCE.