![]()
Windows 2000以降ではかなり簡単にネットワークのパケットモニターを作れる。
ここではWinSock2.2のRAWソケットを利用してパケットデータを取得している。取得結果はVisual Studioの「出力」ウインドウに表示している。
#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;
}
