实验平台: vc6.0, winxp
1。 首先定义IP 头,UDP,TCP 头的结构:
typedef struct _IPHeader // 20字节的IP头
{
UCHAR iphVerLen; // 版本号和头长度(各占4位)
UCHAR ipTOS; // 服务类型
USHORT ipLength; // 封包总长度,即整个IP报的长度
USHORT ipID; // 封包标识,惟一标识发送的每一个数据报
USHORT ipFlags; // 标志
UCHAR ipTTL; // 生存时间,就是TTL
UCHAR ipProtocol; // 协议,可能是TCP、UDP、ICMP等
USHORT ipChecksum; // 校验和
ULONG ipSource; // 源IP地址
ULONG ipDestination; // 目标IP地址
} IPHeader, *PIPHeader;
typedef struct _UDPHeader
{
USHORT sourcePort; // 源端口号
USHORT destinationPort;// 目的端口号
USHORT len; // 封包长度
USHORT checksum; // 校验和
} UDPHeader, *PUDPHeader;
typedef struct _TCPHeader // 20字节的TCP头
{
USHORT sourcePort; // 16位源端口号
USHORT destinationPort; // 16位目的端口号
ULONG sequenceNumber; // 32位序列号
ULONG acknowledgeNumber; // 32位确认号
UCHAR dataoffset; // 高4位表示数据偏移
UCHAR flags; // 6位标志位
//FIN - 0x01
//SYN - 0x02
//RST - 0x04
//PUSH- 0x08
//ACK- 0x10
//URG- 0x20
//ACE- 0x40
//CWR- 0x80
USHORT windows; // 16位窗口大小
USHORT checksum; // 16位校验和
USHORT urgentPointer; // 16位紧急数据偏移量
}TCPHeader, *PTCPHeader;
TCP 头 flags的宏定义:
// 定义TCP标志
#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20
#define TCP_ACE 0x40
#define TCP_CWR 0x80
2。socket 的初始化代码,设置网卡处于杂和方式,接收网卡收到的所有封包:
//按照原始方式初始化socket, 这里只接收IP封包,
//如果想接收ICMP 封包请更改IPPROTO_IP 为IPPROTO_ICMP
//如果想接收TCP 封包请更改IPPROTO_IP 为IPPROTO_TCP
SOCKET sRaw = socket(AF_INET, SOCK_RAW , IPPROTO_IP );
// 获取本地IP地址
char szHostName[56];
SOCKADDR_IN addr_in;
struct hostent *pHost;
gethostname(szHostName, 56);
if((pHost = gethostbyname((char*)szHostName)) == NULL)
return ;
// 在调用ioctl之前,套节字必须绑定;端口号为0
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(0);
memcpy(&addr_in.sin_addr.S_un.S_addr, pHost->h_addr_list[0], pHost->h_length);
if(bind(sRaw, (PSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR)
return ;
//接收所有的封包,注意SIO_RCVALL 在MSTcpIP.h中定义的,所以必须include MSTcpIP.h 文件
DWORD dwValue = 1;
if(ioctlsocket(sRaw, SIO_RCVALL, &dwValue) != 0)
return ;
这样完成了socket的初始化
3。接收部分:
我用了一个最简单的方式,循环接收,有数据就解析:
char buff[1024];
int nRet;
while(TRUE)
{
nRet = recv(sRaw, buff, 1024, 0);
if(nRet > 0)
{
TRACE("Recv some data.../n");
DecodeIPPacket(buff);
}
AfxGetApp()->PumpMessage();
}
closesocket(sRaw);
解析部分代码:
void DecodeIPPacket(char *pData)
{
IPHeader *pIPHdr = (IPHeader*)pData;
in_addr source, dest;
char szSourceIp[32], szDestIp[32];
// 从IP头中取出源IP地址和目的IP地址
source.S_un.S_addr = pIPHdr->ipSource;
dest.S_un.S_addr = pIPHdr->ipDestination;
strcpy(szSourceIp, ::inet_ntoa(source));
strcpy(szDestIp, ::inet_ntoa(dest));
CString sSource=szSourceIp;
CString sdest=szDestIp;
// IP头长度
int nHeaderLen = (pIPHdr->iphVerLen & 0xf) * sizeof(ULONG);
TRACE("/n/n-------------------------------/n");
switch(pIPHdr->ipProtocol)
{
case IPPROTO_TCP: // TCP协议
TRACE("TCP %s -> %s /n", szSourceIp, szDestIp);
DecodeTCPPacket(pData + nHeaderLen);
break;
case IPPROTO_UDP:
TRACE("UDP %s -> %s /n", szSourceIp, szDestIp);
break;
case IPPROTO_ICMP:
TRACE("ICMP %s -> %s /n", szSourceIp, szDestIp);
break;
}
}
当网络是用集线器连接起来的局域网,则可抓到同一局域网内任意两台通讯的数据,如果是通过交换机连接的局域网,则只能抓到发往本机的封包,包括广播报。