zomgistania

Main page | About | Articles | Previous Posts | Archives

Thursday, April 27, 2006

Creating a global keyboard hook in C# without unmanaged code

Since every article about making a global hook with .NET had an unmanaged .dll making the hook, I'll just write here how to make one without any custom hooker DLLs.

Note: .NET cannot make a global hook for other events than WH_KEYBOARD_LL or WH_MOUSE_LL

With some small changes (KBDLLHookStruct to IntPtr), this code can be used for other hooks too.


using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace WinHook {

public class KBHookEventArgs : EventArgs
{
public int HookCode;
public IntPtr wParam;
public KBDLLHookStruct lParam;
}

//Structure returned by a WH_KEYBOARD_LL hook
public struct KBDLLHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}

//Hook Types
//more could be added
public enum HookType : int
{
WH_KEYBOARD_LL = 13,
}

/*
Parts of this class stolen from MSDN and modified
*/
public class GlobalKBHook {
//Callback for the hook
public delegate int KBHookProc(int code, IntPtr wParam, ref KBDLLHookStruct);

protected KBHookProc kbhook = null;
protected IntPtr hhook = IntPtr.Zero;

public delegate void KBHookEventHandler(object sender, KBHookEventArgs e);

public event KBHookEventHandler KBHookInvoked;

protected void OnKBHookInvoked(KBHookEventArgs e)
{
if(KBHookInvoked != null)
KBHookInvoked(this, e);
}

public GlobalKBHook()
{
kbhook = new KBHookProc(this.CoreKBHook);
}

public int CoreKBHook(int code, IntPtr wParam, ref KBDLLHookStruct lParam)
{
if (code < 0)
return CallNextHookEx(hhook, code, wParam, lParam);

KBHookEventArgs e = new KBHookEventArgs();
e.HookCode = code;
e.wParam = wParam;
e.lParam = lParam;
OnHookInvoked(e);

// Yield to the next hook in the chain
return CallNextHookEx(hhook, code, wParam, lParam);
}

public void Install()
{
int hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(
HookType.WH_KEYBOARD_LL,
kbhook,
(IntPtr)hInstance, //IntPtr.Zero for local hooks
0); //zero = global hook, otherwise use local thread ID
}

public void Uninstall()
{
UnhookWindowsHookEx(m_hhook);
}

//win32 api function for creating hooks
[DllImport("user32.dll")]
protected static extern IntPtr SetWindowsHookEx(HookType code,
HookProc func,
IntPtr hInstance,
int threadID);


//win32 api function for unhooking
[DllImport("user32.dll")]
protected static extern int UnhookWindowsHookEx(IntPtr hhook);

//win32 api function for continuing the hook chain
[DllImport("user32.dll")]
protected static extern int CallNextHookEx(IntPtr hhook,
int code,
IntPtr wParam,
KBDLLHookStruct lParam);

//Used to find HWND to user32.dll
[DllImport("kernel32")]
public extern static int LoadLibrary(string lpLibFileName);
}
}


So uhh... that should be it... unless I forgot something. I have it a bit different in my app, so.. if you run into any probs with that and can't fix it.. just ask.

Basically to use that you'd do something like:

public partial class Form1 : Form
{
GlobalKBHook gkbh;

public Form1()
{
gkbh = new GlobalKBHook();
gkbh.KBHookInvoked += new GlobalKBHook.KBHookEventHandler(hookCallback);
gkbh.Install();
}

private void hookCallback(object sender, KBHookEventArgs e)
{
//do something with the args here
//To get the key that was pressed,
//Keys key = (Keys)Enum.Parse(typeof(Keys),e.lParam.vkCode.ToString());

//if the key was pressed down, e.lParam.flags is 0, if the key was lifted, 128
}
}


Onwards, my loyal horde of wannabe-scriptkiddies who will presumably use this code to create their very own keyloggers!


and as a side note, I will probably post more frequently again after this small slow down... I'm again working on some intresting things.

5 Comments:

  • Hi

    this portion of the class

    protected void OnKBHookInvoked(KBHookEventArgs e)

    causes a Inconsistent accessibility error? I have google and tried a few things but no luck any ideas????

    thanks

    By Blogger psymon25, at 6:37 PM  

  • So, did you leave errors in the code to keep the script kiddies guessing? :)

    By Anonymous JebaDaHut, at 8:51 PM  

  • Well... I made this work... but your original code has all kinds of problems... you should fix it!

    By Anonymous Anonymous, at 7:30 AM  

  • i fixed it a bit till it worked, its good :), thx..

    By Blogger yuval, at 6:11 AM  

  • Great, thx.
    You could also use
    [System.Runtime.InteropServices.DllImport("User32.dll")]
    public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpChar, int uFlags);

    to convert virtual keys to ASCII chars

    By Anonymous Anonymous, at 10:45 AM  

Post a Comment

<< Home