RAW Socket 获取流经本地网卡的所有数据包

NDIS nui111 2760浏览 0评论
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog,发送下载链接帮助你免费下载!
本博客日IP超过1800,PV 2600 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog,之前的微信号好友位已满,备注:返现
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

获取本地数据包的方式有很多种,应用层方面可以通过RAW Socket,LSP等,早期的电脑通过IPHelp也是可以的,驱动层的可以通过wireshark,TDI,NDIS等方式。由于自己的目的并不需要拦截数据包,只需要抓取数据包信息,所以选择了RAW Socket。相比TDI及NDIS驱动,这种方式更稳定并且操作系统的兼容性比较好。

下面通过代码介绍一下怎样使用RAW Socket获取流经本机的网卡数据包:

 初始化socket,并设置网卡为混杂模式(SIO_RCVALL),绑定到一块网卡上,然后开启线程获取数据包,本例介绍捕获的TCP的数据包。

int init()
{
	try
	{
		DWORD dwErr ;
		SOCKADDR_IN sa;
		char szHostName[128]={0};
		HOSTENT *pHost= NULL;
		char* pszIp = NULL;
		int iNum = 0;
		int rcvtimeo = 5000;
		DWORD dwBufferLen[10] ;
		DWORD dwBufferInLen = 1 ;
		DWORD dwBytesReturned = 0 ;
		char localIp[IPADDRESSLENGTH]={0};

		if(AfxSocketInit(NULL) == FALSE)
		{
			WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "Sorry, socket load error!");
			return FALSE;
		}

		m_s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
		if(INVALID_SOCKET == m_s)
		{
			dwErr = WSAGetLastError();
			WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "Error socket() = ", dwErr);
			closesocket(m_s);
			return FALSE;
		}

		if(setsockopt(m_s, SOL_SOCKET,SO_RCVTIMEO, (const char*)&rcvtimeo, sizeof(rcvtimeo)) == SOCKET_ERROR)
		{
			dwErr = WSAGetLastError();
			WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "Error socket() = ", dwErr);
			closesocket(m_s);
			return FALSE;
		}

		if(gethostname(szHostName, 128)==0)
		{
			pHost = gethostbyname(szHostName);
			if(pHost != NULL)
			{
				pszIp = inet_ntoa(*(in_addr*)pHost->h_addr_list[iNum]);
				WriteDebugInfomation(LEVEL_DEBUG, MODULE_SNIFFER, LOGLOCATION, "pszIp=", pszIp);
			}
			else
				WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "pHost = NULL!");
		}
		else
			WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "can't find host name!");

			sa.sin_family = AF_INET;
			sa.sin_port = htons(7000);
			sa.sin_addr.s_addr = inet_addr(pszIp);

			if(bind(m_s, (PSOCKADDR)&sa, sizeof(sa)) == SOCKET_ERROR)
			{
				dwErr = WSAGetLastError();
				WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "Error socket() = ", dwErr);
				closesocket(m_s);
				return FALSE;
			}

			if( SOCKET_ERROR != WSAIoctl( m_s, 
				SIO_RCVALL , 
				&dwBufferInLen, 
				sizeof(dwBufferInLen),             
				&dwBufferLen,
				sizeof(dwBufferLen),
				&dwBytesReturned,
				NULL , 
				NULL ) )
			{
				AfxBeginThread( threadFunc, (LPVOID)this );
			}
			else
			{
				dwErr = WSAGetLastError();
				WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "Error socket() = ", dwErr);
				closesocket(m_s);
				return FALSE;
			}
		
	}
	catch (...)
	{
#ifdef _DEBUG
		MessageBox(NULL, L"init except", L"err", 0);
#endif
		WriteExceptionDebugInfomation(LEVEL_DEBUG, MODULE_ALL, LOGLOCATION, "init except");
	}
	return TRUE;
}
UINT threadFunc( LPVOID p )
{
	try
	{
		Csniffer *psniffer = static_cast<Csniffer *>(p) ;
		int iRet = 0;
		char  buf [65535];

		while( true )
		{
			Sleep(100);
			if(psniffer->m_FClose)
			{
				psniffer->CloseSocket();
				break;
			}
          
			memset( buf , 0 , sizeof(buf) );
			iRet = recv( psniffer->m_s , buf , sizeof( buf ), 0 ) ;
			if( iRet == SOCKET_ERROR )
			{
				WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "Error recv() = ", WSAGetLastError());
				psniffer->CloseSocket();
				WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "m_FClose  closesocket");
				break;
			}
			else
			{
				if( *buf )
				{
					ASSERT(buf);
					psniffer->HandleRecvData(buf, iRet);
				}
				else
				{
					WriteDebugInfomation(LEVEL_RELEASE, MODULE_SNIFFER, LOGLOCATION, "No data on network");
					Sleep(100);
					continue ;
				}
			}   
		}
		return TRUE ;
	}
	catch (...)
	{
#ifdef _DEBUG
		MessageBox(NULL, L"threadFunc except", L"err", 0);
#endif
		WriteExceptionDebugInfomation(LEVEL_DEBUG, MODULE_ALL, LOGLOCATION, "threadFunc except");
	}
	return FALSE;
}
void Csniffer::HandleRecvData(char *recvdata, int recvlen)
{
	try
	{
		ASSERT(recvdata);
		char *bufwork ;
		int iRet = recvlen ;
		IPHEADER *pIpHeader ;
		BYTE *pdata = NULL;
		in_addr ina ;
		char szSource[IPADDRESSLENGTH], szDest[IPADDRESSLENGTH] ;
		wchar_t DesIp[IPADDRESSLENGTH] = {L"0"};
		int HdrLen, totallen;
		WORD sourport, destport;
		struct TCPPacketHead *pTCPHead;

		bufwork   = recvdata ;

		pIpHeader = (IPHEADER *)bufwork ;
		WORD iLen = ntohs(pIpHeader->total_len) ;
		if((iLen < 20) ||(iLen > 4096))
		{
			return;
		}

		if(iLen<=iRet)
		{
			ina.S_un.S_addr = pIpHeader->sourceIP ;
			strcpy_s( szSource , inet_ntoa( ina )) ;
			ina.S_un.S_addr = pIpHeader->destIP ;
			strcpy_s( szDest,  inet_ntoa( ina )) ;
			MultiByteToWideChar(CP_ACP, 0, szDest, -1, DesIp, IPADDRESSLENGTH);

			HdrLen = pIpHeader->header_len&0xf;
			HdrLen *= 4;
			totallen = ntohs(pIpHeader->total_len);
			totallen-=HdrLen;
			if(pIpHeader->proto == IPPROTO_TCP)
			{
				pTCPHead=(struct TCPPacketHead *)(recvdata+HdrLen);
				sourport = ntohs(pTCPHead->SourPort);
				destport = ntohs(pTCPHead->DestPort);
				HdrLen = (pTCPHead->HLen)>>4;  //in fact only 4 bits
				HdrLen *= 4;
				pdata=((BYTE *)pTCPHead)+HdrLen;
				totallen -= HdrLen;
				int lentcp =  (ntohs(pIpHeader->total_len)-(sizeof(struct _IPHEADER)+sizeof(struct TCPPacketHead)));
				if(lentcp <= 0)
				{
					Sleep(100);
					return;
				}

				char *datastr = new char[lentcp+1];
				memset(datastr,0,lentcp+1);
				for (int i=0;i<lentcp;i++){
					datastr[i] = (char)toupper(*(pdata+i));
				}
				datastr[lentcp]='\0';
				string data = datastr;
                delete[] datastr;
                datastr = NULL;     
			}
			if(iLen<iRet)
			{
				WriteDebugInfomation(LEVEL_DEBUG, MODULE_SNIFFER, LOGLOCATION, "iLen<iRet");
				iRet -= iLen ;
				bufwork  += iLen ;
				pIpHeader = (IPHEADER *)bufwork ;
			}
		}
		else
		{ 
			WriteDebugInfomation(LEVEL_DEBUG, MODULE_SNIFFER, LOGLOCATION, "iLen>iRet");
			HandleRecvLastData(bufwork, iLen, (WORD)iRet);
		}
	}
	catch (...)
	{
#ifdef _DEBUG
		MessageBox(NULL, L"HandleRecvData except", L"err", 0);
#endif
		WriteExceptionDebugInfomation(LEVEL_DEBUG, MODULE_ALL, LOGLOCATION, "HandleRecvData except");
	}
}

 

对于一般的抓包程序,到此就可以结束了。 实际在使用的过程中还是需要做其他的一些处理:

1.多网卡的情况:使用gethostbyname获取的网卡的ip地址有可能是一个虚拟网卡的ip地址,导致recv接收的时候一直返回10060的错误。通过实践,可以使用GetAdaptersInfo获取所有活动的网卡,然后针对每一块网卡设置混杂模式进行抓包。

2.一些电脑使用RAW Socket获取不到本机发送出去的数据包,只能获取到网卡接收的数据包,需要设置网卡的硬件校验和。网卡本身具有Tx(发送)和Rx(接收)自动设置校验和的功能,默认为开启,这样子系统可以把这一部分的运算直接交给网卡处理来节省系统资源,而 rawsocket属于socket的一种,发送的数据包由于系统偷懒没有计算校验和所以没有被socket承认为合法的数据包抛弃了,然后我们的代码就没有处理到这些包。可以通过在注册表中设置键值修改该属性,从而获取到所有数据包。注册表路径为:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\,添加键值DisableTaskOffload=1(禁用TCP/IP Offload).至此,我们就可以接收到流经网卡的所有数据包了。

版权声明:本文为博主原创文章,未经博主允许不得转载。原文地址:http://www.xttblog.com/?p=452

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763(2000人群已满),QQ2群:454796847(已满),QQ3群:187424846(已满)。QQ群进群密码:xttblog,想加微信群的朋友,之前的微信号好友已满,请加博主新的微信号:xttblog,备注:“xttblog”,添加博主微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » RAW Socket 获取流经本地网卡的所有数据包