通常用的最多的是process_tcp,tcp_callback之类的,但是调用流程是啥样的,也许有人要说用source insight寻找函数call即可,但是估计会让人失望,不出意外只能查看到init过程中才包含有process_tcp,花点时间研究下整个过程
下面的源码全部基于libnids-1.24版本
1:libnids初始化
假如你程序基于libnids来实现的,首先要进行初始化,调用nids_init()
源文件:libnids.c
583 int nids_init() 584 { ............中间省略N行............. 665 init_procs(); 666 tcp_init(nids_params.n_tcp_streams); 667 ip_frag_init(nids_params.n_hosts); 668 scan_init();
这里需要查看的是665行init_procs()
源文件:libnids.c
464 static void init_procs() 465 { 466 ip_frag_procs = mknew(struct proc_node); 467 ip_frag_procs->item = gen_ip_frag_proc; 468 ip_frag_procs->next = 0; 469 ip_procs = mknew(struct proc_node); 470 ip_procs->item = gen_ip_proc; 471 ip_procs->next = 0; 472 tcp_procs = 0; 473 udp_procs = 0; 474 }
这里所有的结构体指针,类型都一个样
源文件:libnids.c
45 static struct proc_node *ip_frag_procs; 46 static struct proc_node *ip_procs; 47 static struct proc_node *udp_procs; 48 49 struct proc_node *tcp_procs;
结构体的定义
源文件:util.h
12 struct proc_node { 13 void (*item)(); 14 struct proc_node *next; 15 };
这么看来init_procs()里面也看不出来啥玩意,仅仅指定了几个链表node内容,函数指针,但是并不知道它们之间的执行关系
2:nids_run()
由于libnids是基于libpcap,假如在实际的抓包分析过程中,肯定会调用nids_run来调用libpcap里的函数pcap_loop来持续抓包的
源文件:libnids.c
683 int nids_run() 684 { 685 if (!desc) { 686 strcpy(nids_errbuf, "Libnids not initialized"); 687 return 0; 688 } 689 START_CAP_QUEUE_PROCESS_THREAD(); /* threading... */ 690 pcap_loop(desc, -1, (pcap_handler) nids_pcap_handler, 0); 691 /* FIXME: will this code ever be called? Don't think so - mcree */ 692 STOP_CAP_QUEUE_PROCESS_THREAD(); 693 nids_exit(); 694 return 0; 695 }
可以看到690行是抓包分析,但是在689行,一个大写字母组成的函数调用,通过表面命名来看貌似创建了一个新线程,定义如下
源文件:libnids.c
543 #define START_CAP_QUEUE_PROCESS_THREAD() \ 544 if(nids_params.multiproc) { /* threading... */ \ 545 if(!(g_thread_create_full((GThreadFunc)cap_queue_process_thread,NULL,0,FALSE,TRUE,G_THREAD_PRIORITY_LOW,&gerr or))) { \ 546 strcpy(nids_errbuf, "thread: "); \ 547 strncat(nids_errbuf, gerror->message, sizeof(nids_errbuf) - 8); \ 548 return 0; \ 549 }; \ 550 }
在545行,可以看到重新创建的新的线程,执行的函数为cap_queue_process_thread,这个函数的定义如下
源文件:libnids.c
558 /* thread entry point 559 * pops capture queue items and feeds them to 560 * the ip fragment processors - mcree 561 */ 562 static void cap_queue_process_thread() 563 { 564 struct cap_queue_item *qitem; 565 566 while(1) { /* loop "forever" */ 567 qitem=g_async_queue_pop(cap_queue); 568 if (qitem==&EOF_item) break; /* EOF item received: we should exit */ 569 call_ip_frag_procs(qitem->data,qitem->caplen); 570 free(qitem->data); 571 free(qitem); 572 } 573 g_thread_exit(NULL); 574 }
在569行,调用了函数call_ip_frag_procs,继续查看其定义
2015-02-19添加:这里其实不全,有可能来自这个START调用,也有可能来自pcap_loop的函数指针,具体标准是nids_params.multiproc的真假,详细可以查看往后两篇日志
libnids-1.24关于glib2 for multiprocessing support错误引发对执行流程的思考
源文件:libnids.c
192 /* called either directly from pcap_hand() or from cap_queue_process_thread() 193 * depending on the value of nids_params.multiproc - mcree 194 */ 195 static void call_ip_frag_procs(void *data,bpf_u_int32 caplen) 196 { 197 struct proc_node *i; 198 for (i = ip_frag_procs; i; i = i->next) 199 (i->item) (data, caplen); 200 }
看到这里似乎有个东西似曾相识,ip_frag_procs,可以回溯开始libnids的初始化init_procs里面,可以看到链表的item
源文件:libnids.c
467 ip_frag_procs->item = gen_ip_frag_proc;
于是,现在就是要看看gen_ip_frag_proc这个默认处理函数的定义
源文件:libnids.c
static void gen_ip_frag_proc(u_char * data, int len) { struct proc_node *i; struct ip *iph = (struct ip *) data; int need_free = 0; int skblen; void (*glibc_syslog_h_workaround)(int, int, struct ip *, void*)= nids_params.syslog; if (!nids_params.ip_filter(iph, len)) return; if (len < (int)sizeof(struct ip) || iph->ip_hl < 5 || iph->ip_v != 4 || ip_fast_csum((unsigned char *) iph, iph->ip_hl) != 0 || len < ntohs(iph->ip_len) || ntohs(iph->ip_len) < iph->ip_hl << 2) { glibc_syslog_h_workaround(NIDS_WARN_IP, NIDS_WARN_IP_HDR, iph, 0); return; } if (iph->ip_hl > 5 && ip_options_compile((unsigned char *)data)) { glibc_syslog_h_workaround(NIDS_WARN_IP, NIDS_WARN_IP_SRR, iph, 0); return; } switch (ip_defrag_stub((struct ip *) data, &iph)) { case IPF_ISF: return; case IPF_NOTF: need_free = 0; iph = (struct ip *) data; break; case IPF_NEW: need_free = 1; break; default:; } skblen = ntohs(iph->ip_len) + 16; if (!need_free) skblen += nids_params.dev_addon; skblen = (skblen + 15) & ~15; skblen += nids_params.sk_buff_size; for (i = ip_procs; i; i = i->next) (i->item) (iph, skblen); if (need_free) free(iph); }
最终可以看到,跟上面类似,默认的是ip_procs,继续回溯到init_procs里,找到相应的初始化部分
源代码:libnids.c
470 ip_procs->item = gen_ip_proc;
也就是最终,查看的处理函数为gen_ip_proc,继续追踪
源代码:libnids.c
447 static void gen_ip_proc(u_char * data, int skblen) 448 { 449 switch (((struct ip *) data)->ip_p) { 450 case IPPROTO_TCP: 451 process_tcp(data, skblen); 452 break; 453 case IPPROTO_UDP: 454 process_udp((char *)data); 455 break; 456 case IPPROTO_ICMP: 457 if (nids_params.n_tcp_streams) 458 process_icmp(data); 459 break; 460 default: 461 break; 462 } 463 }
看到这里,心情顿时豁然开朗了,梦里寻她千百度,暮然回首,就在眼前
显然这里的switch根据头部信息来判断4层协议,进而分别处理;所以这里都是一个包一个包地进行解析,在处理线程里进行