|
tracedump
single application IP packet sniffer
|
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, ®s); 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 }
1.7.3