On 22/06/2025, I started creating a simple keylogger to work on Windows. It would be completely invisible, and would only track characters and keys at a simple level – no mouse clicks!
Check out my GitHub repository here: AutumnHB/Keylogger-Windows
Initial thoughts and learning
To start this project, I learnt what hooks were, and how they are used to record keys pressed alongside various other important information – though the key presses were only what interested me. I was surprised to see how much information Windows was offering up about these hooks given the fact that they are practically begging to be used for keylogging. Though, given they are most likely used in every Windows keylogger in existence, it is probably widely known information already.
I wrote my keylogger in C++ for two reasons. The first was how easily it would integrate with the Windows hooks and functions – it was practically a no-brainer for that alone. But the second reason was that I wrote the keylogger 2 days after completing my placement at BAE Systems, in which I wrote code in C++ every. Single. Day. Using it was as natural as breathing at this point.
Getting Started
The first thing I did when creating my keylogger was look into how to make it run “stealthily”. According to resources I found online and other code examples… it is shockingly simple. Given this being my first attempt at creating any sort of malware, I was surprised at how accommodating Windows was to it’s creation. For this I just used some flags for ShowWindow().
I then split my keylogger into functions I deemed appropriate:
- SetHook() – the function to set up the hook that records key strokes.
- callback() – the function that is called when a key is pressed, which calls Save() for a key, and then CallNextHookEx() to keep the callback chain going.
- Save() – the function that maps a vkcode to a character and saves it to the output stream file.
- main() – the main code block that sets the window at invisible to the user, creates the output file, and runs the other functions. I eventually added some lines to keep the code running in the background using a the msg functionality, and eventually ended with it releasing the hook.
Mid way through
Honestly, coding it was a lot easier than I thought once I understood how the hooks worked. The hardest part of coding was creating the callback function, but with the help of Stack Overflow and some Google searches, it wasn’t too hard. During my placement, one thing I have learnt is that reading documentation is very important, and it exists for a reason. It is whilst scrolling through various documentations and viewing suggested pages that I found the MapVirtualKeyA() function – a God send for this. In this function I can pass a parameter value MAPVK_VK_TO_CHAR which, as the name implies, maps vkcode values to their respective characters, as such recording the character value pressed.
It is also through documentation I discovered the SetWindowsHookEx() function, which is what I used set up the hook (once again, the naming conventions are very revealing). This function and the MapVirtualKeyA() one singlehandedly created the core of the keylogger functionality – all I had to do from there is pass the character values to a stream, which was just using basic C++ knowledge.
Pre-testing concerns
There are many keyboard types. I am British. Microsoft is American. There is a very likely chance that the keys are mapped incorrectly for a British keyboard…
Testing Round 1
My fears were confirmed. Having run the keylogger, it defaults to an American keyboard. It also didn’t fully recognise backspacing, which made for interesting outputs in the log. However – it did work as expected!
Updates Round 1
I played with the idea of when backspace being pressed, to actually remove the character from the document. However, I quickly dismissed this idea as it would not only be memory intensive, but also there could be the chance someone presses a backspace button before typing anything or more than necessary which could result in prior keystrokes for something unrelated being deleted. As such, I decided to print “[BACK] when its pressed to show on the log it was pressed.
It was also at this point that I realised it didn’t seem to be the case that the keyboard was in the wrong language, but rather it wasn’t recognising when the shift button was being pressed… No, I’m not sure why I got this confused. It was also at this point I thought it might be useful to check for capitals/cases.
It get’s a little bit interesting trying to do shift characters for numbers and other values since there isn’t a direct way to check for it. Instead of fussing with the many keyboard layouts and mappings, it would be much clearer, efficient, and simpler if I did [SHIFT + key]. This would be a universal way of displaying shift has been pressed on a non-letter character as it isn’t dependant on keyboard layout. Furthermore, it doesn’t take up much code, especially not intensive code.
Testing Round 2
I’m almost at a point where I’m happy with the keylogger. I keep getting strange artefacts in my log, especially after the backspace for some reason? My theory is it is still trying to add the backspace to the log after I have the [BACK] logged, which would imply my code is not as effective as I wanted.
I also want to explore how it can be made more stealthy or malicious… FOR EDUCATIONAL PURPOSES!! I solemnly swear I am up to only good. The current code is already stealthy to me personally, but my computer will not alert me of a virus warning for code I’ve made myself, so I might have to find a willing test subject… completely consensually and legally!

Leave a comment