Skip to content

Commit

Permalink
finished the 0.5 version!
Browse files Browse the repository at this point in the history
  • Loading branch information
pforemski committed Jan 26, 2012
1 parent 3c319fd commit 322bf67
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 149 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
tracedump
*.pcap
*.o
.*.swp
18 changes: 13 additions & 5 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,18 @@ classification.
ISSUES
======

* if attaching, the already opened connections are not read (yet)
* the garbage collector is not finished
* the command line options facility is not yet implemented
* packets past the first IP fragment will not be captured
* sometimes the traced process segfaults - eg. Firefox started from tracedump
* sometimes the traced process segfaults
* eg. Firefox started from tracedump
* eg. Chrome on restoring multiple tabs
* maybe more work on better ptrace transparency is required - especially on code injection?
* cant start chromium-browser within tracedump, but attaching works (to appropriate pid)

LIMITATIONS
===========

* TODO: write about a probably better architecture? (skb.pid on POSTROUTING)
* IP packets past the first fragment will not be captured
* there is a low probability of loosing TCP/IP packets if the bind() and {connect(), listen()}
syscalls from the monitored application are distant by at least 60 seconds
* maximum number of monitored ports is limited to less than 300 ports, due to limits on the
BPF filter attached to the sniffing socket
30 changes: 20 additions & 10 deletions pcap.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,31 @@ void pcap_init(struct tracedump *td)
memset(&ll, 0, sizeof ll);
ll.sll_family = AF_PACKET;
td->pc->fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
if (td->pc->fd == -1)
if (td->pc->fd == -1) {
dbg(1, "Could not connect to the sniffing socket - insufficient privileges?\n");
die_errno("socket");
}

/* open the resultant file */
td->pc->fp = fopen("dump.pcap", "w"); // TODO
if (!td->pc->fp) die_errno("fopen");
if (streq(td->opts.outfile, "-")) {
td->pc->fp = stdout;
} else {
td->pc->fp = fopen(td->opts.outfile, "w");
if (!td->pc->fp) {
dbg(1, "Could not open the output file: %s\n", td->opts.outfile);
die_errno("fopen");
}

dbg(1, "Writing packets to %s\n", td->opts.outfile);
}

/* write the global header */
ph.magic_number = PCAP_MAGIC_NUMBER;
ph.version_major = 2;
ph.version_minor = 4;
ph.thiszone = 0;
ph.sigfigs = 0;
ph.snaplen = 31337; // TODO
ph.snaplen = td->opts.snaplen;
ph.network = LINKTYPE_LINUX_SLL;
fwrite(&ph, sizeof ph, 1, td->pc->fp);

Expand Down Expand Up @@ -82,10 +93,10 @@ void pcap_update(struct tracedump *td)
{
struct sock_fprog *fp;

/* generate BPF filter code basing on the current port list */
pthread_mutex_lock(&td->mutex_ports);

/* generate BPF filter code basing on the current port list */
fp = gencode_alloc(td);
pthread_mutex_unlock(&td->mutex_ports);

#ifdef CHECK_BPF
/* verify the code on the user side - useful for debugging */
Expand All @@ -96,6 +107,7 @@ void pcap_update(struct tracedump *td)
if (setsockopt(td->pc->fd, SOL_SOCKET, SO_ATTACH_FILTER, fp, sizeof *fp) != 0)
die_errno("setsockopt");

pthread_mutex_unlock(&td->mutex_ports);
mmatic_free(fp->filter);
mmatic_free(fp);
}
Expand All @@ -105,7 +117,7 @@ void pcap_update(struct tracedump *td)
void *sniffer_thread(void *arg)
{
struct tracedump *td;
int i, snaplen, inclen;
int i, inclen;
uint8_t pkt[8192];
struct pcap_pkt_hdr pp;
struct pcap_sll_hdr ps;
Expand All @@ -119,8 +131,6 @@ void *sniffer_thread(void *arg)
sigaddset(&ss, SIGINT);
pthread_sigmask(SIG_SETMASK, &ss, NULL);

snaplen = 31337; // TODO

/* write the packets */
while ((len = sizeof ll) &&
(i = recvfrom(td->pc->fd, pkt, sizeof pkt, 0, (struct sockaddr *) &ll, &len)) > 0) {
Expand All @@ -133,7 +143,7 @@ void *sniffer_thread(void *arg)
die_errno("ioctl");

/* write the PCAP header */
inclen = MIN(snaplen, i);
inclen = MIN(td->opts.snaplen, i);
pp.ts_sec = ts.tv_sec;
pp.ts_usec = ts.tv_usec;
pp.orig_len = i + sizeof ps;
Expand Down
1 change: 0 additions & 1 deletion pcap.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#ifndef _PCAP_H_
#define _PCAP_H_

#include <pthread.h>
#include "tracedump.h"

/** Represents internal data */
Expand Down
31 changes: 2 additions & 29 deletions pid.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,38 +35,11 @@ void pid_del(struct tracedump *td, pid_t pid)
void pid_detach_all(struct tracedump *td)
{
struct pid *sp;
int status, rc;

thash_reset(td->pids);
while ((sp = thash_uint_iter(td->pids, NULL))) {
/* nice & clean? */
if (ptrace_detach(sp, 0) == 0) {
thash_uint_set(td->pids, sp->pid, NULL);
continue;
}

/* ESRCH probably means that the traced process is not in a state appropriate for
* PTRACE_DETACH - send it SIGSTOP, wait, and try again */
if (errno == ESRCH) {
syscall(SYS_tgkill, pid_tgid(sp->pid), sp->pid, SIGSTOP);
ptrace_wait(sp, &status);

if (WIFSTOPPED(status)) {
switch (WSTOPSIG(status)) {
case SIGTRAP:
case SIGSTOP: rc = ptrace_detach(sp, SIGCONT); break;
default: rc = ptrace_detach(sp, WSTOPSIG(status)); break;
}
} else {
rc = ptrace_detach(sp, 0);
}

/* check if that worked */
if (rc == 0)
continue;
}

dbg(1, "error while detaching pid %d: %s\n", sp->pid, strerror(errno));
ptrace_detach(sp, 0);
pid_del(td, sp->pid);
}
}

Expand Down
53 changes: 45 additions & 8 deletions port.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ static void *gc_thread(void *arg)
struct tracedump *td;
sigset_t ss;
uint8_t *tcp = NULL, *udp = NULL;
unsigned int port;
struct port *sp;
int count = 0;
struct timeval now;

td = (struct tracedump *) arg;
sigaddset(&ss, SIGTERM);
sigaddset(&ss, SIGINT);
pthread_sigmask(SIG_SETMASK, &ss, NULL);

while (1) {
sleep(60);

/* read list of active tcp/udp ports */
tcp = port_list(td, true);
udp = port_list(td, false);
Expand All @@ -26,16 +32,47 @@ static void *gc_thread(void *arg)
goto next;
}

/*
* iterate through all monitored TCP/UDP ports and delete those
* that are not needed anymore
*/
pthread_mutex_lock(&td->mutex_ports);
printf("yummie yummie\n");
/* TODO: remove old tcp/udp ports */
/* TODO: autobound TCP ports not visible on list */
gettimeofday(&now, NULL);

/* TCP */
thash_reset(td->tcp_ports);
while ((sp = thash_uint_iter(td->tcp_ports, &port))) {
/* skip ports "younger" than 60 secs
* workaround that autobound TCP ports are not visible in procfs - Linux bug? */
if (pjf_timediff(&now, &sp->since)/1000000 < 60)
continue;

if (!PORT_ISSET(tcp, port)) {
count++;
thash_uint_set(td->tcp_ports, port, NULL);
dbg(3, "port TCP/%d deleted\n", port);
}
}

/* UDP */
thash_reset(td->udp_ports);
while ((sp = thash_uint_iter(td->udp_ports, &port))) {
if (!PORT_ISSET(udp, port)) {
count++;
thash_uint_set(td->udp_ports, port, NULL);
dbg(3, "port UDP/%d deleted\n", port);
}
}

pthread_mutex_unlock(&td->mutex_ports);

/* if any changes were made, run the BPF filter update */
if (count > 0)
pcap_update(td);

next:
if (tcp) mmatic_free(tcp);
if (udp) mmatic_free(udp);
sleep(3);
}

return NULL;
Expand Down Expand Up @@ -77,15 +114,15 @@ void port_add(struct sock *ss, bool local)
if (!sp) {
sp = mmatic_zalloc(ss->td->mm, sizeof *sp);
thash_uint_set(ports, ss->port, sp);

dbg(3, "port %d/%s added\n", ss->port,
ss->type == SOCK_STREAM ? "TCP" : "UDP");
}

gettimeofday(&sp->last, NULL);
gettimeofday(&sp->since, NULL);
sp->local = local;
sp->socknum = ss->socknum;

dbg(3, "port %d/%s added\n", ss->port,
ss->type == SOCK_STREAM ? "TCP" : "UDP");

pthread_mutex_unlock(&ss->td->mutex_ports);
}

Expand Down
71 changes: 45 additions & 26 deletions ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@
* Licensed under GNU GPL v. 3
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <dirent.h>

#include "tracedump.h"

static long _run_ptrace(enum __ptrace_request request, struct pid *sp,
Expand All @@ -37,7 +26,7 @@ static long _run_ptrace(enum __ptrace_request request, struct pid *sp,
goto ret;
}

dbg(1, "%s(req %d, pid %d): %s\n", func, request, pid, strerror(errno));
dbg(2, "%s(req %d, pid %d): %s\n", func, request, pid, strerror(errno));
EXCEPTION(sp->td, EXC_PTRACE, pid);
}

Expand All @@ -46,7 +35,7 @@ static long _run_ptrace(enum __ptrace_request request, struct pid *sp,
}
#define run_ptrace(a, b, c, d) _run_ptrace(a, b, ((void *) (c)), ((void *) (d)), __func__)

void ptrace_attach_pid(struct pid *sp)
void ptrace_attach_pid(struct pid *sp, void (*cb)(struct pid *sp))
{
DIR *dh;
char buf[128];
Expand All @@ -68,24 +57,37 @@ void ptrace_attach_pid(struct pid *sp)

sp2 = pid_get(sp->td, pid);
run_ptrace(PTRACE_ATTACH, sp2, NULL, NULL);
ptrace_attach_child(sp2);
ptrace_attach_child(sp2, cb);
}

closedir(dh);
} else {
run_ptrace(PTRACE_ATTACH, sp, NULL, NULL);
ptrace_attach_child(sp);
ptrace_attach_child(sp, cb);
}
}

void ptrace_attach_child(struct pid *sp)
int ptrace_attach_child(struct pid *sp, void (*cb)(struct pid *sp))
{
dbg(1, "attaching PID %d\n", sp->pid);
int status;

ptrace_wait(sp, &status);
if (WIFSTOPPED(status) &&
(WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTRAP)) {
dbg(2, "attached to PID %d\n", sp->pid);

run_ptrace(PTRACE_SETOPTIONS, sp, NULL,
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE);

ptrace_wait(sp, NULL);
run_ptrace(PTRACE_SETOPTIONS, sp, NULL,
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE);
ptrace_cont_syscall(sp, 0, false);
if (cb)
cb(sp);

ptrace_cont_syscall(sp, 0, false);
return 0;
} else {
dbg(1, "attaching PID %d failed\n", sp->pid);
return -1;
}
}

void ptrace_traceme(void)
Expand All @@ -101,21 +103,21 @@ int ptrace_wait(struct pid *sp, int *st)
rc = waitpid(pid, &status, __WALL);

if (rc == -1) {
dbg(1, "wait(%d): %s\n", pid, strerror(errno));
dbg(2, "wait(%d): %s\n", pid, strerror(errno));
return rc;
}

if (pid == -1)
pid = rc;

if (WIFEXITED(status))
dbg(1, "wait(%d): process exited with status %d\n", pid, WEXITSTATUS(status));
dbg(2, "wait(%d): process exited with status %d\n", pid, WEXITSTATUS(status));
else if (WIFSIGNALED(status))
dbg(1, "wait(%d): process terminated with signal %d\n", pid, WTERMSIG(status));
else if (WIFSTOPPED(status) &&
WSTOPSIG(status) != SIGSTOP &&
WSTOPSIG(status) != SIGTRAP)
dbg(1, "wait(%d): process received signal %d\n", pid, WSTOPSIG(status));
dbg(2, "wait(%d): process received signal %d\n", pid, WSTOPSIG(status));

if (st)
*st = status;
Expand Down Expand Up @@ -152,9 +154,26 @@ void ptrace_cont_syscall(struct pid *sp, unsigned long sig, bool w8)
_ptrace_cont(true, sp, sig, w8);
}

long ptrace_detach(struct pid *sp, unsigned long sig)
void ptrace_detach(struct pid *sp, unsigned long sig)
{
return run_ptrace(PTRACE_DETACH, sp, NULL, (void *) sig);
int status;

/* are we lucky? */
if (run_ptrace(PTRACE_DETACH, sp, NULL, (void *) sig) == 0)
return;

/* no, the process is not detachable - try to stop it */
syscall(SYS_tgkill, pid_tgid(sp->pid), sp->pid, SIGSTOP);
ptrace_wait(sp, &status);

if (WIFSTOPPED(status)) {
if (WSTOPSIG(status) == SIGTRAP || WSTOPSIG(status) == SIGSTOP)
run_ptrace(PTRACE_DETACH, sp, NULL, (void *) SIGCONT);
else
run_ptrace(PTRACE_DETACH, sp, NULL, (void *) WSTOPSIG(status));
} else {
run_ptrace(PTRACE_DETACH, sp, NULL, (void *) 0);
}
}

void ptrace_kill(struct pid *sp)
Expand Down
Loading

0 comments on commit 322bf67

Please sign in to comment.