zoukankan      html  css  js  c++  java
  • 将抓到的pcap文件中Http包转换为可读的txt格式

    编写思路:

        1.首先将要读取、写入信息的两个文件分别进行打开,接下来先进行文件读操作,获得指向该文件头的指针fp

        2.从文件中读取pcap文件头大小的信息存储在pcap_head_buf里,再将偏移偏移量offset设置为pcap头之后

        3.将文件pkt(数据报)头信息存储在pkt_head_buf里,将偏移量置offset设置到pkt头+pkt数据之后,即为下个数据报的偏移量,将fp移动到数据报头之后。

        4从pcap文件头中读出linktype从而获得帧头种类,再从帧头中读出改数据帧是ipv4还是ipv6,本文只处理ipv4。同时指针移动到帧头部后面

        5.当帧为ipv4时,首先将ip头部取出存储在ip_head_buf中,指针后移到ip头后面

        6.从ip头中读出Protocol,从而获取是否为TCP链接,本文只处理TCP链接。当前是TCP连接时,直接将TCP头信息存储在tcp_head_buf里,fp移动至TCP头后

        7.接下来offset-fp大小即为数据域大小。首先将数据域最开始的四个字符存储在tempBuf中,指针随之后移。

        8。将随后部分的数据域全部存储在tcp_data_buf中,并从中读取出method、url、host、user-Agent信息。

        9.将method、url、host、user-Agent信息进行格式化存储。

    数据域结构:1.开头四个字节为请求方式,POST、GET或HEAD。

    用到的一些函数:

      1.ftell

        函数原型:long ftell(FILE *fp);

        作用:获取当前指针和文件开头便宜的字节数

       2.memset

        函数原型:void *memset(void *s, int ch, size_t n);

        作用:将s中当前位置后面的n个字节 用 ch 替换并返回 s 。

      

    mian.cpp

    #include "pcap.h"
    #include "method.hpp"
    #include <iostream>
    #include <memory.h>
    #include <fstream>
    using namespace std;

    int main()
    {
        pcap_header pcap_head_buf;
        pkt_header pkt_head_buf;
        ip_header ip_head_buf;
        tcp_header tcp_head_buf;
        FILE *fp = fopen("Login.pcap" , "rw");
        ofstream outfile("HttpMsg.txt",ios::app);

        getPcapFileHead(fp , pcap_head_buf);//将文件中一个pkt_header大小的内容存入pcap_head_buf的内存地址

        fseek(fp, 0, SEEK_END);
        long fileSize=ftell(fp);
        long fpOffset=sizeof( pcap_header ) ;

        while( (fseek(fp, fpOffset, SEEK_SET) == 0)  && ( fpOffset < fileSize ) )     //在循环中处理每一个网络帧
        {
            getPktHead(fp , pkt_head_buf);
            fpOffset += ( sizeof(pkt_header) + pkt_head_buf.capture_len );
                                                //fpOffset 当前位置 +sizeof( pkt_header) +sizeof (pkt_data) ,得到下一网络帧的 offset

            u_int16 framType=getFramType(fp , pcap_head_buf.linktype);         //framType 标识了该帧是否为 IPV6链接
            if ( framType == 0xdd86 )                                          //IPV6链接 , 跳过该网络帧
            {
                continue ;
            }

            else
            {
                getIpHead(fp , ip_head_buf);

                if ( ip_head_buf.Protocol != 0x06 )            // Protocol != 0x06  表示非TCP链接 , 跳过该网络帧
                {
                    continue ;
                }
                else                                           //TCP 链接类型
                {
                    getTcpHead(fp ,tcp_head_buf) ;
                    int tcp_data_size = fpOffset - ftell(fp);
                      // 当前位置在一个 tcp_header 后  ,fpOffset - 当前位置 得到 tcp_data 的长度
                    if ( tcp_data_size !=0)
                    {
                        u_int8 tempBuf[4];
                        string methodBuf;
                        string urlBuf;
                        string hostBuf;
                        string uaBuf;
                        fread(tempBuf ,4 ,1 ,fp);
                        fseek(fp , -4 ,SEEK_CUR);
                        char tcp_data_buf[1024];
                        memset(tcp_data_buf,0,sizeof(tcp_data_buf)/sizeof(char));//将整个tcp_data_buf空间都置为0

                        if (   ( tempBuf[0]==0x50 && tempBuf[1]==0x4f && tempBuf[2]==0x53  && tempBuf[3]==0x54 )  ||
                               ( tempBuf[0]==0x47 && tempBuf[1]==0x45 && tempBuf[2]==0x54 )
                           )  //两个条件分别表示  "POST " 和 " GET "  ,判断成功表明 该网络帧包含了一个 HTTP  get 或者 post 链接

                        {


                            fread(tcp_data_buf , tcp_data_size ,1 ,fp );
                            matchHttp(tcp_data_buf , methodBuf , urlBuf , hostBuf , uaBuf );
                            outfile<<"method :"<<methodBuf<<endl;
                            outfile<<"url :" <<urlBuf<<endl;
                            outfile<<"host :"<<hostBuf<<sendl;
                            outfile<<"ua :"<<uaBuf<<endl;
                            outfile<<"=======*===========*=========*==========="<<std::endl;
                //将内容存在output.txt中
                //
                        }

                    }
                }

            }
        }
    outfile.close();
    }

    pcap.h

    #ifndef DEFINEPCAP_H
    #define DEFINEPCAP_H
    
    /* pacp文件构成:
    
      1: pacp = pcap_head + pkt_head + pket_data +  next->pkt_head  +  next->pkt_data :  ……
      2:  pkt_data=frame_head + ip_head + tcp_head +tcp_data
    
      //其中 pacp_head 中的 linktype 又决定了 frame_head 的类型
    
    
    */
    
    typedef unsigned int  u_int32;
    typedef unsigned short  u_int16;
    typedef unsigned char u_int8;
    typedef int int32;
    /*
     Pcap文件头24B各字段说明:
     Magic:4B:0x1A 2B 3C 4D:用来标示文件的开始
     Major:2B,0x02 00:当前文件主要的版本号
     Minor:2B,0x04 00当前文件次要的版本号
     ThisZone:4B当地的标准时间;全零
     SigFigs:4B时间戳的精度;全零
     SnapLen:4B最大的存储长度
     LinkType:4B链路类型
     常用类型:
      0            BSD loopback devices, except for later OpenBSD
     1            Ethernet, and Linux loopback devices
     6            802.5 Token Ring
     7            ARCnet
     8            SLIP
     9            PPP
     */
    typedef struct pcap_header {
        u_int32 magic;
        u_int16 version_major;
        u_int16 version_minor;
        int32 thiszone;
        u_int32 sigfigs;
        u_int32 snaplen;
        u_int32 linktype;
    }pcap_header;
    
    /*
     Packet 包头和Packet数据组成
     字段说明:
     Timestamp:时间戳高位,精确到seconds
     Timestamp:时间戳低位,精确到microseconds
     Caplen:当前数据区的长度,即抓取到的数据帧长度,由此可以得到下一个数据帧的位置。
     Len:离线数据长度:网络中实际数据帧的长度,一般不大于caplen,多数情况下和Caplen数值相等。
     Packet 数据:即 Packet(通常就是链路层的数据帧)具体内容,长度就是Caplen,这个长度的后面,就是当前PCAP文件中存放的下一个Packet数据包,也就 是说:PCAP文件里面并没有规定捕获的Packet数据包之间有什么间隔字符串,下一组数据在文件中的起始位置。我们需要靠第一个Packet包确定。
     */
    
    typedef struct  timestamp{
        u_int32 timestamp_s;
        u_int32 timestamp_ms;
    }timestamp;
    
    typedef struct pkt_header{
        timestamp ts;
        u_int32 capture_len;
        u_int32 len;
    
    }pkt_header;
    
    /**以太网帧头格式**/
    typedef struct Ethernet
    {
        u_int8  DstMAC[6];   //目的MAC地址
        u_int8  SrcMAC[6];   //源MAC地址
        u_int16 FrameType;    //帧类型
    } Ethernet;
    
    /**另一种帧头格式**/
    typedef struct Linux_cooked_capture
    {
        u_int16 package_type;
        u_int16 address_type;
        u_int16 address_length;
        u_int16 un_used[4];
        u_int16 FrameType; //帧类型
    }Linux_cooked_capture;
    
    
    typedef struct ip_header
    {    //IP数据报头
        u_int8   Ver_HLen;       //版本+报头长度
        u_int8   TOS;            //服务类型
        u_int16  TotalLen;       //总长度
        u_int16  ID;     //标识
        u_int16  Flag_Segment;   //标志+片偏移
        u_int8   TTL;            //生存周期
        u_int8   Protocol;       //协议类型
        u_int16  Checksum;       //头部校验和
        u_int32  SrcIP;  //源IP地址
        u_int32  DstIP;  //目的IP地址
    }ip_header ;
    
    
    typedef struct tcp_header
    {    //TCP数据报头
    u_int16  SrcPort;    //源端口
    u_int16  DstPort;    //目的端口
    u_int32  SeqNO;  //序号
    u_int32  AckNO;  //确认号
    u_int8   HeaderLen;  //数据报头的长度(4 bit) + 保留(4 bit)
    u_int8   Flags;  //标识TCP不同的控制消息
    u_int16  Window;     //窗口大小
    u_int16  Checksum;   //校验和
    u_int16  UrgentPointer;  //紧急指针
    }tcp_header ;
    
    #endif // DEFINEPCAP_H

    method.hpp

    #ifndef METHOD_H
    #define METHOD_H
    
    #include "pcap.h"
    #include <cstdio>
    #include <iostream>
    #include <vector>
    
    /**将文件中1个pcap_header大小的内容存放在内存中pcap_head的的地址中**/ void getPcapFileHead( FILE *fp , pcap_header &pcap_head ) { fread( &pcap_head , sizeof( pcap_header ) , 1 , fp); } Linux_cooked_capture /**将文件中1个pkt_header大小的内容存放在内存中pkt_head的的地址中**/ void getPktHead(FILE *fp , pkt_header &pkt_head) { fread( &pkt_head , sizeof( pkt_header ) , 1 , fp); }
    u_int32 getFramType(FILE
    *fp , u_int32 linktype) // linktype 决定了 frame_head 的大小 { // FrameType 决定了 该网络帧是 ipv6链接 或是 ipv4链接 if (linktype == 0x71)      //是另一种帧时,从帧头中读取FrameType获得该以太网帧是ipv4还是ipv6 { Linux_cooked_capture temp; fread(&temp ,sizeof(temp) ,1 , fp); return temp.FrameType; } if (linktype == 0x01)      //是以太网帧时,从帧头中读取FrameType获得该以太网帧是ipv4还是ipv6 { Ethernet temp; fread(&temp ,sizeof(temp) ,1 , fp); return temp.FrameType ; } } void getIpHead(FILE *fp , ip_header & ip_head_buf) { fread( &ip_head_buf , sizeof( ip_header ) , 1 , fp); } void getTcpHead(FILE *fp , tcp_header &tcp_head_buf) { fread( &tcp_head_buf , sizeof( tcp_header ) , 1 , fp); fseek(fp , ( tcp_head_buf.HeaderLen>>2 ) - sizeof(tcp_header) ,SEEK_CUR ); // fseek() 是因为 tcp_header 大小是变动的 ,且由 Headerlen>>2 可以计算出来 ,由于只需要关心前半部分数据 ,后半部分数据可以直接跳过 } void matchHttp(char tcp_data_buf[] , std::string & methodBuf ,std::string & urlBuf , std::string & hostBuf ,std::string &uaBuf) { std::vector<std::string> tempStrVector; std::string tempSring(tcp_data_buf); for(std::string::size_type beganPos=0 ; beganPos != tempSring.size() ; ) //将tcp_data_buf[] 内的字符串 { std::string::size_type endPos=beganPos; //按照 " \n " 分组放入tempStrVecor while(++endPos && endPos != tempSring.size()) { if( tempSring[endPos] =='\n' ) { break; } } tempStrVector.push_back( tempSring.substr(beganPos ,endPos - beganPos) ); if( endPos == tempSring.size() ) { break; } beganPos=endPos ; } for(std::vector<std::string>::iterator posVector =tempStrVector.begin() ; posVector !=tempStrVector.end() ; ++posVector ) { //遍历 tempStrVecor 的包含的字符串 ,获取 method url host ua 值 if ( std::string::size_type tempPos = (*posVector).find("GET") != (*posVector).npos ) { methodBuf="GET"; std::string::size_type endPos=(*posVector).find("HTTP/1.1"); urlBuf=(*posVector).substr(tempPos + sizeof("GET") - 1 , endPos - tempPos - sizeof("GET") ); } // “ GET ” 和 “ HTTP/1.1” 之间字符串为 url if ( std::string::size_type tempPos = (*posVector).find("POST") != (*posVector).npos ) { std::string::size_type endPos=(*posVector).find("HTTP/1.1"); methodBuf="POST"; urlBuf=(*posVector).substr(tempPos+sizeof("POST") -1 , endPos - tempPos - sizeof("POST") ); } // “ POST ” 和 “ HTTP/1.1” 之间的字符串为 url if ( std::string::size_type tempPos = (*posVector).find("Host:") != (*posVector).npos ) { hostBuf=(*posVector).substr(tempPos+sizeof("Host:" ) ); } //" Host:" 后的字符串为 host if ( std::string::size_type tempPos = (*posVector).find("User-Agent:") != (*posVector).npos ) { uaBuf=(*posVector).substr(tempPos+sizeof("User-Agent:") ); } // " User-Agent:" 后的字符串为 ua } } #endif // METHOD_H
  • 相关阅读:
    HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别
    手动部署 Ceph Mimic 三节点
    Ceph 分布式存储架构解析与工作原理
    OpenStack 的单元测试
    OpenStack 虚拟机启动流程 UML 分析(内含 UML 源码)
    我非要捅穿这 Neutron(四)Open vSwitch in Neutron
    手动部署 OpenStack Rocky 双节点
    OpenStack Blazar 架构解析与功能实践
    基于 Open vSwitch 的 OpenFlow 实践
    OpenFlow/SDN 的缘起与发展
  • 原文地址:https://www.cnblogs.com/2017Crown/p/7171822.html
Copyright © 2011-2022 走看看