您的位置:首页 > 理论基础 > 计算机网络

WinPcap还原HTTP

2011-03-16 20:12 253 查看
WinPcap简介
WinPcap是Win32平台上开源库,用于网络分析和数据包捕获。操作系统提供的socket,隐藏了网络底层细节,如协议处理,数据包重组等,使用Socket编程类似于操作文件。但是,有些应用程序需要直接访问网络的原始数据,比如Http分析工具,网络监控工具等等,此时操作系统提供的Socket就无法满足我们的要求了。WinPcap可以直接捕获网络中原始数据,主要功能如下:
1.
捕获原始数据,既可以捕获本机数据包,还可以捕获传输给其它机器的数据包;
2.
根据用户自定义规则过滤包
3.
发送原始数据包到网络
4.
收集网络流量统计信息
WinPcap也有自己的局限。它对数据包的操作独立于其它应用程序的。它不能堵塞、过滤和操纵网络。它只能简单的“嗅探”网络中传输的数据包。因此,它不能用于开发类似防火墙的应用程序。
WinPcap安装
点击这里下载WinPcap。点击这里下载WinPcap Win32 SDK。WinPcap安装十分简单,只需要点击下一步就可以。WinPcap的SDK十分有用,里面主要有以下几个文件夹:
l Lib:WinPcap的静态库,里面有32位和64位两个版本。
l Include:WinPcap的头文件
l Docs:WinPcap手册,结合例子描述十分详细。
l Examples-remote和Examples-pcap:示例代码,使用VC 6.0的项目进行组织。
WinPcap还原HTTP
下面,我就是用WinPcap编写一个示例程序,演示WinPcap如何还原Http协议。WinPcap可以捕获到链路层的数据包,在本例子中对应的是以太网协议。对于一个装有Http数据的以太数据包,其结构如下图:



图中E_H是Ethernet
Header的缩写,E_D是Ethernet Data的缩写,HTTP_T是HTTP Text的缩写,其它的依次类推。从此图中,我们可以看出来,最右边(最里面)HTTP_T是由其他协议一层一层包裹起来的。由于http协议是可读文本形式,所以一旦获取以太数据包后,就可以直接分析包中的内容。但是以太包头文件没有长度信息,所以,无法直接分析,只有跳到上一层协议包,也就是ip包中,获取整个ip包的长度,然后在分析ip包数据,找到http协议文本。以太包头部长度为14(dmac,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中需要添加WinPcap的include文件夹,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;
}
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: