今天我们来看看一个小例子,利用前面所学到的WinPcap编程知识来实现一个简单的还原HTTP协议的程序。相信大家对于HTTP协议一定不会陌生,我这里只简单地说一下它的报文格式,即HTTP报文有两种:请求报文和响应报文。为了让大家对于这两种报文有更直观的认识,给大家看两个简单的例子:

下面是一个典型的HTTP请求报文:

GET /somedir/page.html HTTP/1.1Host:  www.someschool.eduConnection: closeUser-agent: Mozilla/4.0Accept-language: fr

再看一个HTTP响应报文:

HTTP/1.1 200 OKConnection: closeDate: Thu, 03 Jul 2003 12:00:15 GMTServer: Apache/1.3.0 (Unix)Last-Modified: Sun, 6 May 2007 09:23:24 GMTContent-Length: 6821Content-Type: text/html

(data data data data data ...)

我们注意到HTTP请求报文中的第一行是以GET打头的,没错,它实际上是HTTP请求的一种方法,类似的还有POST、HEAD等等。一般熟知的大概就是GET和POST了,像Servlet编程中就有doGet和doPost两种提交HTTP请求的方法。而对于HTTP响应报文而言,第一行开头是协议的版本号,如HTTP/1.1,现在普及的也是HTTP/1.1。利用这些我们可以来判断TCP数据报文里是否保存的HTTP数据。

本程序的实现思路有很多种,我采用的是一种最笨拙的方式,即按照 判断是否是IP数据包->判断是否是TCP分组->判断是否是HTTP报文 的逻辑,最后将HTTP报文的内容打印出来。程序开始前我们需要先定义一些重要协议的包格式,因为WinPcap并没有为我们定义这些东西。

/** define struct of ethernet header , ip address , ip header and tcp header*//* ethernet header */typedef struct ether_header {    u_char ether_shost[ETHER_ADDR_LEN]; /* source ethernet address, 8 bytes */    u_char ether_dhost[ETHER_ADDR_LEN]; /* destination ethernet addresss, 8 bytes */    u_short ether_type;                 /* ethernet type, 16 bytes */}ether_header;

/* four bytes ip address */typedef struct ip_address {    u_char byte1;    u_char byte2;    u_char byte3;    u_char byte4;}ip_address;

/* ipv4 header */typedef struct ip_header {    u_char ver_ihl;         /* version and ip header length */    u_char tos;             /* type of service */    u_short tlen;           /* total length */    u_short identification; /* identification */    u_short flags_fo;       // flags and fragment offset    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 and padding */}ip_header;

/* tcp header */typedef struct tcp_header {    u_short th_sport;         /* source port */    u_short th_dport;         /* destination port */    u_int th_seq;             /* sequence number */    u_int th_ack;             /* acknowledgement number */    u_short th_len_resv_code; /* datagram length and reserved code */    u_short th_window;        /* window */    u_short th_sum;           /* checksum */    u_short th_urp;           /* urgent pointer */}tcp_header;

还有一些重要的识别协议的类型,我们需要自己在代码中进行定义。

#define ETHERTYPE_IP 0x0800 /* ip protocol */#define TCP_PROTOCAL 0x0600 /* tcp protocol */

接下来再看看刚才所说的程序的逻辑是如何实现的。

1. 判断是否是IP数据包。我们先回顾一下,在RFC 894中定义了以太网的封装格式,由目的地址(6字节)、源地址(6字节)、类型(2字节)、数据以及CRC(4字节)构成。我们只需要关注头部中类型这个字段,当它为0x0800时,表示数据保存的是IP数据报;当它为0x0806时,表示数据保存的是ARP请求/应答;当为0x8035时,数据保存的是RARP请求/应答。所以通过比较它的类型是否为0x0800,从而可以到达目的。

2. 判断是否是TCP分组。跟上面类似,可以通过判断IP首部中协议字段是否为0x0600即可。

3. 判断是否是HTTP报文。根据上面所讲解的HTTP报文格式,我们只需要判断开头是否为"GET"、"POST"、"HTTP/1.1"就可以做到了。具体程序是如何来判断的我们来看看代码吧!

/* capture packet */    while((res = pcap_next_ex(adhandle, &pheader, &pkt_data)) >= 0) {

        if(res == 0)            continue; /* read time out*/

        ether_header * eheader = (ether_header*)pkt_data; /* transform packet data to ethernet header */        if(eheader->ether_type == htons(ETHERTYPE_IP)) { /* ip packet only */            ip_header * ih = (ip_header*)(pkt_data+14); /* get ip header */

            if(ih->proto == htons(TCP_PROTOCAL)) { /* tcp packet only */                    int ip_len = ntohs(ih->tlen); /* get ip length, it contains header and body */

                    int find_http = false;                    char* ip_pkt_data = (char*)ih;                    int n = 0;                    char buffer[BUFFER_MAX_LENGTH];                    int bufsize = 0;

                    for(; n<ip_len; n++)                    {                        /* http get or post request */                        if(!find_http && ((n+3<ip_len && strncmp(ip_pkt_data+n,"GET",strlen("GET")) ==0 )                       || (n+4<ip_len && strncmp(ip_pkt_data+n,"POST",strlen("POST")) == 0)) )                                find_http = true;

                        /* 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;

                        /* if http is found */                        if(find_http)                        {                            buffer[bufsize] = ip_pkt_data[n]; /* copy http data to buffer */                            bufsize ++;                        }                    }                    /* print http content */                    if(find_http) {                        buffer[bufsize] = '\0';                        printf("%s\n", buffer);                        printf("\n**********************************************\n\n");                    }            }        }    }

看一下运行后的截图:

这里需要注意的是,程序本质上还没有完全还原HTTP协议的功能,对于HTTP请求数据和响应数据进行解析,真正的应该可以通过Content-Type分析数据格式,并按照相应的解析方式进行解码,还有对于中文字符的处理等等~~最后将整个程序的源码贴出来,有任何意见或建议的可以随意吐槽,虚心接受。ps: 注释写得不全请见谅~~

pheader.h头文件

#ifndef PHEADER_H_INCLUDED#define PHEADER_H_INCLUDED/***/#define ETHER_ADDR_LEN 6 /* ethernet address */#define ETHERTYPE_IP 0x0800 /* ip protocol */#define TCP_PROTOCAL 0x0600 /* tcp protocol */#define BUFFER_MAX_LENGTH 65536 /* buffer max length */#define true 1  /* define true */#define false 0 /* define false */

/** define struct of ethernet header , ip address , ip header and tcp header*//* ethernet header */typedef struct ether_header {    u_char ether_shost[ETHER_ADDR_LEN]; /* source ethernet address, 8 bytes */    u_char ether_dhost[ETHER_ADDR_LEN]; /* destination ethernet addresss, 8 bytes */    u_short ether_type;                 /* ethernet type, 16 bytes */}ether_header;

/* four bytes ip address */typedef struct ip_address {    u_char byte1;    u_char byte2;    u_char byte3;    u_char byte4;}ip_address;

/* ipv4 header */typedef struct ip_header {    u_char ver_ihl;         /* version and ip header length */    u_char tos;             /* type of service */    u_short tlen;           /* total length */    u_short identification; /* identification */    u_short flags_fo;       // flags and fragment offset    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 and padding */}ip_header;

/* tcp header */typedef struct tcp_header {    u_short th_sport;         /* source port */    u_short th_dport;         /* destination port */    u_int th_seq;             /* sequence number */    u_int th_ack;             /* acknowledgement number */    u_short th_len_resv_code; /* datagram length and reserved code */    u_short th_window;        /* window */    u_short th_sum;           /* checksum */    u_short th_urp;           /* urgent pointer */}tcp_header;

#endif // PHEADER_H_INCLUDED

main.c文件

#include <stdio.h>#include <stdlib.h>#define HAVE_REMOTE#include <pcap.h>#include "pheader.h"

/** function: a simple program to analyze http* author: blacksword* date: Wed March 21 2012*/int main(){    pcap_if_t* alldevs; // list of all devices    pcap_if_t* d; // device you chose

    pcap_t* adhandle;

    char errbuf[PCAP_ERRBUF_SIZE]; //error buffer    int i=0;    int inum;

    struct pcap_pkthdr *pheader; /* packet header */    const u_char * pkt_data; /* packet data */    int res;

    /* pcap_findalldevs_ex got something wrong */    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL /* auth is not needed*/, &alldevs, errbuf) == -1)    {        fprintf(stderr, "Error in pcap_findalldevs_ex: %s\n", errbuf);        exit(1);    }

    /* print the list of all devices */    for(d = alldevs; d != NULL; d = d->next)    {        printf("%d. %s", ++i, d->name); // print device name , which starts with "rpcap://"        if(d->description)            printf(" (%s)\n", d->description); // print device description        else            printf(" (No description available)\n");    }

    /* no interface found */    if (i == 0)    {        printf("\nNo interface found! Make sure Winpcap is installed.\n");        return -1;    }

    printf("Enter the interface number (1-%d):", i);    scanf("%d", &inum);

    if(inum < 1 || inum > i)    {        printf("\nInterface number out of range.\n");        pcap_freealldevs(alldevs);        return -1;    }

    for(d=alldevs, i=0; i < inum-1; d=d->next, i++); /* jump to the selected interface */

    /* open the selected interface*/    if((adhandle = pcap_open(d->name, /* the interface name */                 65536, /* length of packet that has to be retained */                 PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */                 1000, /* read time out */                 NULL, /* auth */                 errbuf /* error buffer */                 )) == NULL)                 {                     fprintf(stderr, "\nUnable to open the adapter. %s is not supported by Winpcap\n",                             d->description);                     return -1;                 }

    printf("\nListening on %s...\n", d->description);

    pcap_freealldevs(alldevs); // release device list

    /* capture packet */    while((res = pcap_next_ex(adhandle, &pheader, &pkt_data)) >= 0) {

        if(res == 0)            continue; /* read time out*/

        ether_header * eheader = (ether_header*)pkt_data; /* transform packet data to ethernet header */        if(eheader->ether_type == htons(ETHERTYPE_IP)) { /* ip packet only */            ip_header * ih = (ip_header*)(pkt_data+14); /* get ip header */

            if(ih->proto == htons(TCP_PROTOCAL)) { /* tcp packet only */                    int ip_len = ntohs(ih->tlen); /* get ip length, it contains header and body */

                    int find_http = false;                    char* ip_pkt_data = (char*)ih;                    int n = 0;                    char buffer[BUFFER_MAX_LENGTH];                    int bufsize = 0;

                    for(; n<ip_len; n++)                    {                        /* http get or post request */                        if(!find_http && ((n+3<ip_len && strncmp(ip_pkt_data+n,"GET",strlen("GET")) ==0 )                       || (n+4<ip_len && strncmp(ip_pkt_data+n,"POST",strlen("POST")) == 0)) )                                find_http = true;

                        /* http response */                        if(!find_http && n+8<ip_len && strncmp(ip_pkt_data+n,"HTTP/1.1",strlen("HTTP/1.1"))==0)                               find_http = true;

                        /* if http is found */                        if(find_http)                        {                            buffer[bufsize] = ip_pkt_data[n]; /* copy http data to buffer */                            bufsize ++;                        }                    }                    /* print http content */                    if(find_http) {                        buffer[bufsize] = '\0';                        printf("%s\n", buffer);                        printf("\n**********************************************\n\n");                    }            }        }    }

    return 0;}

作者:黑剑 
出处:http://www.cnblogs.com/blacksword/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

WinPcap编程之HTTP协议还原相关推荐

  1. [深入浅出WP8.1(Runtime)]Socket编程之UDP协议

    13.3 Socket编程之UDP协议 UDP协议和TCP协议都是Socket编程的协议,但是与TCP协议不同,UDP协议并不提供超时重传,出错重传等功能,也就是说其是不可靠的协议.UDP适用于一次只 ...

  2. 网络编程之TCP协议与UDP对比

    网络编程之TCP协议与UDP对比 UDP协议: 1,面向无连接. 2,不可靠协议,容易丢包. 3,速度快. 4,包体积有限制,64k以内. 通常,聊天,在线视频,凌波. TCP协议: 1.面向连接. ...

  3. java udp 同一个端口实现收发_Java网络编程之UDP协议

    伙伴们注意了! 小编在这里给大家送上关注福利: 搜索微信公众号"速学Java"关注即可领取小编精心准备的资料一份! 今天我们来聊聊 网络编程这部分的内容 网络编程 1)计算机网络 ...

  4. linux网络编程之IP协议首部格式与其配套使用的四个协议(ARP,RARP,ICMP,IGMP)和TCP、UDP协议头结构总结

    首先声明,这篇博客是几篇博客转载然后总结在一起的,只当是学习笔记,不在意是什么原创和转载了,学到东西就好. 1.IP协议首部格式(IP协议处余网络层) IP数据报首部图片格式: 最高位在左边,记为0 ...

  5. python udp创建addr_一篇文章搞定Python 网络编程之UDP协议

    基于UDP协议的socket PS:udp是无连接的,先启动那一端都不会报错 server端 import socket # 导入socket模块udp_sk = socket.socket(type ...

  6. Python网络协议编程之HTTP协议详解

    前言 这几年一直在it行业里摸爬滚打,一路走来,不少总结了一些python行业里的高频面试,看到大部分初入行的新鲜血液,还在为各样的面试题答案或收录有各种困难问题 于是乎,我自己开发了一款面试宝典,希 ...

  7. java网络编程之TCP通讯

    java中的网络编程之TCP协议的详细介绍,以及如何使用,同时我在下面举2例说明如何搭配IO流进行操作, 1 /* 2 *TCP 3 *建立连接,形成传输数据的通道: 4 *在连接中进行大数据量传输: ...

  8. 知无涯,行者之路莫言终 [- 编程之路2018 -]

    零.前言 2017年标签:"海的彼岸,有我未曾见证的风采" 2018年标签:"海的彼岸,吾在征途" 2019年标签:"向那些曾经无法跨越的鸿沟敬上-- ...

  9. 基于python的modbus协议编程_通往未来的网络可编程之路:Netconf协议与YANG Model

    近年来,随着全球云计算领域的不断发展与业务的不断增长,促使网络技术也不断发展,SDN技术应运而生,从最初的基于Openflow的转发与控制分离的核心思想,人们不断的去扩展SDN的外延,目前,人们可以达 ...

最新文章

  1. JAVA 线上故障排查完整套路,从 CPU、磁盘、内存、网络、GC 一条龙!
  2. access无法 dolby_如何解决windows 8无法开启杜比音效的问题
  3. MyBatis中动态sql实现传递多个参数并使用if进行参数的判断和实现like模糊搜索以及foreach实现in集合
  4. ZOJ1450 Minimal Circle 最小圆覆盖
  5. Hibernate中用到联合主键的使用方法,为何要序列化,为何要重写hashcode 和 equals 方法...
  6. 设计模式之禅读书笔记
  7. Python电话本系统(添加、修改、删除、查询)
  8. 手撕代码合集[短期更新]
  9. request模块发送json请求
  10. 在校大学生如何用编程赚钱?| 我的大学赚钱之路
  11. tensorflow出现如下错误:AttributeError: ‘module’ object has no attribute ‘merge_all_summaries’
  12. R-RCN 论文理解3
  13. VS2008试用版破解方法
  14. 1-编程基础及Python环境部署
  15. 关于QQ的相关代码收集整理
  16. 知物由学 | 舆情数据清洗“动”“静”分离方案
  17. r5处理器_联想拯救者r7000 r7与r5哪个更值得买?差距大吗?下面价格和配置对比评测看完就明白了...
  18. 使用K-Fold训练和预测XGBoost模型的方法
  19. 时间戳timestamp类型
  20. GardenPlanner 下载,园林绿化设计

热门文章

  1. Luogu P1535 【游荡的奶牛】
  2. from..import 语句
  3. sed学习与实践1:sed基本指令
  4. django传值给模板, 再用JS接收并进行操作
  5. ICC2使用report_placement检查floorplan
  6. Windows10 LTSC 2021 开机 wsappx进程 CPU占用高
  7. php对接linepay支付
  8. 如果不做测试了,自己还能干点啥?
  9. access阿里云 mysql_access数据库字段最大
  10. 三星堆的青铜机器人_三星堆出土世界同期最高、最完整的青铜立人像 他有两个未解之谜...