目录:
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