WinSock原始套接字编程(Raw Socket)

目录:

1.原始套接字及其功能

2.原始套接字的通信流程

3.收发ICMP数据包

3.1ping

3.2 Trace Route(tracert)

4发送自定义的IP分组

5.捕获IP数据包(网络嗅探Sniffer)


1.收发ICMP包之ping程序:

// Ping.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include 
#include 
#pragma comment(lib, "WS2_32")
#define DATALEN 1012
#define  PACKAGENUM 10  //发送ICMP会从请求报文的个数
using namespace std;
/***定义ICMP包结构***/
typedef struct icmp_hdr
{
    unsigned char   icmp_type;		// ICMP包类型
    unsigned char   icmp_code;		// 代码
    unsigned short  icmp_checksum;	// 校验和
    unsigned short  icmp_id;		// 惟一确定此请求的标识,通常设置为进程ID
    unsigned short  icmp_sequence;	// 序列号
    unsigned long   icmp_timestamp; // 时间戳
} IcmpHeader;
unsigned short checksum(unsigned short* buff, int size);  //校验和计算函数的声明
int main(int argc, char *argv[ ])
{
	/***加载Winsock2.2***/
	WSADATA wsaData;
	int ret;
	if((ret=WSAStartup(MAKEWORD(1,0),&wsaData))!=0)
	{
		cout<<"初始化WinSock2.2出错!";
		exit(0);
	}
	char szDestIp[256] ={0};//存放要Ping的IP地址或域名
	//检查ping命令的使用格式是否正确,程序调试时可用后面的代码替换
	/*if (argc < 2)
	 {
	 cout<<"\n用法: ping IP地址|域名\n";
	  return -1;
	 }
	strcpy(szDestIp,argv[1]);*/
	/***输入对方IP地址,调试程序时使用***/
	cout<<"请输入你要Ping的IP地址...\n";
	cin.getline(szDestIp,sizeof(szDestIp));
	/***将点分十进制IP地址转换为32位二进制表示的IP地址***/
	unsigned long ulDestIP= inet_addr(szDestIp);   
	/****转换不成功时按域名解析****/
	if(ulDestIP == INADDR_NONE)
	{
		hostent* pHostent = gethostbyname(szDestIp);
		if (pHostent!=NULL)
			ulDestIP = (*(in_addr*)pHostent->h_addr).s_addr;
		else //解析主机名失败
		{
			cout<<"域名解析失败:"<icmp_type = 8;	// ICMP回送请求
	pIcmp->icmp_code = 0;
	pIcmp->icmp_id = (unsigned short)GetCurrentProcessId();//获取进程号作为ID 
	pIcmp->icmp_timestamp = 0; //时间戳暂设置为0,具体值发送时再填
	pIcmp->icmp_checksum = 0;  //校验和在计算前应先设置为0
	pIcmp->icmp_sequence = 0;  //初始序列号
	/***填充数据部分,可以为任意***/
	memset(&buff[sizeof(IcmpHeader)], 'A', DATALEN);
	/***调用connect()函数为原始套接字指定通信对端地址***/
	connect(sRaw,(SOCKADDR *)&dest, sizeof(dest));
	/***收发ICMP报文***/
	int n=0;
	bool bTimeout;  
	unsigned short	nSeq = 0;//发送的ICMP报文的序号
	char recvBuf[32+DATALEN];//定义接收缓冲区
	SOCKADDR_IN from;  //保存收到的数据的源地址
	int nLen = sizeof(from);  //地址长度
	IcmpHeader* pRecvIcmp;  //指向ICMP报文首部的指针
	while(TRUE)
	{
		static int nCount = 0;
		int nRet;
		if(nCount++ == PACKAGENUM)
			break;
		/***填写发送前才能填写的一些字段并发送ICMP包***/
		pIcmp->icmp_checksum = 0; 
		pIcmp->icmp_timestamp = GetTickCount();//时间戳
		pIcmp->icmp_sequence = nSeq++;  //包序号
        pIcmp->icmp_checksum=checksum((unsigned short*)buff, sizeof(IcmpHeader)+DATALEN);
		nRet = send(sRaw, buff, sizeof(IcmpHeader) + DATALEN, 0);
		if(nRet == SOCKET_ERROR)
		{
			cout<<"发送失败!错误码:"<icmp_id != GetCurrentProcessId())
//收到报文是否为本程序发送的请求报文的应答报文
			{
				//不是则重新接收	
				cout<<" 收到一个非预期的ICMP报文,忽略!\n";
			}
			else  //是则退出循环
				break;
		}while(n<10);//重新接收次数不超过10则继续重试
		if(n>10)// 收到太多非预期的ICMP报文则退出
		{
			cout<<"对方机器向本机发送了太多的ICMP报文.\n";
			closesocket(sRaw);
			WSACleanup();
			return -1;
		}
		if(bTimeout)continue;  //接收超时则发送下一个ICPM报文
		/****解析接收到的ICMP包****/
		int nTick = GetTickCount();
		if(nRet < 20+ sizeof(IcmpHeader))  //收到的报文长度不足则不予解析
		{
			cout<<"Too few bytes from"<icmp_timestamp<<" ms \n";
			Sleep(1000);  //延时1秒再发送下一个数据包
		}
	}
	closesocket(sRaw);
	WSACleanup();
	system("pause");
	return 0;
}
/*************计算校验和的函数***************/
unsigned short checksum(unsigned short* buff, int size)
{
	unsigned long cksum = 0;
	while(size>1)
	{
		cksum += *buff++;
		size -= sizeof(unsigned short);
	}
	if(size)// 是奇数
		cksum += *(char*)buff;
	//将32位的chsum高16位和低16位相加然后取反
	cksum = (cksum >> 16) + (cksum & 0xffff);
	cksum += (cksum >> 16);			
	return (unsigned short)(~cksum);
}

/*
请输入你要Ping的IP地址...
101.132.67.215
1044 bytes from :101.132.67.215 icmp_seq = 0 time:15 ms
1044 bytes from :101.132.67.215 icmp_seq = 1 time:15 ms
1044 bytes from :101.132.67.215 icmp_seq = 2 time:16 ms
1044 bytes from :101.132.67.215 icmp_seq = 3 time:0 ms
1044 bytes from :101.132.67.215 icmp_seq = 4 time:16 ms
1044 bytes from :101.132.67.215 icmp_seq = 5 time:16 ms
1044 bytes from :101.132.67.215 icmp_seq = 6 time:15 ms
1044 bytes from :101.132.67.215 icmp_seq = 7 time:16 ms
1044 bytes from :101.132.67.215 icmp_seq = 8 time:0 ms
1044 bytes from :101.132.67.215 icmp_seq = 9 time:32 ms
请按任意键继续. . .

*/

2.路由跟踪程序Traceroute

// Traceroute.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include 
#include 
#include "ws2tcpip.h"
#pragma comment(lib, "WS2_32")
#define DEF_ICMP_DATA_SIZE  1024
#define MAX_ICMP_PACKET_SIZE 2048
/***ICMP回送请求报文首部***/
using namespace std;
typedef struct icmp_hdr
{
    unsigned char   icmp_type;		// 消息类型
    unsigned char   icmp_code;		// 代码
    unsigned short  icmp_checksum;	// 校验和
    unsigned short  icmp_id;		// 用来惟一标识此请求的ID号,通常设置为进程ID
    unsigned short  icmp_sequence;	// 序列号
    unsigned long   icmp_timestamp; // 时间戳
} IcmpHeader;
/*********ICMP报文首部校验和计算函数***********/
unsigned short Checksum(unsigned short* pBuf, int iSize) 
{
unsigned long cksum = 0;
while (iSize>1) 
{
cksum += *pBuf++;
iSize -= sizeof(unsigned short);
}
if (iSize) 
cksum += *(char*)pBuf;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (unsigned short)(~cksum);
}

/*********************主函数**********************/
int main(int argc, char *argv[ ])
{
	char szDestIp[256];//存放目的IP地址	
	WSADATA wsaData;
	int ret;
	if((ret=WSAStartup(MAKEWORD(2,2),&wsaData))!=0)
	{
		cout<<"初始化WinSock出错!\n";
		return -1;
	}
	cout<<"请输入目的IP地址或域名:\n";
	cin.getline(szDestIp,sizeof(szDestIp));
	unsigned long ulDestIP= inet_addr(szDestIp);
	if(ulDestIP == INADDR_NONE)
	{
		 //转换不成功时按域名解析 
		hostent* pHostent = gethostbyname(szDestIp);
		if (pHostent!=NULL)
			ulDestIP = (*(in_addr*)pHostent->h_addr).s_addr;
		else //解析主机名失败 
		{
			cout<<"不能解析域名:"<< szDestIp <<".错误码:"<icmp_type = 8;
	pIcmpHeader->icmp_id  = 0;
	pIcmpHeader->icmp_id  = (unsigned short)GetCurrentProcessId();
	memset(IcmpSendBuf+sizeof(IcmpHeader), 'E', DEF_ICMP_DATA_SIZE);
	 /*****填充发送目的地址****/ 
	sockaddr_in destAddr;
	destAddr.sin_family = AF_INET;
	destAddr.sin_port = htons(22);
	destAddr.sin_addr.S_un.S_addr =ulDestIP;
	int nRet, nTick, nTTL=1, iSeqNo=0;
	/***发送报文并接收路由器的差错报告报文***/
	IcmpHeader *pICMPHdr;  //指向ICMP报文首部的指针
	char *szIP;
	SOCKADDR_IN recvAddr;
	int n;
	do
	{
		/***设置TTL值***/ 
		setsockopt(sRaw, IPPROTO_IP, IP_TTL, (char*)&nTTL, sizeof(nTTL));
		nTick = GetTickCount();
		/*** 填写ICMP报文的序列号并计算校验和***/
		((IcmpHeader*)IcmpSendBuf)->icmp_checksum = 0;
		((IcmpHeader*)IcmpSendBuf)->icmp_sequence = htons(iSeqNo++);
		((IcmpHeader*)IcmpSendBuf)->icmp_checksum = 
Checksum((unsigned short*)IcmpSendBuf, sizeof(IcmpHeader)+DEF_ICMP_DATA_SIZE);
		/***发送数据***/
		nRet = sendto(sRaw, IcmpSendBuf,sizeof(IcmpSendBuf), 0,
 (sockaddr*)&destAddr, sizeof(destAddr));
		if(nRet == SOCKET_ERROR)
		{
			cout<<"发送数据出错!错误码:"<icmp_type==11|| pICMPHdr->icmp_type == 0 || pICMPHdr-> icmp_type== 3)
				break;
		}while(n<10);
		if(n>10)continue;
		printf("第%d个路由器,IP地址:%s 用时%d毫秒\n",nTTL,szIP,GetTickCount() - nTick);	
		if(pICMPHdr->icmp_type == 3 )
		{
			switch(pICMPHdr->icmp_code)
			{
			case 0: cout<<"目的网络不可达!\n";break;
			case 1: cout<<"目的主机不可达!\n";break;
			case 6: cout<<"不知道的目的网络!\n";break;
			case 7: cout<<"不知道的目的主机!\n";break;
			}
			break;
		}
		if(destAddr.sin_addr.S_un.S_addr == recvAddr.sin_addr.S_un.S_addr)
		{	
			cout<<"目标可达。\n";	break;
		}
	}while(nTTL++ <30);
	closesocket(sRaw);
	WSACleanup();
	system("pause");
	return 0;
}


3.发送自定义的IP分组

// SendUDP.cpp : 定义控制台应用程序的入口点。
//
//使用例7.3的程序可收到本程序发出的UDP包,
#include "stdafx.h"

#include  
#include  
#include  
#pragma comment(lib,"ws2_32.lib")
#define DestPort 65432  //目的UDP端口
#define SourcePort 65431 //源UDP端口
using namespace std;
typedef struct _IPHeader // 定义IP首部结构
{
	unsigned char iphVerLen; // 版本号和头长度各占4位
	unsigned char ipTOS;  // 服务类型
	unsigned short ipLength; // 分组总长度
	unsigned short ipID; //分组标识,惟一标识发送的每一个数据报
	unsigned short ipFlags; // 标志
	unsigned char ipTTL; // 生存时间,TTL
	unsigned char ipProtocol; // 协议可以是TCP、UDP、ICMP等
	unsigned short ipChecksum; // 校验和
	unsigned long ipSource; // 源IP地址
	unsigned long ipDestination; // 目的IP地址
} IPHeader;
typedef struct _UDPHeader //定义UDP首部结构
{
	unsigned short sourcePort;    // 源端口号
	unsigned short destinationPort;// 目的端口号
	unsigned short len;          // 包长度
	unsigned short checksum;    // 校验和
} UDPHeader;
typedef struct tsd_hdr //定义UDP伪首部结构
{ 
	unsigned long saddr; //源IP地址 
	unsigned long daddr; //目的IP地址
	char mbz;     //填充
	char ptcl; //协议型 
	unsigned short udpl; //TCP长度 
}PSDHEADER; 
/********CheckSum:计算校验和的函数***************/ 
unsigned short checksum(unsigned short *buffer, int size) 
{ 
	unsigned long cksum=0; 
	while(size >1) 
	{ 
		cksum+=*buffer++; 
		size -=sizeof(unsigned short); 
	} 
	if(size ) 
	{ 
		cksum += *(char *)buffer; 
	} 
	cksum = (cksum >> 16) + (cksum & 0xffff); 
	cksum += (cksum >>16); 
	return (unsigned short)(~cksum); 
} 
/****************主函数************************/ 
int main(int argc, char* argv[])
{
	// 输入参数信息
	char szDestIp[] = "192.168.2.116";// 目的IP地址
	char szSourceIp[] = "192.168.2.116";//源IP地址,必须是本机IP地址
	char szMsg[] = "Hellow! This is a test UDP Package!\n";
	int nMsgLen = strlen(szMsg);
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2,2), &WSAData)!=0) 
	{ 
		cout<<"WSAStartup Error!\n"; 
		return false; 
	} 
	/*** 创建原始套接字***/
	SOCKET sRaw =socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	/***设置IP_HDRINCL选项***/
	int bIncl =1;
	setsockopt(sRaw, IPPROTO_IP, IP_HDRINCL, (char *)&bIncl, sizeof(bIncl));
	/****创建并填充IP头****/
	char buff[1024] = { 0 };//存放自定义IP分组的存储区
	//填充IP分组首部
	IPHeader *pIphdr = (IPHeader *)buff;
	pIphdr->iphVerLen = (4<<4 | (sizeof(IPHeader)/sizeof(unsigned long))); //版本与长度
	pIphdr->ipLength=htons(sizeof(IPHeader)+sizeof(UDPHeader)+nMsgLen);//分组长度
	pIphdr->ipTTL = 128;  //生存时间
	pIphdr->ipProtocol = IPPROTO_UDP;//协议为UDP
	pIphdr->ipSource = inet_addr(szSourceIp);   //源IP地址
	pIphdr->ipDestination = inet_addr(szDestIp);  //目的IP地址
	pIphdr->ipChecksum = checksum((unsigned short*)pIphdr, sizeof(IPHeader)); //校验和
	/**** 填充UDP首部***/
	UDPHeader *pUdphdr = (UDPHeader *)&buff[sizeof(IPHeader)];
	pUdphdr->sourcePort = htons(SourcePort);   //源端口
	pUdphdr->destinationPort = htons(DestPort);//目的端口
	pUdphdr->len = htons(sizeof(UDPHeader) + nMsgLen);//报头长度¨
	pUdphdr->checksum = 0;  //校验和
	/***填充UDP数据****/
	char *pData = &buff[sizeof(IPHeader) + sizeof(UDPHeader)];
	memcpy(pData, szMsg, nMsgLen); 
	/****计算UDP校验和***/
	PSDHEADER psdHeader;//构造伪首部
	psdHeader.saddr=pIphdr->ipSource; 
	psdHeader.daddr=pIphdr->ipDestination; 
	psdHeader.mbz=0; 
	psdHeader.ptcl=IPPROTO_UDP; 
	psdHeader.udpl=htons(sizeof(UDPHeader)+nMsgLen); 
	char szBuff[1024];
	memcpy(szBuff, &psdHeader, sizeof(psdHeader));
	memcpy(szBuff+sizeof(psdHeader),pUdphdr, sizeof(UDPHeader)); 
	memcpy(szBuff+sizeof(psdHeader)+sizeof(UDPHeader),pData, nMsgLen);
	pUdphdr->checksum =  checksum((unsigned short *)szBuff, 
	sizeof(psdHeader)+sizeof(UDPHeader)+nMsgLen);
	/*** 设置目的地址***/
	SOCKADDR_IN destAddr = { 0 };
	destAddr.sin_family = AF_INET;
	destAddr.sin_port = htons(DestPort);
	destAddr.sin_addr.S_un.S_addr = inet_addr(szDestIp);
	/***发送5个同样的原始UDP数据报***/
	int nRet;
	for(int i=0; i<5; i++)
	{
		nRet =sendto(sRaw, buff,sizeof(IPHeader) + sizeof(UDPHeader) + nMsgLen,0, (sockaddr*)&destAddr, sizeof(destAddr));
		if(nRet == SOCKET_ERROR)
		{
			cout<<" 发送错误,错误码:"<

4.捕获IP数据包

// CaptuerIP.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include 
#include 
#include "mstcpip.h"
#pragma comment(lib, "WS2_32")
using namespace std;
void DecodeIPPacket(char *pData);
void DecodeTCPPacket(char *pData);
void DecodeUDPPacket(char *pData);
void DecodeICMPPacket(char *pData);
/*****IP分组首部结构******/
typedef struct _IPHeader // 定义IP首部结构
{
	unsigned char iphVerLen; // 版本号和头长度各占4位
	unsigned char ipTOS;  // 服务类型
	unsigned short ipLength; // 分组总长度
	unsigned short ipID; //分组标识,惟一标识发送的每一个数据报
	unsigned short ipFlags; // 标志
	unsigned char ipTTL; // 生存时间,TTL
	unsigned char ipProtocol; // 协议可以是TCP、UDP、ICMP等
	unsigned short ipChecksum; // 校验和
	unsigned long ipSource; // 源IP地址
	unsigned long ipDestination; // 目的IP地址
} IPHeader, *PIPHeader; 
/****ICMP包头结构******/
typedef struct icmphdr
{
	char  i_type;  // ICMP包类型码
	char  i_code;  //代码
	unsigned short  i_cksum;  //校验和
	unsigned short  i_id;    //标识符,一般可设为发送进程的ID
	unsigned short  i_seq;   //序列号
	unsigned long  timestamp;  //时间戳
}ICMPHeader; 
/**********UDP包头结构******/
typedef struct _UDPHeader
{
unsigned short sourcePort;    // 源端口号
unsigned short destinationPort; // 目的端口号
unsigned short len;          // 包长度
unsigned short checksum;    // 校验和
} UDPHeader;
/******TCP包头结构******/
typedef struct _TCPHeader
{
	unsigned short sourcePort;		// 16位源端口号
	unsigned short destinationPort;	// 16位目的端口号
	unsigned long sequenceNumber;	// 32位序列号
	unsigned long acknowledgeNumber;	// 32位确认号
	char	 dataoffset;	//高4位表示数据偏移
	char flags;    //低6位为URG、ACK、PSH、RST、SYNhe FIN六个标志位
	unsigned short windows;		// 16位窗口大小
	unsigned short checksum;		// 16位校验和
	unsigned short urgentPointer;		// 16位紧急数据偏移量 
} TCPHeader;
/*****主函数******/
int  main()
{
	WSADATA wsaData;
	int ret;
	if((ret=WSAStartup(MAKEWORD(2,2),&wsaData))!=0)
	{
		cout<<"初始化WinSock出错!";
		return -1;
	}
	/**** 创建原始套节字******/
	SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
	/****** 获取本地IP地址******/
	char sHostName[256];
	SOCKADDR_IN addr_in;
	struct hostent *hptr;
	gethostname(sHostName, sizeof(sHostName));
	if((hptr = gethostbyname(sHostName)) == NULL)
	{
		cout<<"未能获取本地IP地址。错误码"<h_addr_list;
	/******在屏幕上显示本机所有的IP地址******/
	cout<<"本机IP地址:\n";
	while(*pptr!=NULL)
	{
		cout<>packetNumber;
	cout<<"正在等待抓取IP数据包...";
	int i, nRet;
	for(i=0;i=50)break;
		nRet = recv(sRaw, buff[i], 4096, 0);
		cout<<"#";
		if(nRet<=0)
		{
			cout<<"抓取数据时出错!错误码:"<ipSource;
	dest.S_un.S_addr = pIPHdr->ipDestination;
	strcpy(szSourceIp, inet_ntoa(source));
	strcpy(szDestIp, inet_ntoa(dest));
	cout<<"Source IP:"<< szSourceIp;
	cout<<" Destionation IP: "<< szDestIp<iphVerLen & 0xf) * sizeof(ULONG);// IP头长度
	switch(pIPHdr->ipProtocol)
	{
	case IPPROTO_TCP: // 调用函数解析TCP包
		DecodeTCPPacket(pData + nHeaderLen);
		break;
	case IPPROTO_UDP:// 调用函数解析UDP包
		DecodeUDPPacket(pData + nHeaderLen);
		break; 
	case IPPROTO_ICMP:// 调用函数解析ICMP包
		DecodeICMPPacket(pData + nHeaderLen);
		break;  
	default:
		cout<<" 协议号:"<<(int)pIPHdr->ipProtocol<sourcePort);
	cout<<"  Destination Port: "<< ntohs(pTCPHdr->destinationPort)<sourcePort); 
	cout<< "  Destination Port: "<< ntohs(pUDPHdr->destinationPort)<i_type <<"Code: "<i_code<i_type)
	{
		case 0:
			cout<<"Echo Response.\n" ; break;
		case 8:
			cout<<"Echo Request.\n"; break;
		case 3:
			cout<<"Destination Unreachable.\n"; break;
		case 11:
			cout<<"Datagram Timeout(TTL=0).\n"; break;
	}
}

WinSock 原始套接字编程


参考文献[1]Windows网络编程基础教程

[2]链接:https://pan.baidu.com/s/1cB_fLBX9HbTIiF4InddxCA 密码:kdi7

你可能感兴趣的:(WinSock原始套接字编程(Raw Socket))