网络数据抓包vc源码(网络抓包)
本文目录一览:
请教:IEEE 802.11协议栈结构,如何用VC++编程,实现无线网络探测,抓取无线数据包?程序最好简单、有注释
p库
我们公司正用他做一个类似QQ的东西,带语音的。
我给你的的另一个附件是一个有网络电话源代码,开发环境是vc6.0
祝:毕业设计顺利!
另外,站长团上有产品团购,便宜有保证
怎样用C语言实现网络抓包?
第一法则:站在巨人肩膀上 不要重复造轮子。
对于这种复杂的过程,第一选择是使用现成的,节约时间,提升效率。
Wireshark(前称Ethereal)是一个网络封包分析软件。网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。
网络封包分析软件的功能可想像成 "电工技师使用电表来量测电流、电压、电阻" 的工作 - 只是将场景移植到网络上,并将电线替换成网络线。在过去,网络封包分析软件是非常昂贵,或是专门属于营利用的软件。Ethereal的出现改变了这一切。在GNUGPL通用许可证的保障范围底下,使用者可以以免费的代价取得软件与其源代码,并拥有针对其源代码修改及客制化的权利。Ethereal是目前全世界最广泛的网络封包分析软件之一。
第二法则:学习 提升。
如果是单纯的学习知识,可以直接尝试写一些具有部分功能的程序,过程会有点艰难,但非常有意义。学习网络编程,需要了解 开放系统互连参考模型的的七层每一层的意义以及现实当中实现的四层的网络协议。然后就可以知道抓包的包位于模型当中的传输层协议,包括UDP和TCP的协议。进一步要学习每种协议的格式,表头,数据包等等。一句话,冰冻三尺非一日之寒。
Windows下的抓包及简单的编程。
Windows2000在TCP/IP协议组件上做了很多改进,功能也有增强。比如在协议栈上的调整,增大了默认窗口大小,以及高延迟链接新算法。同时在安全性上,可应用IPSec加强安全性,比NT下有不少的改进。
Microsoft TCP/IP 组件包含“核心协议”、“服务”及两者之间的“接口”。传输驱动程序接口 (TDI) 与网络设备接口规范 (NDIS) 是公用的。 此外,还有许多用户模型应用程序的更高级接口。最常用的接口是 Windows Sockets、远程过程调用 (RPC) 和 NetBIOS。
Windows Sockets 是一个编程接口,它是在加州大学伯克利分校开发的套接字接口的基础上定义的。它包括了一组扩展件,以充分利用 Microsoft Windows 消息驱动的特点。规范的 1.1 版是在 1993 年 1 月发行的,2.2.0 版在 1996 年 5 月发行。Windows 2000 支持 Winsock 2.2 版。在Winsock2中,支持多个传输协议的原始套接字,重叠I/O模型、服务质量控制等。
这里介绍Windows Sockets的一些关于原始套接字(Raw Socket)的编程。同Winsock1相比,最明显的就是支持了Raw Socket套接字类型,通过原始套接字,我们可以更加自如地控制Windows下的多种协议,而且能够对网络底层的传输机制进行控制。
1、创建一个原始套接字,并设置IP头选项。
SOCKET sock;
sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
或者:
s = WSASoccket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);
这里,我们设置了SOCK_RAW标志,表示我们声明的是一个原始套接字类型。创建原始套接字后,IP头就会包含在接收的数据中,如果我们设定 IP_HDRINCL 选项,那么,就需要自己来构造IP头。注意,如果设置IP_HDRINCL 选项,那么必须具有 administrator权限,要不就必须修改注册表:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Afd\Parameter\
修改键:DisableRawSecurity(类型为DWORD),把值修改为 1。如果没有,就添加。
BOOL blnFlag=TRUE;
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)blnFlag, sizeof(blnFlag);
对于原始套接字在接收数据报的时候,要注意这么几点:
a、如果接收的数据报中协议类型和定义的原始套接字匹配,那么,接收的所有数据就拷贝到套接字中。
b、如果绑定了本地地址,那么只有接收数据IP头中对应的远端地址匹配,接收的数据就拷贝到套接字中。
c、如果定义的是外部地址,比如使用connect(),那么,只有接收数据IP头中对应的源地址匹配,接收的数据就拷贝到套接字中。
2、构造IP头和TCP头
这里,提供IP头和TCP头的结构:
// Standard TCP flags
#define URG 0x20
#define ACK 0x10
#define PSH 0x08
#define RST 0x04
#define SYN 0x02
#define FIN 0x01
typedef struct _iphdr //定义IP首部
{
unsigned char h_lenver; //4位首部长度+4位IP版本号
unsigned char tos; //8位服务类型TOS
unsigned short total_len; //16位总长度(字节)
unsigned short ident; //16位标识
unsigned short frag_and_flags; //3位标志位
unsigned char ttl; //8位生存时间 TTL
unsigned char proto; //8位协议 (TCP, UDP 或其他)
unsigned short checksum; //16位IP首部校验和
unsigned int sourceIP; //32位源IP地址
unsigned int destIP; //32位目的IP地址
}IP_HEADER;
typedef struct psd_hdr //定义TCP伪首部
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz;
char ptcl; //协议类型
unsigned short tcpl; //TCP长度
}PSD_HEADER;
typedef struct _tcphdr //定义TCP首部
{
USHORT th_sport; //16位源端口
USHORT th_dport; //16位目的端口
unsigned int th_seq; //32位序列号
unsigned int th_ack; //32位确认号
unsigned char th_lenres; //4位首部长度/6位保留字
unsigned char th_flag; //6位标志位
USHORT th_win; //16位窗口大小
USHORT th_sum; //16位校验和
USHORT th_urp; //16位紧急数据偏移量
}TCP_HEADER;
TCP伪首部并不是真正存在的,只是用于计算检验和。校验和函数:
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while (size 1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}
if (size)
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum 16) + (cksum 0xffff);
cksum += (cksum 16);
return (USHORT)(~cksum);
}
当需要自己填充IP头部和TCP头部的时候,就同时需要自己计算他们的检验和。
3、发送原始套接字数据报
填充这些头部稍微麻烦点,发送就相对简单多了。只需要使用sendto()就OK。
sendto(sock, (char*)tcpHeader, sizeof(tcpHeader), 0, (sockaddr*)addr_in,sizeof(addr_in));
下面是一个示例程序,可以作为SYN扫描的一部分。
#include stdio.h
#include winsock2.h
#include ws2tcpip.h
#define SOURCE_PORT 7234
#define MAX_RECEIVEBYTE 255
typedef struct ip_hdr //定义IP首部
{
unsigned char h_verlen; //4位首部长度,4位IP版本号
unsigned char tos; //8位服务类型TOS
unsigned short total_len; //16位总长度(字节)
unsigned short ident; //16位标识
unsigned short frag_and_flags; //3位标志位
unsigned char ttl; //8位生存时间 TTL
unsigned char proto; //8位协议 (TCP, UDP 或其他)
unsigned short checksum; //16位IP首部校验和
unsigned int sourceIP; //32位源IP地址
unsigned int destIP; //32位目的IP地址
}IPHEADER;
typedef struct tsd_hdr //定义TCP伪首部
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz;
char ptcl; //协议类型
unsigned short tcpl; //TCP长度
}PSDHEADER;
typedef struct tcp_hdr //定义TCP首部
{
USHORT th_sport; //16位源端口
USHORT th_dport; //16位目的端口
unsigned int th_seq; //32位序列号
unsigned int th_ack; //32位确认号
unsigned char th_lenres; //4位首部长度/6位保留字
unsigned char th_flag; //6位标志位
USHORT th_win; //16位窗口大小
USHORT th_sum; //16位校验和
USHORT th_urp; //16位紧急数据偏移量
}TCPHEADER;
//CheckSum:计算校验和的子函数
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while(size 1)
{
cksum+=*buffer++;
size -=sizeof(USHORT);
}
if(size )
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum 16) + (cksum 0xffff);
cksum += (cksum 16);
return (USHORT)(~cksum);
}
void useage()
{
printf("******************************************\n");
printf("TCPPing\n");
printf("\t Written by Refdom\n");
printf("\t Email: refdom@263.net\n");
printf("Useage: TCPPing.exe Target_ip Target_port \n");
printf("*******************************************\n");
}
int main(int argc, char* argv[])
{
WSADATA WSAData;
SOCKET sock;
SOCKADDR_IN addr_in;
IPHEADER ipHeader;
TCPHEADER tcpHeader;
PSDHEADER psdHeader;
char szSendBuf[60]={0};
BOOL flag;
int rect,nTimeOver;
useage();
if (argc!= 3)
{ return false; }
if (WSAStartup(MAKEWORD(2,2), WSAData)!=0)
{
printf("WSAStartup Error!\n");
return false;
}
if ((sock=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET)
{
printf("Socket Setup Error!\n");
return false;
}
flag=true;
if (setsockopt(sock,IPPROTO_IP, IP_HDRINCL,(char *)flag,sizeof(flag))==SOCKET_ERROR)
{
printf("setsockopt IP_HDRINCL error!\n");
return false;
}
nTimeOver=1000;
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)nTimeOver, sizeof(nTimeOver))==SOCKET_ERROR)
{
printf("setsockopt SO_SNDTIMEO error!\n");
return false;
}
addr_in.sin_family=AF_INET;
addr_in.sin_port=htons(atoi(argv[2]));
addr_in.sin_addr.S_un.S_addr=inet_addr(argv[1]);
//
//
//填充IP首部
ipHeader.h_verlen=(44 | sizeof(ipHeader)/sizeof(unsigned long));
// ipHeader.tos=0;
ipHeader.total_len=htons(sizeof(ipHeader)+sizeof(tcpHeader));
ipHeader.ident=1;
ipHeader.frag_and_flags=0;
ipHeader.ttl=128;
ipHeader.proto=IPPROTO_TCP;
ipHeader.checksum=0;
ipHeader.sourceIP=inet_addr("本地地址");
ipHeader.destIP=inet_addr(argv[1]);
//填充TCP首部
tcpHeader.th_dport=htons(atoi(argv[2]));
tcpHeader.th_sport=htons(SOURCE_PORT); //源端口号
tcpHeader.th_seq=htonl(0x12345678);
tcpHeader.th_ack=0;
tcpHeader.th_lenres=(sizeof(tcpHeader)/44|0);
tcpHeader.th_flag=2; //修改这里来实现不同的标志位探测,2是SYN,1是FIN,16是ACK探测 等等
tcpHeader.th_win=htons(512);
tcpHeader.th_urp=0;
tcpHeader.th_sum=0;
psdHeader.saddr=ipHeader.sourceIP;
psdHeader.daddr=ipHeader.destIP;
psdHeader.mbz=0;
psdHeader.ptcl=IPPROTO_TCP;
psdHeader.tcpl=htons(sizeof(tcpHeader));
//计算校验和
memcpy(szSendBuf, psdHeader, sizeof(psdHeader));
memcpy(szSendBuf+sizeof(psdHeader), tcpHeader, sizeof(tcpHeader));
tcpHeader.th_sum=checksum((USHORT *)szSendBuf,sizeof(psdHeader)+sizeof(tcpHeader));
memcpy(szSendBuf, ipHeader, sizeof(ipHeader));
memcpy(szSendBuf+sizeof(ipHeader), tcpHeader, sizeof(tcpHeader));
memset(szSendBuf+sizeof(ipHeader)+sizeof(tcpHeader), 0, 4);
ipHeader.checksum=checksum((USHORT *)szSendBuf, sizeof(ipHeader)+sizeof(tcpHeader));
memcpy(szSendBuf, ipHeader, sizeof(ipHeader));
rect=sendto(sock, szSendBuf, sizeof(ipHeader)+sizeof(tcpHeader),
0, (struct sockaddr*)addr_in, sizeof(addr_in));
if (rect==SOCKET_ERROR)
{
printf("send error!:%d\n",WSAGetLastError());
return false;
}
else
printf("send ok!\n");
closesocket(sock);
WSACleanup();
return 0;
}
4、接收数据
和发送原始套接字数据相比,接收就比较麻烦了。因为在WIN我们不能用recv()来接收raw socket上的数据,这是因为,所有的IP包都是先递交给系统核心,然后再传输到用户程序,当发送一个raws socket包的时候(比如syn),核心并不知道,也没有这个数据被发送或者连接建立的记录,因此,当远端主机回应的时候,系统核心就把这些包都全部丢掉,从而到不了应用程序上。所以,就不能简单地使用接收函数来接收这些数据报。
要达到接收数据的目的,就必须采用嗅探,接收所有通过的数据包,然后进行筛选,留下符合我们需要的。可以再定义一个原始套接字,用来完成接收数据的任务,需要设置SIO_RCVALL,表示接收所有的数据。
SOCKET sniffersock;
sniffsock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
DWORD lpvBuffer = 1;
DWORD lpcbBytesReturned = 0 ;
WSAIoctl(sniffersock, SIO_RCVALL, lpvBuffer, sizeof(lpvBuffer), NULL, 0, lpcbBytesReturned, NULL, NULL);
创建一个用于接收数据的原始套接字,我们可以用接收函数来接收数据包了。然后在使用一个过滤函数达到筛选的目的,接收我们需要的数据包。
如果在XP以上的操作系统,微软封杀了Raw Soccket,只能用wincpap之类的开发包了。
用VC编写网络嗅探工具
目前,已经有不少的Sniff工具软件,如Windows环境下,最富盛名的工具是Netxray和Sniffer pro,用它们在 Windows环境下抓包来分析,非常方便。在UNIX环境下如Sniffit,Snoop,Tcpdump,Dsniff 等都是比较常见的。这里介绍一个用C语言和网络数据包和分析开发工具libpcap及winpcap实现的简易网络Sniffer。
2网络嗅探器程序实现
在c环境下编程,源码如下:
/* June 2nd,2002
* Project for graduation qualification By Bby Team 19 */
#include
#include
//必须加路径,必须把头文件packet32.h包含进去
#include "..\..\Include\packet32.h"
#include "..\..\Include\ntddndis.h"
#define Max_Num_Adapter 10
// Prototypes原形
//发包
void PrintPackets(LPPACKET lpPacket);
//设备列表
char AdapterList[Max_Num_Adapter][1024];
// 主程序开始
int main()
{
//define a pointer to an ADAPTER structure设备指针
LPADAPTER lpAdapter = 0;
//define a pointer to a PACKET structure包指针
LPPACKET lpPacket;
int i;
DWORD dwErrorCode;
DWORD dwVersion;
DWORD dwWindowsMajorVersion;
//Unicode strings (WinNT)
WCHAR AdapterName[8192]; //网络适配器设备列表
WCHAR *temp,*temp1;
//ASCII strings (Win9x)
char AdapterNamea[8192]; //网络适配器设备列表
char *tempa,*temp1a;
int AdapterNum=0,Open;
ULONG AdapterLength;
char buffer[256000]; // 容纳来自驱动器的数据的缓冲区
struct bpf_stat stat;
// 获得本机网卡名
AdapterLength=4096;
printf("Packet.dll test application. Library version:%s\n", PacketGetVersion());
printf("Adapters installed:\n");
i=0;
下面这段代码是用来在不同版本下得到网络适配器名:
Win9x 和WinNT中的网卡名称是分别用ASCII和UNICODE实现的,所以首先要得到本地操作系统的版本号.:
dwVersion=GetVersion();
dwWindowsMajorVersion= (DWORD)(LOBYTE(LOWORD(dwVersion)));
这里首先用到的Packet.dll函数是PacketGetAdapterNames(PTSTR pStr,PULONG BufferSize,通常它是与驱动程序通信并被调用的第一个函数,它将返回的用户本地系统中安装的网络适配器的名字放在缓冲区pStr中;BufferSize是缓冲区的长度:
if (!(dwVersion = 0x80000000 dwWindowsMajorVersion = 4))
{ //是Windows NT
// 找不到设备列表
if(PacketGetAdapterNames(AdapterName,AdapterLength)==FALSE){
printf("Unable to retrieve the list of the adapters!\n");
return -1;
一个简易网络嗅探器的实现 来自: 书签论文网
}
// 找到设备列表
temp=AdapterName;
temp1=AdapterName;
while ((*temp!='\0')||(*(temp-1)!='\0'))
{
if (*temp=='\0')
{
memcpy(AdapterList,temp1,(temp-temp1)*2);
temp1=temp+1;
i++;
}
temp++;
}
// 显示适配器列表
AdapterNum=i;
for (i=0;i wprintf(L"\n%d- %s\n",i+1,AdapterList);
printf("\n");
}
else //否则就是windows 9x,获取适配器名的方法同WinNT下
{
if(PacketGetAdapterNames(AdapterNamea,AdapterLength)==FALSE){
printf("Unable to retrieve the list of the adapters!\n");
论文一个简易网络嗅探器的实现来自
return -1;
}
tempa=AdapterNamea;
temp1a=AdapterNamea;
while ((*tempa!='\0')||(*(tempa-1)!='\0'))
{
if (*tempa=='\0')
{
memcpy(AdapterList,temp1a,tempa-temp1a);
temp1a=tempa+1;
i++;
}
tempa++;
}
AdapterNum=i;
for (i=0;i printf("\n%d- %s\n",i+1,AdapterList);
printf("\n");
}
下面这段代码就是让用户选择监听的网络适配器号:
// 选择设备
do
{
printf("Select the number of the adapter to open : ");
scanf("%d",Open);
if (OpenAdapterNum)
printf("\nThe number must be smaller than %d",AdapterNum);
} while (OpenAdapterNum);
然后,将所选择的设备打开,这里可以设置为“混杂”模式打开,也可以是“直接”模式打开。代码如下:
// 打开设备
lpAdapter = PacketOpenAdapter(AdapterList[Open-1]);
// 当设备无法打开时,出示错误信息:
if (!lpAdapter || (lpAdapter-hFile == INVALID_HANDLE_VALUE))
{
dwErrorCode=GetLastError();
printf("Unable to open the adapter, Error Code : %lx\n",dwErrorCode);
return -1;
}
将网卡设置为“混杂”模式,代码如下:
这里用到函数PacketSetHwFilter(LPADAPTER AdapterObject,ULONG Filter),它在到来的包上设置了一个硬件过滤器,如操作成功,返回TRUE。AdapterObject是过滤器所在的网卡设备指针;过滤器的常量Filter定义在头文件ntddndis.h 中,包括有:
•NDIS-PACKET-TYPE-PROMISCUOUS:设置混杂模式,每个到来的包都会被网卡接受;
•NDIS-PACKET-TYPE-DIRECTED:只有直接到主机网卡的包才会被接受;
•NDIS-PACKET-TYPE-BROADCAST:只接受广播包;
•NDIS-PACKET-TYPE-MULTICAST:只接受到主机所在的组的多播包;
•NDIS-PACKET-TYPE-ALL-MULTICAS:接受每个多播的包。
// set the network adapter in promiscuous mode
// 如果混杂模式设置失败,提示错误:
if(PacketSetHwFilter(lpAdapter,NDIS_PACKET_TYPE_PROMISCUOUS)==FALSE){
一个简易网络嗅探器的实现 来自: 书签论文网
printf("Warning: unable to set promiscuous mode!\n");
}
然后在driver中置512K的缓冲:
这里用到函数PacketSetBuff(LPADAPTER AdapterObject,int dim),它被用于设置AdapterObject指向的网卡的驱动程序的缓冲区,成功则返回TRUE。Dim是新的缓冲区的大小,当它被设定时,旧缓冲区中的数据将被丢弃,其中存储的包也会失去。
需要注意的地方:驱动器缓冲区的大小设置是否恰当,将影响截包进程的性能,设置应能保证运行快且不会丢包。这里设置的是512000Byte。
// set a 512K buffer in the driver
// 当无法设置缓冲区时,提示错误:
if(PacketSetBuff(lpAdapter,512000)==FALSE){
printf("Unable to set the kernel buffer!\n");
return -1;
}
PacketSetReadTimeout(LPADAPTER AdapterObject,int timeout)函数的功能是,设置与AdapterObject指定网卡绑定的读操作超时的值,timeout以毫秒为单位,0表示没有超时,当没有包到时,read就不返回。
// set a 1 second read timeout
// 设置1秒的读取操作超时
if(PacketSetReadTimeout(lpAdapter,1000)==FALSE){
printf("Warning: unable to set the read tiemout!\n");
}
接下来,定位设备,代码如下:
这里用到函数PacketAllocatePacket(Void)将在内存中分配一个PACKET结构并返回一个指向它的指针,但这个结构的Buffer字段还没有设定,所以应再调用PacketInitPacket函数来对其进行初始化。
//allocate and initialize a packet structure that will be used to
//receive the packets.
// 当定位失败时,提示错误:
if((lpPacket = PacketAllocatePacket())==NULL){
printf("\nError: failed to allocate the LPPACKET structure.");
return (-1);
}
然后,就可以初始化设备,开始接受网络包了:
用函数PacketInitPacket(LPPACKET lpPacket,PVOID Buffer,UINT Length)来初始化PACKET结构。lpPacket是要被初始化的指针;Buffer为指向用户分配的包含包的数据的缓冲区的指针;Length为缓冲区长度。
需要注意的地方:PACKET结构关联的缓冲区存储由packet capture driver 截获的包,包的数量被缓冲区大小所限制,最大缓冲区的大小就是应用程序从驱动器中一次能读到的数据的多少。所以设置大的缓冲区可减少系统调用的次数,提高截获效率。这里设置的是256K。
PacketInitPacket(lpPacket,(char*)buffer,256000);
接下来,是截包主循环:
//main capture loop
这里又用到函数PacketReceivePacket(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync),它将接受(截获)一个包的集合。参数包括一个指向用来指定截包的网卡的ADAPTER结构指针、一个指向用来容纳包的PACKET结构、一个指出是同步还是异步方式操作的标记。当操作同步时,函数锁定程序;当操作异步时,函数不锁定程序,必须调用PacketWaitPacket过程来检查是否正确完成。一般采用同步模式。
// 直到有键盘键入:
while(!kbhit())
{
// capture the packets 捕获包
// 捕获包失败时,提示错误:
if(PacketReceivePacket(lpAdapter,lpPacket,TRUE)==FALSE){
printf("Error: PacketReceivePacket failed");
一个简易网络嗅探器的实现 来自: 书签论文网
return (-1);
}
// 打印包中的数据,调用自定义函数PrintPackets()
PrintPackets(lpPacket);
}
最后将得到的统计数据打印出来,代码如下:
这里用到函数PacketGetStats(LPADAPTER AdapterObject,struct bpf_star*s)可以得到两个驱动程序的内部变量的值:从调用PacketOpenAdapter开始,已经被指定网卡接收的包数目;以及已经被网卡接收但被内核丢弃的包数目。这两个值被驱动程序拷贝到应用提供的bpf_stat结构中。
//print the capture statistics
// 得到统计值
// 当无法从内核读取状态时,提示错误:
if(PacketGetStats(lpAdapter,stat)==FALSE){
printf("Warning: unable to get stats from the kernel!\n");
}
// 打印“XX包被截取;XX包被丢弃”:
else
printf("\n\n%d packets received.\n%d Packets lost",stat.bs_recv,stat.bs_drop);
这里用函数PacketFreePacket(LPPACKET lpPacket)来释放由lpPacket指向的结构:
// 释放空间
PacketFreePacket(lpPacket);
用函数PacketCloseAdapter(LPADAPTER lpAdapter)来释放ADAPTER结构lpAdapter,并关闭网卡指针:
// close the adapter and exit
// 关闭设备退出
PacketCloseAdapter(lpAdapter);
return (0);
} // 主程序结束
其中用来打印数据报的自定义的函数PrintPackets()的代码在这里就不详细说明了。
3结束语
通过对网络嗅探器的编写,目的使大家知道网络管理的重要性,时刻注意网络信息安全问题,做好信息的加密和解密工作。