Computer Network Course Design Network Sniffer

1. Title

Network sniffer

2. Environment

Win10    codeblocks

Three, code implementation

WinPcap installation tutorial

To use winpcap in CB, some .h files need to be configured. Please use Baidu. Baidu has it.

Please be familiar with the format of the textbook IP data packet and the header protocol.
code show as below:

#define HAVE_REMOTE//宏定义
#define LINE_LEN 16
#include "winsock.h"
#include <string.h>
#include "pcap.h"
#pragma comment(lib, "Ws2_32.lib")
typedef struct ip_address
{
    
     //ip地址
    u_char b1;//u_char无符号char
    u_char b2;
    u_char b3;
    u_char b4;
} ip_address;

typedef struct mac_address
{
    
    //mac地址
	u_char b1;
	u_char b2;
	u_char b3;
	u_char b4;
	u_char b5;
	u_char b6;
} mac_address;

typedef struct ethe_header
{
    
     //mac帧首部
	mac_address mac_dest_address;//目的地址
	mac_address mac_source_address;//源地址
	u_short ether_type;//长度或类型
} ethe_header;

typedef struct ip_header
{
    
     //ip地址首部
    u_char  ver_ihl;//版本号和头长度各占4位
    u_char  tos;//服务类型
    u_short tlen;//分组长度
    u_short identification;//分组标识,唯一标识发送的每一个数据报
    u_short flags_fo;//标志
    u_char  ttl;//生存时间,TTL
    u_char  proto;//协议可以是TCP,UDP,ICMP等
    u_short crc;//校验和
    ip_address  saddr;//源IP地址
    ip_address  daddr;//目的IP地址
    u_int   op_pad;// 选项与填充
} ip_header;

typedef struct udp_header
{
    
     //UPD首部
    u_short sport;//源端口号
    u_short dport;//目的端口号
    u_short len;//包长度
    u_short crc;//校验和
} udp_header;

typedef struct tcp_header
{
    
     //TCP首部
	u_short sport;//16位源端口号
                u_short dport;//16位目的端口号
    	u_int num;//32位序列号
	u_int ack;//32位确认号
	u_short sum;//可能是包长度
	u_short windonw;//16位窗口大小
	u_short crc;//16位校验和
	u_short ugr;//16位紧急数据偏移量
} tcp_header;

void packet_handler(u_char * param, const struct pcap_pkthdr * header, const  u_char *pkt_data);
char judge;
int length;
int main()
{
    
    
    pcap_if_t * alldevs, *device;//pcap_if是winpcap这个抓包框架中自带的函数库中的主要函数之一,用来描述一个网络设备结构,
	int i = 0;
	int iNum;
	u_int netmask;
	struct bpf_program fcode;//使用于pcap_compile,格式过滤
	pcap_t * adhandle;//libpcap函数库中pcap_t及pcap_dumper_t类型的数据结构
	char errbuf[PCAP_ERRBUF_SIZE];
	//修改这里可以更改捕获的数据包使用的协议类型
    char packet_filter[] = "(ip and udp) or (ip and tcp) or (ip and icmp)";

	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
	{
    
     //获取设备列表
		fprintf(stderr,"无法打开网络设备:%s\n", errbuf);
		return 1;
	}
	for (device = alldevs; device  != NULL; device = device->next)
	{
    
     //打印列表
		if (i == 0)
		{
    
    
			printf("请按CTRL + C退出!\n\n");
			printf("网络设备如下:\n");
		}
		printf("%d. %s\n", ++i, device -> name);
		if (device->description)
			printf(" (%s)\n", device->description);
		else
			printf("没有设备描述信息!");
	}
	if (i == 0)
	{
    
    
		printf("\n请先安装WinPcap!");
		return -1;
	}
	printf("请选择网络设备接口:(1 - %d):", i);
	scanf("%d", &iNum);
	getchar();
	if (iNum < 1 || iNum > i)
	{
    
    
		printf("设备不存在!\n");
		pcap_freealldevs(alldevs);//不需要网卡列表时,释放设备列表
		return -1;
	}
	//跳转到已选设备
    for (device = alldevs, i = 0;i < iNum -1 ; device = device -> next,i++);

    // 打开适配器
    if ( (adhandle= pcap_open(device->name,  // 设备名
                             65536,     // 要捕捉的数据包的部分
                                        // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
                             PCAP_OPENFLAG_PROMISCUOUS,         // 混杂模式
                             1000,      // 读取超时时间
                             NULL,      // 远程机器验证
                             errbuf     // 错误缓冲池
                             ) ) == NULL)
    {
    
    
        fprintf(stderr,"\n不能打开适配器!\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }

    if (pcap_datalink(adhandle) != DLT_EN10MB)
	{
    
     //检查数据链路层,为了简单,只考虑以太网
		fprintf(stderr, "\n系统网卡链路出错!\n");
		pcap_freealldevs(alldevs); //释放设备列表
		return -1;
	}

	if (device->addresses != NULL) //获得接口第一个地址的掩码
		netmask = ((struct sockaddr_in *)(device->addresses->netmask))->sin_addr.S_un.S_addr;
	else //如果接口没有地址,那么我们假设一个C类的掩码
		netmask = 0xffff00;
	if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
	{
    
     //编译过滤器
		fprintf(stderr, "不能监听过滤该数据报!\n");
		pcap_freealldevs(alldevs);
		return -1;
	}

	if (pcap_setfilter(adhandle, &fcode) < 0)
	{
    
     //设置过滤器
		fprintf(stderr, "过滤设置错误!\n");
		pcap_freealldevs(alldevs);
		return -1;
	}
	printf("请输入是否要输出捕捉到的报文信息(y/n) : ");
	scanf("%c",&judge);
	if (judge!='n')
	{
    
    
		printf("请输入要限制要输出报文信息长度(-1不限制) : ");
		scanf("%d",&length);
	}
	printf("\n正在监听通过%s的数据报...\n", device->description);
	pcap_freealldevs(alldevs); //释放设备列表
	pcap_loop(adhandle, 0, packet_handler, NULL); //开始捕捉

	return 0 ;
}

void packet_handler(u_char *dumpfile, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    
     //回调函数,当收到每一个数据包时会被libpcap所调用
	if(header->caplen>400) return;
	int len;
    struct tm *ltime;
    char timestr[16];
	ip_header * ip_hd;
	udp_header * udp_hd;
	tcp_header * tcp_hd;
    ethe_header * ethe_hd;
	int ip_len,tcp_len,start;
	u_short sport,dport;

	printf("\n");
    ltime=localtime(&header->ts.tv_sec); //将时间戳转换为可读字符
    strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
    printf("时间:%s\n",timestr);

    ethe_hd = (ethe_header *)pkt_data;
    ip_hd = (ip_header *)(pkt_data + 14);
	ip_len = (ip_hd ->ver_ihl & 0xf) * 4; //ip首部长度
	udp_hd = (udp_header *)((u_char *)ip_hd + ip_len);
	sport = ntohs(udp_hd->sport);
	dport = ntohs(udp_hd->dport);
	if(ip_hd->proto==17)
	{
    
    
		printf("协议:UDP");
		start=ip_len+8;
	}
	else if(ip_hd->proto==6)
	{
    
    
		printf("协议:TCP");
		tcp_hd = (tcp_header *)((u_char *)ip_hd + ip_len);
		tcp_len=ntohs(tcp_hd->sum)>>12;
		start=ip_len+tcp_len*4;
	}
	else if(ip_hd->proto==1)
	{
    
    
		printf("协议:ICMP");
		start=ip_len+23;
	}
	else printf("协议:其他");
	printf("start=%d\n",start);
	printf("                      数据报的长度:%d\n",header->caplen);

	printf("IP头的长度:%d              IP包存活时间:%d\n",ip_hd->tlen,ip_hd->ttl);

    printf("源IP地址: %d.%d.%d.%d:%d      目的IP地址:%d.%d.%d.%d:%d\n      源端口:%d                     目的端口:%d\n源物理地址: %x-%x-%x-%x-%x-%x   目的物理地址:%x-%x-%x-%x-%x-%x\n",
		  ip_hd->saddr.b1, ip_hd->saddr.b2, ip_hd->saddr.b3, ip_hd->saddr.b4,
		           ip_hd->daddr.b1, ip_hd->daddr.b2, ip_hd->daddr.b3, ip_hd->daddr.b4, sport, dport,
				   ethe_hd->mac_source_address.b1, ethe_hd->mac_source_address.b2, ethe_hd->mac_source_address.b3,
				   ethe_hd->mac_source_address.b4, ethe_hd->mac_source_address.b5, ethe_hd->mac_source_address.b6,
				   ethe_hd->mac_dest_address.b1, ethe_hd->mac_dest_address.b2, ethe_hd->mac_dest_address.b3,
				   ethe_hd->mac_dest_address.b4, ethe_hd->mac_dest_address.b5, ethe_hd->mac_dest_address.b6);
	//输出数据部分
	if (judge=='y')
	{
    
    
	    printf("数据部分内容为:\n");
	    if(length==-1) len=(header->caplen) + 1 ;
	    else len=(length>header->caplen + 1-start)?(header->caplen+1)-start:length;
	    for (int i=start; (i < start + len ) ; i++)
	    {
    
    
	        printf("%.2x ", pkt_data[i-1]); //也可以改为 %c 以 ascii码形式输出。
	        if ( (i % LINE_LEN) == 0) printf("\n");
	    }
	    printf("\n\n");
	}
}



4. Experimental results
result

Guess you like

Origin blog.csdn.net/weixin_43752257/article/details/112965606