通常用的最多的是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层协议,进而分别处理;所以这里都是一个包一个包地进行解析,在处理线程里进行
