C/C++知识点之C语言实现Linux网络嗅探器
小标 2018-09-18 来源 : 阅读 1729 评论 0

摘要:本文主要向大家介绍了C/C++知识点之C语言实现Linux网络嗅探器,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。

本文主要向大家介绍了C/C++知识点之C语言实现Linux网络嗅探器,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。               

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("\033[H\033[2J")
#define INITCOLOR(color) printf("\033[%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 !\n");
}

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,"\n");
    fprintf(sniffer->logfile,"IP Header\n");
    fprintf(sniffer->logfile,"   |-IP Version        : %d\n",(unsigned int)iph->version);
    fprintf(sniffer->logfile,"   |-IP Header Length  : %d DWORDS or %d Bytes\n",(unsigned int)iph->ihl,((unsigned int)(iph->ihl))*4);
    fprintf(sniffer->logfile,"   |-Type Of Service   : %d\n",(unsigned int)iph->tos);
    fprintf(sniffer->logfile,"   |-IP Total Length   : %d  Bytes(size of Packet)\n",ntohs(iph->tot_len));
    fprintf(sniffer->logfile,"   |-Identification    : %d\n",ntohs(iph->id));
    fprintf(sniffer->logfile,"   |-TTL      : %d\n",(unsigned int)iph->ttl);
    fprintf(sniffer->logfile,"   |-Protocol : %d\n",(unsigned int)iph->protocol);
    fprintf(sniffer->logfile,"   |-Checksum : %d\n",ntohs(iph->check));
    fprintf(sniffer->logfile,"   |-Source IP        : %s\n",inet_ntoa(source.sin_addr));
    fprintf(sniffer->logfile,"   |-Destination IP   : %s\n",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,"\n");
    fprintf(sniffer->logfile,"TCP Header\n");
    fprintf(sniffer->logfile,"   |-Source Port      : %u\n",ntohs(tcph->source));
    fprintf(sniffer->logfile,"   |-Destination Port : %u\n",ntohs(tcph->dest));
    fprintf(sniffer->logfile,"   |-Sequence Number    : %u\n",ntohl(tcph->seq));
    fprintf(sniffer->logfile,"   |-Acknowledge Number : %u\n",ntohl(tcph->ack_seq));
    fprintf(sniffer->logfile,"   |-Header Length      : %d DWORDS or %d BYTES\n" ,(unsigned int)tcph->doff,(unsigned int)tcph->doff*4);
    fprintf(sniffer->logfile,"   |-Urgent Flag          : %d\n",(unsigned int)tcph->urg);
    fprintf(sniffer->logfile,"   |-Acknowledgement Flag : %d\n",(unsigned int)tcph->ack);
    fprintf(sniffer->logfile,"   |-Push Flag            : %d\n",(unsigned int)tcph->psh);
    fprintf(sniffer->logfile,"   |-Reset Flag           : %d\n",(unsigned int)tcph->rst);
    fprintf(sniffer->logfile,"   |-Synchronise Flag     : %d\n",(unsigned int)tcph->syn);
    fprintf(sniffer->logfile,"   |-Finish Flag          : %d\n",(unsigned int)tcph->fin);
    fprintf(sniffer->logfile,"   |-Window         : %d\n",ntohs(tcph->window));
    fprintf(sniffer->logfile,"   |-Checksum       : %d\n",ntohs(tcph->check));
    fprintf(sniffer->logfile,"   |-Urgent Pointer : %d\n",tcph->urg_ptr);
    fprintf(sniffer->logfile,"\n");
    fprintf(sniffer->logfile,"                        DATA Dump                         ");
    fprintf(sniffer->logfile,"\n");
  
    fprintf(sniffer->logfile,"IP Header\n");
    PrintData(buf, iphdrlen, sniffer);
  
    fprintf(sniffer->logfile,"TCP Header\n");
    PrintData(buf+iphdrlen, tcph->doff*4, sniffer);
  
    fprintf(sniffer->logfile,"Data Payload\n");

    /* 把用户数据写入日志文件 */
    PrintData(buf + iphdrlen + tcph->doff*4,
        (size - tcph->doff*4-iph->ihl*4),
        sniffer );
  
    fprintf(sniffer->logfile,"\n###########################################################");
}

/* 写 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,"\n\n***********************UDP Packet*************************\n");
  
    print_ip_header(buf, size, sniffer);
    
    /* 把 udp 头信息写入日志文件中 */
    fprintf(sniffer->logfile,"\nUDP Header\n");
    fprintf(sniffer->logfile,"   |-Source Port      : %d\n" , ntohs(udph->source));
    fprintf(sniffer->logfile,"   |-Destination Port : %d\n" , ntohs(udph->dest));
    fprintf(sniffer->logfile,"   |-UDP Length       : %d\n" , ntohs(udph->len));
    fprintf(sniffer->logfile,"   |-UDP Checksum     : %d\n" , ntohs(udph->check));
  
    fprintf(sniffer->logfile,"\n");
    fprintf(sniffer->logfile,"IP Header\n");
    PrintData(buf , iphdrlen, sniffer);
  
    fprintf(sniffer->logfile,"UDP Header\n");
    PrintData(buf+iphdrlen, sizeof(udph), sniffer);
  
    fprintf(sniffer->logfile,"Data Payload\n");

    /* 把用户数据写入日志文件 */
    PrintData(buf + iphdrlen + sizeof udph,
        (size - sizeof udph - iph->ihl * 4),
        sniffer);
  
    fprintf(sniffer->logfile,"\n###########################################################");
}

/* 写 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,"\n\n***********************ICMP Packet*************************\n");  
    print_ip_header(buf , size, sniffer);
    fprintf(sniffer->logfile,"\n");
    fprintf(sniffer->logfile,"ICMP Header\n");
    fprintf(sniffer->logfile,"   |-Type : %d",(unsigned int)(icmph->type));  
    if((unsigned int)(icmph->type) == 11) 
    fprintf(sniffer->logfile,"  (TTL Expired)\n");
    else if((unsigned int)(icmph->type) == ICMP_ECHOREPLY) 
    fprintf(sniffer->logfile,"  (ICMP Echo Reply)\n");
    fprintf(sniffer->logfile,"   |-Code : %d\n",(unsigned int)(icmph->code));
    fprintf(sniffer->logfile,"   |-Checksum : %d\n",ntohs(icmph->checksum));
    fprintf(sniffer->logfile,"\n");
    fprintf(sniffer->logfile,"IP Header\n");
    PrintData(buf, iphdrlen, sniffer);
    fprintf(sniffer->logfile,"UDP Header\n");
    PrintData(buf + iphdrlen , sizeof(icmph), sniffer);
  
    fprintf(sniffer->logfile,"Data Payload\n");  

    /* 最后将用户数据写入日志文件中 */
    PrintData(buf + iphdrlen + sizeof(icmph),
        (size - sizeof(icmph) - iph->ihl * 4),
        sniffer);
  
    fprintf(sniffer->logfile,"\n###########################################################");
}

/* 写用户数据到日志文件 */
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, "\n");
        fprintf(sniffer->logfile, " %02X",(unsigned int)buf[i]);
      
        if( i == size - 1)
            fprintf(sniffer->logfile, "\n");
    }
}

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\n\n");  
}

/* 主函数入口 */
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)***\n", __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\n",
     sniffer->prot->tcp, sniffer->prot->udp,
     sniffer->prot->icmp, sniffer->prot->igmp,
     sniffer->prot->others, sniffer->prot->total);
}
0x04数据报分析
以一个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 ‘\nQUIT !\n‘
    exit 1
}

main()
{
    clear
    printf "\t\t\t\t\tWelcome to Sniffer Project r\n\n"

    while [ 1 ]; do
    printf "Select option: \n\n"
    printf "1 : Build Project\n"
    printf "2 : Launch Project\n"
    printf "3 : Remove Object files\n"
    printf "4 : Rebuild\n"
    printf "0 : Exit\n"

    printf "\nYou 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\n"
    fi
    done
}

trap ‘sigint‘ 2

main
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!


本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程