简单的套接字发送数据包

系统以及编译测试环境:

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;
}

你可能感兴趣的:(windows,struct,socket,tcp,XP,Sockets)