Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

Object Oriented WinAPI hooks

Some time ago I was playing with a WH_GETMESSAGE message hook to intercept any mouse and keyboard input. The hook was installed in several UI threads controlled by different application modules. In order to install a message hook, you issue the following code:

const HHOOK hHook = ::SetWindowsHookEx(WH_GETMESSAGE, &GetMsgProc, NULL, ::GetCurrentThreadId());

and your GetMsgProc looks like this one:

LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
{
   // Extra work here...
   return ::CallNextHookEx(NULL, code, wParam, lParam);
}

What if you need to pass control over to some object, invoke a member function? Keep in mind that GetMsgProc is a free function (that doesn't receive hook handle in its parameters). Therefore you can't use anything to map invocation context to OOP world citizens. You could use a global object to overcome this issue, but still you can't install more than one message hook with the same callback function.

So the only thing that can map to an object ... is the callback function itself. Hopefully, we are able to create that function on the fly, in memory:

struct IGetMessageHookTarget
{
   virtual LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
   {
      return ::CallNextHookEx(NULL, code, wParam, lParam);
   }
};

class GetMessageHook : IGetMessageHookTarget
{
   static const size_t ProcSize = 18;
public:
   GetMessageHook() {
      BYTE getMsgProcBytes[ProcSize] =
      {
         0x55,                           // push        ebp
         0x8B, 0xEC,                     // mov         ebp,esp
         0xA1, 0x00, 0x00, 0x00, 0x00,   // mov         eax,dword ptr ds:[this]
         0xB9, 0x00, 0x00, 0x00, 0x00,   // mov         ecx,this
         0x8B, 0x10,                     // mov         edx,dword ptr [eax]
         0x5D,                           // pop         ebp
         0xFF, 0xE2,                     // jmp         edx
      };

      *reinterpret_cast<size_t *>(&getMsgProcBytes[4]) = reinterpret_cast<size_t>(this);
      *reinterpret_cast<size_t *>(&getMsgProcBytes[4 + 1 + sizeof(size_t)]) = reinterpret_cast<size_t>(this);

      const HANDLE hCurrentProcess = ::GetCurrentProcess();
      _getMsgProcBytes = ::VirtualAllocEx(hCurrentProcess, NULL, ProcSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
      CopyMemory(_getMsgProcBytes, getMsgProcBytes, sizeof(getMsgProcBytes));
      ::VirtualProtectEx(hCurrentProcess, _getMsgProcBytes, ProcSize, PAGE_EXECUTE, NULL);
      
      _hHook = ::SetWindowsHookEx(WH_GETMESSAGE, reinterpret_cast<HOOKPROC>(_getMsgProcBytes), NULL, ::GetCurrentThreadId());
   }

   ~GetMessageHook() {
      ::UnhookWindowsHookEx(_hHook);
      ::VirtualFreeEx(::GetCurrentProcess(), _getMsgProcBytes, ProcSize, MEM_RELEASE);
   }

protected:
   virtual LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
   {
      return ::CallNextHookEx(NULL, code, wParam, lParam);
   }

private:
   HHOOK _hHook;
   LPVOID _getMsgProcBytes;
};

GetMessageHook constructor creates an in-memory code that passes control over to GetMsgProc member function. Believe me - it works. At least on x86.

2 коммент.:

Анонимный комментирует...

Thanks for the good writeup. It actually was a enjoyment account it.
Look advanced to far brought agreeable from you! However, how could we keep in touch?
Also visit my webpage - GFI Norte

Анонимный комментирует...

After checking out a handful of the blog articles on your site, I seriously appreciate your way of blogging.

I added it to my bookmark site list and will be checking back soon.
Please visit my website as well and let me know your opinion.


Feel free to visit my web-site iphone 5 release date uk

Отправить комментарий

Copyright 2007-2011 Chabster