Skip to content

Commit

Permalink
tools: execsnoop add -U and -u flags
Browse files Browse the repository at this point in the history
Add flags to display UID and filter by UID
  • Loading branch information
bacher09 authored and yonghong-song committed Feb 21, 2020
1 parent 788bc29 commit c949f61
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 4 deletions.
25 changes: 23 additions & 2 deletions man/man8/execsnoop.8
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
.SH NAME
execsnoop \- Trace new processes via exec() syscalls. Uses Linux eBPF/bcc.
.SH SYNOPSIS
.B execsnoop [\-h] [\-T] [\-t] [\-x] [\-q] [\-n NAME] [\-l LINE]
.B [\-\-max-args MAX_ARGS] [\-\-cgroupmap MAPPATH]
.B execsnoop [\-h] [\-T] [\-t] [\-x] [\-\-cgroupmap CGROUPMAP] [\-u USER]
.B [\-q] [\-n NAME] [\-l LINE] [\-U] [\-\-max-args MAX_ARGS]
.SH DESCRIPTION
execsnoop traces new processes, showing the filename executed and argument
list.
Expand All @@ -28,9 +28,15 @@ Print usage message.
\-T
Include a time column (HH:MM:SS).
.TP
\-U
Include UID column.
.TP
\-t
Include a timestamp column.
.TP
\-u USER
Filter by UID (or username)
.TP
\-x
Include failed exec()s
.TP
Expand Down Expand Up @@ -59,6 +65,18 @@ Trace all exec() syscalls, and include timestamps:
#
.B execsnoop \-t
.TP
Display process UID:
#
.B execsnoop \-U
.TP
Trace only UID 1000:
#
.B execsnoop \-u 1000
.TP
Trace only processes launched by root and display UID column:
#
.B execsnoop \-Uu root
.TP
Include failed exec()s:
#
.B execsnoop \-x
Expand Down Expand Up @@ -86,6 +104,9 @@ Time of exec() return, in HH:MM:SS format.
TIME(s)
Time of exec() return, in seconds.
.TP
UID
User ID
.TP
PCOMM
Parent process/command name.
.TP
Expand Down
45 changes: 45 additions & 0 deletions tools/execsnoop.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,35 @@
import argparse
import re
import time
import pwd
from collections import defaultdict
from time import strftime


def parse_uid(user):
try:
result = int(user)
except ValueError:
try:
user_info = pwd.getpwnam(user)
except KeyError:
raise argparse.ArgumentTypeError(
"{0!r} is not valid UID or user entry".format(user))
else:
return user_info.pw_uid
else:
# Maybe validate if UID < 0 ?
return result


# arguments
examples = """examples:
./execsnoop # trace all exec() syscalls
./execsnoop -x # include failed exec()s
./execsnoop -T # include time (HH:MM:SS)
./execsnoop -U # include UID
./execsnoop -u 1000 # only trace UID 1000
./execsnoop -u user # get user UID and trace only them
./execsnoop -t # include timestamps
./execsnoop -q # add "quotemarks" around arguments
./execsnoop -n main # only print command lines containing "main"
Expand All @@ -50,6 +71,8 @@
help="include failed exec()s")
parser.add_argument("--cgroupmap",
help="trace cgroups in this BPF map only")
parser.add_argument("-u", "--uid", type=parse_uid, metavar='USER',
help="trace this UID only")
parser.add_argument("-q", "--quote", action="store_true",
help="Add quotemarks (\") around arguments."
)
Expand All @@ -59,6 +82,8 @@
parser.add_argument("-l", "--line",
type=ArgString,
help="only print commands where arg contains this line (regex)")
parser.add_argument("-U", "--print-uid", action="store_true",
help="print UID column")
parser.add_argument("--max-args", default="20",
help="maximum number of arguments parsed and displayed, defaults to 20")
parser.add_argument("--ebpf", action="store_true",
Expand All @@ -81,6 +106,7 @@
struct data_t {
u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
u32 ppid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel)
u32 uid;
char comm[TASK_COMM_LEN];
enum event_type type;
char argv[ARGSIZE];
Expand Down Expand Up @@ -114,6 +140,11 @@
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
u32 uid = bpf_get_current_uid_gid() & 0xffffffff;
UID_FILTER
#if CGROUPSET
u64 cgroupid = bpf_get_current_cgroup_id();
if (cgroupset.lookup(&cgroupid) == NULL) {
Expand Down Expand Up @@ -164,7 +195,11 @@
struct data_t data = {};
struct task_struct *task;
u32 uid = bpf_get_current_uid_gid() & 0xffffffff;
UID_FILTER
data.pid = bpf_get_current_pid_tgid() >> 32;
data.uid = uid;
task = (struct task_struct *)bpf_get_current_task();
// Some kernels, like Ubuntu 4.13.0-generic, return 0
Expand All @@ -182,6 +217,12 @@
"""

bpf_text = bpf_text.replace("MAXARG", args.max_args)

if args.uid:
bpf_text = bpf_text.replace('UID_FILTER',
'if (uid != %s) { return 0; }' % args.uid)
else:
bpf_text = bpf_text.replace('UID_FILTER', '')
if args.cgroupmap:
bpf_text = bpf_text.replace('CGROUPSET', '1')
bpf_text = bpf_text.replace('CGROUPPATH', args.cgroupmap)
Expand All @@ -202,6 +243,8 @@
print("%-9s" % ("TIME"), end="")
if args.timestamp:
print("%-8s" % ("TIME(s)"), end="")
if args.print_uid:
print("%-6s" % ("UID"), end="")
print("%-16s %-6s %-6s %3s %s" % ("PCOMM", "PID", "PPID", "RET", "ARGS"))

class EventType(object):
Expand Down Expand Up @@ -251,6 +294,8 @@ def print_event(cpu, data, size):
printb(b"%-9s" % strftime("%H:%M:%S").encode('ascii'), nl="")
if args.timestamp:
printb(b"%-8.3f" % (time.time() - start_ts), nl="")
if args.print_uid:
printb(b"%-6d" % event.uid, nl="")
ppid = event.ppid if event.ppid > 0 else get_ppid(event.pid)
ppid = b"%d" % ppid if ppid > 0 else b"?"
argv_text = b' '.join(argv[event.pid]).replace(b'\n', b'\\n')
Expand Down
33 changes: 31 additions & 2 deletions tools/execsnoop_example.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,32 @@ with an externally created map.

For more details, see docs/filtering_by_cgroups.md

The -U option include UID on output:

# ./execsnoop -U

UID PCOMM PID PPID RET ARGS
1000 ls 171318 133702 0 /bin/ls --color=auto
1000 w 171322 133702 0 /usr/bin/w

The -u options filters output based process UID. You also can use username as
argument, in that cause UID will be looked up using getpwnam (see man 3 getpwnam).

# ./execsnoop -Uu 1000
UID PCOMM PID PPID RET ARGS
1000 ls 171335 133702 0 /bin/ls --color=auto
1000 man 171340 133702 0 /usr/bin/man getpwnam
1000 bzip2 171341 171340 0 /bin/bzip2 -dc
1000 bzip2 171342 171340 0 /bin/bzip2 -dc
1000 bzip2 171345 171340 0 /bin/bzip2 -dc
1000 manpager 171355 171340 0 /usr/bin/manpager
1000 less 171355 171340 0 /usr/bin/less

USAGE message:

# ./execsnoop -h
usage: execsnoop [-h] [-T] [-t] [-x] [-q] [-n NAME] [-l LINE] [--max-args MAX_ARGS]
usage: execsnoop.py [-h] [-T] [-t] [-x] [--cgroupmap CGROUPMAP] [-u USER] [-q]
[-n NAME] [-l LINE] [-U] [--max-args MAX_ARGS]

Trace exec() syscalls

Expand All @@ -98,19 +119,27 @@ optional arguments:
-T, --time include time column on output (HH:MM:SS)
-t, --timestamp include timestamp on output
-x, --fails include failed exec()s
-q, --quote Add quotemarks (") around arguments
--cgroupmap CGROUPMAP
trace cgroups in this BPF map only
-u USER, --uid USER trace this UID only
-q, --quote Add quotemarks (") around arguments.
-n NAME, --name NAME only print commands matching this name (regex), any
arg
-l LINE, --line LINE only print commands where arg contains this line
(regex)
-U, --print-uid print UID column
--max-args MAX_ARGS maximum number of arguments parsed and displayed,
defaults to 20

examples:
./execsnoop # trace all exec() syscalls
./execsnoop -x # include failed exec()s
./execsnoop -T # include time (HH:MM:SS)
./execsnoop -U # include UID
./execsnoop -u 1000 # only trace UID 1000
./execsnoop -u root # get root UID and trace only this
./execsnoop -t # include timestamps
./execsnoop -q # add "quotemarks" around arguments
./execsnoop -n main # only print command lines containing "main"
./execsnoop -l tpkg # only print command where arguments contains "tpkg"
./execsnoop --cgroupmap ./mappath # only trace cgroups in this BPF map

0 comments on commit c949f61

Please sign in to comment.