手把手教你捕获数据包1

一.捕获数据包的实现原理: 
在通常情况下,网络通信的套接字程序只能响应与自己硬件地址相匹配的或是以广播形式发出的数据帧,对于其他形式的数据帧比如已到达网络接口但却不是发给此地址的数据帧,网络接口在验证投递地址并非自身地址之后将不引起响应,也就是说应用程序无法收取与自己无关的的数据包。 
所以我们要想实现截获流经网络设备的所有数据包,就要采取一点特别的手段了: 
将网卡设置为混杂模式。 
这样一来,该主机的网卡就可以捕获到所有流经其网卡的数据包和帧。 
但是要注意一点,这种截获仅仅是数据包的一份拷贝,而不能对其进行截断,要想截断网络流量就要采用一些更底层的办法了,不在本文的讨论范围之内。 

二. 捕获数据包的编程实现: 
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资源 
B. 打开相应网卡并设置为混杂模式: 
   在此之前肯定要有一段让用户选择网卡、并获得用户选择的网卡的名字的代码,既然上面已经可以获得所有网卡的名字了,这段代码就暂且略过了。 
   我们主要是要用到 pcap_open_live 函数,不过这个函数winpcap的开发小组已经建议用pcap_open 函数来代替,不过因为我的代码里面用的就是pcap_open_live,所以也不便于修改了,不过pcap_open_live使用起来也是没有任何问题的,下面是pcap_open_live的函数声明: 
/************************************************* 
pcap_t* pcap_open_live  (  char *    device,   
                             int    snaplen,   
  int    promisc,   
  int    to_ms,   
  char *    ebuf 
)   
     功能: 
           根据网卡名字打开网卡,并设置为混杂模式,然后返回其句柄 
     参数: 
           Device  : 就是前前面我们获得的网卡的名字; 
           Snaplen :  我们从每个数据包里取得数据的长度,比如设置为100,则每次我们只是获得每个数据包 100 个长度的数据,没有什么特殊需求的话就把它设置为65535最大值就可以了; 
           Promisc:这个参数就是设置是否把网卡设置为“混杂模式”,设置为 1 即可; 
           to_ms :   超时时间,毫秒,一般设置为 1000即可。 
     返回值: 
           pcap_t :  类似于一个网卡“句柄”之类的,不过当然不是,这个参数是后面截获数据要用到的。 
虽然看起来比较复杂,不过用起来还是非常简单的,其实 1 行就OK了: 
    pcap_t* adhandle; 
       char errbuf[PCAP_ERRBUF_SIZE]; 
// 打开网卡,并且设置为混杂模式 
// pCardName是前面传来的网卡名字参数 
adhandle = pcap_open_live(pCardName,65535,1,1000,errbuf); 

  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值