線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:1476
推到 Plurk!
推到 Facebook!

再談突破TCP-IP過濾-防火牆進入內網(icmp篇)

 
conundrum
尊榮會員


發表:893
回覆:1272
積分:643
註冊:2004-01-06

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-09-23 15:10:58 IP:61.221.xxx.xxx 未訂閱
 http://www.xfocus.net/articles/200208/442.html
再談突破TCP-IP過濾-防火牆進入內網(icmp篇)
創建時間:2002-08-25
文章屬性:原創
文章提交:TOo2y (too2y_at_safechina.net)    再談突破TCP-IP過濾/防火牆進入內網(icmp篇)    Author:T0o2y [原創]
E-mail: nightcolor@hotmail.com
HomePage: www.safechina.net
Date: 8-25-2002        隨著Internet網絡的普及,各個中大型公司均建立了自己的局域網絡。而公司內部人員上網的限制也逐漸成為一個大家關心的話題。目前最為流行的網絡工具大多是基於TCP/IP協議的,而其中最主要的兩個協議就是TCP和UDP協議。HTTP,FTP等上層協議均是建立在TCP協議之上了,而DNS,ICQ,TFTP等則是建立在UDP協議之上的。往往我們會遇到這樣情況:公司禁止了UDP協議,因為很大一部分的網絡通訊軟件都是建立在UDP協議之上的,而開通了TCP協議。這樣,我們就可以通過TCP協議來為我們轉發UDP數據報,具體實現原理可以參看eyas的《突破TCP-IP過濾/防火牆進入內網》,裡面詳細討論了如何實現TCP與UDP數據報之間的相互轉發,也可以參看本文相關軟件T-QQ的源代碼,裡面也包含了TCP與UDP相互轉發的功能,在此就不多說了。現在進入正題,如何實現用ICMP數據報來突破網關的限制?        ICMP協議(Internet Control Messages Protocol, 網際控制報文協議)是一種多功能的協議,在網絡上有很多用處,比如ICMP掃瞄,拒絕服務(DOS)攻擊,隧道攻擊,以及我們最常用到的PING程序。而我們就是利用ICMP協議來為我們傳送(TCP/UDP)數據。大家知道一般的防火牆都是過濾了來自外部主機的回送請求(echo Request)報文,但為了是自己能夠探測外部主機的當前狀態,防火牆都不會過濾掉回送應答(echo Reply)數據報,而且ICMP報文可以在廣域網上傳送,這樣我們就可以利用它來突破網關的種種限制。本文主要針對使用ICMP協議來轉發UDP數據報的功能,並以OICQ為背景,至於突破TCP的限制,也大同小異。
    以下是QQicmp的工作原理:                                ----->-----                    ----->-----                         ----->-----
        QQ客戶端 <     UDP     > QQicmp(l) <    ICMP     > QQicmp(g) <     UDP     >Tencent服務器
                            -----<-----                     -----<-----                         -----<-----          其中QQ客戶端和QQicmp(l)都運行在本機上,而QQicmp(g)則是運行在網關上(QQicmp(l) 與 QQicmp(g)均是同一程序,只是運行模式不同:-l 運行於本地主機, -g 運行於網關上),Tencent服務器我想大家都清楚吧。QQ客戶端與QQicmp(l),QQicmp(g)與Tencent服務器之間以UDP通信,QQicmp(l)與QQicmp(g)之間則是以ICMP通信。  Win2000/xp都提供了自己構造數據報的功能,也就是我們可以自己定義發送數據報的各項內容,當然也可以監聽通過主機的基於IP協議的各種數據報。為了發送ICMP數據報及接收所有的IP數據報,我們必須自定義數據報的格式及校驗和的求解:
    typedef struct ipheader
    {
    unsigned char  h_lenver;          //頭部長度及版本
    unsigned char  tos;               //服務類型
    unsigned short total_len;         //報文總長度
    unsigned short ident;             //信息包標誌
    unsigned short frag_and_flags;    //標誌及分段偏移量
    unsigned char  ttl;               //生命週期
    unsigned char  proto;             //協議類型
    unsigned short checksum;          //IP校驗和  
    unsigned int   sourceip;          //源IP地址  
    unsigned int   destip;            //目的IP地址 
    }ipheader;        typedef struct icmpheader
    {
    unsigned char   type;             //ICMP類型 0->回送應答 8->回送請求
    unsigned char   code;             //代碼
    unsigned short  checksum;         //校驗和
    unsigned short  seq;              //序號
    unsigned short  id;               //標識符  
    }icmpheader;        unsigned short checksum(unsigned short *buffer,int size)
    {
    unsigned long cksum=0;
    while(size>0)                         //各位求和
    {
        cksum =*buffer  ;
        size-=sizeof(unsigned short);
    }
    if(size)
        cksum =*(unsigned char *)buffer;
    cksum=(cksum>>16) (cksum & 0xffff);
    cksum =(cksum>>16);
    return (unsigned short)(~cksum);      //再求補
    }        首先,我們更改QQ客戶端裡的服務器地址為127.0.0.1,端口改為QQicmp(l)的監聽QQ客戶端端口,當然你也可以保持默認的8000,這樣QQicmp(l)就應該在8000端口監聽QQ客戶端的數據。同時,QQ客戶端也在端口4000(假設為非內網主機上的第一個QQ)監聽來自QQicmp(l)的數據報。
    我們可以看到,QQicmp(l)的主要作用就是將接收到了來自QQ客戶端的UPD數據報,
       sock[0][0]=socket(AF_INET,SOCK_DGRAM,0);              //創建基於UDP協議的套接字
       bind(sock[0][0],(struct sockaddr *)&sin[0][1],addrlen);           //綁定到指定地址,指定端口上
       iret=recvfrom(sock[0][0],msgrecv,sizeof(msgrecv),0,(struct sockaddr *)&tempr,&addrlen);   //接收來自QQ客戶端的UDP數據        然後以ICMP數據報的形式發送到QQicmp(g),在此需要自己構造ICMP echo Reply數據報,並將接收到的UDP數據報填充到ICMP報文的數據段,
    sock[0][1]=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);           //創建ICMP協議的原始套接字,用來發送自定義數據報
    bind(sock[0][1],(struct sockaddr *)&sin[0][2],addrlen);     //並捆綁到指定地址,指定端口上           icmphdr.type=0;    //類型:echo reply
    icmphdr.code=0;    //代碼
    icmphdr.id=htons(65456);   //序號
    icmphdr.seq=htons(65456);  //標誌符,用以過濾數據報
    icmphdr.checksum=0;        if(istbcs==0)     //填充ICMP數據報頭部
    {
          memset(msgsend,0,sizeof(msgsend));
             memcpy(msgsend,&icmphdr,sizeof(icmphdr));
          istbcs =sizeof(icmphdr);
    }
    memcpy(msgsend istbcs,msgrecv,iret);  //將接收到的UDP數據報的內容提取,準備以ICMP的形式發送        iret=sendto(sock[0][1],msgsend,istbcs,0,(struct sockaddr *)&sin[0][3],addrlen); //發送到網關
    同時,QQicmp(l)監聽通過本機的IP數據報,篩選出來自QQicmp(g)及網關的數據報,
    sock[1][0]=socket(AF_INET,SOCK_RAW,IPPROTO_IP);            //創建原始套接字,接收所有的IP數據報
    bind(sock[1][0],(struct sockaddr *)&sin[1][1],addrlen);    //綁定到指定地址,端口        DWORD   dwbufferlen[10];
    DWORD   dwbufferinlen=1;
    DWORD   dwbytesreturned=0;
    WSAIoctl(sock[1][0],SIO_RCVALL,&dwbufferinlen,sizeof(dwbufferinlen),&dwbufferlen,sizeof(dwbufferlen),&dwbytesreturned,NULL,NULL);
       //設置為接收所有的數據報,需要mstcpip.h頭文件,T-QQ相關文件裡就有,或安裝SDK        iret=recvfrom(sock[1][0],msgrecv,sizeof(msgrecv),0,(struct sockaddr *)&temps,&addrlen);   //接收所有數據報
    if(iret<=28)     //文件過小
    {
        break;
    }
    if((icmphdr->type!=0) || (icmphdr->code!=0) || ((icmphdr->id)!=htons(65456)) || ((icmphdr->seq)!=htons(65456)))  //不符合接收條件
    {
        break;
    }        memcpy(msgsend istbcs,msgrecv,iret);    //將接收到的ICMP數據報的內容提取,準備以UDP的形式發送
     解包後,用UDP數據報將接收到的來自網關的數據發送到QQ客戶端,
    idx=28;    //ICMP數據報的前20字節是IP頭部,接著的8字節是ICMP頭部,
    iret=sendto(sock[1][1],&msgsend[idx],ileft,0,(struct sockaddr *)&sin[1][3],addrlen);    //發送到QQ客戶端
       
     我們創建了兩個線程在兩個方向(udp-->icmp,icmp-->udp)上接收並傳送數據,如果某個線程出錯,就重新創建該線程,而未出錯的線程則保持不變,
    hthreads[0]=CreateThread(NULL,0,u2i,(LPVOID)0,NULL,&hthreadid[0]);     //創建udp接收數據,icmp發送數據的線程0
    hthreads[1]=CreateThread(NULL,0,i2u,(LPVOID)1,NULL,&hthreadid[1]);     //創建icmp接收數據,udp發送數據的線程1        while(1)
    {
    dwret=WaitForMultipleObjects(2,hthreads,false,INFINITE);                //等待某個線程的結束
    if(dwret==WAIT_FAILED)                                                  //等待出錯
    {
        cout<<"WaitForMultipleObjects Error: "<
#include 
#include 
#include     #pragma comment (lib,"ws2_32")
#define maxsize 64*1024    typedef struct ipheader
{
    unsigned char  h_lenver;
    unsigned char  tos;
    unsigned short total_len;
    unsigned short ident;
    unsigned short frag_and_flags;
    unsigned char  ttl;
    unsigned char  proto;
    unsigned short checksum;
    unsigned int   sourceip;
    unsigned int   destip;
}ipheader;    typedef struct icmpheader
{
    unsigned char   type;
    unsigned char   code;
    unsigned short  checksum;
    unsigned short  seq;
    unsigned short  id;
}icmpheader;    unsigned short checksum(unsigned short *buffer,int size)
{
    unsigned long cksum=0;
    while(size>0)
    {
        cksum =*buffer  ;
        size-=sizeof(unsigned short);
    }
    if(size)
        cksum =*(unsigned char *)buffer;
    cksum=(cksum>>16) (cksum & 0xffff);
    cksum =(cksum>>16);
    return (unsigned short)(~cksum);
}    void start()
{
    cout<<"            ---------------------------------------------------\n";
    cout<<"              ||                                                  || \n";
    cout<<"              ||             QQicmp  (ICMP轉發)             || \n";
    cout<<"              ||                                                  || \n";
    cout<<"              ||         Author:TOo2y     SafeChina        || \n";
    cout<<"              ||                                                  || \n";
    cout<<"            ---------------------------------------------------"<0)
        {
            if(FD_ISSET(sock[0][0],&fdread))
            {
                iret=recvfrom(sock[0][0],msgrecv,sizeof(msgrecv),0,(struct sockaddr *)&tempr,&addrlen);
                if(iret==SOCKET_ERROR)
                {
                    cout<<"\nRecvfrom sock[0][0] Error: "<0)   
                {                        if(sin[0][3].sin_addr.s_addr==htonl(0))            
                {
                    cout<<"sin[0][3].sin_addr.s_addr==htonl(0)"<0)
        {
            if(FD_ISSET(sock[1][0],&fdread))
            {
                {
                iret=recvfrom(sock[1][0],msgrecv,sizeof(msgrecv),0,(struct sockaddr *)&temps,&addrlen);
                if(iret==SOCKET_ERROR)
                {
                    cout<<"Recvfrom sock[1][0] Error: "<type!=0) || (icmphdr->code!=0) || ((icmphdr->id)!=htons(65456)) || ((icmphdr->seq)!=htons(65456)))
                {
                    break;
                }
                if((sin[1][0].sin_addr.s_addr!=htonl(0)) && (sin[1][0].sin_addr.s_addr!=temps.sin_addr.s_addr))
                    break;
                }
                else if(sin[1][0].sin_addr.s_addr==htonl(0))           
                {
                    sin[1][0].sin_addr.s_addr=temps.sin_addr.s_addr;
                    sin[0][3].sin_addr.s_addr=temps.sin_addr.s_addr;
                    cout<<"sin[0][3] ==> "<0)
                {
                    iret=sendto(sock[1][1],&msgsend[idx],ileft,0,(struct sockaddr *)&sin[1][3],addrlen);
                    if(iret==SOCKET_ERROR)
                    {
                        cout<<"Sendto sock[1][1] Error: "<h_addr_list[ipnum]!=NULL;ipnum  )
        sag.sin_addr=*(in_addr *)hp->h_addr_list[ipnum];
    sag.sin_family=AF_INET;
    sag.sin_port=htons(65456);        sal=sag;
       if(ipnum>1)
        sal.sin_addr=*(in_addr *)hp->h_addr_list[ipnum-2];        if(gl)
    {
        sin[0][0].sin_addr.s_addr=inet_addr(argv[2]);
        sin[0][0].sin_family=AF_INET;
        sin[0][0].sin_port=htons(8000);            sin[0][1].sin_addr.s_addr=htonl(INADDR_ANY);       
        sin[0][1].sin_family=AF_INET;
        sin[0][1].sin_port=htons(atoi(argv[3]));               sin[0][2]=sal;                                                memset(&sin[0][3],0,sizeof(sin[0][3]));            
        sin[0][3].sin_family=AF_INET;
    }
    else
    {
        sin[0][0].sin_addr.s_addr=inet_addr("127.0.0.1");
        sin[0][0].sin_family=AF_INET;
        sin[0][0].sin_port=htons(4000);                            sin[0][1].sin_addr.s_addr=htonl(INADDR_ANY);
        sin[0][1].sin_family=AF_INET;
        sin[0][1].sin_port=htons(atoi(argv[3]));            sin[0][2]=sal;                                               sin[0][3].sin_addr.s_addr=inet_addr(argv[2]);
        sin[0][3].sin_family=AF_INET;
    }
       sin[1][0]=sin[0][3];       
       sin[1][1]=sin[0][2];  
       sin[1][2]=sin[0][1];   
       sin[1][3]=sin[0][0];                  if((sock[0][0]=socket(AF_INET,SOCK_DGRAM,0))==INVALID_SOCKET)
    {
        cout<<"Socket sock[0][0] Error: "<     
        
系統時間:2024-05-09 9:46:13
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!