http请求数据包的格式:头部(request line + header)+ 数据(data)
头部和数据包体通过一个空行来隔开,头部的格式主要包括请求行+请求头部。如下图
请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔如:
GET /index.html HTTP/1.1。
HTTP协议的请求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。这里介绍最常用的GET方法和POST方法。
GET方式:在URL里面就说明要请求的资源,URL里面包含参数,“?”后面就是参数,而“?”前面就是URL的结束。“?ip=192.168.156.11&active=on”这种就是GET方式的包,而服务器把客户端请求的内容在数据段里面发回给客户端。
POST方式:传输的数据不在URL里面出现,而是在数据段里面出现。但是请求头部多了Content-Type和Content-Length两个字段。
请求头部由(关键字:<空格>值)对组成,每行一对,关键字和值用英文冒号“:<空格>”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:
User-Agent:产生请求的浏览器类型。
Accept:客户端可识别的内容类型列表。
Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。
下面是GET包的一个例子:传输的数据在URL里
再看看POST包的例子:传输的数据在数据段里面
HTTP响应也由两个个部分组成,分别是:响应头(状态行+消息报头)+响应正文。
状态行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。
1xx:指示信息–表示请求已接收,继续处理。
2xx:成功–表示请求已被成功接收、理解、接受。
3xx:重定向–要完成请求必须进行更进一步的操作。
4xx:客户端错误–请求有语法错误或请求无法实现。
5xx:服务器端错误–服务器未能实现合法的请求。
下面是http响应包的例子
在前面的一篇文章中,简单了介绍了HTTP报文格式,详情参考 http://www.firefoxbug.net/?cat=47 。
这里大概介绍下基本的,常见的HTTP包头格式。
POST /report/getComment.jsp HTTP/1.1 Host: yeeg.com Connection: keep-alive Content-Length: 161 Origin: http://www.1g1g.com User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7 content-type: application/x-www-form-urlencoded Accept: */* Referer: http://www.1g1g.com/player/loader.swf?uid=0.8106261373031884 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 Cookie: JSESSIONID=C3F105F72E3602D6292D3E4561E8E400
上面是一个 POST包的包头 ,其中Content-Length字段里面的值就是POST包 数据段的长度 。可以用
wireshark抓取POST包,会发现,post包是把报文头和数据内容分开来发的,会被TCP分片,然后重组。
具体这里不详细讨论。
GET /enclosure/2010-09-10T02_51_05-07_00.mp3 HTTP/1.1 Host: 805665086.podomatic.com Connection: keep-alive User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7 Accept: */* Referer: http://www.1g1g.com/player/loader.swf?uid=0.8106261373031884 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
上面是一个GET包,GET包请求的资源都是在URL里面的,所以数据段也就没有了,可以通过抓包分析。
HTTP/1.1 200 OK Date: Tue, 10 Jul 2012 09:12:52 GMT Server: Apache/2.2.14 (Ubuntu) Last-Modified: Thu, 23 Dec 2010 19:29:26 GMT ETag: "960fcf-4a6459-49818e3486374" Accept-Ranges: bytes Content-Length: 487 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: audio/mpeg
上面是一个http响应包,Content-Length指明了数据段的大小。
下面是我今天用C写了解析HTTP报文头的程序。注意:下面代码只主要用libcap实现实现了部分功能,具体
是解析GET包头,POST包头,还有HTTP相应包头,可能存在一些不足,希望大家多多交流。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
/*
capture http packet by firefoxbug */ #include #include #include #include #include #include #include #include #include #include #include #define BUFFSIZE 1500 typedef struct port { unsigned int src_port ; unsigned int des_port ; } Port ; Port port ; int tcp = 0 ; FILE * logfile ; int size ; void process_packet ( u_char * args , const struct pcap_pkthdr * header , const u_char * buffer ) ; char * print_tcp_packet ( const char * Buffer ) ; void get_line ( char * data , char * buff , int length ) ; void print_http_req_packet ( char * data ) ; void print_http_ans_packet ( char * data ) ; int main ( int argc , char * argv [ ] ) { pcap_if_t * alldevsp , * device ; pcap_t * handle ; //Handle of the device that shall be sniffed char errbuf [ 100 ] , * devname , devs [ 100 ] [ 100 ] ; int count = 1 , n ; //First get the list of available devices printf ( "Finding available devices ... " ) ; if ( pcap_findalldevs ( & alldevsp , errbuf ) ) { printf ( "Error finding devices : %s" , errbuf ) ; exit ( 1 ) ; } printf ( "Done" ) ; //Print the available devices printf ( " \n Available Devices are : \n " ) ; for ( device = alldevsp ; device != NULL ; device = device -> next ) { printf ( "%d. %s - %s \n " , count , device -> name , device -> description ) ; if ( device -> name != NULL ) { strcpy ( devs [ count ] , device -> name ) ; } count ++; } //Ask user which device to sniff printf ( "Enter the number of the device you want to sniff : " ) ; scanf ( "%d" , & n ) ; devname = devs [ n ] ; //Open the device for sniffing printf ( "Opening device %s for sniffing ... " , devname ) ; handle = pcap_open_live ( devname , 65536 , 1 , 0 , errbuf ) ; if ( handle == NULL ) { fprintf ( stderr , "Couldn't open device %s : %s \n " , devname , errbuf ) ; exit ( 1 ) ; } printf ( "Done \n " ) ; logfile = fopen ( "log.txt" , "w" ) ; if ( logfile == NULL ) { printf ( "Unable to create file." ) ; } //Put the device in sniff loop pcap_loop ( handle , - 1 , process_packet , NULL ) ; return 0 ; } void process_packet ( u_char * args , const struct pcap_pkthdr * header , const u_char * buffer ) { size = header -> len ; // fprintf(logfile,"length of packet : %d \n",size); //Get the IP Header part of this packet , excluding the ethernet header struct iphdr * iph = ( struct iphdr * ) ( buffer + sizeof ( struct ethhdr ) ) ; switch ( iph -> protocol ) //Check the Protocol and do accordingly... { case 6 : //TCP Protocol ++ tcp ; // printf("TCP : %d \n", tcp); unsigned char * data = print_tcp_packet ( buffer ) ; if ( size <= 0 ) break ; if ( port. des_port == 80 ) { print_http_req_packet ( data ) ; } else if ( port. src_port == 80 ) { print_http_ans_packet ( data ) ; } break ; } } char * print_tcp_packet ( const char * Buffer ) { //IP header struct iphdr * iph = ( struct iphdr * ) ( Buffer + sizeof ( struct ethhdr ) ) ; unsigned int iphdrlen = iph -> ihl * 4 ; //TCP header struct tcphdr * tcph = ( struct tcphdr * ) ( Buffer + iphdrlen + sizeof ( struct ethhdr ) ) ; port. src_port = ntohs ( tcph -> source ) ; port. des_port = ntohs ( tcph -> dest ) ; // mac_header + ip_header + tcp_header int header_size = sizeof ( struct ethhdr ) + iphdrlen + tcph -> doff * 4 ; size = size - header_size ; // fprintf(logfile,"length of header : %d \n",header_size ); return ( char * ) ( Buffer + header_size ) ; } void print_http_req_packet ( char * data ) { if ( strncmp ( data , "GET" , 3 ) == 0 || strncmp ( data , "POST" , 4 ) == 0 ) { fprintf ( logfile , " \n /***********************length of data : %d**********************/ \n " , size ) ; fprintf ( logfile , "From %d To %d \n " , port. src_port , port. des_port ) ; int i = 0 ; for ( ; i < size ; ++ i ) { fprintf ( logfile , "%c" ,* ( data + i ) ) ; } fprintf ( logfile , "/***************************** end *******************************/ \n \n " ) ; } return ; } void print_http_ans_packet ( char * data ) { if ( strncmp ( data , "HTTP" , 4 ) == 0 ) { fprintf ( logfile , " \n /***********************length of data : %d**********************/ \n " , size ) ; fprintf ( logfile , "From %d To %d \n " , port. src_port , port. des_port ) ; char buff [ BUFFSIZE ] = { ' \0 ' } ; get_line ( data , buff , size ) ; fprintf ( logfile , "%s" , buff ) ; unsigned int off = strlen ( buff ) ; size = size - off ; while ( strcmp ( buff , " \r \n " ) != 0 ) { memset ( buff , ' \0 ' , sizeof ( buff ) ) ; get_line ( data + off , buff , size ) ; fprintf ( logfile , "%s" , buff ) ; off = off + strlen ( buff ) ; size = size - off ; } fprintf ( logfile , "/***************************** end *******************************/ \n \n " ) ; } } void get_line ( char * data , char * buff , int length ) { int i = 0 ; char ch ; for ( ; i < length ;++ i ) { ch = * ( data + i ) ; * ( buff + i ) = ch ; if ( ch == ' \n ' ) break ; } } |
# gcc parse_http.c -o parse_http -lpcap