现在的位置: 首页 > 综合 > 正文

(转)手把手教你捕获数据包(上)【数据包的游戏系列之二】

2013年05月24日 ⁄ 综合 ⁄ 共 3810字 ⁄ 字号 评论关闭

  

      经常看到论坛有人问起关于数据包的截获、分析等问题,幸好本人也对此略有所知,也写过很多的 sniffer ,所以就想写一系列的文章来详细深入的探讨关于数据包的知识。

我希望通过这一系列的文章,能使得关于数据包的知识得以普及,所以这系列的每一篇文章我都会有由浅入深的解释、详细的分析、以及编码步骤,另外附上带有详细注释的源码 ( 为了照顾大多数朋友,我提供的都是 MFC 的源码 )

不过由于也是初学者,疏漏之处还望不吝指正。

本文凝聚着笔者心血,如要转载,请指明原作者及出处,谢谢! ^_^

 

OK,. Let’s go !  Have fun ! q^_^p

 

第二篇    手把手教你捕获数据包

目录:

一. 捕获数据包的实现原理

二. 捕获数据包的编程实现:

1.   raw socket 的实现方法

2.   Winpcap 的实现方法

a.      枚举本机网卡的信息

b.      打开相应网卡并设置为混杂模式

c.       截获数据包并保存为文件

 

作者:

CSDN  VC/MFC 网络编程版主 PiggyXP 

 

一.捕获数据包的实现原理: --------------------------------------------------------------------

在通常情况下,网络通信的套接字程序只能响应与自己硬件地址相匹配的或是以广播形式发出的数据帧,对于其他形式的数据帧比如已到达网络接口但却不是发给此地址的数据帧,网络接口在验证投递地址并非自身地址之后将不引起响应,也就是说应用程序无法收取与自己无关的的数据包。

所以我们要想实现截获流经网络设备的所有数据包,就要采取一点特别的手段了:

将网卡设置为混杂模式

这样一来,该主机的网卡就可以捕获到所有流经其网卡的数据包和帧。

但是要注意一点,这种截获仅仅是数据包的一份拷贝,而不能对其进行截断,要想截断网络流量就要采用一些更底层的办法了,不在本文的讨论范围之内。

 

二. 捕获数据包的编程实现:

1.raw socket 的实现方法 --------------------------------------------------------------------

不同于我们常用的数据流套接字和数据报套接字,在创建了原始套接字后,需要用 WSAIoctl() 函数来设置一下,它的定义是这样的

int WSAIoctl(

  SOCKET s,

  DWORD dwIoControlCode,

  LPVOID lpvInBuffer,

  DWORD cbInBuffer,

  LPVOID lpvOutBuffer,

  DWORD cbOutBuffer,

  LPDWORD lpcbBytesReturned,

  LPWSAOVERLAPPED lpOverlapped,

  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

);

虽然咋一看参数比较多,但是其实我们最关心的只是其中的第二项而已,我们需要做的就是把第二项设置为 SIO_RCVALL ,讲了这么多其实要做的就是这么一行代码,很简单吧? ^_^

  当然我们还可以指定是否亲自处理 IP 头,但是这并不是必须的。

完整的代码类似与如下这样,加粗的代码是与平常不同的需要注意的地方:

( 为了让代码一目了然,我把错误处理去掉了,下同 )

 

#include “WinSock2.h”

#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)

 

SOCKET SnifferSocket

  WSADATA wsaData;

  iFlag=WSAStartup(MAKEWORD(2,2),&wsaData);           // 开启 winsock.dll

                                 

SnifferSocket=WSASocket(AF_INET,             // 创建 raw  socket

SOCK_RAW, IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);

 

  char FAR name[128];                                // 获取本机 IP 地址

gethostname(name, sizeof(name));

  struct hostent FAR * pHostent;

  pHostent = gethostbyname(name);

 

  SOCKADDR_IN sa;                           // 填充 SOCKADDR_IN 结构的内容

  sa.sin_family = AF_INET;

  sa.sin_port = htons(6000);           // 端口号可以随便改,当然与当然系统不能冲突

  memcpy(&(sa.sin_addr),pHostent->h_addr,pHostent->h_length);

 

bind(SnifferSocket,(LPSOCKADDR)&sa,sizeof(sa));            // 绑定

 

  // ioctl 来接收所有网络数据 , 关键步骤

  DWORD dwBufferLen[10] ;

  DWORD dwBufferInLen = 1 ;

  DWORD dwBytesReturned = 0 ;

  WSAIoctl(SnifferSocket, IO_RCVALL,&dwBufferInLen, izeof(dwBufferInLen),

        &dwBufferLen, sizeof(dwBufferLen),&dwBytesReturned , NULL , NULL );

 

至此,实际就可以开始对网络数据包进行嗅探了,而对于数据包的接收还是和普通的 socket 一样,通过 recv() 函数来完成,因为这里涉及到不同的 socket 模型,接收方法差别很大,所以在此就不提供接收的代码了。

 

2.winpcap 的实现方法: -----------------------------------------------------------------------

winpcap 驱动包,是我们玩转数据包不可或缺的好东东, winpcap 的主要功能在于独立于主机协议(如 TCP-IP) 而发送和接收原始数据报,主要为我们提供了四大功能:

功能:
    1>
捕获原始数据报,包括在共享网络上各主机发送 / 接收的以及相互之间交换的数据报;
    2>
在数据报发往应用程序之前,按照自定义的规则将某些特殊的数据报过滤掉;
    3>
在网络上发送原始的数据报;
    4>
收集网络通信过程中的统计信息

如果环境允许的话 ( 比如你做的不是木马程序 ) ,我还是推荐大家用 winpcap 来截获数据包,因为它的功能更强大,工作效率更高,唯一的缺点就是在运行用 winpcap 开发的程序以前,都要在主机上先安装 winpcap driver

而且一会我们就会发现它比 raw socket 功能强大的多,而且工作得更为底层,最明显的理由就是 raw socket 捕获的数据包是没有以太头的,此乃后话。

至于怎么来安装使用,请参考本系列的系列一《手把手教你玩转 ARP 包中的》,里面有详细的加载 winpcap 驱动的方法 ^_^

废话不多说了,让我们转入正题 , 具体用 winpcap 来截获数据包需要做如下的一些工作:

A . 枚举本机网卡的信息 ( 主要是获得网卡的名称 )

   其中要用到 pcap_findalldevs 函数,它是这样定义的

   /*************************************************

int pcap_findalldevs  (  pcap_if_t **    alldevsp, 

                               char *    errbuf

                             ) 

      功能:

              枚举系统所有网络设备的信息

     参数:   alldevsp   是一个 pcap_if_t 结构体的指针,如果函数 pcap_findalldevs 函数执行成功,将获得一个可用网卡的列表,而里面存储的就是第一个元素的指针。

              Errbuf     存储错误信息的字符串

     返回值: int    如果返回 0 则执行成功,错误返回 -1

    *************************************************/

   我们利用这个函数来获得网卡名字的完整代码如下:

 

       pcap_if_t* alldevs;

       pcap_if_t* d;

       char errbuf[PCAP_ERRBUF_SIZE];

       pcap_findalldevs(&alldevs,errbuf);       // 获得网络设备指针

       for(d=alldevs;d;d=d->next)               // 枚举网卡然后添加到 ComboBox

       {

d->name;                            // d->name 就是我们需要的网卡名字字符串,按照你 // 自己的需要保存到你的相应变量中去

       }

pcap_freealldevs(alldevs);               // 释放 alldev 资源

 

抱歉!评论已关闭.