2016年11月23日 星期三

libpcap - Dump TCP segment and UDP datagram(11)



  • Source Port:2 bytes。
  • Destination Port:2 bytes。
  • Sequence Number:4 bytes。
  • Acknowledgement Number:4 bytes。
  • Header Length:4 bits。
  • Reserved:4 bits。
  • Flags:8 bits。
  • Window Size:2 bytes。
  • Checksum:2 bytes。
  • Urgent Pointer:2 bytes。
  • TCP Option + Padding:0 ~ 40 bytes。
  • 長度:20 ~ 60 bytes。

Flags由左到右分別為:CWR、ECE、URG、ACK、PUSH、RST、SYN以及FIN,共8個bit。



  • Source Port:2 bytes。
  • Destination Port:2 bytes。
  • Length:2 bytes。
  • Checksum:2 bytes。
  • 長度:8 bytes。




先include的header。
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>


打開預設的device,過濾器是"tcp or udp"表示只抓tcp和udp,然後只抓一個封包。
char errbuf[PCAP_ERRBUF_SIZE];
char *device = NULL;

//get default interface name
device = pcap_lookupdev(errbuf);
if(!device) {
    fprintf(stderr, "pcap_lookupdev(): %s\n", errbuf);
    exit(1);
}//end if

printf("Sniffing: %s\n", device);

pcap_t *handle = pcap_open_live(device, 65535, 1, 1, errbuf);
if(!handle) {
    fprintf(stderr, "pcap_open_live(): %s\n", errbuf);
    exit(1);
}//end if

//generate bpf filter
bpf_u_int32 net, mask;
struct bpf_program fcode;

//get network and mask
if(-1 == pcap_lookupnet(device, &net, &mask, errbuf)) {
    fprintf(stderr, "pcap_lookupnet(): %s\n", errbuf);
    mask = PCAP_NETMASK_UNKNOWN;
}//end if

//compile filter
if(-1 == pcap_compile(handle, &fcode, "tcp or udp", 1, mask)) {
    fprintf(stderr, "pcap_compile(): %s\n", pcap_geterr(handle));
    pcap_close(handle);
    exit(1);
}//end if

//set filter
if(-1 == pcap_setfilter(handle, &fcode)) {
    fprintf(stderr, "pcap_pcap_setfilter(): %s\n", pcap_geterr(handle));
    pcap_freecode(&fcode);
    pcap_close(handle);
    exit(1);
}//end if

//free bpf code
pcap_freecode(&fcode);

//start capture
pcap_loop(handle, 1, pcap_callback, NULL);


switch (protocol) {
    case IPPROTO_UDP:
        printf("Next is UDP\n");
        dump_udp(length, content);
        break;
        
    case IPPROTO_TCP:
        printf("Next is TCP\n");
        dump_tcp(length, content);
        break;
        
    case IPPROTO_ICMP:
        printf("Next is ICMP\n");
        break;
        
    default:
        printf("Next is %d\n", protocol);
        break;
}//end switch

解析ip層的時候,當下一層協定是17(或marcos IPPROTO_UDP)呼叫函數dump_udp()解析udp;當下一層是6(或marcos IPPROTO_TCP)呼叫函數dump_tcp()解析tcp。

先來看udp部分。
static void dump_udp(u_int32_t length, const u_char *content) {
    struct ip *ip = (struct ip *)(content + ETHER_HDR_LEN);
    struct udphdr *udp = (struct udphdr *)(content + ETHER_HDR_LEN + (ip->ip_hl << 2));
    
    u_int16_t source_port = ntohs(udp->uh_sport);
    u_int16_t destination_port = ntohs(udp->uh_dport);
    u_int16_t len = ntohs(udp->uh_ulen);
    u_int16_t checksum = ntohs(udp->uh_sum);
    
    printf("Protocol: UDP\n");
    printf("+-------------------------+-------------------------+\n");
    printf("| Source Port:       %5u| Destination Port:  %5u|\n", source_port, destination_port);
    printf("+-------------------------+-------------------------+\n");
    printf("| Length:            %5u| Checksum:          %5u|\n", len, checksum);
    printf("+-------------------------+-------------------------+\n");
}//end dump_udp

udp比較簡單,udp開始位置就是略過ethernet和ip層;ip層的表頭長度是結構struct ip成員ip_hl的4倍。

結構struct udphdr宣告:
/*
 * Udp protocol header.
 * Per RFC 768, September, 1981.
 */
struct udphdr {
 u_short uh_sport;  /* source port */
 u_short uh_dport;  /* destination port */
 u_short uh_ulen;  /* udp length */
 u_short uh_sum;   /* udp checksum */
};


接著來看tcp部分。
static void dump_tcp(u_int32_t length, const u_char *content) {
    struct ip *ip = (struct ip *)(content + ETHER_HDR_LEN);
    struct tcphdr *tcp = (struct tcphdr *)(content + ETHER_HDR_LEN + (ip->ip_hl << 2));
    //copy header
    u_int16_t source_port = ntohs(tcp->th_sport);
    u_int16_t destination_port = ntohs(tcp->th_dport);
    u_int32_t sequence = ntohl(tcp->th_seq);
    u_int32_t ack = ntohl(tcp->th_ack);
    u_int8_t header_len = tcp->th_off << 2;
    u_int8_t flags = tcp->th_flags;
    u_int16_t window = ntohs(tcp->th_win);
    u_int16_t checksum = ntohs(tcp->th_sum);
    u_int16_t urgent = ntohs(tcp->th_urp);

tcp層的表頭長度是結構struct tcphdr成員th_off的4倍。

結構struct tcphdr宣告:
/*
 * TCP header.
 * Per RFC 793, September, 1981.
 */
struct tcphdr {
 unsigned short th_sport; /* source port */
 unsigned short th_dport; /* destination port */
 tcp_seq th_seq;   /* sequence number */
 tcp_seq th_ack;   /* acknowledgement number */
#if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN
 unsigned int th_x2:4, /* (unused) */
   th_off:4; /* data offset */
#endif
#if __DARWIN_BYTE_ORDER == __DARWIN_BIG_ENDIAN
 unsigned int th_off:4, /* data offset */
   th_x2:4; /* (unused) */
#endif
 unsigned char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)

 unsigned short th_win;  /* window */
 unsigned short th_sum;  /* checksum */
 unsigned short th_urp;  /* urgent pointer */
};


接著就把tcp表頭列印出來。
//print
printf("Protocol: TCP\n");
printf("+-------------------------+-------------------------+\n");
printf("| Source Port:       %5u| Destination Port:  %5u|\n", source_port, destination_port);
printf("+-------------------------+-------------------------+\n");
printf("| Sequence Number:                        %10u|\n", sequence);
printf("+---------------------------------------------------+\n");
printf("| Acknowledgement Number:                 %10u|\n", ack);
printf("+------+-------+----------+-------------------------+\n");
printf("| HL:%2u|  RSV  |F:%8s| Window Size:       %5u|\n", header_len, tcp_ftoa(flags), window);
printf("+------+-------+----------+-------------------------+\n");
printf("| Checksum:          %5u| Urgent Pointer:    %5u|\n", checksum, urgent);
printf("+-------------------------+-------------------------+\n");

函數tcp_ftoa()是將tcp的flags轉成字串。


做法跟函數ip_ftoa()一樣。
static const char *tcp_ftoa(u_int8_t flag) {
    static int  f[] = {'W', 'E', 'U', 'A', 'P', 'R', 'S', 'F'};
#define TCP_FLG_MAX (sizeof f / sizeof f[0])
    static char str[TCP_FLG_MAX + 1];
    u_int32_t mask = 1 << 7;
    int i;
    
    for (i = 0; i < TCP_FLG_MAX; i++) {
        if(mask & flag)
            str[i] = f[i];
        else
            str[i] = '-';
        mask >>= 1;
    }//end for
    str[i] = '\0';
    
    return str;
}//end tcp_ftoa


編譯:
libpcap % gcc -I/usr/local/opt/libpcap/include -Wall -std=gnu99 -L/usr/local/opt/libpcap/lib -lpcap dump-tcp-and-udp.c -o dump-tcp-and-udp


執行結果(Mac OS X):
libpcap % ./dump-tcp-and-udp
Sniffing: en0
No. 1
 Time: 14:10:14.698494
 Length: 54 bytes
 Capture length: 54 bytes
Ethernet Frame:
+-------------------------+-------------------------+-------------------------+
| Destination MAC Address:                                   d8:fe:e3:a4:d3:78|
+-------------------------+-------------------------+-------------------------+
| Source MAC Address:                                        6c:40:08:bc:ae:98|
+-------------------------+-------------------------+-------------------------+
| Ethernet Type:    0x0800|
+-------------------------+
Next protocol is IP
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         40|
+-----+------+------------+-------+-----------------+
| Identifier:        65227| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:      6| Header Checksum:  0xbcb5|
+------------+------------+-------------------------+
| Source IP Address:                    192.168.1.50|
+---------------------------------------------------+
| Destination IP Address:             64.233.188.139|
+---------------------------------------------------+
Next is TCP
Protocol: TCP
+-------------------------+-------------------------+
| Source Port:       51736| Destination Port:    443|
+-------------------------+-------------------------+
| Sequence Number:                        3339194354|
+---------------------------------------------------+
| Acknowledgement Number:                 1712036847|
+------+-------+----------+-------------------------+
| HL:20|  RSV  |F:---A----| Window Size:        4096|
+------+-------+----------+-------------------------+
| Checksum:          15292| Urgent Pointer:        0|
+-------------------------+-------------------------+



執行結果(CentOS):
[root@tutu libpcap]# ./dump-tcp-and-udp 
Sniffing: eth0
No. 1
 Time: 14:11:11.535115
 Length: 74 bytes
 Capture length: 74 bytes
Ethernet Frame:
+-------------------------+-------------------------+-------------------------+
| Destination MAC Address:                                   d8:fe:e3:a4:d3:78|
+-------------------------+-------------------------+-------------------------+
| Source MAC Address:                                        6c:40:08:bc:ae:98|
+-------------------------+-------------------------+-------------------------+
| Ethernet Type:    0x0800|
+-------------------------+
Next protocol is IP
Protocol: IP
+-----+------+------------+-------------------------+
| IV:4| HL:20| T: --------| Total Length:         60|
+-----+------+------------+-------+-----------------+
| Identifier:         1292| FF:---| FO:            0|
+------------+------------+-------+-----------------+
| TTL:     64| Pro:     17| Header Checksum:  0xf221|
+------------+------------+-------------------------+
| Source IP Address:                    192.168.1.50|
+---------------------------------------------------+
| Destination IP Address:                192.168.1.1|
+---------------------------------------------------+
Next is UDP
Protocol: UDP
+-------------------------+-------------------------+
| Source Port:       62017| Destination Port:     53|
+-------------------------+-------------------------+
| Length:               40| Checksum:          23279|
+-------------------------+-------------------------+



Source code on Github

沒有留言:

張貼留言