王朝网络
分享
 
 
 

Sniffer 实现之 用 Raw Socket 实现 Sniffer(2)

王朝vc·作者佚名  2006-01-09
宽屏版  字体: |||超大  

○、序

这篇文章写于1年前,因为某些原因,没有把它完成。今天整理一下 shadowstar's home,偶然发现这篇未完的文章。虽是年前的东西,但现在仍没有过时,对想了解 Sniffer 的朋友应该有所帮助。爸爸说做事情要有始有终,今天是端午节,谨以此文给远隔千里的亲人送一份心意。

一、引言

上一次介绍了用 Raw Socket 实现 Sniffer 的方法,实现起来比较简单,但有个缺点就是只能截获 IP 层以上的包,数据包头不含帧信息。对一些特殊的要求就不能满足了,其中很重要的一条就是不能对 ARP 包进行处理。用 NDIS 驱动程序可以实现对整个以太网包的截获,但复杂的驱动程序让好多人望而却步。没关系,有现成的东西干嘛不好好利用呢?在微软的 DDK 里提供了一个 Packet 的例子,Packet.sys 可以对网卡进行任意的操作,Packete32.dll 提供给应用程序一个方便的接口,而与驱动程序通讯相关的复杂的内部操作由 DLL 完成,面向应用层的程序员不需要了解这些细节。可惜我按 Packet32.dll 的提供的接口一步一步的做下去,却总也得不到想要的结果,一抓包就死在那儿不动了。看来它是不想给我干活了:(还是不想自已写驱动程序……

幸运的是有一套 WinPcap 的东东,专门用来在 Win32 平台下抓包的,可以在 http://winpcap.polito.it 上下载到。而且接口基本上和微软的 Packet 是一样的。哈哈,这下好了,原来的代码还可以用,一试就灵!下面就介绍怎样利用 WinPcap 直接对网卡进行操作及对接收到的数据进行分析。

二、Windows 系统中的网络通信结构

1.Windows 系统中的网络通信结构

图1的上层应用程序包括 IE、Outlook 等各种基于网络的软件,网络驱动协议包括 TCP/IP、NETBEUI 等各种 Windows 支持的网络层、传输层协议,NDIS 是 Windows 操作系统网络功能驱动的关键部分,下面对 NDIS 进行介绍。

2.NDIS及其特点

NDIS(Network Driver Interface Specification) 是 Microsoft 和 3Com 公司联合制定的网络驱动规范,并提供了大量的操作函数。它为上层的协议驱动提供服务,屏蔽了下层各种网卡的差别。

NDIS 向上支持多种网络协议,比如 TCP/IP、NWLink IPX/SPX、NETBEUI 等,向下支持不同厂家生产的多种网卡。NDIS 还支持多种工作模式,支持多处理器,提供一个完备的 NDIS 库(Library)。 但库中所提供的各个函数都是工作在核心模式下的,用户不宜直接操作,这就需要寻找另外的接口。

三、WinPcap 简介

1. WinPcap结构图

2. WinPcap 包括三个部分

第一个模块NPF(Netgroup Packet Filter),是一个虚拟设备驱动程序文件。它的功能是过滤数据包,并把这些数据包原封不动地传给用户态模块,这个过程中包括了一些操作系统特有的代码。 第二个模块packet.dll为win32平台提供了一个公共的接口。不同版本的Windows系统都有自己的内核模块和用户层模块。Packet.dll用于解决这些不同。调用Packet.dll的程序可以运行在不同版本的Windows平台上,而无需重新编译。 第三个模块 Wpcap.dll是不依赖于操作系统的。它提供了更加高层、抽象的函数。 3. packet.dll和Wpcap.dll

packet.dll直接映射了内核的调用。 Wpcap.dll提供了更加友好、功能更加强大的函数调用。 4. WinPcap的优势

提供了一套标准的抓包接口,与libpcap兼容,可使得原来许多UNIX平台下的网络分析工具快速移植过来

便于开发各种网络分析工具 充分考虑了各种性能和效率的优化,包括对于NPF内核层次上的过滤器支持 支持内核态的统计模式 提供了发送数据包的能力 四、Packet.dll 的使用

WinPcap的主页:http://winpcap.polito.it/你可以到这里下载它的驱动、DLLs和开发包。这里只是对WinPcap实现Sniffer做一个简单的介绍,不做深入研究。你只需要把下载回来的驱动安装到你的计算机上,用你的程序调用Packet.dll就可以了。Packet.dll在安装的时候会被拷贝到你的系统目录下,也可以用WinRAR打开安装包,可以看到里面的文件,直接提取你想要的Packet.dll。

Packet.dll提供了一套完整的、功能强大的API,其接口形式与Microsoft DDK提供的Packet32.dll基本一致。开发过Windows应用程序的人,对调用DLL一定不会莫生,如果你还不知道怎么使用DLL请参考相关书籍,这里不多讲了。新建一个DLL工程命名为sniffer2,保存到硬盘。把开发包里的include、lib目录拷贝到工程目录中。如果你用的是Visual C++,可以直接使用lib里面的引入库。shadowstar用的是C++Builder,需要用C++Builder提供的implib工具为Packet.dll生成一个lib文件,命令行如下:

implib -a packet.lib packet.dll

五、简单实现

shadowstar用C++Builder写了一个简单的演示程序,这里只给出主要部分的代码,完整的代码可以到http://shadowstar.126.com/下载。

void __fastcall TMainForm::btnCtrlClick(TObject *Sender)

{

//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]; // string that contains a list of the network adapters

WCHAR *temp,*temp1;

//ascii strings (win95)

char AdapterNamea[8192]; // string that contains a list of the network adapters

char *tempa,*temp1a;

int AdapterNum=0,Open;

ULONG AdapterLength;

char buffer[256000]; // buffer to hold the data coming from the driver

struct bpf_stat stat;

// obtain the name of the adapters installed on this machine

AdapterLength=4096;

ShowMessage(AnsiString("Packet.dll test application. Library version: ") + PacketGetVersion());

ShowMessage("Adapters installed:");

i=0;

// the data returned by PacketGetAdapterNames is different in Win95 and in WinNT.

// We have to check the os on which we are running

dwVersion=GetVersion();

dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));

if (!(dwVersion >= 0x80000000 && dwWindowsMajorVersion >= 4))

{ // Windows NT

if(PacketGetAdapterNames((PTSTR)AdapterName,&AdapterLength) == FALSE)

{

ShowMessage("Unable to retrieve the list of the adapters!\n");

return;

}

temp=AdapterName;

temp1=AdapterName;

while ((*temp!='\0')||(*(temp-1)!='\0'))

{

if (*temp=='\0')

{

memcpy(AdapterList[i],temp1,(temp-temp1)*2);

temp1=temp+1;

i++;

}

temp++;

}

AdapterNum=i;

for (i=0;i<AdapterNum;i++)

ShowMessage(Format(L"\n%d- %s\n",ARRAYOFCONST((i+1, AdapterList[i]))));

}

else //windows 95

{

if(PacketGetAdapterNames((PTSTR)AdapterNamea,&AdapterLength) == FALSE)

{

ShowMessage("Unable to retrieve the list of the adapters!\n");

return;

}

tempa=AdapterNamea;

temp1a=AdapterNamea;

while ((*tempa!='\0')||(*(tempa-1)!='\0'))

{

if (*tempa=='\0')

{

memcpy(AdapterList[i],temp1a,tempa-temp1a);

temp1a=tempa+1;

i++;

}

tempa++;

}

AdapterNum=i;

for (i=0;i<AdapterNum;i++)

ShowMessage(Format("\n%d- %s\n", ARRAYOFCONST((i+1,AdapterList[i]))));

}

lpAdapter = PacketOpenAdapter(AdapterList[0]);

if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE))

{

dwErrorCode=GetLastError();

ShowMessage(Format("Unable to open the adapter, Error Code : %lx\n",

ARRAYOFCONST(((int)dwErrorCode))));

return;

}

// set the network adapter in promiscuous mode

if(PacketSetHwFilter(lpAdapter,NDIS_PACKET_TYPE_PROMISCUOUS)==FALSE)

{

ShowMessage("Warning: unable to set promiscuous mode!\n");

}

// set a 512K buffer in the driver

if(PacketSetBuff(lpAdapter,512000) == FALSE)

{

ShowMessage("Unable to set the kernel buffer!\n");

return;

}

// set a 1 second read timeout

if(PacketSetReadTimeout(lpAdapter,1000)==FALSE)

{

ShowMessage("Warning: unable to set the read tiemout!\n");

}

//allocate and initialize a packet structure that will be used to

//receive the packets.

if((lpPacket = PacketAllocatePacket()) == NULL)

{

ShowMessage("\nError: failed to allocate the LPPACKET structure.");

return;

}

PacketInitPacket(lpPacket,(char*)buffer,256000);

if (btnCtrl->Caption == "&Start")

{

bStop = false;

btnCtrl->Caption = "&Stop";

}

else

{

bStop = true;

btnCtrl->Caption = "&Start";

}

int nIndex = 0;

LPIP ip;

LPTCP tcp;

TListItem *Item;

struct bpf_hdr *hdr;

int off;

BYTE* buf;

//main capture loop

while(!bStop)

{

// capture the packets

if(PacketReceivePacket(lpAdapter,lpPacket,TRUE)==FALSE)

ShowMessage("Error: PacketReceivePacket failed");

off = 0;

buf = (BYTE*)lpPacket->Buffer;

while(off<lpPacket->ulBytesReceived & !bStop)

{

nIndex++;

hdr = (struct bpf_hdr *)(buf+off);

off+= hdr->bh_hdrlen;

ip = (IP*)(buf + off + ETHERNET_HEADER_LENGTH);

tcp = (TCP*)((BYTE*)ip + (ip->HdrLen & IP_HDRLEN_MASK));

off = Packet_WORDALIGN(off+hdr->bh_caplen);

Item = lsvPacket->Items->Add();

Item->Caption = nIndex;

Item->SubItems->Add(GetProtocolTxt(ip->Protocol));

Item->SubItems->Add(inet_ntoa(*(in_addr*)&ip->SrcAddr));

Item->SubItems->Add(inet_ntoa(*(in_addr*)&ip->DstAddr));

Item->SubItems->Add(tcp->SrcPort);

Item->SubItems->Add(tcp->DstPort);

Item->SubItems->Add(hdr->bh_datalen);

Application->ProcessMessages();

}

}

//print the capture statistics

if(PacketGetStats(lpAdapter,&stat)==FALSE)

ShowMessage("Warning: unable to get stats from the kernel!\n");

else

ShowMessage(Format("\n\n%d packets received.\n%d Packets lost",

ARRAYOFCONST(((int)stat.bs_recv,(int)stat.bs_drop))));

PacketFreePacket(lpPacket);

// close the adapter and exit

PacketCloseAdapter(lpAdapter);

return;

}

六、结束语

如果在一个繁忙的网络上进行截获,而且不设置任何过滤,那得到的数据包是非常多的,可能在一秒钟内得到上千的数据包。如果应用程序不进行必要的性能优化,那么将会大量的丢失数据包,下面就是我对性能的一个优化方案。

这个方案使用了多线程来处理数据包。在程序中建立一个公共的数据包缓冲池,这个缓冲池是一个LILO的队列。程序中使用三个线程进行操作:一个线程只进行捕获操作,它将从驱动程序获得的数据包添加到数据包队列的头部;另一个线程只进行过滤操作,它检查新到的队尾的数据包,检查其是否满足过滤条件,如果不满足则将其删除出队列;最后一个线程进行数据包处理操作,象根据接收的数据包发送新数据包这样的工作都由它来进行。上面三个线程中,考虑尽可能少丢失数据包的条件,应该是进行捕获操作的线程的优先级最高,当然具体问题具体分析,看应用的侧重点是什么了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有