2

I was trying to make a simple keylog test, but my program doesn't work as expected and I don't know why.

In my program i had a low level keyboard hook and attach a simple process to it. The process just opens/creates a file and writes "Hello World" then closes. However it isn't creating the file, probably because my process isn't correct or because my hook wasn't established correctly.

Code:

#include<windows.h>
#include<stdio.h>
#include <iostream>
#include <fstream>

using namespace std;

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam){
    ofstream myfile;
        myfile.open ("[PATH]/example.txt");
        myfile << "Hello world";//((KBDLLHOOKSTRUCT *)lParam)->vkCode
    myfile.close();
    return CallNextHookEx(NULL,code,wParam,lParam);
}

int main(void){
    HINSTANCE hInst = NULL;
    HHOOK hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, hInst, 0);
    printf("HHOOK is not null: %s\n", (hHook != NULL) ? "True" : "False");
    char q;
    for (cout << "q to quit\n"; q != 'q'; cin >> q);

    printf("Successfully unhooked: %s", UnhookWindowsHookEx(hHook) ? "True" : "False");
}

Solution I needed to add a message loop to the main function:

LPMSG Msg;
while(GetMessage(Msg, NULL, 0, 0) > 0)
{
    TranslateMessage(Msg);
    DispatchMessage(Msg);
}
Alter
  • 3,332
  • 4
  • 31
  • 56
  • Check the result of the call to `SetWindowsHookEx`. Quote from MSDN: "If the function fails, the return value is NULL. To get extended error information, call GetLastError." – bialpio Nov 21 '14 at 20:56
  • it isn't null, I'll update the code so that it includes the check – Alter Nov 21 '14 at 20:59
  • Your solution has a BIG memory issue! You are using a pointer to MSG, but you are not allocating the memory for storing it. You better use: MSG Msg; and pass its pointer to each function (i.e. &Msg) – cabbi Nov 22 '14 at 05:52

1 Answers1

6

Thre are two kind of hooks:

Global Hooks
Global hook procedures should be placed into a separate DLL, then in your main process you load the hook dll and it's hooking procedure and set the hook.

A global hook monitors messages for all threads in the same desktop as the calling thread. A thread-specific hook monitors messages for only an individual thread. A global hook procedure can be called in the context of any application in the same desktop as the calling thread, so the procedure must be in a separate DLL module.

That said: KeyboardProc goes into a separate DLL (e.g. myhookdll.dll).
In your process you do something like this:

HOOKPROC hkprc;
static HINSTANCE hhookDLL ; 
static HHOOK hhook ; 

hhookDLL = LoadLibrary(TEXT("c:\\...\\myhookdll.dll")); 
hkprc = (HOOKPROC)GetProcAddress(hhookDLL, "KeyboardProc"); 

hhook = SetWindowsHookEx( 
                    WH_KEYBOARD,
                    hkprc,
                    hhookDLL,
                    0); 

Here a good reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644960(v=vs.85).aspx

Thread Level Hooks
about this specific 'WH_KEYBOARD_LL' that hooks at thread level, it does not need a separate DLL.

A thread-specific hook procedure is called only in the context of the associated thread. If an application installs a hook procedure for one of its own threads, the hook procedure can be in either the same module as the rest of the application's code or in a DLL. If the application installs a hook procedure for a thread of a different application, the procedure must be in a DLL. For information, see Dynamic-Link Libraries.

But thread level hook needs a message pump:

This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.

Here a basic message loop:

   while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
   {
     if(bRet == -1)
     {
         // Handle Error
     }
     else
     {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
     }
   }
cabbi
  • 393
  • 2
  • 12
  • I'm working through this, trying to adapt it to my code, do you know how to break out of the message loop without closing the process manually? I tried putting a counter into the while loop, but it's not working – Alter Nov 21 '14 at 22:05
  • If 'GetMessage' retrieves the WM_QUIT message, the return value is zero and the loop exits. – cabbi Nov 21 '14 at 22:21
  • Maybe I misunderstood your question, sorry. 'GetMessage' is a blocking method (i.e. does not return until a message is received). If you want to use counters or timeouts within the message loop use 'PeekMessage' indeed. – cabbi Nov 21 '14 at 22:24
  • Hmm, I'm having troubles either way. using GetMessage and PostQuitMessage in the message loop isn't exiting it, and PeekMessage exits right away because it first reads WM_QUIT – Alter Nov 21 '14 at 22:53
  • You are most probably having troubles because the solution you have posted above has a BIG memory issue! Look at my comment above. – cabbi Nov 22 '14 at 05:56