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:
- Open the PCAP dump using pcap_open_offline which is a PCAP API
- Retrieve the packets (in a while loop) from the file using pcap_next_ex which is a PCAP API
- Whether it is a DNS or NETBIOS Query, request starts at the end of UDP Header.
- 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
if ((fp = pcap_open_offline(filename_pktdump, errbuf )) == NULL){
return -1;}
Step 2: Get the request data which might be either DNS/NETBIOS in a while loop
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; 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;
}
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);
processstring(query, lenofquery, nbns);
PushQueryToVector(query);
free(query);
break;
default:
break;
} } } }
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'.
{0x45, 0x42, 'A'},
{0x45, 0x43, 'B'},
{0x45, 0x44, 'C'},
{0x45, 0x45, 'D'},
{0x45, 0x46, 'E'},
{0x45, 0x47, 'F'},
{0x45, 0x48, 'G'},
{0x45, 0x49, 'H'},
{0x45, 0x4A, 'I'},
{0x45, 0x4B, 'J'},
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:
if(nbns == 0) { for(; i<length; i++) {
if( (str[i]==0x02) || (str[i]==0x04) || (str[i]==0x07) )
str[i] = 46;
} }
But if query is a Netbios query we need to search in the map.
else if(nbns == 1){ 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++; } j += 2;
} free(temp);
}
I could not swallow netbios character maps for a few seconds. I thought might be this article will help some causual searcher.