Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Scanning a PCAP dump to find DNS and NETBIOS queries

5.00/5 (1 vote)
26 Sep 2012CPOL2 min read 15.8K   403  
Parse the PCAP dump and extract NETBIOS and DNS queries.

Introduction 

PCAP library is widely used to capture network packets. Using PCAP library we can dump packets onto a file and then later analyze the dump. In this write-up I have written a small program to extract DNS queries and NETBIOS queries from a PCAP dump. PCAP Library is very simple to use and nicely documented. 

Background

Reader may read about PCAP at www.winpcap.org.

Using the code

We need to parse the PCAP dump and extract NETBIOS and DNS queries. This is done in following manner:

  1. Open the PCAP dump using pcap_open_offline which is a PCAP API 
  2. Retrieve the packets (in a while loop) from the file using pcap_next_ex which is a PCAP API
    1. Whether it is a DNS or NETBIOS Query, request starts at the end of UDP Header.
  3. A very little work is required to make DNS request readable while for netbios queries we do require a complete map of characters.

The shared source code is written as part of some other code where some variables were global. If required, user can easily make it independent.  

Step 1: Open the PCAP file  

C++
/* Open the capture file */
if ((fp = pcap_open_offline(filename_pktdump, // name of the device 
                               errbuf         // error buffer
                  )) == NULL){
    return -1;}

Step 2: Get the request data which might be either DNS/NETBIOS in a while loop

C++
/* Retrieve the packets from the file */
while((res = pcap_next_ex(fp, &header, &pkt_data)) >= 0){
    pkt_num++;
    eh = (ether_header *) (pkt_data) ;
    if (ETHERNET_IP == ntohs(eh->type)) {
        ih = (ip_header *)(pkt_data + 14);
        if(UDP == ih->proto){
            uh = (udp_header *)(pkt_data + sizeof(ether_header) + sizeof(ip_header));
            datalen = ntohs(uh->len) - sizeof(udp_header);
            switch(ntohs(uh->dport)){
                case 137: 
                    nbns = 1;  // if it is a netbios query, it will be directed to 137
                case 53:
                    dns = (dns_header *)(pkt_data + sizeof(ether_header) + 
                    sizeof(ip_header) + sizeof(udp_header));
                    data = (u_char *)(pkt_data + sizeof(ether_header) + sizeof(ip_header) + 
                    sizeof(udp_header) + sizeof(dns_header));
                    dns->qdcount = ntohs(dns->qdcount);      
                if(dns->qdcount != 1) {
                    printf("multiple query packet \n");
                    break;
                }
                // now parse the dns query, assuming one per packet !
                lenofquery = ntohs(uh->len) - (sizeof(udp_header) + sizeof(dns_header) + 4 );

                offset = 0;
                query = (char *)malloc(lenofquery+1);
                StringCbCopyNA(query, lenofquery, (const char *)&data[offset+1], lenofquery);

                // this function will find the Request String which is to be resolved
                processstring(query, lenofquery, nbns);

                // save the string in vector
                PushQueryToVector(query);
                free(query);
                break;
                default:
                break;
            } // switch 
        } // an udp packet
    } // an ip packet
} // while

Step 3: Process the query string to recover readable string

There is no much problem with DNS as all characters are ok but only thing is that a dot character(.) is represnted by STX, BEL and EOT.  Ascii value of dot chracter is 46 in decimal and we need to replace these characters by 46. NETBIOS characters are constructed like following. It means when we see 0x45 0x42 in byte stream it means 'A'.

C++
{0x45, 0x42, 'A'},            /*0*/
{0x45, 0x43, 'B'},            /*1*/
{0x45, 0x44, 'C'},            /*2*/
{0x45, 0x45, 'D'},            /*3*/
{0x45, 0x46, 'E'},            /*4*/
{0x45, 0x47, 'F'},            /*5*/
{0x45, 0x48, 'G'},            /*6*/
{0x45, 0x49, 'H'},            /*7*/
{0x45, 0x4A, 'I'},            /*8*/
{0x45, 0x4B, 'J'},            /*9*/

A complete list can be found on msdn articles or netname.cpp associated with this article. If query is a dns string it can be processes like following:

C++
if(nbns == 0) { // that is a dns query
    for(; i<length; i++) {
        if( (str[i]==0x02) || (str[i]==0x04) || (str[i]==0x07) )
            str[i] = 46;
    } // for 
} //if

But if query is a Netbios query we need to search in the map.

C++
else if(nbns == 1){ // that is a netbios query
    temp = (char *)malloc(length+1);        
    StringCbCopyNA(temp, length, str,  length);
    memset(str, 0, length);
    while( j < length-2 ) {
        i=0;
        while(i < N)
        {                
            if( (netbiosmap[i][0] == temp[j]) && (netbiosmap[i][1] == temp[j+1]))
                str[k++] = netbiosmap[i][2];
            i++;    // increment the index in the map
        } // while
        j += 2;
    } // while
    free(temp);
} // else

I could not swallow netbios character maps for a few seconds. I thought might be this article will help some causual searcher.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)