再談突破TCP-IP過濾-防火牆進入內網(icmp篇) |
|
conundrum
尊榮會員 發表:893 回覆:1272 積分:643 註冊:2004-01-06 發送簡訊給我 |
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: "< |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |