libnids处理流程

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

发表回复