I am working on my university project and stuck with a problem.
My task is to write a program and .dll to intercept calls from WinAPI.
For example I launch my program and it should inject .dll using SetWindowsHookEx to any process by PID. I successfully made this task for function CreateFile, but I need to realise it for multiple functions and allow user to choose which function to intercept via command line arguments.
In my code, when installing hook I define callback function:
HINSTANCE hinst = LoadLibrary(L"ConsoleApplication1.dll");
HOOKPROC addr = (HOOKPROC)GetProcAddress(hinst, "meconnect");
HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, addr, hinst, threadID);
But there are only three arguments in this callback function for launching CallNextHookEx(NULL, code, wParam, lParam) and it seem we can't change them.
The other problem is that when we inject our .dll to another process we are only able to run tasks from DLL_PROCESS_ATTACH section, so it doesn't link with our program, so we can't pass our arguments.
Maybe the solution is to create a temporary file and write arguments to it and then read it from .dll, but I hope you will help me with more elegant solution. I will appreciate any help.
You can use separate function for every hook for example.
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
SetWindowsHookEx(WH_CBT, CBTProc, (HINSTANCE) NULL, GetCurrentThreadId());
SetWindowsHookEx(WH_CALLWNDPROCRET, CallWndRetProc, (HINSTANCE) NULL, GetCurrentThreadId());
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0)
return CallNextHookEx(___cbt_message_hookptr, nCode, wParam, lParam);
// your code here
return CallNextHookEx(___cbt_message_hookptr, nCode, wParam, lParam);
}
LRESULT CALLBACK CallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0)
return CallNextHookEx(___callwndprocret_message_hookptr, nCode, wParam, lParam);
// your code here
return CallNextHookEx(___callwndprocret_message_hookptr, nCode, wParam, lParam);
}
For reference your DLL_PROCESS_ATTACH issue, it's common problem with communication between intercepted process and your process.
Best solution in my opinion is using Pipes as inter process communication mechanism, bcs it's simplest and most elegant solution.
My advice is you should start another thread from DLL_PROCESS_ATTACH which will be receiving commands via pipe from main application to hook certain functions and also it will be sending intercepted data over same pipe to main application where you will archive or display this data.
Example of sending message over pipe from intercepted process, assuming that you have something listening for this data on other side:
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\payload_datapipe");
CHAR chReadBuf[1024];
DWORD cbRead = 0;
BOOL fResult;
fResult = CallNamedPipe(
lpszPipename, // pipe name
_Message, // message to server
strlen(_Message), // message length
chReadBuf, // buffer to receive reply
sizeof(chReadBuf), // size of read buffer
&cbRead, // number of bytes read
NMPWAIT_NOWAIT);//NMPWAIT_WAIT_FOREVER); // wait;-)
if (!fResult)
{
return;
}
More information's about Named Pipes you can find at relevant documentation pages. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365592%28v=vs.85%29.aspx
Related
I am using the Windows API on Windows7 with Visual Studio 2019.
I'm trying to set a low level keyboard hook and read the key input to the console, to do so I use the function: HHOOK SetWindowsHookExA( [in] int idHook, [in] HOOKPROC lpfn, [in] HINSTANCE hmod, [in] DWORD dwThreadId )
with the argument: WH_KEYBOARD_LL so I can monitor keyboard events.
This is my main function:
int main()
{
...
HHOOK keyBoardHook = SetWindowsHookExW(WH_KEYBOARD_LL, keyboardHook, NULL, 0);
MSG message;
while (true)
{
while(PeekMessage(&message, NULL, 0, 0, PM_REMOVE) != 0)
{
DispatchMessage(&message);
}
}
...
UnhookWindowsHookEx(keyBoardHook);
return 0;
}
And this is the callback implementation:
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT* keyData = (KBDLLHOOKSTRUCT*)lParam;
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
{
fileHandler.addStr(keyHandler.translate(keyData,true,topWindowThreadId));
}
else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
{
fileHandler.addStr(keyHandler.translate(keyData,false,topWindowThreadId));
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
How both variables work is not the issue here but just know that
in the callback keyHandler translates the message and fileHandler prints the result to a file.
Here comes the issue. The callback receives:
a key down event when I press a key
a key up event when I release
a repeated key down event when I hold a key
a control key up event VK_LCONTROL when focused window changes
Why does that happen ? This behavior is never mentioned in the MSDN documentation as far as I know. If I switch from console window or any window to any other window this event is generated and a [left control up] message is printed by my keyHandler !
Now to make sure it's not a problem with my code I changed the Callback to this:
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT* keyData = (KBDLLHOOKSTRUCT*)lParam;
if (keyData->vkCode == VK_LCONTROL)
{
printf("CTRL");
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
And still CTRL is printed to the consol whenever I change the focused window, Note that only VK_LCONTROL is generated and wParam is always WM_KEYUP
This is not supposed to happen!
I tried analyzing and modifying my code, tried removing everything from the main function until it looked just like in the example above even rebooted the computer to check if it's an internal issue, nothing helps. and when searching the web for a similar post with a similar issue I found absolutely none, which makes it even more confusing.
Update
I tried compiling someone else's keyboard-hook related code from GitHub, same issue.. is this normal ? Why is it not documented ?
According to the description of the SetWindowsHookExA in the official Microsoft documentation.
The global hooks are a shared resource, and installing one affects all applications in the same desktop as the calling thread. All global hook functions must be in libraries. Global hooks should be restricted to special-purpose applications or to use as a development aid during application debugging. Libraries that no longer need a hook should remove its hook procedure.
HHOOK SetWindowsHookExA(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);
For desktop apps, if this parameter(dwThreadId) is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread. That's why after you changed the focused window, CTRL still printed to the console. So it is recommended that you use hook in DLL.
I want to learn something about Windows Hooks. Right now, I'm only interested in "catching" messages.
So I did the following, but it doesn't work.
I want to catch the message in the same thread I'm using. I don't want to catch another thread's messages.
Can somebody explain to me what I'm doing wrong?
I install the hook as follows:
myhookdata.nType = WH_GETMESSAGE;
myhookdata.hkprc = GetMsgProc;
myhookdata.hhook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, NULL, GetCurrentThreadId());
Then in the hook procedure I do this, just for testing. But the "WM_LBUTTONDOWN" never get catched!!
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
LPCWPSTRUCT message = (LPCWPSTRUCT)lParam;
if (nCode < 0)
return CallNextHookEx(myhookdata.hhook, nCode,
wParam, lParam);
switch (nCode)
{
case HC_ACTION:
if (wParam)
if (message->message == WM_LBUTTONDOWN)
Sleep(0);
break;
default:
break;
}
return CallNextHookEx(myhookdata.hhook, nCode, wParam, lParam);
}
According to the documentation:
lParam [in]
Type: LPARAM
A pointer to an MSG structure that contains details about the message.
So you only need to change the code:
PMSG message = (PMSG)lParam;
Then it works for me.
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);
So a bit of a weird bug going on when getting a WM_KEYDOWN msg in my Window class.
I have a Global WndProc function that determines which window instance it is and sends the message to it's own local WndProc function.
//Every Windows Message will hit this function.
LRESULT CALLBACK GlobalWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
//Get the Window Instance from the userdata
Window* targetWindow = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
//If no window exists, then it must be the first time so we should set it
if (!targetWindow) {
//First let's try and extract the Window instance pointer from the lparam
CREATESTRUCT* createStruct = (CREATESTRUCT*)lparam;
if (createStruct) {
targetWindow = (Window*)createStruct->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)targetWindow);
}
else {
//It was some other message which we just can't deal with right now
return DefWindowProc(hwnd, msg, wparam, lparam);
}
}
//Now we can pipe it to the Window's local wnd proc function
return targetWindow->LocalWndProc(hwnd, msg, wparam, lparam);
}
And my local wndproc looks like this:
LRESULT CALLBACK Window::LocalWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
switch (msg) {
case WM_KEYDOWN:
ToggleFullScreen(!m_fullScreen);
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
break;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
}
So it's pretty simple at this point. If any key is pressed, the window should call its member function ToggleFullScreen.
Now for some reason when i run this and I hit any key on the keyboard, I get an exception fault:
Unhandled exception at 0x77c015ee in Athena_Debug.exe: 0xC0000005: Access violation reading location 0x0000012d.
With CallStack:
ntdll.dll!77c015ee()
ntdll.dll!77bf015e()
user32.dll!7588788a()
Athena_Debug.exe!Athena::Window::HandleWindowsMessage() Line 195 + 0xc bytes C++
Athena_Debug.exe!Athena::AthenaCore::StartEngine() Line 96 + 0x12 bytes C++
Athena_Debug.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 44 C++
Athena_Debug.exe!__tmainCRTStartup() Line 547 + 0x2c bytes C
Athena_Debug.exe!WinMainCRTStartup() Line 371 C
kernel32.dll!7702339a()
The line it actually breaks on is the DispatchMessage(&msg) line located here:
MSG Window::HandleWindowsMessage() {
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
if (m_realTime) {
PeekMessage(&msg, m_hwnd, 0, 0, PM_REMOVE);
}
else {
GetMessage(&msg, m_hwnd, 0, 0);
}
if (msg.message != WM_NULL) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg;
}
So the interesting thing is that if I comment out ToggleFullScreen and instead put an OutputDebugTrace there it works just fine and outputs the debug trace. It appears like it can't resolve the function for an instance? If I comment everything out in ToggleFullScreen, it still crashes. If i create a brand new function that does nothing and call it in response to WM_KEYDOWN it still errors.
Anyone have any idea why this is happening or some ideas on how I can go about fixing it?
Thanks!
I would be guessing that you have wrong pointer obntained from GetWindowLongPtr, as it was not set by SetWindowLongPtr. As I wrote in my comment, you should not test if it's empty (I have no idea what is there by default but i wouldn't assume anything.), but set it when you recieve WM_CRERATE (which is guaranteed to be the first message sent to a window). Have you tested if SetWindowLongPtr is acctually called?
Other messages are not dependent on any contents of Window class, so they are working well with wrong this pointer - it is an error to call method for wrong pointer, but if method is not actually using it, it will still work well.
EDIT:
Another possbile cause: Why are you not using result value from PeekMessage? i see that you are testing for WM_NULL, which should be 0 at this point (you are zeroing memory), but PeekMessage does not guarantee that it's not touching MSG structure even if it's not retrieving anything. Using PeekMessage result should be used at all times. Plus zeroing memory is rather inefficient at this point.
The first message does not have to be WM_NCCREATE (or WM_CREATE), you have to be prepared to get WM_SIZE or WM_GETMINMAXINFO (and probably other messages) before any create message!
In your code you would change if (createStruct) { to if (WM_NCCREATE==msg && createStruct) {...
You can see this in Raymond Chen's scratch program.
Your WindowProc callback should look more like this:
//Every Windows Message will hit this function.
LRESULT CALLBACK GlobalWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
Window* targetWindow;
if (msg == WM_CREATE)
{
//extract the Window instance pointer from the lparam
CREATESTRUCT* createStruct = (CREATESTRUCT*)lparam;
targetWindow = (Window*)createStruct->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)targetWindow);
}
else
{
//Get the Window Instance from the userdata
targetWindow = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (targetWindow)
{
//Now we can pipe it to the Window's local wnd proc function
return targetWindow->LocalWndProc(hwnd, msg, wparam, lparam);
}
//It was some other message which we just can't deal with right now
return DefWindowProc(hwnd, msg, wparam, lparam);
}