主要實現運用libpcap(winpcap)的api進行數據鏈路層的抓包操作,並一層層進行解析,解析TCP, UDP, ICMP等協議。
主要代碼及解析如下:
/*************************************************************************
> File Name: sniffer.c
> Author: PCB
> Mail: [email protected]
> Created Time: 2016年04月10日 星期日 20時40分52秒
************************************************************************/
#include<stdio.h>
#include<pcap.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netinet/ip.h>
#include<netinet/tcp.h>
#include<netinet/udp.h>
#include<netinet/ip_icmp.h>
pcap_t* pd;
int linkhdrlen; //鏈路層的長度
/*
* device: 要連接的設備名字
* bpfstr: 要過濾的規則
* */
pcap_t* open_pcap_socket(char* device, const char* bpfstr)
{
char errbuf[PCAP_ERRBUF_SIZE]; //存放出錯信息
pcap_t* pd;
uint32_t srcip, netmask; //存放源的ip地址和子網掩碼
struct bpf_program bpf; //存取bpf過濾規則的一個結構
//找到一個設備
if(!*device && !(device == pcap_lookupdev(errbuf)))
{
printf("pcap_lookupdev(): %s\n", errbuf);
return NULL;
}
//打開該設備,並且等到一個包到來
if((pd = pcap_open_live(device, BUFSIZ, 1, 0, errbuf)) == NULL)
{
printf("pcap_open_live(): %s\n", errbuf);
return NULL;
}
//得到設備的ip地址和ip的掩碼
if(pcap_lookupnet(device, &srcip, &netmask, errbuf) < 0)
{
printf("pcap_lookupnet: %s\n", errbuf);
return NULL;
}
//應用過濾規則
if(pcap_compile(pd, &bpf, (char*)bpfstr, 0, netmask))
{
printf("pcap_compile(): %s\n", pcap_geterr(pd));
return NULL;
}
if(pcap_setfilter(pd, &bpf)<0)
{
printf("pcap_setfilter(): %s\n", pcap_geterr(pd));
return NULL;
}
//printf("Open_pcapSocket()\n");
return pd;
}//end open_pcap_socket(char* device, const char* bpfstr);
void capture_loop(pcap_t* pd, int packets, pcap_handler func)
{
int linktype;
//判斷數據鏈路層的類型
if((linktype = pcap_datalink(pd)) < 0)
{
printf("pcap_datalink(): %s\n", pcap_geterr(pd));
return;
}
switch(linktype)
{
case DLT_NULL://BSD迴路封裝;鏈路層協議是一個4字節的域
linkhdrlen = 4;
break;
case DLT_EN10MB: //以太網,鏈路層協議是一個14字節的域
linkhdrlen = 14;
break;
case DLT_SLIP:
case DLT_PPP:
linkhdrlen = 24; //鏈路層長度爲24個字節
break;
default:
printf("Unsupport datalink (%d)\n", linktype);
return;
}
//開始抓包
if(pcap_loop(pd, packets, func, 0) < 0)
printf("pcap_loop failed: %s\n", pcap_geterr(pd));
}
/*
* 解析包的程序*/
void parse_packet(u_char *user, struct pcap_pkthdr* packethdr, u_char* packetptr)
{
struct ip* iphdr; //netinet庫的ip結構
struct icmphdr* icmphdr; //icmp結構
struct tcphdr* tcphdr;//tcp結構
struct udphdr* udphdr;//udp結構
char iphdrInfo[256], srcip[256], dstip[256];
unsigned short id, seq;
packetptr += linkhdrlen; //過濾掉數據鏈路層的數據
iphdr = (struct ip*)packetptr;
strcpy(srcip, inet_ntoa(iphdr->ip_src));
strcpy(dstip, inet_ntoa(iphdr->ip_dst));
sprintf(iphdrInfo, "ID:%d TOS:0x%x, TTL:%d, Iplen:%d Dglen:%d",
ntohs(iphdr->ip_id), iphdr->ip_tos, iphdr->ip_ttl, 4*iphdr->ip_hl, ntohs(iphdr->ip_len)
);
//將ip的字段跳過去
packetptr += 4*iphdr->ip_hl;
switch(iphdr->ip_p)
{
case IPPROTO_TCP: //如果是TCP協議
tcphdr = (struct tcphdr*)packetptr;
printf("TCP %s:%d -> %s:%d\n", srcip, ntohs(tcphdr->source), dstip, ntohs(tcphdr->dest));
printf("%s\n", iphdrInfo);
printf("%c%c%c%c%c%c Seq: 0x%x Ack: 0x%x Win: 0x%x TcpLen: %d\n", (tcphdr->urg ? 'U' : '*'),
(tcphdr->ack ? 'A' : '*'),
(tcphdr->psh ? 'P' : '*'),
(tcphdr->rst ? 'R' : '*'),
(tcphdr->syn ? 'S' : '*'),
(tcphdr->fin ? 'F' : '*'),
ntohl(tcphdr->seq), ntohl(tcphdr->ack_seq), ntohs(tcphdr->window), 4*tcphdr->doff
);
break;
case IPPROTO_UDP: //UDP協議
udphdr = (struct udphdr*) packetptr;
printf("UDP %s:%d -> %s:%d\n", srcip, ntohs(udphdr->source),
dstip, ntohs(udphdr->dest)
);
printf("%s\n", iphdrInfo);
break;
case IPPROTO_ICMP: //ICMP協議
icmphdr = (struct icmphdr*)packetptr;
printf("ICMP %s -> %s\n", srcip, dstip);
printf("%s\n", iphdrInfo);
memcpy(&id, (u_char*)icmphdr+4, 2);
memcpy(&seq, (u_char*)icmphdr+6, 2);
printf("Type:%d Code:%d ID:%d Seq:%d\n", icmphdr->type, icmphdr->code, ntohs(id), ntohs(seq));
break;
}
printf("---------------------------------------------\n\n");
}
void quiteOut(int signo)
{
struct pcap_stat stats;
if(pcap_stats(pd, &stats) >= 0)
{
printf("%d packets received\n", stats.ps_recv);
printf("%d packtes dropped\n\n", stats.ps_drop);
}
pcap_close(pd);
exit(0);
}
int main(int argc, char **argv)
{
char interface[256] = "", bpfstr[256] = "";
int packets = 0, c,i;
while((c = getopt(argc, argv, "i:n:") ) != -1) //讀取參數,當爲-i時,指定接口,當爲-n時,指定讀取多少packets
{
switch(c)
{
case 'i':
strcpy(interface, optarg);
break;
case 'n':
packets = atoi(optarg);
break;
}
}
//獲取過濾的字符串
for(i = optind; i < argc; i++)
{
strcat(bpfstr, argv[i]);
strcat(bpfstr, " ");
}
//連接接口
if((pd = open_pcap_socket(interface, bpfstr)))
{
/*/
* 綁定中斷事件給quiteOut函數*/
signal(SIGINT, quiteOut);
signal(SIGTERM, quiteOut);
signal(SIGQUIT, quiteOut);
capture_loop(pd, packets, (pcap_handler)parse_packet);
quiteOut(0);
}
exit(0);
}
主要使用:
該程序支持監聽的接口及一共要接收的數據包的大小,並且提供過濾功能,具體使用如下:
sudo ./sniffer -i eth0 tcp port 80
運行結果如下: