winpcap 获取数据包源码——可直接编译可用

源码

winpcap 获取数据包 

         定义了TCP头部,IPv4地址,ip头结构体。此外还有一些和pcap相关的结构体和函数。

         结构体:pcap_if_t,pcap_t,bpf_program

         函数:pcap_findalldevs,pcap_freealldevs,pcap_open_live,pcap_datalink,pcap_compile,pcap_setfilter,pcap_loop,packet_handler

         pcap_findalldevs这个函数同pcap_findalldevs_ex,返回系统选中可用的接口。组建一个网络设备表,该表可被pcap_open_live()打开。(注意可能有些网络设备不能被pcap_open_live()通过调用pcap_findalldevs()打开,因为,例如进程不具有打开它们的特权;出现这种情况,这些设备将不会在列表中出现)alldevsp指向列表的第一项,列表的每一项都是一个pcap_if_t结构。

pcap_if_t*

pcap_if* next 如果不是为null,则指向列表的下一个元素;如果是空,则代表当前元素是最后一个元素。

char* name 指向字符串的指针 字符串用来向pcap_open_live()传递设备的名称

char* description 如果不为null,它指向的字符串是对设备的一个简单的描述。

pcap_addr* addresses 指向设备列表中第一个元素的地址;

u_int flags  PCAP_IF_接口标志,当前只有PCAP_IDF_LOOPBACK,考察接口是不是一个自环接口。

pcap_freealldevs(pcap_if_t* alldevsp)

释放由pcap_findalldevs()返回的接口列表。

pcap_t* pcap_open_live(char* device, intsnaplen, int promisc,int to_ms, char* ebuf)

Deprecated: usethe pcap_open() instead. 打开一个物理接口进行捕获。

pcap_open_live() 用来获得一个观察网络数据包的包捕获描述符。Device是一个标识要打开的设备的字符串,on Linux systems with 2.2 or later kernels, a device argument of "any"or NULL can be used to capture packets from all interfaces. Snaplen定义要捕获的最大字节数。promisc定义是否将接口设置为混杂模式 (promiscuousmode.)。(注意:即使这个参数为false,接口也有可能因为其他原因而处于混杂模式下) For now, this doesn't work on the "any" device; if anargument of "any" or NULL is supplied, the promisc flag is ignored.to_ms用毫秒定义读取超时。读取超时用以解决没有必要捕获数据包就开始读取,而是等待一段时间,有较多数据包到达,然后从操作系统内核一次读取多个数据包的问题。并不是所有平台支持读取超时。在不支持的平台上,这个参数被忽略。errbuf用来返回错误或者警告信息。当pcap_open_live()调用失败返回null时,errbuf被设定为错误信息。pcap_open_live() 调用成功时errbuf也可以被设置为警告信息。为得到这个信息,调用者应该在调用pcap_open_live()之前在errbuf中设定一个长度为0的字符串。调用后如果errbuf的长度不再为0,那么警告信息已经发生并可以显示出来。

int pcap_next_ex(pcap_t* p, structpcap_pkthdr** pkt_header, u_char** pkt_data)

Win32 Specific. Read a packet from aninterface or from an offline capture.

该函数用来检索下一个可用的数据包, bypassing the callback method traditionally provided bylibpcap.

pcap_next_ex 用指向下一个捕获的数据包的数据头和数据的指针填充pkt_header和pkt_data参数。(see pcap_handler())

返回值可以是:

l        1如果数据包被顺利读取

l        0如果pcap_open_live()规定的读取超时已经过去。在这种情况下pkt_header和pkt_data指向的不是一个有用的数据包

l        -1如果出现错误

l        -2如果在离线捕获中遇到EOF。

timeval结构如下:

struct timeval{long tv_sec;longtv_usec;}

其中tv_sec为秒数,tv_usec微秒数。

pcap_datalink

返回链路层的类型,链路层的类型包括:

DLT_NULL: BSD回路封装;链路层协议头是一个4字节的域,以主机字节顺序(host byte order),包含一个从socket.h来的PF_value。主机字节顺序(host byte order)是捕获数据包的机器的字节顺序,而PF_value是捕获数据包的机器的OS。如果一个读取一个文件,字节顺序和PF_value不一定是抓取文件的那些机器。

DLT_EN10MB: 以太网(10Mb, 100Mb,1000Mb, 或者更高)。

DLT_IEEE802: IEEE802.5令牌环网。

DLT_ARCNET:ARCNET。

DLT_SLIP:SLIP。

DLT_PPP:PPP;如果第一个字节是0xff或0x03,它是类HDLC帧上的PPP。

DLT_FDDI:FDDI

DLT_ATM_RFC1483:RFC1483LLC/SNAPATM;数据包以IEEE802.2 LLC头开始。

DLT_RAW:原始IP(raw IP);数据包以IP头开始。

DLT_PPP_SERIAL:按照RFC1662,基于类HDLC帧的PPP,或者按照RFC1547的4.3.1,基于HDLC帧的Cisco PPP;前者的第一个字节是0xFF,后者的第一个字节是0x0F或0x8F。

DLT_PPP_ETHER:按照RFC2516,PPPoE;数据包以PPPoE头开始。

DLT_C_HDLC:按照RFC1547的4.3.1,基于HDLC帧的Cisco PPP。

DLT_IEEE802_11:IEEE 802.11无线局域网。

DLT_FRELAY:帧中继(Frame Relay)。

DLT_LOOP:OpenBSD回路封装。

DLT_LINUX_SLL:Linux抓包封装。

DLT_LTALK:苹果的LocalTalk,数据包以AppleTalkLLAP头开始。

DLT_PFLOG:OpenBSD pflog。

DLT_PRISM_HEADER:后接802.11头的棱镜监视器模式(Prismmonitor mode)信息。

DLT_IP_OVER_FC:RFC2625IP-over-Fiber 频道,以RFC2625中定义的Network_Header开始。

DLT_SUNATM:SunATM设备。

DLT_IEEE802_11_RADIO:后接802.11头的链路层信息。

DLT_ARCNET_LINUX:没有异常帧的ARCNET。

DLT_LINUX_IRDA:Linux-IrDA数据包,DLT_LINUX_SLL头后接IrLAP头。

pcap_compile

intpcap_compile(pcap_t*                    p,

struct bpf_program*    fp,

char*                          str,

int                              optimize,

bpf_u_int32                netmask)

编译一个数据包过滤器,将一个能被核心态(kernel-level)过滤器引擎解释的程序中的高层过滤表达式(filtering expression)进行转化。pcap_compile()被用来将字符串str编译进过滤器程序(fp),程序(fp)是一个指向bpf_program结构体并被pcap_compile()赋值的指针。optimize控制是否对目标代码(resulting code)的性能进行优化。Netmask表明IPv4掩码,它仅在检查过滤器程序中的IPv4广播地址的时候被使用。如果网络掩码对于程序是不可知的或者数据包是在Linux的”任何(any)”伪接口上被捕获的,则赋值0;IPv4广播地址的测试将不被正确进行,但过滤器程序中的其他所有的测试都不会有问题。返回-1表示发生了错误,此时,pcap_geterr()将被用来显示错误信息。

pcap_setfilter

intpcap_setfilter(pcap_t*         p,

struct bpf_program* fp)

    把一个过滤器同一次抓包关联起来。pcap_setfilter被用来指定一个过滤器程序。fp是一个指向bpf_program结构体的指针,通常是pcap_compile()执行的结果。当失败时返回-1,此时,pcap_geterr()被用来显示错误信息;返回0表示成功。

pcap_loop

int pcap_loop(pcap_t * p,int cnt, pcap_handler callback, uchar *user);

捕获数据包,不会响应pcap_open_live()函数设置的超时时间

p 是由pcap_open_live()返回的所打开的网卡的指针;cnt用于设置所捕获数据包的个数;pcap_handler是与void packet_handler()使用的一个参数,即回调函数的名称;user值一般为NULL

pcap_loop原型是pcap_loop(pcap_t *p,int cnt,pcap_handler callback,u_char *user)

其中第一个参数是winpcap的句柄,第二个是指定捕获的数据包个数,如果为-1则无限循环捕获。第四个参数user是留给用户使用的。

第三个是回调函数。

packet_handler

可进行包处理,与pcap_loop等回调函数联合使用进行抓包  

u_char *param:数据包存储的文件指针   

struct pcap_pkthdr * header: 并非是数据包的指针,只是与数据包捕获驱动有关的一个header ,是堆文件包的结构体首部指针。可以得到时间值,数据包长度。  

const_char * pkt_data:指向数据包内容的指针,包括了协议头,可以经过计算获得IP数据包头部的位置, UDP首部的位置。 

源码如下

//捕获网络数据包的C++程序

//可以获得数据包长度、通过以太网类型确定上层协议、源以太网地址和目的以太网地址!

 

#include<tchar.h>

#include<stdio.h>

#include"pcap.h"

 

#pragmacomment(lib,"wpcap.lib")  

#pragmacomment(lib,"packet.lib")  

#pragmacomment(lib,"ws2_32.lib")  

 

 

typedefstructip_address{

    u_char byte1;

    u_char byte2;

    u_char byte3;

    u_char byte4;

}ip_address;

 

typedefstructip_header{

    u_char ver_ihl;        // 版本 (4 bits) + 首部长度 (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) (13bits)

    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)

}ip_header;

 

typedefstructtcp_header{

    u_short sport;         // 源端口(Source port)

    u_short dport;         // 目的端口(Destination port)

    u_int seqNum;            //tcp请求序列号

    u_int ackNum;            //ack应答序列号

    u_char hLen;   //tcp头长度

    u_char flags;  //标志字段

    u_short winSize; //窗口大小

    u_short checkSum; //校验和

}tcp_header;

 

 

voidpacket_handler(u_char *param, conststructpcap_pkthdr *header, constu_char*pkt_data);

int_tmain(intargc, _TCHAR* argv[])

{

    pcap_if_t *alldevs;

    pcap_if_t *d;

    int inum;

    int i=0;

    pcap_t *adhandle;

    char errbuf[PCAP_ERRBUF_SIZE];

    u_int netmask;

    char packet_filter[] = "tcp and ip";

    structbpf_program fcode;

 

    if (pcap_findalldevs(&alldevs, errbuf) == -1)

    {

        fprintf(stderr,"Error in pcap_findalldevs:%s\n", errbuf);

        exit(1);

    }

 

    for(d=alldevs; d; d=d->next)

    {

        printf("%d. %s", ++i, d->name);

        if (d->description)

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

        else

            printf(" (No description available)\n");

    }

    if(i==0)

    {

        printf("\nNo interfaces found! Make sure WinPcap is installed.\n");

        return -1;

    }

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

    scanf_s("%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++);

    if ( (adhandle= pcap_open_live(d->name,          //设备名

        65536,            //65535保证能捕获到不同数据链路层上的每个数据包的全部内容

        TRUE,    // 混杂模式

        1000,             //读取超时时间 

        errbuf            //错误缓冲池

        )) == NULL)

    {

        fprintf(stderr,"\nUnable to open theadapter. %s is not supported by WinPcap\n",d->name);

        pcap_freealldevs(alldevs);

        return -1;

    }

 

    if(pcap_datalink(adhandle) != DLT_EN10MB)

    {

        fprintf(stderr,"\nThis program works onlyon Ethernet networks.\n");

        pcap_freealldevs(alldevs);

        return -1;

    }

    if(d->addresses != NULL)

        netmask=((structsockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;

    else

        netmask=0xffffff;

 

    //编译过滤器

    if (pcap_compile(adhandle, &fcode,packet_filter, 1, netmask) <0 )

    {

        fprintf(stderr,"\nUnable to compile thepacket filter. Check the syntax.\n");

        pcap_freealldevs(alldevs);

        return -1;

    }

    //设置过滤器

    if (pcap_setfilter(adhandle, &fcode)<0)

    {

        fprintf(stderr,"\nError setting thefilter.\n");

        pcap_freealldevs(alldevs);

        return -1;

    }

 

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

    pcap_freealldevs(alldevs);

    pcap_loop(adhandle,0, packet_handler, NULL);

    return 0;

 

}

 

voidpacket_handler(u_char *param, conststructpcap_pkthdr *header, constu_char *pkt_data)

{

    structtm*ltime;

    char timestr[16];

    ip_header *ih;

    tcp_header *th;

    u_int ip_len;

    u_short sport,dport;

    time_t local_tv_sec;

 

    local_tv_sec= header->ts.tv_sec;

    ltime=localtime(&local_tv_sec);

    strftime(timestr, sizeof timestr, "%H:%M:%S", ltime);

 

    printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);

 

    ih= (ip_header*) (pkt_data+

        14);//以太网头部长度

 

    ip_len= (ih->ver_ihl & 0xf) * 4;

    th= (tcp_header*) ((u_char*)ih + ip_len);

 

    sport= ntohs( th->sport );

    dport= ntohs( th->dport );

 

    printf("%d.%d.%d.%d:%d -> %d.%d.%d.%d:%d\n",

        ih->saddr.byte1,

        ih->saddr.byte2,

        ih->saddr.byte3,

        ih->saddr.byte4,

        sport,

        ih->daddr.byte1,

        ih->daddr.byte2,

        ih->daddr.byte3,

        ih->daddr.byte4,

        dport);

}

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值