將抓到的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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM