Home | History | Annotate | Line # | Download | only in Bin
shellsnoop revision 1.1.1.1
      1 #!/bin/sh
      2 #
      3 # shellsnoop - A program to print read/write details from shells,
      4 #	       such as keystrokes and command outputs.
      5 #	       Written using DTrace (Solaris 10 3/05).
      6 #
      7 # This program sounds somewhat dangerous (snooping keystrokes), but is
      8 # no more so than /usr/bin/truss, and both need root or dtrace privileges to
      9 # run. In fact, less dangerous, as we only print visible text (not password
     10 # text, for example). Having said that, it goes without saying that this
     11 # program shouldn't be used for breeching privacy of other users.
     12 #
     13 # This was written as a tool to demonstrate the capabilities of DTrace.
     14 #
     15 # $Id: shellsnoop,v 1.1.1.1 2015/09/30 22:01:07 christos Exp $
     16 #
     17 # USAGE:	shellsnoop [-hqsv] [-p PID] [-u UID]
     18 #
     19 #		-q		# quiet, only print data
     20 #		-s		# include start time, us
     21 #		-v		# include start time, string
     22 #		-p PID		# process ID to snoop
     23 #		-u UID		# user ID to snoop
     24 #  eg,
     25 #		shellsnoop		# default output
     26 #		shellsnoop -v		# human readable timestamps
     27 #		shellsnoop -p 1892	# snoop this PID only
     28 #		shellsnoop -qp 1892	# watch this PID data only
     29 # 	
     30 # FIELDS:
     31 #		UID		User ID
     32 #		PID		process ID
     33 #		PPID		parent process ID
     34 #		COMM		command name
     35 #		DIR		direction (R read, W write)
     36 #		TEXT		text contained in the read/write
     37 #		TIME		timestamp for the command, us
     38 #		STRTIME		timestamp for the command, string
     39 #
     40 # SEE ALSO: ttywatcher
     41 #
     42 # COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
     43 #
     44 # CDDL HEADER START
     45 #
     46 #  The contents of this file are subject to the terms of the
     47 #  Common Development and Distribution License, Version 1.0 only
     48 #  (the "License").  You may not use this file except in compliance
     49 #  with the License.
     50 #
     51 #  You can obtain a copy of the license at Docs/cddl1.txt
     52 #  or http://www.opensolaris.org/os/licensing.
     53 #  See the License for the specific language governing permissions
     54 #  and limitations under the License.
     55 #
     56 # CDDL HEADER END
     57 #
     58 # Author: Brendan Gregg  [Sydney, Australia]
     59 #
     60 # 28-Mar-2004	Brendan Gregg	Created this.
     61 # 21-Jan-2005	   "	  "	Wrapped in sh to provide options.
     62 # 30-Nov-2005	   "	  "	Fixed trailing buffer text bug.
     63 # 30-Nov-2005	   "	  "	Fixed sh no keystroke text in quiet bug.
     64 # 30-Nov-2005	   "	  "	Last update.
     65 # 
     66 
     67 
     68 ##############################
     69 # --- Process Arguments ---
     70 #
     71 opt_pid=0; opt_uid=0; opt_time=0; opt_timestr=0; opt_quiet=0; opt_debug=0
     72 filter=0; pid=0; uid=0
     73 
     74 while getopts dhp:qsu:v name
     75 do
     76 	case $name in
     77 	d)	opt_debug=1 ;;
     78 	p)	opt_pid=1; pid=$OPTARG ;;
     79 	q)	opt_quiet=1 ;;
     80 	s)	opt_time=1 ;;
     81 	u)	opt_uid=1; uid=$OPTARG ;;
     82 	v)	opt_timestr=1 ;;
     83 	h|?)	cat <<-END >&2
     84 		USAGE: shellsnoop [-hqsv] [-p PID] [-u UID]
     85 		       shellsnoop		# default output
     86 		                -q		# quiet, only print data
     87 		                -s		# include start time, us
     88 		                -v		# include start time, string
     89 		                -p PID		# process ID to snoop
     90 		                -u UID		# user ID to snoop
     91 		END
     92 		exit 1
     93 	esac
     94 done
     95 
     96 if [ $opt_quiet -eq 1 ]; then
     97 	opt_time=0; opt_timestr=0
     98 fi
     99 if [ $opt_pid -eq 1 -o $opt_uid -eq 1 ]; then
    100 	filter=1
    101 fi
    102 
    103 
    104 #################################
    105 # --- Main Program, DTrace ---
    106 #
    107 dtrace -n '
    108  /*
    109   * Command line arguments
    110   */
    111  inline int OPT_debug 	= '$opt_debug';
    112  inline int OPT_quiet 	= '$opt_quiet';
    113  inline int OPT_pid 	= '$opt_pid';
    114  inline int OPT_uid 	= '$opt_uid';
    115  inline int OPT_time 	= '$opt_time';
    116  inline int OPT_timestr	= '$opt_timestr';
    117  inline int FILTER 	= '$filter';
    118  inline int PID 	= '$pid';
    119  inline int UID 	= '$uid';
    120  
    121  #pragma D option quiet
    122  #pragma D option switchrate=20hz
    123  
    124  /*
    125   * Print header
    126   */
    127  dtrace:::BEGIN /OPT_time == 1/
    128  { 
    129  	printf("%-14s ","TIME");
    130  }
    131  dtrace:::BEGIN /OPT_timestr == 1/
    132  { 
    133  	printf("%-20s ","STRTIME");
    134  }
    135  dtrace:::BEGIN /OPT_quiet == 0/
    136  {
    137 	printf("%5s %5s %8s %3s  %s\n", "PID", "PPID", "CMD", "DIR", "TEXT");
    138  }
    139 
    140  /*
    141   * Remember this PID is a shell child
    142   */
    143  syscall::execve:entry
    144  /execname == "sh"   || execname == "ksh"  || execname == "csh"  || 
    145   execname == "tcsh" || execname == "zsh"  || execname == "bash"/
    146  {
    147 	child[pid] = 1;
    148  
    149  }
    150  syscall::execve:entry
    151  /(OPT_pid == 1 && PID != ppid) || (OPT_uid == 1 && UID != uid)/
    152  {
    153 	/* forget if filtered */
    154 	child[pid] = 0;
    155  }
    156 
    157  /*
    158   * Print shell keystrokes
    159   */
    160  syscall::write:entry, syscall::read:entry
    161  /(execname == "sh"   || execname == "ksh"  || execname == "csh"  ||
    162   execname == "tcsh" || execname == "zsh"  || execname == "bash")
    163   && (arg0 >= 0 && arg0 <= 2)/
    164  {
    165 	self->buf = arg1;
    166  }
    167  syscall::write:entry, syscall::read:entry
    168  /(OPT_pid == 1 && PID != pid) || (OPT_uid == 1 && UID != uid)/
    169  {
    170 	self->buf = 0;
    171  }
    172  syscall::write:return, syscall::read:return
    173  /self->buf && child[pid] == 0 && OPT_time == 1/
    174  {
    175  	printf("%-14d ", timestamp/1000);
    176  }
    177  syscall::write:return, syscall::read:return
    178  /self->buf && child[pid] == 0 && OPT_timestr == 1/
    179  {
    180 	printf("%-20Y ", walltimestamp);
    181  }
    182  syscall::write:return, syscall::read:return
    183  /self->buf && child[pid] == 0 && OPT_quiet == 0/
    184  {
    185 	this->text = (char *)copyin(self->buf, arg0);
    186 	this->text[arg0] = '\'\\0\'';
    187  
    188 	printf("%5d %5d %8s %3s  %s\n", pid, curpsinfo->pr_ppid, execname, 
    189 	    probefunc == "read" ? "R" : "W", stringof(this->text));
    190  }
    191  syscall::write:return
    192  /self->buf && child[pid] == 0 && OPT_quiet == 1/
    193  {
    194 	this->text = (char *)copyin(self->buf, arg0);
    195 	this->text[arg0] = '\'\\0\'';
    196 	printf("%s", stringof(this->text));
    197  }
    198  syscall::read:return
    199  /self->buf && execname == "sh" && child[pid] == 0 && OPT_quiet == 1/
    200  {
    201 	this->text = (char *)copyin(self->buf, arg0);
    202 	this->text[arg0] = '\'\\0\'';
    203 	printf("%s", stringof(this->text));
    204  }
    205  syscall::write:return, syscall::read:return
    206  /self->buf && child[pid] == 0/
    207  {
    208 	self->buf = 0;
    209  }
    210 
    211  /*
    212   * Print command output
    213   */
    214  syscall::write:entry, syscall::read:entry
    215  /child[pid] == 1 && (arg0 == 1 || arg0 == 2)/
    216  {
    217 	self->buf = arg1;
    218  }
    219  syscall::write:return, syscall::read:return
    220  /self->buf && OPT_time == 1/
    221  {
    222  	printf("%-14d ", timestamp/1000);
    223  }
    224  syscall::write:return, syscall::read:return
    225  /self->buf && OPT_timestr == 1/
    226  {
    227 	printf("%-20Y ", walltimestamp);
    228  }
    229  syscall::write:return, syscall::read:return
    230  /self->buf && OPT_quiet == 0/
    231  {
    232 	this->text = (char *)copyin(self->buf, arg0);
    233 	this->text[arg0] = '\'\\0\'';
    234  
    235 	printf("%5d %5d %8s %3s  %s", pid, curpsinfo->pr_ppid, execname,
    236 	    probefunc == "read" ? "R" : "W", stringof(this->text));
    237  
    238 	/* here we check if a newline is needed */
    239 	this->length = strlen(this->text);
    240 	printf("%s", this->text[this->length - 1] == '\'\\n\'' ? "" : "\n");
    241 	self->buf = 0;
    242  }
    243  syscall::write:return, syscall::read:return
    244  /self->buf && OPT_quiet == 1/
    245  {
    246 	this->text = (char *)copyin(self->buf, arg0);
    247 	this->text[arg0] = '\'\\0\'';
    248 	printf("%s", stringof(this->text));
    249 	self->buf = 0;
    250  }
    251 
    252  /*
    253   *  Cleanup
    254   */
    255  syscall::exit:entry
    256  {
    257 	child[pid] = 0;
    258 
    259 	/* debug */
    260 	this->parent = (char *)curthread->td_proc->p_pptr->p_comm;
    261 	OPT_debug == 1 ? printf("PID %d CMD %s exited. (%s)\n",
    262 	 pid, execname, stringof(this->parent)) : 1;
    263  }
    264 '
    265