ネットワークに流れるパケットをキャプチャする

test125.gif
Windows 2000以降ではかなり簡単にネットワークのパケットモニターを作れる。

ここではWinSock2.2のRAWソケットを利用してパケットデータを取得している。取得結果はVisual Studioの「出力」ウインドウに表示している。

依存環境:ATL
#include <mstcpip.h>
#include <winsock2.h>
#pragma	comment(lib,"Ws2_32.lib")



//////////////////////////////////////////////////////
//以下の構造体はWindows SDKのサンプルに含まれる「iphdr.h」内からコピー
//

#pragma	pack(push,1)

//
// IPv4 Header (without any IP options)
//
typedef struct ip_hdr
{
    unsigned char  ip_verlen;        // 4-bit IPv4 version
                                     // 4-bit header length (in 32-bit words)
    unsigned char  ip_tos;           // IP type of service
    unsigned short ip_totallength;   // Total length
    unsigned short ip_id;            // Unique identifier 
    unsigned short ip_offset;        // Fragment offset field
    unsigned char  ip_ttl;           // Time to live
    unsigned char  ip_protocol;      // Protocol(TCP,UDP etc)
    unsigned short ip_checksum;      // IP checksum
    unsigned int   ip_srcaddr;       // Source address
    unsigned int   ip_destaddr;      // Source address
} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;

//
// Define the UDP header 
//
typedef struct udp_hdr
{
    unsigned short src_portno;       // Source port no.
    unsigned short dest_portno;      // Dest. port no.
    unsigned short udp_length;       // Udp packet length
    unsigned short udp_checksum;     // Udp checksum
} UDP_HDR, *PUDP_HDR;

//
// Define the TCP header
//
typedef struct tpc_hdr				// <- スペルミス?動作に問題なし
{
    unsigned short src_portno;       // Source port no.
    unsigned short dest_portno;      // Dest. port no.
    unsigned long  seq_num;          // Sequence number
    unsigned long  ack_num;          // Acknowledgement number;
    unsigned short lenflags;         // Header length and flags
    unsigned short window_size;      // Window size
    unsigned short tcp_checksum;     // Checksum
    unsigned short tcp_urgentptr;    // Urgent data?
} TCP_HDR, *PTCP_HDR;

#pragma	pack(pop)


//////////////////////////////////////////////////////

#pragma	pack(push,1)

typedef	struct ip_hdr_tcp
{
	IPV4_HDR	sHdrIp;
	TCP_HDR		sHdrTcp;
	BYTE		pData[1];

} TCP_IP_HDR;

typedef	struct ip_hdr_udp
{
	IPV4_HDR	sHdrIp;
	UDP_HDR		sHdrUdp;
	BYTE		pData[1];

} UDP_IP_HDR;


#pragma	pack(pop)


#define	SWAP_BYTE_ALIGNMENT(data)	\
{									\
	int		i;						\
	int		nSize;					\
	int		nLoop;					\
	BYTE*	pData;					\
	BYTE	cbTmp;					\
									\
	pData = (BYTE*)&data;			\
	nSize = sizeof(data);			\
	nLoop = nSize / 2;				\
	for(i = 0; i < nLoop; i++)		\
	{								\
		cbTmp = *(pData + i);		\
		*(pData + i) = *(pData + nSize - i - 1);	\
		*(pData + nSize - i - 1) = cbTmp;			\
	}								\
}


bool	PacketCapture()
{
	int		nRet;
	SOCKET	sock;
	DWORD	dwReturned;

	//RAW SOCKETを作る
	sock = ::WSASocket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);
	if(sock == INVALID_SOCKET)
		return false;

	{
		SOCKADDR_IN	sSockAddrIn;

		//パケットを監視するインターフェースを選ぶ
		//ここでは簡易のため最初に見つかったインターフェースを利用している
		//PCに複数のインターフェース(無線LANと有線LANなど)がある場合は適宜変更すること
		//無線LANが何番目に見つかるかなど、物理的なNICとの関連付けをしたい場合は、GetIfTableを利用する
		{
			int		nNo;
			BYTE	pBuff[1024];
			SOCKET_ADDRESS_LIST* pSockAddrList;

			//何番目に見つかったインターフェースを利用するか
			//2番目のインターフェースを利用したい場合は「1」に変更
			nNo = 0;

			// アダプタの列挙
			nRet = ::WSAIoctl(sock,SIO_ADDRESS_LIST_QUERY,NULL,0,pBuff,1024,&dwReturned,NULL,NULL);
			if(nRet == SOCKET_ERROR || dwReturned == 0)
			{
				::closesocket(sock);
				return false;
			}

			pSockAddrList = (SOCKET_ADDRESS_LIST*)pBuff;

			if(nNo >= pSockAddrList->iAddressCount)
			{
				::closesocket(sock);
				return false;
			}
			sSockAddrIn.sin_addr.s_addr= ((SOCKADDR_IN*)pSockAddrList->Address[nNo].lpSockaddr)->sin_addr.s_addr;
		}


		//バインドとキャプチャ-の有効化
		//WSAIoctlに渡すコマンドによってキャプチャ-種などを変更できる
		{
			BOOL	bOption;

			//sSockAddrIn.sin_addr.s_addr	= INADDR_ANY; <-これはNG.必ずSIO_ADDRESS_LIST_QUERYで求めた値を利用する
			sSockAddrIn.sin_family	= AF_INET;
			sSockAddrIn.sin_port	= htons(0);

			nRet = ::bind(sock,(sockaddr*)&sSockAddrIn,sizeof(SOCKADDR_IN));
			if(nRet == SOCKET_ERROR) 
			{
				::closesocket(sock);
				return false;
			}

			//パケットキャプチャの有効化(Windows 2000以降対応)
			bOption = TRUE;
			nRet = ::WSAIoctl(sock,SIO_RCVALL,&bOption,sizeof(BOOL),NULL,0,&dwReturned,NULL,NULL);
			if(nRet == SOCKET_ERROR)
			{
				::closesocket(sock);
				return false;
			}
		}
	}





	//キャプチャの実行とキャプチャデータの表示
	{
		char	pBuff[204800];
		WSABUF	sWsaBuf;
		DWORD	dwFlags;
		DWORD	dwTick;

		dwTick = ::GetTickCount();

		while(1)
		{
			::Sleep(0);

			//キャプチャ時間を10秒間に制限
			if(::GetTickCount() - dwTick > 10*1000)
				break;


			dwFlags = 0;
			::ZeroMemory(pBuff,sizeof(pBuff));
			sWsaBuf.buf = pBuff;
			sWsaBuf.len = sizeof(pBuff);

			//パケット受信
			nRet = ::WSARecv(sock,&sWsaBuf,1,&dwReturned,&dwFlags,NULL,NULL);
			if(nRet == SOCKET_ERROR)
				continue;

			//受信パケット情報の表示
			//ここではIPv4に限って送信元と送信先、TCPかUDPの場合はポート番号の表示のみを処理している
			{
				IPV4_HDR*	pHdr;

				if(dwReturned < sizeof(IPV4_HDR))
					continue;

				pHdr = (IPV4_HDR*)pBuff;

				//IPv4、かつ、IPヘッダーサイズが5×32bit以外のときは処理しない
				if(pHdr->ip_verlen != 0x45)
					continue;

				ATLTRACE("送信元:%d.%d.%d.%d\n",(pHdr->ip_srcaddr >> 0) & 0xFF,(pHdr->ip_srcaddr >> 8) & 0xFF,(pHdr->ip_srcaddr >> 16) & 0xFF,(pHdr->ip_srcaddr >> 24) & 0xFF);
				ATLTRACE("送信先:%d.%d.%d.%d\n",(pHdr->ip_destaddr >> 0) & 0xFF,(pHdr->ip_destaddr >> 8) & 0xFF,(pHdr->ip_destaddr >> 16) & 0xFF,(pHdr->ip_destaddr >> 24) & 0xFF);

				//プロトコルに応じた処理(プロトコルの一覧は「ws2def.h」内で定義されている)
				switch(pHdr->ip_protocol)
				{
				case	IPPROTO_TCP:
					if(dwReturned >= sizeof(TCP_IP_HDR) - 1)
					{
						USHORT		nSrcPort;
						USHORT		nDstPort;
						TCP_IP_HDR*	pTcp;

						pTcp = (TCP_IP_HDR*)pHdr;

						//ポート番号などはエンディアンが異なるので注意
						nSrcPort = pTcp->sHdrTcp.src_portno;
						nDstPort = pTcp->sHdrTcp.dest_portno;
						SWAP_BYTE_ALIGNMENT(nSrcPort);
						SWAP_BYTE_ALIGNMENT(nDstPort);
						ATLTRACE("送信元ポート番号:%d\n",nSrcPort);
						ATLTRACE("送信先ポート番号:%d\n",nDstPort);
					}
					break;

				case	IPPROTO_UDP:
					if(dwReturned >= sizeof(UDP_IP_HDR) - 1)
					{
						USHORT		nSrcPort;
						USHORT		nDstPort;
						UDP_IP_HDR*	pUdp;

						pUdp = (UDP_IP_HDR*)pHdr;

						//ポート番号などはエンディアンが異なるので注意
						nSrcPort = pUdp->sHdrUdp.src_portno;
						nDstPort = pUdp->sHdrUdp.dest_portno;
						SWAP_BYTE_ALIGNMENT(nSrcPort);
						SWAP_BYTE_ALIGNMENT(nDstPort);
						ATLTRACE("送信元ポート番号:%d\n",nSrcPort);
						ATLTRACE("送信先ポート番号:%d\n",nDstPort);
					}
					break;

				//今回はTCP/UDP以外は処理しない
				default:
					break;
				}

				ATLTRACE("\n");
			}
		}
	} 

	::closesocket(sock);

	return	true;
}


bool	Test()
{
	WSAData wsaData;

	//Winsock2.2
	::WSAStartup(MAKEWORD(2,2),&wsaData);

	PacketCapture();

	::WSACleanup();

	return	true;
}


プロジェクトファイルをダウンロード


カテゴリー「ネットワーク」 のエントリー