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
  • 相关阅读:
    Python入门11 —— 基本数据类型的操作
    Win10安装7 —— 系统的优化
    Win10安装6 —— 系统的激活
    Win10安装5 —— 系统安装步骤
    Win10安装4 —— 通过BIOS进入PE
    Win10安装2 —— 版本的选择与下载
    Win10安装1 —— 引言与目录
    Win10安装3 —— U盘启动工具安装
    虚拟机 —— VMware Workstation15安装教程
    Python入门10 —— for循环
  • 原文地址:https://www.cnblogs.com/2017Crown/p/7171822.html
Copyright © 2011-2022 走看看