WinPcap还原HTTP

来源:互联网 发布:java程序员转行做什么 编辑:程序博客网 时间:2024/05/16 01:19

 

WinPcap简介

WinPcapWin32平台上开源库,用于网络分析和数据包捕获。操作系统提供的socket,隐藏了网络底层细节,如协议处理,数据包重组等,使用Socket编程类似于操作文件。但是,有些应用程序需要直接访问网络的原始数据,比如Http分析工具,网络监控工具等等,此时操作系统提供的Socket就无法满足我们的要求了。WinPcap可以直接捕获网络中原始数据,主要功能如下:

1.        捕获原始数据,既可以捕获本机数据包,还可以捕获传输给其它机器的数据包;

2.        根据用户自定义规则过滤包

3.        发送原始数据包到网络

4.        收集网络流量统计信息

WinPcap也有自己的局限。它对数据包的操作独立于其它应用程序的。它不能堵塞、过滤和操纵网络。它只能简单的“嗅探”网络中传输的数据包。因此,它不能用于开发类似防火墙的应用程序。

WinPcap安装

点击这里下载WinPcap。点击这里下载WinPcap Win32 SDKWinPcap安装十分简单,只需要点击下一步就可以。WinPcapSDK十分有用,里面主要有以下几个文件夹:

l  LibWinPcap的静态库,里面有32位和64位两个版本。

l  IncludeWinPcap的头文件

l  DocsWinPcap手册,结合例子描述十分详细。

l  Examples-remoteExamples-pcap:示例代码,使用VC 6.0的项目进行组织。

WinPcap还原HTTP

下面,我就是用WinPcap编写一个示例程序,演示WinPcap如何还原Http协议。WinPcap可以捕获到链路层的数据包,在本例子中对应的是以太网协议。对于一个装有Http数据的以太数据包,其结构如下图:

图中E_HEthernetHeader的缩写,E_DEthernet Data的缩写,HTTP_THTTP Text的缩写,其它的依次类推。从此图中,我们可以看出来,最右边(最里面)HTTP_T是由其他协议一层一层包裹起来的。由于http协议是可读文本形式,所以一旦获取以太数据包后,就可以直接分析包中的内容。但是以太包头文件没有长度信息,所以,无法直接分析,只有跳到上一层协议包,也就是ip包中,获取整个ip包的长度,然后在分析ip包数据,找到http协议文本。以太包头部长度为14dmac,smac,type,以下代码说明了如何获取ip包长度。

ether_header *eh = (ether_header*)pkt_data;

if(ntohs(eh->ether_type)==0x0800){ // ip packet only

    ip_header *ih = (ip_header*)(pkt_data+14);

    if(ntohs(ih->proto) == 0x0600){ // tcp packet only

        int ip_len = ntohs(ih->tlen);//ip_len = ip_body + ip_header

       

    }

}

在上面的代码中,尤其需要注意ntohs函数的使用,该函数是将一个short整数由网络字节序转化成主机字节序(net to host short)。如果不使用以上函数,无法保证ip包长度正确。 

获取的ip包的长度和ip包的首地址,现在可以分析包中内容。HTTP协议的请求信息(request)一般是以“GET ”或“POST ”开头,响应(response)一般是以“HTTP/1.1 ”开头,所以在ip包中直接搜索这些字段即可。

bool find_http = false;

string http_txt = "";

 

char* ip_pkt_data = (char*)ih;

for(int i=0;i<ip_len;++i){ 

//check the http request

    if(!find_http && (i+3<ip_len && strncmp(ip_pkt_data+i,"GET ",strlen("GET ")) ==0 )  || (i+4<ip_len && strncmp(ip_pkt_data+i,"POST ",strlen("POST ")) == 0) ){

        find_http = true;

    }

 

    //check the http response

    if(!find_http && i+8<ip_len && strncmp(ip_pkt_data+i,"HTTP/1.1 ",strlen("HTTP/1.1 "))==0){

        find_http = true;

    }

 

    //collect the http text

    if(find_http && is_readable(ip_pkt_data[i])){

        http_txt += ip_pkt_data[i];

    }

}

最后,将所有可读字段打印出来,就实现了简单的HTTP协议还原,当然此例子还有些不足,无法实现图片的还原,也没有处理HTTP请求或响应由多个TCP数据包组成的情况,经过进一步的学习后,将改进这些功能。下面是整个示例程序的完整代码,在VS 2005中需要添加WinPcapinclude文件夹,lib文件夹,定义宏“HAVE_REMOTE;WPCAP”和添加“wpcap.lib ws2_32.lib”连接输入。

 


#include "pcap.h"

#include <iostream>

#include <iomanip>

#include <string>

using namespace std;

 

/*Ethernet Heder*/

struct ether_header

{

    u_int8_t  ether_dhost[6];      /* destination eth addr */

    u_int8_t  ether_shost[6];      /* source ether addr    */

    u_int16_t ether_type;          /* packet type ID field */

};

 

/* 4 bytes IP address */

typedef struct ip_address{

    u_char byte1;

    u_char byte2;

    u_char byte3;

    u_char byte4;

};

 

/* IPv4 header */

typedef struct ip_header{

    u_char  ver_ihl;        // Version (4 bits) + Internet header length (4 bits)

    u_char  tos;            // Type of service

    u_short tlen;           // Total length

    u_short identification; // Identification

    u_short flags_fo;       // Flags (3 bits) + Fragment offset (13 bits)

    u_char  ttl;            // Time to live

    u_char  proto;          // Protocol

    u_short crc;            // Header checksum

    ip_address  saddr;      // Source address

    ip_address  daddr;      // Destination address

    u_int   op_pad;         // Option + Padding

};

 

/* UDP header*/

typedef struct udp_header{

    u_short sport;          // Source port

    u_short dport;          // Destination port

    u_short len;            // Datagram length

    u_short crc;            // Checksum

};

 

/*TCP Header*/

struct tcp_header

  {

    u_int16_t th_sport;         /* source port */

    u_int16_t th_dport;         /* destination port */

    u_int32_t th_seq;             /* sequence number */

    u_int32_t th_ack;             /* acknowledgement number */

    u_int16_t th_len_resv_code; //   Datagram   length and reserved code

    u_int16_t th_win;           /* window */

    u_int16_t th_sum;           /* checksum */

    u_int16_t th_urp;           /* urgent pointer */

};

 

/*

 * check whether a char is readable

 */

bool is_readable(char c){  

    return isalnum(c) || ispunct(c) || isspace(c) || isprint(c);

}

 

/*

 * This demo show how to use winpcap sdk to capture the http request/respone, then print the readable content.

 * Note: in Visual Studio 2005,it should set the "project->config->c/c++->language->default unsigned char" to yes(/J)

 *       to stop the assution.

 */

void main(int argc,char* argv[]){

   

    //retrieve the devices list

    pcap_if_t *all_devs;

    char err_buff[PCAP_ERRBUF_SIZE];

    if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&all_devs, err_buff)==-1){

        cerr<<"Error in pcap_findalldevs_ex "<<err_buff<<endl;

        return;

    }

 

    //get the device index,default is the first one

    int dev_idx = 0;

    if(argc == 2){

        dev_idx = atoi(argv[1]);

    }

    pcap_if_t *dev=all_devs;

    for(int i=0;i<dev_idx;++i,dev=dev->next);//jump to the device of the specified index

    cout<<"Listen on: "<<dev->name<<endl;

    cout<<"****************************************"<<endl;

    //get the netcard adapter

    pcap_t *adpt_hdl = pcap_open(dev->name,65536,PCAP_OPENFLAG_PROMISCUOUS,1000,NULL,err_buff);

    if(adpt_hdl==NULL){

        cerr<<"Unable to open adapter "<<dev->name<<endl;

        pcap_freealldevs(all_devs);

        return;

    }

    /* At this point, we don't need any more the device list. Free it */

    pcap_freealldevs(all_devs);

 

    //analyze each packet

    struct pcap_pkthdr *header;

    const u_char *pkt_data;

    int rst=0;

    while((rst=pcap_next_ex(adpt_hdl,&header,&pkt_data))>=0){

        if(rst==0){

            //time out and not packet captured

            continue;

        }

       

        ether_header *eh = (ether_header*)pkt_data;

        if(ntohs(eh->ether_type)==0x0800){ // ip packet only

            ip_header *ih = (ip_header*)(pkt_data+14);

 

            if(ntohs(ih->proto) == 0x0600){ // tcp packet only

                int ip_len = ntohs(ih->tlen);//ip_len = ip_body + ip_header

                bool find_http = false;

                string http_txt = "";

 

                char* ip_pkt_data = (char*)ih;

                for(int i=0;i<ip_len;++i){

                   

                    //check the http request

                    if(!find_http && (i+3<ip_len && strncmp(ip_pkt_data+i,"GET ",strlen("GET ")) ==0 )

                       || (i+4<ip_len && strncmp(ip_pkt_data+i,"POST ",strlen("POST ")) == 0) ){

                        find_http = true;

                    }

 

                    //check the http response

                    if(!find_http && i+8<ip_len && strncmp(ip_pkt_data+i,"HTTP/1.1 ",strlen("HTTP/1.1 "))==0){

                        find_http = true;

                    }

 

                    //collect the http text

                    if(find_http && is_readable(ip_pkt_data[i])){

                        http_txt += ip_pkt_data[i];

                    }

                }

 

                //print the http request or response

                if(http_txt != ""){

                    cout<<http_txt;

                    cout<<endl<<"***********************************************************"<<endl<<endl;

                }

            }

        }

    }

}

 

 

原创粉丝点击