在98或NT下需要驱动程序,在2000下不需要,具体程序如下(你可以参考sniffer的工作原理):
无需驱动程序的Sniffer-IPMon
在Windows平台上实现的Sniffer一般都需要自己写驱动程序,例如在Win9x
下面是通过写一个驱动程序用Hook_Device_Service( )来挂接ndis.vxd提供
的服务,在WinNT/2K下面则一般是需要写一个中间驱动程序(请参考白云黄
鹤站的相关文章以及http://www.pcausa.com的论述)。
Arkady Frankel(arkadyf@hotmail.com) 在CodeGuru上给出了一个无需自
己编写驱动程序的Sniffer-IPMon。该程序利用了WinSock 2的特性,只能
运行在Win2K平台上。WinSock 2允许程序使用WSAIoctl( )给一个SOCK_RAW类
型的socket设置SIO_RCVALL属性,这样该socket就可以收到所有经过本机的
数据。 下面简单分析一下其实现方法。
首先打开一个socket,参数必须是 AF_INET、SOCK_RAW和IPPROTO_IP,否则
不能设置SIO_RCVALL属性:
m_s = socket( AF_INET , SOCK_RAW , IPPROTO_IP ) ;
if( INVALID_SOCKET == m_s )
{
dwErr = WSAGetLastError() ;
sprintf( szErr , "Error socket() = %ld " , dwErr ) ;
AfxMessageBox( szErr ) ;
return ;
}
然后可以设置一下该socket的超时参数等选项:
int rcvtimeo = 5000 ;
if( setsockopt( m_s , SOL_SOCKET , SO_RCVTIMEO ,
(const char *)&rcvtimeo , sizeof(rcvtimeo) ) == SOCKET_ERROR)
{
dwErr = WSAGetLastError() ;
sprintf( szErr , "Error WSAIoctl = %ld " , dwErr ) ;
AfxMessageBox( szErr ) ;
closesocket( m_s ) ;
return ;
}
再将该socket与本机的某个网络接口绑定(注意绑定的IP地址不能是INADDR_ANY):
sa.sin_family = AF_INET;
sa.sin_port = htons(7000);
sa.sin_addr.s_addr= m_iphostsource;
if (bind(m_s,(PSOCKADDR)&sa, sizeof(sa)) == SOCKET_ERROR)
{
dwErr = WSAGetLastError() ;
sprintf( szErr , "Error bind() = %ld " , dwErr ) ;
AfxMessageBox( szErr ) ;
closesocket( m_s ) ;
return ;
}
接下来就可以设置SIO_RCVALL属性。此后就可以利用这个socket来读取经
过本机的数据包了。IPMon创建了一个新线程专门来读取该socket,以防止
处理用户输入的主线程被阻塞。
if( SOCKET_ERROR != WSAIoctl( m_s, SIO_RCVALL , &dwBufferInLen,
sizeof(dwBufferInLen),&dwBufferLen, sizeof(dwBufferLen),
&dwBytesReturned , NULL , NULL ) )
AfxBeginThread( threadFunc , (LPVOID)this );
else
{
dwErr = WSAGetLastError() ;
sprintf( szErr , "Error WSAIoctl = %ld " , dwErr ) ;
AfxMessageBox( szErr ) ;
closesocket( m_s ) ;
return ;
}
新线程中读取socket的代码也非常简单,只需要反复调用recv( )即可:
memset( buf , 0 , sizeof(buf) ) ;
iRet = recv( pDlg->m_s , buf , sizeof( buf ) , 0 ) ;
if( iRet == SOCKET_ERROR )
{
dwErr = WSAGetLastError() ;
sprintf( szErr , "Error recv() = %ld " , dwErr ) ;
continue ;
}
else
if( *buf )
{
bufwork = buf ;
pIpHeader = (IPHEADER *)bufwork ;
WORD iLen = ntohs(pIpHeader->total_len) ;
......
}
此时pIpHeader就指向buf中的RAW IP数据包,如何处理就看自己的需要了。
总之这个实现是非常方便的,不用自己编写复杂的抓包代码,所有重要
的工作都由WinSock自己封装了。WSAIoctl( )还有其它一些比较有用的
参数,如SIO_RCVALL_MCAST参数使socket可以接收所有的多播数据,
SIO_RCVALL_IGMPMCAST参数则可以接收IGMP多播数据。实际测试的时候
发现IPMon经常无法看到本机发出去的数据包(偶尔能收到),不知是什
么原因。
参考资料:
1、http://www.codeguru.com/network/ipmon.html
2、MSDN