tracedump
single application IP packet sniffer

tracedump.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2011-2012 IITiS PAN Gliwice <http://www.iitis.pl/>
00003  * Author: Paweł Foremski <pjf@iitis.pl>
00004  * Licensed under GNU GPL v. 3
00005  */
00006 
00007 #include <libpjf/main.h>
00008 #include "tracedump.h"
00009 
00010 static bool EXITING = false;
00011 
00013 static void help(void)
00014 {
00015         printf("Usage: tracedump [OPTIONS] <COMMAND...>\n");
00016         printf("       tracedump [OPTIONS] <PID...>\n");
00017         printf("\n");
00018         printf("  A single application IP packet sniffer.\n");
00019         printf("\n");
00020         printf("Options:\n");
00021         printf("  -w <file>              write output to <file> [dump.pcap]\n");
00022         printf("                         use -w - for stdout output\n");
00023         printf("  -s <bytes>             number of bytes to capture in each IP packet\n");
00024         printf("                         use -s 0 for full packet contents\n");
00025         printf("  --verbose,-V           be verbose (alias for --debug=5)\n");
00026         printf("  --debug=<num>          set debugging level\n");
00027         printf("  --help,-h              show this usage help screen\n");
00028         printf("  --version,-v           show version and copying information\n");
00029 }
00030 
00032 static void version(void)
00033 {
00034         printf("tracedump %s\n", TRACEDUMP_VERSION);
00035         printf("Copyright (C) 2011-2012 IITiS PAN\n");
00036         printf("Licensed under GNU GPL v3\n");
00037         printf("Author: Paweł Foremski <pjf@iitis.pl>\n");
00038         printf("Part of the MuTriCs project: <http://mutrics.iitis.pl/>\n");
00039         printf("Realized under grant nr 2011/01/N/ST6/07202 of the Polish National Science Centre\n");
00040 }
00041 
00046 static int parse_argv(struct tracedump *td, int argc, char *argv[])
00047 {
00048         int i, c;
00049 
00050         static char *short_opts = "hvVw:s:";
00051         static struct option long_opts[] = {
00052                 /* name, has_arg, NULL, short_ch */
00053                 { "verbose",    0, NULL,  1  },
00054                 { "debug",      1, NULL,  2  },
00055                 { "help",       0, NULL,  3  },
00056                 { "version",    0, NULL,  4  },
00057                 { 0, 0, 0, 0 }
00058         };
00059 
00060         /* defaults */
00061         debug = 1;
00062         td->opts.outfile = "dump.pcap";
00063 
00064         for (;;) {
00065                 c = getopt_long(argc, argv, short_opts, long_opts, &i);
00066                 if (c == -1) break; /* end of options */
00067 
00068                 switch (c) {
00069                         case 'V':
00070                         case  1 : debug = 5; break;
00071                         case  2 : debug = atoi(optarg); break;
00072                         case 'h':
00073                         case  3 : help(); return 2;
00074                         case 'v':
00075                         case  4 : version(); return 2;
00076                         case 'w': td->opts.outfile = mmatic_strdup(td->mm, optarg); break;
00077                         case 's': td->opts.snaplen = atoi(optarg); break;
00078                         default: help(); return 1;
00079                 }
00080         }
00081 
00082         if (td->opts.snaplen <= 0)
00083                 td->opts.snaplen = UINT16_MAX;
00084 
00085         if (argc - optind > 0) {
00086                 td->opts.src = argv + optind;
00087                 td->opts.srclen = argc - optind;
00088         } else {
00089                 help();
00090                 return 1;
00091         }
00092 
00093         return 0;
00094 }
00095 
00096 static void sighandler(int signum)
00097 {
00098         signal(SIGINT, SIG_IGN);
00099         signal(SIGTERM, SIG_IGN);
00100         EXITING = true;
00101 }
00102 
00103 static void handle_socket(struct pid *sp, int fd)
00104 {
00105         struct tracedump *td = sp->td;
00106         char buf[128], buf2[128];
00107         int len, socknum;
00108         struct sock *ss;
00109         struct sockaddr_in sa;
00110         socklen_t optlen;
00111 
00112         /* TODO: speed-up cache? */
00113 
00114         snprintf(buf, sizeof buf, "/proc/%d/fd/%d", sp->pid, fd);
00115         len = readlink(buf, buf2, sizeof buf2);
00116 
00117         /* is it a socket at all? */
00118         if (len < 10 || strncmp(buf2, "socket:[", 8) != 0)
00119                 return;
00120 
00121         /* get socknum */
00122         buf2[len - 1] = 0;
00123         socknum = atoi(buf2 + 8);
00124 
00125         /* check if in cache */
00126         if (sp->ss && sp->ss->socknum == socknum)
00127                 return;
00128 
00129         /* get socket info */
00130         ss = thash_uint_get(td->socks, socknum);
00131         if (!ss) {
00132                 ss = mmatic_zalloc(td->mm, sizeof *ss);
00133                 ss->td = td;
00134                 ss->socknum = socknum;
00135                 thash_uint_set(td->socks, socknum, ss);
00136 
00137                 /* cancel the original socketcall */
00138                 if (sp->in_socketcall)
00139                         inject_escape_socketcall(td, sp);
00140 
00141                 /* check if AF_INET, get local address */
00142                 if (inject_getsockname_in(td, sp, fd, &sa) != 0)
00143                         goto handled;
00144 
00145                 /* check if TCP/UDP */
00146                 optlen = sizeof ss->type;
00147                 if (inject_getsockopt(td, sp, fd, SOL_SOCKET, SO_TYPE, &ss->type, &optlen) != 0)
00148                         goto handled;
00149                 if (optlen != sizeof ss->type || (ss->type != SOCK_STREAM && ss->type != SOCK_DGRAM))
00150                         goto handled;
00151 
00152                 /* autobind if necessary */
00153                 if (!sa.sin_port) {
00154                         if (inject_autobind(td, sp, fd) != 0) {
00155                                 dbg(1, "pid %d fd %d: autobind failed\n", sp->pid, fd);
00156                                 goto handled;
00157                         }
00158 
00159                         if (inject_getsockname_in(td, sp, fd, &sa) != 0) {
00160                                 dbg(1, "pid %d fd %d: getsockname after autobind failed\n", sp->pid, fd);
00161                                 goto handled;
00162                         }
00163                 }
00164 
00165                 ss->port = ntohs(sa.sin_port);
00166 
00167                 port_add(ss, true);
00168                 pcap_update(td);
00169 
00170 handled:
00171                 /* finish the original socketcall */
00172                 if (sp->in_socketcall) {
00173                         inject_restore_socketcall(td, sp);
00174                         sp->in_socketcall = false;
00175                 }
00176         }
00177 }
00178 
00179 static void handle_attached_pid(struct pid *sp)
00180 {
00181         DIR *dh;
00182         char buf[128];
00183         struct dirent *de;
00184         int fd;
00185 
00186         snprintf(buf, sizeof buf, "/proc/%d/fd", sp->pid);
00187         dh = opendir(buf);
00188         if (!dh)
00189                 return;
00190 
00191         while ((de = readdir(dh))) {
00192                 if (!isdigit(de->d_name[0]))
00193                         continue;
00194 
00195                 fd = atoi(de->d_name);
00196                 handle_socket(sp, atoi(de->d_name));
00197         }
00198 
00199         closedir(dh);
00200 }
00201 
00202 int main(int argc, char *argv[])
00203 {
00204         mmatic *mm;
00205         struct tracedump *td;
00206         pid_t pid;
00207         struct pid *sp;
00208         unsigned long fd_arg;
00209         struct user_regs_struct regs;
00210         int status;
00211         int stopped_pid;
00212         int i;
00213         struct sigaction sa;
00214 
00215         /*************/
00216 
00217         /* initialize */
00218         mm = mmatic_create();
00219         td = mmatic_zalloc(mm, sizeof *td);
00220         td->mm = mm;
00221         pthread_mutex_init(&td->mutex_ports, NULL);
00222 
00223         /* create hashing tables */
00224         td->pids = thash_create_intkey(mmatic_free, td->mm);
00225         td->socks = thash_create_intkey(mmatic_free, td->mm);
00226         td->tcp_ports = thash_create_intkey(mmatic_free, td->mm);
00227         td->udp_ports = thash_create_intkey(mmatic_free, td->mm);
00228 
00229         /*************/
00230 
00231         /* parse command line options */
00232         if (parse_argv(td, argc, argv))
00233                 return 1;
00234 
00235         /************ start threads */
00236 
00237         /* start the garbage collector and sniffer threads
00238          * note: the GC calls pcap_update(), so the order of initialization is important */
00239         pcap_init(td);
00240         port_init(td);
00241 
00242         /************ attach the victim :) */
00243 
00244         /* handle premature exceptions */
00245         if ((i = setjmp(td->jmp)) != 0)
00246                 die("exception %d, arg %d\n", EXC_CODE(i), EXC_ARG(i));
00247 
00248         if (isdigit(td->opts.src[0][0])) {
00249                 /* attach to processes */
00250                 for (i = 0; i < td->opts.srclen; i++) {
00251                         pid = atoi(td->opts.src[i]);
00252                         ptrace_attach_pid(pid_get(td, pid), handle_attached_pid);
00253                 }
00254         } else {
00255                 /* attach to child */
00256                 pid = fork();
00257 
00258                 if (pid == 0) {
00259                         ptrace_traceme();
00260                         execvp(td->opts.src[0], td->opts.src + 0);
00261 
00262                         /* if fails: */
00263                         fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
00264                         exit(127);
00265                 }
00266 
00267                 if (ptrace_attach_child(pid_get(td, pid), NULL) < 0)
00268                         return 127;
00269         }
00270 
00271         /************ main thread */
00272 
00273         /* setup signal handling */
00274         memset(&sa, 0, sizeof sa);
00275         sa.sa_handler = sighandler;
00276         sigaction(SIGINT, &sa, NULL);
00277         sigaction(SIGTERM, &sa, NULL);
00278 
00279         /* handle exceptions */
00280         if ((i = setjmp(td->jmp)) != 0) {
00281                 switch (EXC_CODE(i)) {
00282                         case EXC_PTRACE:
00283                                 dbg(1, "ptrace error: pid %d\n", EXC_ARG(i));
00284                                 break;
00285                         default:
00286                                 dbg(1, "exception %d, arg %d\n", EXC_CODE(i), EXC_ARG(i));
00287                                 break;
00288                 }
00289         }
00290 
00291         while (EXITING == false) {
00292                 /* wait for syscall from any pid */
00293                 stopped_pid = ptrace_wait(NULL, &status);
00294 
00295                 /* TODO?: filter out our threads :) */
00296 
00297                 if (stopped_pid == -1) {
00298                         break;
00299                 } else if (WIFEXITED(status) || WIFSIGNALED(status)) {
00300                         pid_del(td, stopped_pid);
00301                         continue;
00302                 }
00303 
00304                 /* fetch pid info */
00305                 sp = pid_get(td, stopped_pid);
00306 
00307                 /* handle signal passing */
00308                 if (WIFSTOPPED(status)) {
00309                         if (WSTOPSIG(status) != SIGTRAP && WSTOPSIG(status) != SIGSTOP) {
00310                                 /* pass the signal to child */
00311                                 ptrace_cont_syscall(sp, WSTOPSIG(status), false);
00312                                 continue;
00313                         }
00314                 }
00315 
00316                 /* get regs, skip syscalls other than socketcall */
00317                 ptrace_getregs(sp, &regs);
00318                 if (regs.orig_eax != SYS_socketcall)
00319                         goto next_syscall;
00320 
00321                 /* filter anything different than bind(), connect() and sendto() */
00322                 sp->code = regs.ebx;
00323                 switch (sp->code) {
00324                         case SYS_BIND:
00325                         case SYS_CONNECT:
00326                         case SYS_SENDTO:
00327                                 break;
00328                         default:
00329                                 goto next_syscall;
00330                 }
00331 
00332                 sp->in_socketcall = !sp->in_socketcall;
00333 
00334                 /* on exit from a successful bind() or enter to connect()/sendto() */
00335                 if ((sp->in_socketcall == false && sp->code == SYS_BIND && regs.eax == 0) ||
00336                     (sp->in_socketcall == true  && sp->code != SYS_BIND)) {
00337                         /* get fd number */
00338                         ptrace_read(sp, regs.ecx, &fd_arg, 4);
00339 
00340                         /* handle the socket underlying given fd */
00341                         handle_socket(sp, fd_arg);
00342                 }
00343 
00344 next_syscall:
00345                 ptrace_cont_syscall(sp, 0, false);
00346         }
00347 
00348         /*****************************/
00349 
00350         port_deinit(td);
00351         pcap_deinit(td);
00352         pid_detach_all(td);
00353         mmatic_destroy(mm);
00354 
00355         return 0;
00356 }
 All Data Structures Files Functions Variables Enumerations Enumerator Defines