|
![]() | 作者: boydream [boydream]
![]() |
登录 |
随着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客户端 < --- > QQicmp(l) < --- > QQicmp(g) < --- > 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: "<<GetLastError()<<endl; return -1; } log=dwret-WAIT_OBJECT_0; if(log==0) //线程0结束 { CloseHandle(hthreads[0]); //关闭线程handle closesocket(sock[0][1]); //关闭套接字 hthreads[0]=CreateThread(NULL,0,u2i,(LPVOID)0,NULL,&hthreadid[0]); //重新创建线程0 } else if(log==1) //线程1结束 { CloseHandle(hthreads[1]); closesocket(sock[1][0]); hthreads[1]=CreateThread(NULL,0,i2u,(LPVOID)1,NULL,&hthreadid[1]); } 以上就是QQicmp(l)的工作原理,QQicmp(g)运行在网关上,虽然模式不同,但工作原理是一样的 ,只是数据报的流动方向有点差异。 QQicmp之源代码如下: #include <iostream.h> #include <winsock2.h> #include <windows.h> #include <mstcpip.h> #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<<" ---------------------------------------------------"<<endl; } void usage() { cout<<"\nUsage:\r\n\tQQicmp -l[-g] ip port"<<endl; cout<<"\tQQicmp -h"<<endl; cout<<"Example:\r\n"; cout<<"\tQQicmp -l 192.168.0.1 8000"<<endl; cout<<"\tQQicmp -g 61.144.238.156 11282"<<endl; cout<<"Attention:"<<endl; cout<<"\t选项 -l : 运行于本机上,ip填网关地址,port为本地监听客户端端口;"<<endl; cout<<"\t选项 -g : 运行于网关上,ip填腾讯服务器地址,port为自定义端口;"<<endl; cout<<"\t选项 -h : 查看相关帮助文件。"<<endl; } int addrlen=sizeof(struct sockaddr_in); SOCKET sock[2][2]; struct sockaddr_in sin[2][4],sag,sal,tempr,temps; DWORD WINAPI u2i(LPVOID num) { UNREFERENCED_PARAMETER(num); char msgrecv[maxsize]={0},msgsend[maxsize]={0}; fd_set fdread,fdwrite; int iret,ret,istbcs=0; struct icmpheader icmphdr; memset(&icmphdr,0,sizeof(icmphdr)); icmphdr.code=0; icmphdr.id=htons(65456); icmphdr.seq=htons(65456); icmphdr.type=0; icmphdr.checksum=checksum((unsigned short *)&icmphdr,sizeof(icmphdr)); if((sock[0][1]=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP))==INVALID_SOCKET) { cout<<"Socket sock[0][1] Error: "<<GetLastError()<<endl; return -1; } if(bind(sock[0][1],(struct sockaddr *)&sin[0][2],addrlen)==SOCKET_ERROR) { cout<<"Bind sock[0][1] Error: "<<GetLastError()<<endl; return -1; } while(1) { FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_SET(sock[0][0],&fdread); FD_SET(sock[0][1],&fdwrite); if((ret=select(0,&fdread,&fdwrite,NULL,NULL))==SOCKET_ERROR) { cout<<"Select in thread 0 Error: "<<GetLastError()<<endl; break; } if(ret>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: "<<GetLastError()<<endl; break; } else if(iret==0) { cout<<"Iret==0"<<endl; break; } cout<<"\nThread 0 Recv "<<iret<<" bytes from\t"<<inet_ntoa(tempr.sin_addr)<<endl; if(istbcs==0) { memset(msgsend,0,sizeof(msgsend)); memcpy(msgsend,&icmphdr,sizeof(icmphdr)); istbcs+=sizeof(icmphdr); } memcpy(msgsend+istbcs,msgrecv,iret); istbcs+=iret; memset(msgrecv,0,sizeof(msgrecv)); } else if(FD_ISSET(sock[0][1],&fdwrite)) { while(istbcs>0) { if(sin[0][3].sin_addr.s_addr==htonl(0)) { cout<<"sin[0][3].sin_addr.s_addr==htonl(0)"<<endl; istbcs=0; memset(msgsend,0,sizeof(msgsend)); break; } iret=sendto(sock[0][1],msgsend,istbcs,0,(struct sockaddr *)&sin[0][3],addrlen); if(iret==SOCKET_ERROR) { cout<<"Sendto sock[0][1] Error: "<<GetLastError()<<endl; break; } cout<<"Thread 0 send "<<iret<<" bytes to \t"<<inet_ntoa(sin[0][3].sin_addr)<<endl; istbcs-=iret; } memset(msgsend,0,sizeof(msgsend)); istbcs=0; } Sleep(20); } } return 0; } DWORD WINAPI i2u(LPVOID num) { UNREFERENCED_PARAMETER(num); fd_set fdread,fdwrite; char msgrecv[maxsize]={0},msgsend[maxsize]={0}; int ret,iret,idx,istbcs=0,ileft; DWORD dwbufferlen[10]; DWORD dwbufferinlen=1; DWORD dwbytesreturned=0; struct ipheader *iphdr; struct icmpheader *icmphdr; if((sock[1][0]=socket(AF_INET,SOCK_RAW,IPPROTO_IP))==INVALID_SOCKET) { cout<<"Socket sock[1][0] Error: "<<GetLastError()<<endl; return -1; } if(bind(sock[1][0],(struct sockaddr *)&sin[1][1],addrlen)==SOCKET_ERROR) { cout<<"Bind sock[1][0] Error: "<<GetLastError()<<endl; return -1; } WSAIoctl(sock[1][0],SIO_RCVALL,&dwbufferinlen,sizeof(dwbufferinlen), &dwbufferlen,sizeof(dwbufferlen),&dwbytesreturned,NULL,NULL); iphdr=(struct ipheader *)msgrecv; icmphdr=(struct icmpheader *)(msgrecv+sizeof(struct ipheader)); while(1) { FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_SET(sock[1][0],&fdread); FD_SET(sock[1][1],&fdwrite); if((ret=select(0,&fdread,&fdwrite,NULL,NULL))==SOCKET_ERROR) { cout<<"Select in thread 1 Error: "<<GetLastError()<<endl; break; } if(ret>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: "<<GetLastError()<<endl; break; } if(iret<=28) { break; } if((icmphdr->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] ==> "<<inet_ntoa(sin[0][3].sin_addr)<<endl; } cout<<"\nThread 1 Recv "<<iret<<" bytes from \t" <<inet_ntoa(temps.sin_addr)<<endl; memcpy(msgsend+istbcs,msgrecv,iret); istbcs+=iret; memset(msgrecv,0,sizeof(msgrecv)); } } else if(FD_ISSET(sock[1][1],&fdwrite)) { ileft=istbcs-28; idx=28; while(ileft>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: "<<GetLastError()<<endl; break; } cout<<"Thread 1 send "<<iret<<" bytes to \t"<<inet_ntoa(sin[1][3]. sin_addr)<<endl; ileft-=iret; idx+=iret; } istbcs=0; memset(msgsend,0,sizeof(msgsend)); } Sleep(20); } } return 0; } int main(int argc,char *argv[]) { WSADATA wsa; BOOL gl; HANDLE hthreads[2]; DWORD hthreadid[2]; struct hostent *hp; char cname[100]; int dwret,log; system("cls.exe"); start(); if(argc==2) { if(strcmp(argv[1],"-h")==0) { ShellExecute(NULL,"open","help.txt",NULL,NULL,SW_SHOWMAXIMIZED); return 0; } else { usage(); return -1; } } else if(argc!=4) { usage(); return -1; } if(!strcmp(argv[1],"-g")) gl=true; else if(!strcmp(argv[1],"-l")) gl=false; else { usage(); return -1; } if(WSAStartup(MAKEWORD(2,2),&wsa)!=0) { cout<<"WSAStartup Error: "<<GetLastError()<<endl; return -1; } gethostname(cname,sizeof(cname)); hp=gethostbyname(cname); for(int ipnum=0;hp->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: "<<GetLastError()<<endl; return -1; } if(bind(sock[0][0],(struct sockaddr *)&sin[0][1],addrlen)==SOCKET_ERROR) { cout<<"Bind sock[0][0] Error: "<<GetLastError()<<endl; return -1; } sock[1][1]=sock[0][0]; cout<<"\n正常工作中..."<<endl; hthreads[0]=CreateThread(NULL,0,u2i,(LPVOID)0,NULL,&hthreadid[0]); hthreads[1]=CreateThread(NULL,0,i2u,(LPVOID)1,NULL,&hthreadid[1]); while(1) { dwret=WaitForMultipleObjects(2,hthreads,false,INFINITE); if(dwret==WAIT_FAILED) { cout<<"WaitForMultipleObjects Error: "<<GetLastError()<<endl; return -1; } log=dwret-WAIT_OBJECT_0; if(log==0) { CloseHandle(hthreads[0]); closesocket(sock[0][1]); hthreads[0]=CreateThread(NULL,0,u2i,(LPVOID)0,NULL,&hthreadid[0]); } else if(log==1) { CloseHandle(hthreads[1]); closesocket(sock[1][0]); hthreads[1]=CreateThread(NULL,0,i2u,(LPVOID)1,NULL,&hthreadid[1]); } else { for(int no1=0;no1<2;no1++) { CloseHandle(hthreads[no1]); for(int no2=0;no2<2;no2++) closesocket(sock[no1][no2]); } } } WSACleanup(); return 0; } 本文相关软件T-QQ主要针对禁止使用QQ的网关,提供UDP,TCP及ICMP数据报转发功能,本软件同样适用于各种基于UDP协议的通信软件。其中的TCP数据报转发功能,也可以使用UDP数据报来转发TCP数据。 |
地主 发表时间: 10/11 23:38 |
|
20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon
粤ICP备05087286号