系统以及编译测试环境:
windows xp sp2 环境
VC6
wireshark
说明:
微软发布的sp2补丁包升级说明包含以下内容
大致意思是说xp sp2的raw sockets不能发送TCP raw 数据;udp可以,但是源地址必须合法。
目的是防止恶意代码利用此进行臭名昭著的DOS攻击。
否则会返回10004或者10013的错误码
1、使用流套接字发送简单的udp数据包
代码如下:
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
//#pragma comment(lib,"Wsock32.lib")
int main(int argc, char* argv[])
{
WSADATA WSAData;
SOCKET sock;
SOCKADDR_IN addr_in;
unsigned char szSendBuf[]={
0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,/
0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb};
BOOL flag;
int rect;
if (WSAStartup(MAKEWORD(2,2), &WSAData)!=0)
{
printf("WSAStartup Error!/n");
return FALSE;
}
// if ((sock=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET)
// if ((sock=socket(AF_INET,SOCK_RAW,IPPROTO_UDP))==INVALID_SOCKET)
if ((sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_IP))==INVALID_SOCKET)
{
printf("Socket Setup Error!/n");
return FALSE;
}
// flag=TRUE;
// if (setsockopt(sock,IPPROTO_IP, IP_HDRINCL,(char *)&flag,sizeof(flag))==SOCKET_ERROR)
// {
// printf("setsockopt IP_HDRINCL error!/n");
// return FALSE;
// }
// nTimeOver=1000;
// if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeOver, sizeof(nTimeOver))==SOCKET_ERROR)
// {
// printf("setsockopt SO_SNDTIMEO error!/n");
// return FALSE;
// }
addr_in.sin_family=AF_INET;
addr_in.sin_port=htons(5000);//目的端口
addr_in.sin_addr.S_un.S_addr=inet_addr("192.168.1.1");//目的IP
printf("size of szSendBuf is %d/n",sizeof(szSendBuf));
rect=sendto(sock, szSendBuf, sizeof(szSendBuf),
0, (struct sockaddr*)&addr_in, sizeof(addr_in));
if (rect==SOCKET_ERROR)
{
printf("send error!:%d/n",WSAGetLastError());
return FALSE;
}
else
printf("send ok!/n");
closesocket(sock);
WSACleanup();
return 0;
}
系统自动填充其余数据
2、xp2下使用原始套接字发送udp数据包,验证过
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
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;
USHORT checksum(USHORT* buff, int size)//ICMP与校验和的计算
{
unsigned long cksum = 0;
while(size>1)
{
cksum += *buff++;
size -= sizeof(USHORT);
}
// 是奇数
if(size)
{
cksum += *(UCHAR*)buff;
}
// 将32位的chsum高16位和低16位相加,然后取反
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16); // ???
return (USHORT)(~cksum);
}
void ComputeUdpPseudoHeaderChecksum(IPHeader *pIphdr,UDPHeader *pUdphdr,char *payload,int payloadlen)
{
char buff[1024];
char *ptr = buff;
int chksumlen = 0;
ULONG zero = 0;
// 包含源IP地址和目的IP地址
memcpy(ptr, &pIphdr->ipSource, sizeof(pIphdr->ipSource));
ptr += sizeof(pIphdr->ipSource);
chksumlen += sizeof(pIphdr->ipSource);
memcpy(ptr, &pIphdr->ipDestination, sizeof(pIphdr->ipDestination));
ptr += sizeof(pIphdr->ipDestination);
chksumlen += sizeof(pIphdr->ipDestination);
// 包含8位0域
memcpy(ptr, &zero, 1);
ptr += 1;
chksumlen += 1;
// 协议
memcpy(ptr, &pIphdr->ipProtocol, sizeof(pIphdr->ipProtocol));
ptr += sizeof(pIphdr->ipProtocol);
chksumlen += sizeof(pIphdr->ipProtocol);
// UDP长度
memcpy(ptr, &pUdphdr->len, sizeof(pUdphdr->len));
ptr += sizeof(pUdphdr->len);
chksumlen += sizeof(pUdphdr->len);
// UDP源端口号
memcpy(ptr, &pUdphdr->sourcePort, sizeof(pUdphdr->sourcePort));
ptr += sizeof(pUdphdr->sourcePort);
chksumlen += sizeof(pUdphdr->sourcePort);
// UDP目的端口号
memcpy(ptr, &pUdphdr->destinationPort, sizeof(pUdphdr->destinationPort));
ptr += sizeof(pUdphdr->destinationPort);
chksumlen += sizeof(pUdphdr->destinationPort);
// 又是UDP长度
memcpy(ptr, &pUdphdr->len, sizeof(pUdphdr->len));
ptr += sizeof(pUdphdr->len);
chksumlen += sizeof(pUdphdr->len);
// 16位的UDP校验和,置为0
memcpy(ptr, &zero, sizeof(USHORT));
ptr += sizeof(USHORT);
chksumlen += sizeof(USHORT);
// 净荷
memcpy(ptr, payload, payloadlen);
ptr += payloadlen;
chksumlen += payloadlen;
// 补齐到下一个16位边界
for(int i=0; i<payloadlen%2; i++)
{
*ptr = 0;
ptr++;
chksumlen++;
}
// 计算这个校验和,将结果填充到UDP头
pUdphdr->checksum = checksum((USHORT*)buff, chksumlen);
}
int main()
{// 输入参数信息
char szDestIp[] = "192.168.1.1";
//char szDestIp[] = "255.255.255.254";
// <<== 填写目的IP地址
char szSourceIp[] = "127.0.0.1";
//char szSourceIp[] = "0.0.0.0";
// <<== 填写你自己的IP地址
USHORT nDestPort = 4567; //目的端口
USHORT nSourcePort = 8888;//源端口
WSADATA WSAData;
char szMsg[] = "大家好,我是Hokkien!/r/n";
char buff[1024] = { 0 };
int nMsgLen = strlen(szMsg);
if (WSAStartup(MAKEWORD(2,2), &WSAData)!=0)
{
printf("WSAStartup Error!/n");
return FALSE;
}
// 创建原始套节字
SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_IP);
// 有效IP头包含选项
BOOL bIncl = TRUE;
::setsockopt(sRaw, IPPROTO_IP, IP_HDRINCL, (char *)&bIncl, sizeof(bIncl));
// 填充IP头
IPHeader *pIphdr = (IPHeader *)buff;
pIphdr->iphVerLen = (4<<4 | (sizeof(IPHeader)/sizeof(ULONG)));
//版本与长度
pIphdr->ipLength = ::htons(sizeof(IPHeader) + sizeof(UDPHeader) + nMsgLen);
//数据包长度
pIphdr->ipTTL = 128; //生存时间
pIphdr->ipProtocol = 252;//IPPROTO_UDP;//UDP //可能自定义IP协议
pIphdr->ipSource = ::inet_addr(szSourceIp); //源IP
pIphdr->ipDestination = ::inet_addr(szDestIp); //目的IP
pIphdr->ipChecksum = checksum((USHORT*)pIphdr, sizeof(IPHeader));
//校验码,这是必需的!
// 填充UDP头
UDPHeader *pUdphdr = (UDPHeader *)&buff[sizeof(IPHeader)];
pUdphdr->sourcePort = htons(8888); //源端口
pUdphdr->destinationPort = htons(nDestPort);//目的端口
pUdphdr->len = htons(sizeof(UDPHeader) + nMsgLen);//报头长度
//pUdphdr->checksum = 0; //校验和,不是必需的
char *pData = &buff[sizeof(IPHeader) + sizeof(UDPHeader)];
memcpy(pData, szMsg, nMsgLen);
//填充校验和
ComputeUdpPseudoHeaderChecksum(pIphdr, pUdphdr, pData, nMsgLen);
// 设置目的地址
SOCKADDR_IN destAddr = { 0 };
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(nDestPort);
destAddr.sin_addr.S_un.S_addr = ::inet_addr(szDestIp);
// 发送原始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)
{
printf(" sendto() failed: %d /n", ::WSAGetLastError());
//break;
}
else
{
printf(" sent %d bytes /n", nRet);
}
}
::closesocket(sRaw);
//getchar();
return 0;
}
对上述代码改进
忽略了UDP包的包头,使用自定义的IP包格式
去掉对UDP校验的算法,保证真正发送的数据是想要发出去的
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
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;
USHORT checksum(USHORT* buff, int size)//ICMP与校验和的计算
{
unsigned long cksum = 0;
while(size>1)
{
cksum += *buff++;
size -= sizeof(USHORT);
}
// 是奇数
if(size)
{
cksum += *(UCHAR*)buff;
}
// 将32位的chsum高16位和低16位相加,然后取反
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16); // ???
return (USHORT)(~cksum);
}
int main()
{// 输入参数信息
char szDestIp[] = "192.168.1.1";
//char szDestIp[] = "255.255.255.254";
// <<== 填写目的IP地址
char szSourceIp[] = "127.0.0.1";
//char szSourceIp[] = "0.0.0.0";
// <<== 填写你自己的IP地址
USHORT nDestPort = 4567; //目的端口
USHORT nSourcePort = 8888;//源端口
WSADATA WSAData;
char szMsg[] = "大家好,我是Hokkien!/r/n";
char buff[1024] = { 0 };
int nMsgLen = strlen(szMsg);
if (WSAStartup(MAKEWORD(2,2), &WSAData)!=0)
{
printf("WSAStartup Error!/n");
return FALSE;
}
// 创建原始套节字
SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_IP);
// 有效IP头包含选项
BOOL bIncl = TRUE;
::setsockopt(sRaw, IPPROTO_IP, IP_HDRINCL, (char *)&bIncl, sizeof(bIncl));
// 填充IP头
IPHeader *pIphdr = (IPHeader *)buff;
pIphdr->iphVerLen = (4<<4 | (sizeof(IPHeader)/sizeof(ULONG)));
//版本与长度
pIphdr->ipLength = ::htons(sizeof(IPHeader) + nMsgLen);
//数据包长度
pIphdr->ipTTL = 128; //生存时间
pIphdr->ipProtocol = 252;//IPPROTO_UDP;//UDP
pIphdr->ipSource = ::inet_addr(szSourceIp); //源IP
pIphdr->ipDestination = ::inet_addr(szDestIp); //目的IP
pIphdr->ipChecksum = checksum((USHORT*)pIphdr, sizeof(IPHeader));
//校验码,这是必需的!
char *pData = &buff[sizeof(IPHeader)];
memcpy(pData, szMsg, nMsgLen);
//填充校验和
// 设置目的地址
SOCKADDR_IN destAddr = { 0 };
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(nDestPort);
destAddr.sin_addr.S_un.S_addr = ::inet_addr(szDestIp);
// 发送原始UDP封包
int nRet;
//for(int i=0; i<5; i++)
{
nRet = ::sendto(sRaw, buff,
sizeof(IPHeader) + nMsgLen, 0, (sockaddr*)&destAddr, sizeof(destAddr));
if(nRet == SOCKET_ERROR)
{
printf(" sendto() failed: %d /n", ::WSAGetLastError());
//break;
}
else
{
printf(" sent %d bytes /n", nRet);
}
}
::closesocket(sRaw);
//getchar();
return 0;
}