当前位置:   article > 正文

[网络编程技术]Winpcap学习二_基于winpcap编程,实现一个手动发送tcp报文的小程序

基于winpcap编程,实现一个手动发送tcp报文的小程序

第五天

处理离线的存储文件( offline dump file

    Winpcap 提供了一些函数把网络通信保存到文件并且可以读取这些文件的内容。 dump 文件的格式跟 libpcap 是一样的。它以二进制形式保存了被捕获的数据包的数据并且其他网络工具(包括 WinDump Ethereal Snort )也以此为标准。

处理 dump 文件的程序结构跟前面的程序结构大致是一样的,只是这里出现了几个新的函数:(试验代码略)

 

函数 1

pcap_dumper_t* pcap_dump_open(pcap_t*    p,

                                                  const char* fname)

    打开一个保存数据包的文件。“-”的含义跟 stdout 是一样的。发生错误时返回 NULL p 是一个由 pcap_open_offline() pcap_open_live() 返回的 pcap 结构体; fname 指定了文件的名字。你也可以调用 pcap_dump_fopen() 把数据包保存到一个已存在的流 fp 中,在 Windows 中,这个流必须以二进制方式打开。如果返回 NULL pcap_geterr() 会显示错误信息。

 

函数 2

void pcap_dump(u_char*                          user,

const struct pcap_pkthdr* h,

const u_char*                   sp)

    把数据包保存到硬盘。 pcap_dump() 把数据包输出到 pcap_dump_open() 打开的文件中。注意,它的参数要跟 pcap_dispatch() pcap_loop() 的回调函数的参数一致。如果直接被调用,则 user 这个参数的类型应该是 pcap_dumper_t ,也就是 pcap_dump_open() 的返回值类型。

在读取 dump 文件的内容时,用 pcap_open_offline() 来打开 dump 文件,然后用 pcap_loop() 来顺序读取数据包。读数据包跟接收数据包的过程是一样的。

 

函数 3

int pcap_live_dump(pcap_t*     p,

                            char*         filename,

                            int             maxsize,

                            int             maxpacks)

    保存数据包到文件。 pcap_live_dump() 把网络通信从一个接口保存到一个文件中。这个函数将运行在核心态( kernel level ),因此它的效率比 pcap_dump() 的更高。它的参数是一个用 pcap_open_live() 获得的接口描述符,一个 dump 文件名的字符串,文件的最大尺寸( maxsize ,以字节为单位)和文件能容纳的数据包的最大个数( maxpcaks )。把 maxsize maxpacks 设置为 0 意味着没有限制。当达到 maxsize maxpacks 时,存储结束。注意,当两个限制( maxsize maxpacks )中的一个到达时,存储将停止,但是文件仍然是被打开的。为了正确的保存数据并且使文件处于一致状态,必须用 pcap_close() 关闭适配器。

      pcap_live_dump() pcap_ dump() 的区别是设置限制和性能。 pcap_live_dump() 使用 Winpcap NPF 驱动在核心态进行转储,最大限度的降低了上下文的交换次数和内存副本数。 pcap_live_dump() 在其他操作系统上是不可用的,它是 Winpcap 特定的函数,只能在 Win32 上使用。

 

函数 4

pcap_t* pcap_open_offline(const char*           fname,

char*               errbuf)

    为读数据包打开一个 tcpdump/libpcap 格式的文件。 Fname 指定了要打开的文件名,“-”的含义与 stdin 是一样的。你也可以调用 pcap_fopen_offlline() 从一个已经存在的流 fp 中读取数据。注意,在 Windows 中,这个流必须以二进制方式打开。 errbuf 保存 pcap_open_offline() 出错是返回的错误信息。


第六天

  尽管 Winpcap 清楚地指出了它的目的是数据包的截获,但是它还提供了一些对于原始网络( raw networking )的有用特性。用户可以找到一组完整的发包( send packets )函数。需要注意的是, libpcap 目前并没有提供任何的发包的方法。

 

pcap_sendpacket() 发送单个数据包

    下面的代码片断表现了最简单的发送一个数据包的过程。打开适配器后, pcap_sendpacket() 被用来发送一个手工的( hand

-crafted )数据包。

试验代码:

 

     

函数 1

int pcap_sendpacket(pcap_t*   p,

u_char*    buf,

int        size)

    发送一个原始数据包( raw packet )到网络上。 p 是用来发送数据包的那个接口, buf 包含着要发送的数据包的数据(包括各种各样的协议头), size buf 所指的缓冲区的尺寸,也就是要发送的数据包的大小。 MAC 循环冗余码校验不必被包含,因为它很容易被计算出来并被网络接口驱动添加。如果数据包被成功发送,返回 0 ;否则,返回- 1

 

发送队列( Send queues

      pcap_sendpacket() 提供了一个简单快捷的发送单个数据包的方法,发送队列( send queues )提供了一个高级的,强大的,优化的发送一组数据包的机制。发送队列是一个用来保存将要发送到网络上的的众多数据包的容器。它有一个大小,描述了它所能容纳的最大字节数。

    通过指定发送队列的大小, pcap_sendqueue_alloc() 函数创建一个发送队列。一旦发送队列被创建好, pcap_sendqueue_queue() 可以把一个数据包添加到发送队列里。函数 pcap_sendqueue_alloc() 的参数必须与 pcap_next_ex() pcap_handler() 的相同,因此,从一个文件捕获或读取数据包的时候,如何进行 pcap_sendqueue_alloc() 的参数传递是一个问题。

函数 1

pcap_send_queue* pcap_sendqueue_alloc(u_int memsize)

    为一个发送队列分配空间,即创建一个用来存储一组原始数据包( raw packet )的缓冲区,这些数据包将用 pcap_sendqueue_transmit() 提交到网络上。 memsize 是队列容纳的字节数,因此它决定了队列所能容纳的最大数据量。使用 pcap_sendqueue_queue() 可以在发送队列中插入数据包。

 

函数 2

int pcap_sendqueue_queue(pcap_send_queue*             queue

const struct pcap_pkthdr* pkt_header,

const u_char*              pkt_data)

    添加一个数据包到发送队列中。 queue 指向发送队列的尾部; pkt_header 指向一个 pcap_pkthdr 结构体,该结构体包含时间戳和数据包的长度; pkt_data 指向存放数据包数据部分的缓冲区。

    为了提交一个发送队列, Winpcap 提供了 pcap_sendqueue_transmit() 函数。

 

函数 3

u_int pcap_sendqueue_transmit(pcap_t*                  p,

pcap_send_queue*      queue,

int                    sync)

    该函数将队列里的内容提交到线路上。 p 是一个指向适配器的指针,数据包将在这个适配器上被发送; queue 指向 pcap_send_queue 结构体,它包含着要发送的所有数据包; sync 决定了发送操作是否被同步:如果它是非 0 non-zero ),发送数据包关系到时间戳,否则,他们将以最快的速度发送(即不考虑时间戳)。

    返回值是发送的字节数。如果它小于 size 参数,将发生一个错误。该错误可能是由于驱动 / 适配器( driver/adapter )问题或发送队列的不一致 / 伪造( inconsistent/bogus )引起。

注意:

l         使用该函数的效率比使用 pcap_sendpacket() 发送一系列数据包的效率高,因为数据包在核心态( kernel-level )被缓冲,所以降低了上下文的交换次数。因此,使用 pcap_sendqueue_transmit() 更好。

l         sync 被设置为 TRUE 时,随着一个高精度的时间戳,数据包将在内核伴被同步。这就要求 CPU 的数量是不可忽略的,通常允许以一个微秒级的精度发送数据包(这依赖于机器性能计数器的准确度)。然而,用 pcap_sendpacket() 发送数据包不能达到这样一个精确度。

    如果第三个参数非 0 ,发送将被同步( synchronized ),即相关的时间戳将被注意。这个操作要求注意 CPU 的数量,因为使用“繁忙 等待( busy wait )”循环,同步发生在内核驱动。

    当不再需要一个队列时,可以用 pcap_sendqueue_destroy() 来删除之,这将释放与该发送队列相关的所有缓冲区。


精彩评论

问: :关于winpacap,有这样的问题请教,如果我要发一个我自己写的包,要求IP头中的src ip和dst ip都写成我想要的ip,而且tcp或者udp的头里面的port我也可以任意的写。这样可以吗?:)

答:    可以的。不过就是你必须对传输层的数据段(segment)和网络层的数据分组(datagram)的结构非常熟悉,然后按照各个层上的数据包封装方式自己封装数据包。        文章中的示例代码只是简单的进行了一下数据联路层数据帧的封装,只规定了源mac地址和目的mac地址,而对数据帧的数据部分(包括网络层的包头和传输层的包头)进行了简单的填充。即:  /* Supposing to be on ethernet, set mac destinat to 1:1:1:1:1:1 */ packet[0] = 1; packet[1] = 1; packet[2] = 1; packet[3] = 1; packet[4] = 1; packet[5] = 1;  /* set mac source to 2:2:2:2:2:2 */ packet[6] = 2; packet[7] = 2; packet[8] = 2; packet[9] = 2; packet[10] = 2; packet[11] = 2;  /* Fill the rest of the packet */ for (i = 12; i < 100; ++ i) {  packet[i] = i % 256; }        如果你想自己封装数据包,上面for循环中的语句必须按照TCP/IP协议栈的传输层和网络层的封包方式进行封装,而不是进行简单地填充i % 256。

问: 1.我不明白发送单个数据包时 /* Check the validity of the command line*/ if (argc != 2) {  printf("/tusage: %s interface (e.g. 'rpcap://eth0')", argv[0]);  return; } 为什么argc != 2是什么原理呢?而发送队列  /* Check the validity of the command line */    if (argc <= 2 || argc >= 5)    {        usage();        return;    }是(argc <= 2 || argc >= 5)这又是原理?   2. /* Fill the rest of the packet */ for (i = 12; i < 100; ++ i) {  packet[i] = i % 256; } 这里为什么要填充信息包呢?这么做的含义是什么?而想自己封装数据包时又不同呢?

答: 1. argc是main函数的一个参数,表示的是在运行程序的时候参数的个数。argc!=2和argc<=2 || argc >=5表示是判断输入的参数个数是否在允许的范围内。 2. 因为你要把一个数据包发送到网络上,为了使这个数据包是一个有意义的数据包,所以要添加数据。这里为了做示范,所以添加的数据是i。而当你自己要发送数据包的时候,为了让数据包携带你自己想传输的信息,所以需要根据实际情况来添加数据包,而不能简单的添加i,否则,别人即使收到你发送的数据包,由于你封装数据包的时候没有按照协议进行,所以他(她)也无法解析。

问: argv[1]参数应该是包含要打开的源名称的以’/0’结尾的字符串! 按这样说法 应该填写 网卡NAME 但为什么行不通

答: argv[1]是要打开的网络适配器的名称,不过这个名称不是普通的字符串,它有自己的格式,例如我的机子上输入的是: rpcap:///Device/NPF_{3482E5BA-5798-4A83-8574-D3710D7B3E40} 你看下是不是你的格式不对。

问: 你这里有个错误..头文件里面缺少一个Win32-Extensions.h  要不让找不到 对pcap_send_queue类型的声明

答: 我的没有问题。你看下是不是你的Project -> Setting -> C++ -> Preprocessor definitions里没有添加WPCAP。添加进去就好了。

问: 我写的用winpcap发udp包的程序不成功 发送返回正确啊 可是另一端的udp断口(socket)收不到任何数据啊~~ 还有就是mac头里面目标的mac真么填啊~~我只知道对方的ip啊~~

答: winpcap发送数据包的好像是有点问题,我一个同学写了一个程序发送了也收不到。 mac头里面肯定是要填正确的mac地址的,不然还不到ip层,链路层就会把这个数据包丢弃了。

答:发包的话先看看自己的包是否封装得正确,如果是在VC或CB中,请注意结构数据边界对齐。如果找不到pcap_send_queue类型,请先 include Win32-Extensions.h

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/487171
推荐阅读
相关标签
  

闽ICP备14008679号