做过网管或协议分析的人一般都熟悉sniffer这个工具,它可以捕捉流经本地网卡的所有数据包。抓取网络数据包进行分析有很多用处,如分析网络是否有网络病毒等异常数据,通信协议的分析(数据链路层协议、IP、UDP、TCP、甚至各种应用层协议),敏感数据的捕捉等。下面我们就来看看在windows下如何实现数据包的捕获。
WINSOCK本身就提供了抓取流经网卡的所有数据包的函数,虽然只能在IP协议层上捕捉,但只要您的工作没有涉及到数据链路层的话,这也就足够用了。抓取数据包的编程方法基本和编写其它网络应用程序一样,只需多一个步骤,即将SOCKET设置为接收所有数据的模式,这是用WSAIoctl来实现的。
编程实现主要有以下几个步骤:
1. 初始化WINSOCK库;
2. 创建SOCKET句柄;
3. 绑定SOCKET句柄到一个本地地址;
4. 设置该SOCKET为接收所有数据的模式;
5. 接收数据包;
6. 关闭SOCKET句柄,清理WINSOCK库;
除第4个步骤外,其它的步骤都和编写其它网络应用程序一样。那我们就主要来看一下第4个步骤WSAIoctl。
WSAIoctl是定义在mstcpip.h里面的。系统SDK里面本身就有。函数原型如下:
int WSAIoctl(
SOCKET s,
DWORD dwIoControlCode,
LPVOID lpvInBuffer,
DWORD cbInBuffer,
LPVOID lpvOutBuffer,
DWORD cbOutBuffer,
LPDWORD lpcbBytesReturned,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
参数很多,但我们只需用到5个。s是步骤2创建的句柄;dwIoControlCode设为SIO_RCVALL;lpvInBuffer和cbInBuffer是输入参数,SIO_RCVALL的输入参数是u_long,给它一个1的值就可以了;lpcbBytesReturned是返回字节数,给一个DWORD变量的地址;其它的都没用,传0就可以了;
DWORD wt = 0 ;
WSAIoctl( h, SIO_RCVALL , & sioarg, sizeof (sioarg),NULL, 0 , & wt,NULL,NULL ) ;
下面我们就来看一下完整的代码:
#include < winsock2.h >
#include < windows.h >
#include < Mstcpip.h >
#pragma comment(lib,"Ws2_32.lib")
#include < iostream >
using namespace std;
// IP首部
typedef struct tIPPackHead
... {
enum PROTOCOL_TYPE...{
PROTOCOL_TCP = 6,
PROTOCOL_UDP = 17,
PROTOCOL_ICMP = 1,
PROTOCOL_IGMP = 2
};
inline unsigned HeadLen() const
...{
//首部长度单位为4bytes。因此乘4
return (ver_hlen & 0x0F) << 2;
}
inline unsigned PackLen() const
...{
return wPacketLen;
}