主要实现运用libpcap(winpcap)的api进行数据链路层的抓包操作,并一层层进行解析,解析TCP, UDP, ICMP等协议。
主要代码及解析如下:
/*************************************************************************
> File Name: sniffer.c
> Author: PCB
> Mail: pangbin2415@gmai.com
> 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
运行结果如下: