zoukankan      html  css  js  c++  java
  • C语言实现Linux网络嗅探器

    C语言实现Linux网络嗅探器

    0x01 实验简介

    网络嗅探器是拦截通过网络接口流入和流出的数据的程序。所以,如果你正在浏览的互联网,嗅探器以数据包的形式抓到它并且显示。在本实验中,我们用 C 语言实现了一个网络嗅探器。

    0x02程序框架和功能描述

    本程序使用c语言编程,实现linux环境下网络嗅探的功能,并实现对接收到的UDP数据报进行解析。

    0x03程序代码

    sniffer.h

    #ifndef	__SNIFFER_H__
    #define	__SNIFFER_H__
    
    
    typedef struct	s_protocol
    {
    	int	tcp;
    	int	udp;
    	int	icmp;
    	int	igmp;
    	int	others;
    	int	total;
    } t_protocol;
    
    typedef struct	s_sniffer
    {
    	FILE *logfile;
    	t_protocol *prot;
    } t_sniffer;
    
    void ProcessPacket(unsigned char*, int, t_sniffer *);
    void print_ip_header(unsigned char* , int, t_sniffer *);
    void print_tcp_packet(unsigned char* , int, t_sniffer *);
    void print_udp_packet(unsigned char * , int, t_sniffer *);
    void print_icmp_packet(unsigned char* , int, t_sniffer *);
    void PrintData (unsigned char* , int, t_sniffer *);
    void display_time_and_date();
    void getting_started();
    void signal_white_now(int);
    
    #endif
    
    

    tools.h

    #ifndef	__COLOR_H__
    #define	__COLOR_H__
    
    #include <stdio.h>
    
    #define	CLEARSCREEN() printf("33[H33[2J")
    #define	INITCOLOR(color) printf("33[%sm", color)
    #define	RED_COLOR "31"
    #define	GREEN_COLOR	"32"
    #define	YELLOW_COLOR "33"
    #define	BLUE_COLOR "34"
    #define	ZERO_COLOR "0"
    
    #endif		
    
    

    tools.c

    #include	<signal.h>
    #include	<stdio.h>
    
    /* 信号处理函数 */
    void signal_white_now(int signum)
    {
    	printf("Bye Bye !
    ");
    }
    
    

    show_data.c

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <netinet/ip_icmp.h>
    #include <netinet/udp.h>
    #include <netinet/tcp.h>
    #include <netinet/ip.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    
    #include "sniffer.h"
    #include "tools.h"
    
    /* 写 IP 头部到日志文件 */
    void print_ip_header(unsigned char *buf, int size, t_sniffer *sniffer)
    {
    	unsigned short iphdrlen;
    	struct iphdr *iph;
    	struct sockaddr_in source;
    	struct sockaddr_in dest;
    
    	iph = (struct iphdr *)buf;
    	iphdrlen = iph->ihl*4; 
    	(void)iphdrlen;
    	(void)size;
    	memset(&source, 0, sizeof(source));
    	source.sin_addr.s_addr = iph->saddr;
      
    	memset(&dest, 0, sizeof(dest));
    	dest.sin_addr.s_addr = iph->daddr;
      
    	fprintf(sniffer->logfile,"
    ");
    	fprintf(sniffer->logfile,"IP Header
    ");
    	fprintf(sniffer->logfile,"   |-IP Version        : %d
    ",(unsigned int)iph->version);
    	fprintf(sniffer->logfile,"   |-IP Header Length  : %d DWORDS or %d Bytes
    ",(unsigned int)iph->ihl,((unsigned int)(iph->ihl))*4);
    	fprintf(sniffer->logfile,"   |-Type Of Service   : %d
    ",(unsigned int)iph->tos);
    	fprintf(sniffer->logfile,"   |-IP Total Length   : %d  Bytes(size of Packet)
    ",ntohs(iph->tot_len));
    	fprintf(sniffer->logfile,"   |-Identification    : %d
    ",ntohs(iph->id));
    	fprintf(sniffer->logfile,"   |-TTL      : %d
    ",(unsigned int)iph->ttl);
    	fprintf(sniffer->logfile,"   |-Protocol : %d
    ",(unsigned int)iph->protocol);
    	fprintf(sniffer->logfile,"   |-Checksum : %d
    ",ntohs(iph->check));
    	fprintf(sniffer->logfile,"   |-Source IP        : %s
    ",inet_ntoa(source.sin_addr));
    	fprintf(sniffer->logfile,"   |-Destination IP   : %s
    ",inet_ntoa(dest.sin_addr));
    }
    
    /* 写 TCP 数据包到日志文件 */
    void print_tcp_packet(unsigned char *buf, int size, t_sniffer *sniffer)
    {
    	unsigned short iphdrlen;
    	struct iphdr *iph;
    	struct tcphdr *tcph;
      
    	iph = (struct iphdr *)buf;
    	iphdrlen = iph->ihl * 4;  
    	tcph = (struct tcphdr*)(buf + iphdrlen);
    	print_ip_header(buf, size, sniffer);
        
    	/* 把 tcp 头信息写入日志文件中 */
    	fprintf(sniffer->logfile,"
    ");
    	fprintf(sniffer->logfile,"TCP Header
    ");
    	fprintf(sniffer->logfile,"   |-Source Port      : %u
    ",ntohs(tcph->source));
    	fprintf(sniffer->logfile,"   |-Destination Port : %u
    ",ntohs(tcph->dest));
    	fprintf(sniffer->logfile,"   |-Sequence Number    : %u
    ",ntohl(tcph->seq));
    	fprintf(sniffer->logfile,"   |-Acknowledge Number : %u
    ",ntohl(tcph->ack_seq));
    	fprintf(sniffer->logfile,"   |-Header Length      : %d DWORDS or %d BYTES
    " ,(unsigned int)tcph->doff,(unsigned int)tcph->doff*4);
    	fprintf(sniffer->logfile,"   |-Urgent Flag          : %d
    ",(unsigned int)tcph->urg);
    	fprintf(sniffer->logfile,"   |-Acknowledgement Flag : %d
    ",(unsigned int)tcph->ack);
    	fprintf(sniffer->logfile,"   |-Push Flag            : %d
    ",(unsigned int)tcph->psh);
    	fprintf(sniffer->logfile,"   |-Reset Flag           : %d
    ",(unsigned int)tcph->rst);
    	fprintf(sniffer->logfile,"   |-Synchronise Flag     : %d
    ",(unsigned int)tcph->syn);
    	fprintf(sniffer->logfile,"   |-Finish Flag          : %d
    ",(unsigned int)tcph->fin);
    	fprintf(sniffer->logfile,"   |-Window         : %d
    ",ntohs(tcph->window));
    	fprintf(sniffer->logfile,"   |-Checksum       : %d
    ",ntohs(tcph->check));
    	fprintf(sniffer->logfile,"   |-Urgent Pointer : %d
    ",tcph->urg_ptr);
    	fprintf(sniffer->logfile,"
    ");
    	fprintf(sniffer->logfile,"                        DATA Dump                         ");
    	fprintf(sniffer->logfile,"
    ");
      
    	fprintf(sniffer->logfile,"IP Header
    ");
    	PrintData(buf, iphdrlen, sniffer);
      
    	fprintf(sniffer->logfile,"TCP Header
    ");
    	PrintData(buf+iphdrlen, tcph->doff*4, sniffer);
      
    	fprintf(sniffer->logfile,"Data Payload
    ");
    
    	/* 把用户数据写入日志文件 */
    	PrintData(buf + iphdrlen + tcph->doff*4,
    		(size - tcph->doff*4-iph->ihl*4),
    		sniffer );
      
    	fprintf(sniffer->logfile,"
    ###########################################################");
    }
    
    /* 写 UDP 数据包到日志文件 */
    void print_udp_packet(unsigned char *buf , int size, t_sniffer *sniffer)
    {
    	unsigned short iphdrlen;
    
    	struct iphdr *iph;
    	struct udphdr *udph;
    
    	iph = (struct iphdr *)buf;
    	iphdrlen = iph->ihl*4;
    	udph = (struct udphdr*)(buf + iphdrlen);
    	fprintf(sniffer->logfile,"
    
    ***********************UDP Packet*************************
    ");
      
    	print_ip_header(buf, size, sniffer);
        
    	/* 把 udp 头信息写入日志文件中 */
    	fprintf(sniffer->logfile,"
    UDP Header
    ");
    	fprintf(sniffer->logfile,"   |-Source Port      : %d
    " , ntohs(udph->source));
    	fprintf(sniffer->logfile,"   |-Destination Port : %d
    " , ntohs(udph->dest));
    	fprintf(sniffer->logfile,"   |-UDP Length       : %d
    " , ntohs(udph->len));
    	fprintf(sniffer->logfile,"   |-UDP Checksum     : %d
    " , ntohs(udph->check));
      
    	fprintf(sniffer->logfile,"
    ");
    	fprintf(sniffer->logfile,"IP Header
    ");
    	PrintData(buf , iphdrlen, sniffer);
      
    	fprintf(sniffer->logfile,"UDP Header
    ");
    	PrintData(buf+iphdrlen, sizeof(udph), sniffer);
      
    	fprintf(sniffer->logfile,"Data Payload
    ");
    
    	/* 把用户数据写入日志文件 */
    	PrintData(buf + iphdrlen + sizeof udph,
    		(size - sizeof udph - iph->ihl * 4),
    		sniffer);
      
    	fprintf(sniffer->logfile,"
    ###########################################################");
    }
    
    /* 写 ICMP 数据包到日志文件 */
    void print_icmp_packet(unsigned char *buf , int size, t_sniffer *sniffer)
    {
    	unsigned short iphdrlen;
    	struct iphdr	*iph;
    	struct icmphdr *icmph;
      
    	iph = (struct iphdr *)buf;
    	iphdrlen = iph->ihl * 4;
    	icmph = (struct icmphdr *)(buf + iphdrlen);
    
    	/* 把 icmp 头信息写入日志文件中 */
    	fprintf(sniffer->logfile,"
    
    ***********************ICMP Packet*************************
    ");  
    	print_ip_header(buf , size, sniffer);
    	fprintf(sniffer->logfile,"
    ");
    	fprintf(sniffer->logfile,"ICMP Header
    ");
    	fprintf(sniffer->logfile,"   |-Type : %d",(unsigned int)(icmph->type));  
    	if((unsigned int)(icmph->type) == 11) 
    	fprintf(sniffer->logfile,"  (TTL Expired)
    ");
    	else if((unsigned int)(icmph->type) == ICMP_ECHOREPLY) 
    	fprintf(sniffer->logfile,"  (ICMP Echo Reply)
    ");
    	fprintf(sniffer->logfile,"   |-Code : %d
    ",(unsigned int)(icmph->code));
    	fprintf(sniffer->logfile,"   |-Checksum : %d
    ",ntohs(icmph->checksum));
    	fprintf(sniffer->logfile,"
    ");
    	fprintf(sniffer->logfile,"IP Header
    ");
    	PrintData(buf, iphdrlen, sniffer);
    	fprintf(sniffer->logfile,"UDP Header
    ");
    	PrintData(buf + iphdrlen , sizeof(icmph), sniffer);
      
    	fprintf(sniffer->logfile,"Data Payload
    ");  
    
    	/* 最后将用户数据写入日志文件中 */
    	PrintData(buf + iphdrlen + sizeof(icmph),
    		(size - sizeof(icmph) - iph->ihl * 4),
    		sniffer);
      
    	fprintf(sniffer->logfile,"
    ###########################################################");
    }
    
    /* 写用户数据到日志文件 */
    void PrintData(unsigned char *buf, int size, t_sniffer *sniffer)
    {
      int i;
    
      for(i = 0 ; i < size ; i++)
    	{
    		if(i % 16 == 0)
    		fprintf(sniffer->logfile, "
    ");
    		fprintf(sniffer->logfile, " %02X",(unsigned int)buf[i]);
          
    		if( i == size - 1)
    			fprintf(sniffer->logfile, "
    ");
    	}
    }
    
    

    main.c

    #include	<signal.h>
    #include	<unistd.h>
    #include	<stdio.h>
    #include	<stdlib.h>
    #include	<string.h>
    #include	<netinet/ip.h>
    #include	<sys/socket.h>
    #include	<sys/select.h>
    #include	<fcntl.h>
    #include	<sys/types.h>
    #include	<sys/time.h>
    #include	<errno.h>
    
    #include	"sniffer.h"
    #include	"tools.h"
    
    #define ETH_P_IP 0x0800
    
    int	exec_cmd(char *buffer, int len)
    {
    	if (strncmp(buffer, "quit", 4) == 0)
    		return (1);
    	return (0);
    }
    
    int	command_interpreter(int sd)
    {
    	int	len;
    	char buf[512];
    
    	len = read(0, buf, 512);
    	if (len > 0)
    	{
    		if (exec_cmd(buf, len) == 1)
    			return (1);
    	}
    	return (0);
    }
    
    void display_time_and_date()
    {
    	INITCOLOR(RED_COLOR);
    	printf("[%s]", __DATE__); /* 打印日期 */
    	INITCOLOR(GREEN_COLOR);
    	printf("[%s]  ", __TIME__); /* 打印时间 */
    	INITCOLOR(ZERO_COLOR);
    }
    
    void getting_started()
    {
    	CLEARSCREEN(); /* 清空屏幕 */
    	display_time_and_date();
    	printf("Getting started of Network sniffer
    
    ");  
    }
    
    /* 主函数入口 */
    int	main()
    {
    	/* 声明部分 */
    	int	sd;
    	int	res;
    	int	saddr_size;
    	int	data_size;
    	struct sockaddr saddr;
    	unsigned char *buffer; /* 保存数据包的数据 */
    	t_sniffer sniffer; /* 保存数据包的类型和日志文件等信息 */
    	fd_set fd_read;
    
    	buffer = malloc(sizeof(unsigned char *) * 65536); 
    
    	/* 以可写的方式在当前文件夹中创建日志文件 */
    	sniffer.logfile = fopen("log.txt", "w");
    	fprintf(sniffer.logfile,"***LOGFILE(%s - %s)***
    ", __DATE__, __TIME__);
    	if (sniffer.logfile == NULL)
    	{
    		perror("fopen(): ");
    		return (EXIT_FAILURE);
    	}
    
    	sniffer.prot = malloc(sizeof(t_protocol *));  
    
    	/* 创建原始套接字,ETH_P_ALL 表示侦听负载为 IP 数据报的以太网帧 */
    	sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); 
    	if (sd < 0)
    	{
    		perror("socket(): ");
    		return (EXIT_FAILURE);
    	}
    	getting_started();
    	signal(SIGINT, &signal_white_now);
    	signal(SIGQUIT, &signal_white_now);
    
    	/* 循环侦听以太网帧,并调用 ProcessPacket 函数解析 */
    	while (1)
    	{
    		FD_ZERO(&fd_read);
    		FD_SET(0, &fd_read);
    		FD_SET(sd, &fd_read);
    
    		/* 多路复用检测可读的套接字和标准输入 */
    		res = select(sd + 1, &fd_read, NULL, NULL, NULL);
    		if (res < 0)
    			{
    				close(sd);
    				if (errno != EINTR)
    				perror("select() ");
    				return (EXIT_FAILURE);
    			}
    		else
    			{
    				/* 如果是标准输入可读,进入命令行处理程序 command_interpreter,暂时只支持 'quit' 命令 */
    				if (FD_ISSET(0, &fd_read)) 
    				{
    					if (command_interpreter(sd) == 1)
    					break;
    				}
    
    				/* 如果是套接字可读,则读取以太网数据帧的内容,并调用 ProcessPacket 函数解析出数据包的类型 */
    				else if (FD_ISSET(sd, &fd_read))
    					{
    						/* 读取以太网数据帧的内容 */
    						saddr_size = sizeof(saddr);
    						data_size = recvfrom(sd, buffer, 65536, 0, &saddr,(socklen_t*)&saddr_size); /* 读取以太网数据帧的内容 */
    						if (data_size <= 0)
    							{
    								close(sd);
    								perror("recvfrom(): ");
    								return (EXIT_FAILURE);
    							}
    
    						ProcessPacket(buffer, data_size, &sniffer); /* 调用 ProcessPacket 函数解析出数据包的类型 */
    					}
    			}
    	}
    	
    	close(sd);
    	return (EXIT_SUCCESS);
    }
    
    void ProcessPacket(unsigned char* buffer, int size, t_sniffer *sniffer)
    {
    	buffer = buffer + 6 + 6 + 2; /* 根据太网帧结构,前 6B 是目的 MAC 地址,接下来的是源 MAC 地址,接下来 2B 是帧长度,其余的是负载(上层的 IP 数据报) */
    	struct iphdr *iph = (struct iphdr*)buffer;
    	++sniffer->prot->total; /* 数据包总数加 1 */
    
    	/* 根据 TCP/IP 协议规定的 IP 数据报头部的 protocol 字段的值,判断上层的数据包类型 */
    	switch (iph->protocol)
    		{
    			/* 1 表示 icmp 协议 */
    			case 1: 
    				++sniffer->prot->icmp;
    				print_icmp_packet(buffer, size, sniffer);
    				break;
    				
    			/* 2 表示 igmp 协议 */
    			case 2:
    				++sniffer->prot->igmp;
    				break;
    				
    			/* 6 表示 tcp 协议 */
    			case 6:
    				++sniffer->prot->tcp;
    				print_tcp_packet(buffer , size, sniffer);
    				break;
    				
    			/* 17 表示 udp 协议 */
    			case 17:
    				++sniffer->prot->udp;
    				print_udp_packet(buffer , size, sniffer);
    				break;
          
    			default:
    				++sniffer->prot->others;
    				break;
    		}
    
    	display_time_and_date(); /* 显示时间 */
    
    	/* 打印 sniffer 中的信息 */
    	printf("TCP : %d   UDP : %d   ICMP : %d   IGMP : %d   Others : %d Total : %d
    ",
    	 sniffer->prot->tcp, sniffer->prot->udp,
    	 sniffer->prot->icmp, sniffer->prot->igmp,
    	 sniffer->prot->others, sniffer->prot->total);
    }
    

    0x04程序运行

    本程序运行界面如下:

    实现的功能之一就是解析UDP数据报,其扫描解析结果存放在log.txt中。这里从log.txt中以一个UDP数据包的解析为例

    ***LOGFILE(Dec 14 2017 - 21:41:38)***
    
    
    ***********************UDP Packet*************************
    
    IP Header
       |-IP Version        : 4
       |-IP Header Length  : 5 DWORDS or 20 Bytes
       |-Type Of Service   : 0
       |-IP Total Length   : 213  Bytes(size of Packet)
       |-Identification    : 6639
       |-TTL      : 64
       |-Protocol : 17
       |-Checksum : 31927
       |-Source IP        : 172.16.69.82
       |-Destination IP   : 172.16.69.255
    
    UDP Header
       |-Source Port      : 138
       |-Destination Port : 138
       |-UDP Length       : 193
       |-UDP Checksum     : 26258
    
    IP Header
    
    保护隐私,这部分我删除了
     AC 10 45 FF
    UDP Header
    
    保护隐私,这部分我删除了
    Data Payload
    保护隐私,这部分我删除了
    
    ###########################################################
    
    

    0x05附

    我认为本实验的亮点在于使用脚本控制,有必要好好学习下脚本编程
    launcher.sh

    #!/bin/sh
    
    sigint()
    {
        printf '
    QUIT !
    '
        exit 1
    }
    
    main()
    {
        clear
        printf "					Welcome to Sniffer Project r
    
    "
    
        while [ 1 ]; do
    	printf "Select option: 
    
    "
    	printf "1 : Build Project
    "
    	printf "2 : Launch Project
    "
    	printf "3 : Remove Object files
    "
    	printf "4 : Rebuild
    "
    	printf "0 : Exit
    "
    
    	printf "
    You choose: "
    	read option
    	
    	if [ $option = 0 ]
    	then
    	    exit
    	fi
    	if [ $option -ge 1 ] && [ $option -le 4 ]
    	then
    	    if [ $option = 1 ]
    	    then
    		make "network_sniffer"
    	    fi
    	    if [ $option = 2 ]
    	    then
    		"./network_sniffer"
    	    fi
    	    if [ $option = 3 ]
    	    then
    		make clean
    	    fi
    	    if [ $option = 4 ]
    	    then
    		make re
    	    fi	    
    	else
    	    printf "This option does not exist
    "
    	fi
        done
    }
    
    trap 'sigint' 2
    
    main
    
    
  • 相关阅读:
    「酷客多」关注:马化腾公开演讲,透露2017年春节前会推出“小程序”
    微信小程序购物商城系统开发系列-目录结构
    微信小程序购物商城系统开发系列-工具篇
    上海闪酷成为京东商城第一批独立软件开发商(ISV)
    【FFMPEG】关于硬解码和软解码
    Git 别名配置
    【Linux】扩展阿里云数据盘分区和文件系统
    Python实现MQTT接收订阅数据
    【Linux】Devops的一些运维工具
    【Linux】YUM Repositories for CentOS, RHEL & Fedora Systems
  • 原文地址:https://www.cnblogs.com/ghost00011011/p/8040468.html
Copyright © 2011-2022 走看看