Commit d9cc3de3 authored by Adrian Lopez's avatar Adrian Lopez

Add capture for GnuTLS and argparse options

Now it also captures gnutls_record_send and gnutls_record_recv calls.
Added options to filter by pid or command name, or just one lib.
parent d496d5ce
#!/usr/bin/python #!/usr/bin/python
# #
# sslsniff Captures data on SSL_READ or SSL_WRITE functions of OpenSSL # sslsniff Captures data on read/recv or write/send functions of OpenSSL and
# GnuTLS
# For Linux, uses BCC, eBPF. # For Linux, uses BCC, eBPF.
# #
# USAGE: sslsniff.py [-h] [-p PID] [-c COMM] [-o] [-g] [-d]
#
# Licensed under the Apache License, Version 2.0 (the "License") # Licensed under the Apache License, Version 2.0 (the "License")
# #
# 12-Aug-2016 Adrian Lopez Created this. # 12-Aug-2016 Adrian Lopez Created this.
# 13-Aug-2016 Mark Drayton Fix SSL_Read # 13-Aug-2016 Mark Drayton Fix SSL_Read
# 17-Aug-2016 Adrian Lopez Capture GnuTLS and add options
#
from __future__ import print_function from __future__ import print_function
import ctypes as ct import ctypes as ct
from bcc import BPF from bcc import BPF
import argparse
# arguments
examples = """examples:
./sslsniff # sniff OpenSSL and GnuTLS functions
./sslsniff -p 181 # sniff PID 181 only
./sslsniff -c curl # sniff curl command only
./sslsniff --no-openssl # don't show OpenSSL calls
./sslsniff --no-gnutls # don't show GnuTLS calls
"""
parser = argparse.ArgumentParser(
description="Sniff SSL data",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=examples)
parser.add_argument("-p", "--pid", help="sniff this PID only.")
parser.add_argument("-c", "--comm",
help="sniff only commands matching string.")
parser.add_argument("-o", "--no-openssl", action="store_false", dest="openssl",
help="do not show OpenSSL calls.")
parser.add_argument("-g", "--no-gnutls", action="store_false", dest="gnutls",
help="do not show GnuTLS calls.")
parser.add_argument('-d', '--debug', dest='debug', action='count', default=0,
help='debug mode.')
args = parser.parse_args()
prog = """ prog = """
#include <linux/ptrace.h> #include <linux/ptrace.h>
...@@ -27,9 +57,12 @@ struct probe_SSL_data_t { ...@@ -27,9 +57,12 @@ struct probe_SSL_data_t {
BPF_PERF_OUTPUT(perf_SSL_write); BPF_PERF_OUTPUT(perf_SSL_write);
int probe_SSL_write(struct pt_regs *ctx, void *ssl, void *buf, int num) { int probe_SSL_write(struct pt_regs *ctx, void *ssl, void *buf, int num) {
u32 pid = bpf_get_current_pid_tgid();
FILTER
struct probe_SSL_data_t __data = {0}; struct probe_SSL_data_t __data = {0};
__data.timestamp_ns = bpf_ktime_get_ns(); __data.timestamp_ns = bpf_ktime_get_ns();
__data.pid = bpf_get_current_pid_tgid(); __data.pid = pid;
__data.len = num; __data.len = num;
bpf_get_current_comm(&__data.comm, sizeof(__data.comm)); bpf_get_current_comm(&__data.comm, sizeof(__data.comm));
...@@ -48,12 +81,16 @@ BPF_HASH(bufs, u32, u64); ...@@ -48,12 +81,16 @@ BPF_HASH(bufs, u32, u64);
int probe_SSL_read_enter(struct pt_regs *ctx, void *ssl, void *buf, int num) { int probe_SSL_read_enter(struct pt_regs *ctx, void *ssl, void *buf, int num) {
u32 pid = bpf_get_current_pid_tgid(); u32 pid = bpf_get_current_pid_tgid();
FILTER
bufs.update(&pid, (u64*)&buf); bufs.update(&pid, (u64*)&buf);
return 0; return 0;
} }
int probe_SSL_read_exit(struct pt_regs *ctx, void *ssl, void *buf, int num) { int probe_SSL_read_exit(struct pt_regs *ctx, void *ssl, void *buf, int num) {
u32 pid = bpf_get_current_pid_tgid(); u32 pid = bpf_get_current_pid_tgid();
FILTER
u64 *bufp = bufs.lookup(&pid); u64 *bufp = bufs.lookup(&pid);
if (bufp == 0) { if (bufp == 0) {
return 0; return 0;
...@@ -77,17 +114,34 @@ int probe_SSL_read_exit(struct pt_regs *ctx, void *ssl, void *buf, int num) { ...@@ -77,17 +114,34 @@ int probe_SSL_read_exit(struct pt_regs *ctx, void *ssl, void *buf, int num) {
} }
""" """
b = BPF(text=prog) if args.pid:
prog = prog.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid)
else:
prog = prog.replace('FILTER', '')
if args.debug:
print(prog)
# Join to ssl_write b = BPF(text=prog)
b.attach_uprobe(name="ssl", sym="SSL_write", fn_name="probe_SSL_write")
# Join to ssl_read
# It looks like SSL_read's arguments aren't available in a return probe so you # It looks like SSL_read's arguments aren't available in a return probe so you
# need to stash the buffer address in a map on the function entry and read it # need to stash the buffer address in a map on the function entry and read it
# on its exit (Mark Drayton) # on its exit (Mark Drayton)
b.attach_uprobe(name="ssl", sym="SSL_read", fn_name="probe_SSL_read_enter") #
b.attach_uretprobe(name="ssl", sym="SSL_read", fn_name="probe_SSL_read_exit") if args.openssl:
b.attach_uprobe(name="ssl", sym="SSL_write", fn_name="probe_SSL_write")
b.attach_uprobe(name="ssl", sym="SSL_read", fn_name="probe_SSL_read_enter")
b.attach_uretprobe(name="ssl", sym="SSL_read",
fn_name="probe_SSL_read_exit")
if args.gnutls:
b.attach_uprobe(name="gnutls", sym="gnutls_record_send",
fn_name="probe_SSL_write")
b.attach_uprobe(name="gnutls", sym="gnutls_record_recv",
fn_name="probe_SSL_read_enter")
b.attach_uretprobe(name="gnutls", sym="gnutls_record_recv",
fn_name="probe_SSL_read_exit")
# define output data structure in Python # define output data structure in Python
TASK_COMM_LEN = 16 # linux/sched.h TASK_COMM_LEN = 16 # linux/sched.h
...@@ -114,16 +168,22 @@ start = 0 ...@@ -114,16 +168,22 @@ start = 0
def print_event_write(cpu, data, size): def print_event_write(cpu, data, size):
print_event(cpu, data, size, "SSL_WRITE") print_event(cpu, data, size, "WRITE/SEND")
def print_event_read(cpu, data, size): def print_event_read(cpu, data, size):
print_event(cpu, data, size, "SSL_READ") print_event(cpu, data, size, "READ/RECV")
def print_event(cpu, data, size, rw): def print_event(cpu, data, size, rw):
global start global start
event = ct.cast(data, ct.POINTER(Data)).contents event = ct.cast(data, ct.POINTER(Data)).contents
# Filter events by command
if args.comm:
if not args.comm == event.comm:
return
if start == 0: if start == 0:
start = event.timestamp_ns start = event.timestamp_ns
time_s = (float(event.timestamp_ns - start)) / 1000000000 time_s = (float(event.timestamp_ns - start)) / 1000000000
...@@ -133,16 +193,16 @@ def print_event(cpu, data, size, rw): ...@@ -133,16 +193,16 @@ def print_event(cpu, data, size, rw):
e_mark = "-" * 5 + " END DATA " + "-" * 5 e_mark = "-" * 5 + " END DATA " + "-" * 5
truncated_bytes = event.len - MAX_BUF_SIZE truncated_bytes = event.len - MAX_BUF_SIZE
if truncated_bytes > 0 : if truncated_bytes > 0:
e_mark = "-" * 5 + " END DATA (TRUNCATED, " + str(truncated_bytes) + \ e_mark = "-" * 5 + " END DATA (TRUNCATED, " + str(truncated_bytes) + \
" bytes lost) " + "-" * 5 " bytes lost) " + "-" * 5
print("%-12s %-18.9f %-16s %-6d %-6d\n%s\n%s\n%s\n" % (rw, time_s, print("%-12s %-18.9f %-16s %-6d %-6d\n%s\n%s\n%s\n\n" % (rw, time_s,
event.comm, event.comm,
event.pid, event.pid,
event.len, event.len,
s_mark, event.v0, s_mark, event.v0,
e_mark)) e_mark))
b["perf_SSL_write"].open_perf_buffer(print_event_write) b["perf_SSL_write"].open_perf_buffer(print_event_write)
b["perf_SSL_read"].open_perf_buffer(print_event_read) b["perf_SSL_read"].open_perf_buffer(print_event_read)
......
...@@ -59,3 +59,27 @@ SSL_READ 0.129967972 curl 12915 1270 ...@@ -59,3 +59,27 @@ SSL_READ 0.129967972 curl 12915 1270
div { div {
w w
----- END DATA (TRUNCATED, 798 bytes lost) ----- ----- END DATA (TRUNCATED, 798 bytes lost) -----
USAGE message:
usage: sslsniff.py [-h] [-p PID] [-c COMM] [-o] [-g] [-d]
Sniff SSL data
optional arguments:
-h, --help show this help message and exit
-p PID, --pid PID sniff this PID only.
-c COMM, --comm COMM sniff only commands matching string.
-o, --no-openssl do not show OpenSSL calls.
-g, --no-gnutls do not show GnuTLS calls.
-d, --debug debug mode.
examples:
./sslsniff # sniff OpenSSL and GnuTLS functions
./sslsniff -p 181 # sniff PID 181 only
./sslsniff -c curl # sniff curl command only
./sslsniff --no-openssl # don't show OpenSSL calls
./sslsniff --no-gnutls # don't show GnuTLS calls
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment