zoukankan      html  css  js  c++  java
  • Linux数据链路层的包解析

    仅以此文作为学习笔记,初学者,如有错误欢迎批评指正,但求轻喷。
    一般而言,Linux系统截获数据包后,会通过协议栈,按照TCP/IP层次进行解析,那我们如何直接获得更为底层的数据报文呢,这里用到一个类型SOCK_PACKET类型。

    1 int sockfd = socket(AF_INET,SOCK_PACKET,htons(0x0003));  

    通过上面这个函数可以获得一个特殊的套接字,其中:
    AF_INET:                              表示因特网协议族
    SOCK_PACKET:                   表示数据包截取在物理层
    0x0003:                                 表示数据帧类型不确定
    修改网络接口结构:

    1 struct ifreq ifr;
    2 strcpy(ifr.ifr_name,"eth0");
    3 ioctl(sockfd,SIOCGIFFLAGS,&ifr);

    设置混杂模式

    1 ifr.ifr_flags| = IFR_PROMISC;
    2 ioctl(sockfd,SIOCSIFFLAGS,&ifr);

    注意:

     进行标志位设定时,遵循以下步骤:
    (1)取出原标识位;
    (2)与待设定标志位进行位或运算(“|”);
    (3)重新写入;
    一个小小的抓包程序

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<iostream>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<sys/ioctl.h>
    #include<net/if.h>
    #include<arpa/inet.h>
    #include<netinet/if_ether.h>
    #include<netinet/in.h>
    #include<netinet/ip.h>
    #include<netinet/tcp.h>
    #include<netinet/udp.h>
    
    using namespace std;
    
    char ethernet_frame[ETH_FRAME_LEN];//定义一个数据帧的长度
    struct iphdr *ipheader;//定义ip头部指针
    
    int socketcreate()
    {
        int sockfd = socket(AF_INET,SOCK_PACKET,htons(0x0003));    
        //构建了一个数据链路层的数据包;
        if(sockfd == -1)
        {
            cout<<"Socket init error!"<<endl;    
            return -1;
        }
        /*
            下面设置接口结构
        */
        char *ifname = "eth0";
        struct ifreq ifr;
        strcpy(ifr.ifr_name,ifname);
        int result = ioctl(sockfd,SIOCGIFFLAGS,&ifr);
        if(result == -1)
        {
            cout<<"Can't get flags!"<<endl;
            close(sockfd);
            return -2;
        }
        ifr.ifr_flags|= IFF_PROMISC;
        /*
        一般而言,Linux系统截获数据包后,会通过协议栈,按照TCP/IP层次进行解析,那我们如何直接获得更为底层的数据报文呢,这里用到一个类型SOCK_PACKET类型。
                       int sockfd = socket(AF_INET,SOCK_PACKET,htons(0x0003));
                       通过上面这个函数可以获得一个特殊的套接字,其中:
                       AF_INET:                              表示因特网协议族
                       SOCK_PACKET:                   表示数据包截取在物理层
                       0x0003:                                 表示数据帧类型不确定
    
                       修改网络接口结构:
                       struct ifreq ifr;
                       strcpy(ifr.ifr_name,"eth0");
                       ioctl(sockfd,SIOCGIFFLAGS,&ifr);
    
                       设置混杂模式
                       ifr.ifr_flags| = IFR_PROMISC;
                       ioctl(sockfd,SIOCSIFFLAGS,&ifr);
                       注意:
                              进行标志位设定时,遵循以下步骤:
                            (1)取出原标识位;
                            (2)与待设定标志位进行位或运算(“|”);
                            (3)重新写入;
        */
        result = ioctl(sockfd,SIOCSIFFLAGS,&ifr);
        if(result == -1)
        {
            cout<<"Can't set flags!"<<endl;
            close(sockfd);
            return -3;
        }
        
        
        
        return sockfd;
    }
    int getframe(int sockfd,int num)
    {
        struct ethhdr* fheader;
        fheader = (struct ethhdr*)ethernet_frame;
        memset(ethernet_frame,0,ETH_FRAME_LEN);
        int size = read(sockfd,ethernet_frame,ETH_FRAME_LEN);
        if(size <= 0)
        {
            cout<<"No packet or packet error!"<<endl;    
            return -1;
        }
        cout<<"************************Packet"<<num<<"received from eth0 START!************************"<<endl;
        printf("DST MAC: ");
        for(int i=0;i<ETH_ALEN-1;i++)
        {
            printf("%2x-",fheader->h_dest[i]);    
        }
        printf("%2x
    ",fheader->h_dest[ETH_ALEN-1]);
        printf("SRC MAC: ");
        for(int i=0;i<ETH_ALEN-1;i++)
        {
            printf("%2x-",fheader->h_source[i]);    
        }
        printf("%2x
    ",fheader->h_source[ETH_ALEN-1]);
        if(ntohs(fheader->h_proto) == 0x0800)
        {
            cout<<"Protocol: IP"<<endl;    
        }
        if(ntohs(fheader->h_proto) == 0x0806)
        {
            cout<<"Protocol: RAP"<<endl;    
        }
        if(ntohs(fheader->h_proto) == 0x8035)
        {
            cout<<"Protocol: RARP"<<endl;    
        }
        int ret = ntohs(fheader->h_proto);
        return ret;
    }
    
    int getip(int protocol,int num)
    {
            if(protocol != 0x0800)
            {
                cout<<"NO IP Packet!"<<endl;
                cout<<"************************Packet"<<num<<"received from eth0 END!**************************"<<endl;
                return 0;    
            }
            ipheader = (struct iphdr*)(ethernet_frame+ETH_HLEN);
            printf("Version: 4");
            cout<<endl;
            in_addr *p,*q;
            p = (struct in_addr*)&ipheader->saddr;
            printf("SRC IP: %s",inet_ntoa(*p));
            cout<<endl;
            q = (struct in_addr*)&ipheader->daddr;
            printf("DST IP: %s",inet_ntoa(*q));
            cout<<endl;
            if(ipheader->protocol == 1)
            {
                cout<<"PROTOCOL: ICMP"<<endl;    
            }
            if(ipheader->protocol == 6)
            {
                cout<<"PROTOCOL: TCP"<<endl;    
            }
            if(ipheader->protocol == 17)
            {
                cout<<"PROTOCOL: UDP"<<endl;    
            }
            return ipheader->protocol;
    }
    
    int gettcp(int protocol)
    {
        if(protocol != 6)
        {
            return -1;    
        }
        struct tcphdr* tcph;
        tcph = (struct tcphdr*)(ipheader+((ipheader->ihl)*4));
        printf("SRC PORT: %d",ntohs(tcph->source));
        cout<<endl;
        printf("DST PORT: %d",ntohs(tcph->dest));
        cout<<endl;
        return 0;
    }
    
    int getudp(int protocol)
    {
        if(protocol != 17)
        {
            return -1;    
        }
        struct udphdr* udph;
        udph = (struct udphdr*)(ipheader+((ipheader->ihl)*4));
        printf("SRC PORT: %d",ntohs(udph->source));
        cout<<endl;
        printf("DST PORT: %d",ntohs(udph->dest));
        cout<<endl;
        return 0;
    }
    
    int main(int argc,char *argv[])
    {        
        if(argc < 2)
        {
            cout<<"Please input the nummber of packet that you want to catch!"<<endl;
            return 0;
        }
        int num = (int)argv[1][0];
        int sock = socketcreate();
        for(int i=1;i<num;i++)
        {
            int ip_protocol = getframe(sock,i);
            int trasnport_protocol = getip(ip_protocol,i);
            gettcp(trasnport_protocol);
            getudp(trasnport_protocol);
            cout<<"************************Packet"<<num<<"received from eth0 END!**************************"<<endl;
            cout<<endl;
            cout<<endl;
            cout<<endl;
            cout<<endl;
        }
        return 0;
    }
  • 相关阅读:
    【转】80后安稳上班,90后看心情上班,95后……太形象了!
    未解决问题列表
    从键盘上输入一个正整数n,请按照以下五行杨辉三角形的显示方式, 输出杨辉三角形的前n行。请采用循环控制语句来实现。
    按要求编写Java应用程序。 编写一个名为Test的主类,类中只有一个主方法; 在主方法中定义一个大小为50的一维整型数组,数组名为x,数组中存放着{1, 3,5,…,99}输出这个数组中的所有元素,每输出十个换一行;在主方法中定义一 个大小为10*10的二维字符型数组,数组名为y,正反对角线上存的是‘*’,其余 位置存的是‘#’;输出这个数组中的所有元素。
    找出如下数组中最大的元素和最小的元素, a[][]={{3,2,6},{6,8,2,10},{5},{12,3,23}}
    已知2个一维数组:a[]={3,4,5,6,7},b[]={1,2,3,4,5,6,7};把数组a与数组b ,对应的元素乘积再赋值给数组b,如:b[2]=a[2]*b[2];最后输出数组b的元素。
    用自己的算法实现startsWith和endsWith功能。
    采用字符的移位方式实现字符文本加密解密。
    生成4位验证码,最多输入5次
    二分查找(转载的别人的,看的不是太懂)
  • 原文地址:https://www.cnblogs.com/KevinGeorge/p/7866735.html
Copyright © 2011-2022 走看看