Raw Socket编程

这里介绍Windows   Sockets的一些关于原始套接字(Raw   Socket)的编程。同Winsock1相比,最明显的就是支持了Raw   Socket套接字类型,通过原始套接字,我们可以更加自如地控制Windows下的多种协议,而且能够对网络底层的传输机制进行控制。  

1、创建一个原始套接字,并设置IP头选项。  

SOCKET   sock;  
sock   =   socket(AF_INET,SOCK_RAW,IPPROTO_IP);  
或者:  
s   =   WSASoccket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);  

    这里,我们设置了SOCK_RAW标志,表示我们声明的是一个原始套接字类型。创建原始套接字后,IP头就会包含在接收的数据中,如果我们设定   IP_HDRINCL   选项,那么,就需要自己来构造IP头。注意,如果设置IP_HDRINCL   选项,那么必须具有   administrator权限,要不就必须修改注册表:  
HKEY_LOCAL_MACHINE/System/CurrentControlSet/Services/Afd/Parameter/  
修改键:DisableRawSecurity(类型为DWORD),把值修改为   1。如果没有,就添加。  

BOOL   blnFlag=TRUE;  
setsockopt(sock,   IPPROTO_IP,   IP_HDRINCL,   (char   *)&blnFlag,   sizeof(blnFlag);  

    对于原始套接字在接收数据报的时候,要注意这么几点:  
1、如果接收的数据报中协议类型和定义的原始套接字匹配,那么,接收的所有数据就拷贝到套接字中。  
2、如果绑定了本地地址,那么只有接收数据IP头中对应的远端地址匹配,接收的数据就拷贝到套接字中。  
3、如果定义的是外部地址,比如使用connect(),那么,只有接收数据IP头中对应的源地址匹配,接收的数据就拷贝到套接字中。  


2、构造IP头和TCP头  

这里,提供IP头和TCP头的结构:  
//   Standard   TCP   flags  
#define   URG   0x20  
#define   ACK   0x10  
#define   PSH   0x08  
#define   RST   0x04  
#define   SYN   0x02  
#define   FIN   0x01  
typedef   struct   _iphdr   //定义IP首部  
{  
unsigned   char   h_lenver;   //4位首部长度+4位IP版本号  
unsigned   char   tos;   //8位服务类型TOS  
unsigned   short   total_len;   //16位总长度(字节)  
unsigned   short   ident;   //16位标识  
unsigned   short   frag_and_flags;   //3位标志位  
unsigned   char   ttl;   //8位生存时间   TTL  
unsigned   char   proto;   //8位协议   (TCP,   UDP   或其他)  
unsigned   short   checksum;   //16位IP首部校验和  
unsigned   int   sourceIP;   //32位源IP地址  
unsigned   int   destIP;   //32位目的IP地址  
}IP_HEADER;  

typedef   struct   psd_hdr   //定义TCP伪首部  
{  
unsigned   long   saddr;   //源地址  
unsigned   long   daddr;   //目的地址  
char   mbz;  
char   ptcl;   //协议类型  
unsigned   short   tcpl;   //TCP长度  
}PSD_HEADER;  

typedef   struct   _tcphdr   //定义TCP首部  
{  
USHORT   th_sport;   //16位源端口  
USHORT   th_dport;   //16位目的端口  
unsigned   int   th_seq;   //32位序列号  
unsigned   int   th_ack;   //32位确认号  
unsigned   char   th_lenres;       //4位首部长度/6位保留字  
unsigned   char   th_flag;   //6位标志位  
USHORT   th_win;   //16位窗口大小  
USHORT   th_sum;   //16位校验和  
USHORT   th_urp;   //16位紧急数据偏移量  
}TCP_HEADER;  

TCP伪首部并不是真正存在的,只是用于计算检验和。校验和函数:  

USHORT   checksum(USHORT   *buffer,   int   size)  
{  
    unsigned   long   cksum=0;  
    while   (size   >   1)  
    {  
        cksum   +=   *buffer++;  
        size   -=   sizeof(USHORT);      
    }  
    if   (size)  
    {  
        cksum   +=   *(UCHAR*)buffer;      
    }  
    cksum   =   (cksum   > >   16)   +   (cksum   &   0xffff);  
    cksum   +=   (cksum   > > 16);  
    return   (USHORT)(~cksum);  
}  

    当需要自己填充IP头部和TCP头部的时候,就同时需要自己计算他们的检验和。  

3、发送原始套接字数据报  

    填充这些头部稍微麻烦点,发送就相对简单多了。只需要使用sendto()就OK。  

sendto(sock,   (char*)&tcpHeader,   sizeof(tcpHeader),   0,   (sockaddr*)&addr_in,sizeof(addr_in));  

 

4、接收数据  
    和发送原始套接字数据相比,接收就比较麻烦了。因为在WIN我们不能用recv()来接收raw   socket上的数据,这是因为,所有的IP包都是先递交给系统核心,然后再传输到用户程序,当发送一个raws   socket包的时候(比如syn),核心并不知道,也没有这个数据被发送或者连接建立的记录,因此,当远端主机回应的时候,系统核心就把这些包都全部丢掉,从而到不了应用程序上。所以,就不能简单地使用接收函数来接收这些数据报。  

    要达到接收数据的目的,就必须采用嗅探,接收所有通过的数据包,然后进行筛选,留下符合我们需要的。可以再定义一个原始套接字,用来完成接收数据的任务,需要设置SIO_RCVALL,表示接收所有的数据。  

SOCKET   sniffersock;  
sniffsock   =   WSASocket(AF_INET,   SOCK_RAW,   IPPROTO_IP,   NULL,   0,   WSA_FLAG_OVERLAPPED);  

DWORD   lpvBuffer   =   1;  
DWORD   lpcbBytesReturned   =   0   ;  
WSAIoctl(sniffersock,   SIO_RCVALL,   &lpvBuffer,   sizeof(lpvBuffer),   NULL,   0,   &   lpcbBytesReturned,   NULL,   NULL);  

    创建一个用于接收数据的原始套接字,我们可以用接收函数来接收数据包了。然后在使用一个过滤函数达到筛选的目的,接收我们需要的数据包。  

 

下面是一个示例程序,可以作为SYN扫描的一部分。  

#include   <stdio.h>  
#include   <winsock2.h>  
#include   <ws2tcpip.h>  

#define   SOURCE_PORT   7234  
#define   MAX_RECEIVEBYTE   255  

typedef   struct   ip_hdr   //定义IP首部  
{  
unsigned   char   h_verlen;   //4位首部长度,4位IP版本号  
unsigned   char   tos;   //8位服务类型TOS  
unsigned   short   total_len;   //16位总长度(字节)  
unsigned   short   ident;   //16位标识  
unsigned   short   frag_and_flags;   //3位标志位  
unsigned   char   ttl;   //8位生存时间   TTL  
unsigned   char   proto;   //8位协议   (TCP,   UDP   或其他)  
unsigned   short   checksum;   //16位IP首部校验和  
unsigned   int   sourceIP;   //32位源IP地址  
unsigned   int   destIP;   //32位目的IP地址  
}IPHEADER;  

typedef   struct   tsd_hdr   //定义TCP伪首部  
{  
unsigned   long   saddr;   //源地址  
unsigned   long   daddr;   //目的地址  
char   mbz;  
char   ptcl;   //协议类型  
unsigned   short   tcpl;   //TCP长度  
}PSDHEADER;  

typedef   struct   tcp_hdr   //定义TCP首部  
{  
USHORT   th_sport;   //16位源端口  
USHORT   th_dport;   //16位目的端口  
unsigned   int   th_seq;   //32位序列号  
unsigned   int   th_ack;   //32位确认号  
unsigned   char   th_lenres;   //4位首部长度/6位保留字  
unsigned   char   th_flag;   //6位标志位  
USHORT   th_win;   //16位窗口大小  
USHORT   th_sum;   //16位校验和  
USHORT   th_urp;   //16位紧急数据偏移量  
}TCPHEADER;  

//CheckSum:计算校验和的子函数  
USHORT   checksum(USHORT   *buffer,   int   size)  
{  
unsigned   long   cksum=0;  
while(size   > 1)  
{  
cksum+=*buffer++;  
size   -=sizeof(USHORT);  
}  
if(size   )  
{  
cksum   +=   *(UCHAR*)buffer;  
}  

cksum   =   (cksum   > >   16)   +   (cksum   &   0xffff);  
cksum   +=   (cksum   > > 16);  
return   (USHORT)(~cksum);  
}  

void   useage()  
{  
printf( "******************************************/n ");  
printf( "TCPPing/n ");  
printf( "/t   Written   by   Refdom/n ");  
printf( "/t   Email:   [email protected]/n ");  
printf( "Useage:   TCPPing.exe   Target_ip   Target_port   /n ");  
printf( "*******************************************/n ");  
}  

int   main(int   argc,   char*   argv[])  
{  
WSADATA   WSAData;  
SOCKET   sock;  
SOCKADDR_IN   addr_in;  
IPHEADER   ipHeader;  
TCPHEADER   tcpHeader;  
PSDHEADER   psdHeader;  

char   szSendBuf[60]={0};  
BOOL   flag;  
int   rect,nTimeOver;  

useage();  

if   (argc!=   3)  
{   return   false;   }  

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)  
{  
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(atoi(argv[2]));  
addr_in.sin_addr.S_un.S_addr=inet_addr(argv[1]);  

//  
//  
//填充IP首部  
ipHeader.h_verlen=(4 < <4   |   sizeof(ipHeader)/sizeof(unsigned   long));  
//   ipHeader.tos=0;  
ipHeader.total_len=htons(sizeof(ipHeader)+sizeof(tcpHeader));  
ipHeader.ident=1;  
ipHeader.frag_and_flags=0;  
ipHeader.ttl=128;  
ipHeader.proto=IPPROTO_TCP;  
ipHeader.checksum=0;  
ipHeader.sourceIP=inet_addr( "本地地址 ");  
ipHeader.destIP=inet_addr(argv[1]);  

//填充TCP首部  
tcpHeader.th_dport=htons(atoi(argv[2]));  
tcpHeader.th_sport=htons(SOURCE_PORT);   //源端口号  
tcpHeader.th_seq=htonl(0x12345678);  
tcpHeader.th_ack=0;  
tcpHeader.th_lenres=(sizeof(tcpHeader)/4 < <4|0);  
tcpHeader.th_flag=2;   //修改这里来实现不同的标志位探测,2是SYN,1是FIN,16是ACK探测   等等  
tcpHeader.th_win=htons(512);  
tcpHeader.th_urp=0;  
tcpHeader.th_sum=0;  

psdHeader.saddr=ipHeader.sourceIP;  
psdHeader.daddr=ipHeader.destIP;  
psdHeader.mbz=0;  
psdHeader.ptcl=IPPROTO_TCP;  
psdHeader.tcpl=htons(sizeof(tcpHeader));  

//计算校验和  
memcpy(szSendBuf,   &psdHeader,   sizeof(psdHeader));  
memcpy(szSendBuf+sizeof(psdHeader),   &tcpHeader,   sizeof(tcpHeader));  
tcpHeader.th_sum=checksum((USHORT   *)szSendBuf,sizeof(psdHeader)+sizeof(tcpHeader));  

memcpy(szSendBuf,   &ipHeader,   sizeof(ipHeader));  
memcpy(szSendBuf+sizeof(ipHeader),   &tcpHeader,   sizeof(tcpHeader));  
memset(szSendBuf+sizeof(ipHeader)+sizeof(tcpHeader),   0,   4);  
ipHeader.checksum=checksum((USHORT   *)szSendBuf,   sizeof(ipHeader)+sizeof(tcpHeader));  

memcpy(szSendBuf,   &ipHeader,   sizeof(ipHeader));  

rect=sendto(sock,   szSendBuf,   sizeof(ipHeader)+sizeof(tcpHeader),  
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;  
}  

你可能感兴趣的:(Raw Socket编程)