防火墙阻止tftp_再谈突破TCP-IP过滤/防火墙进入内网(icmp篇)
下载本文示例代码
一 现状
二 ICMP协议转发数据报原理
三 QQicmp工作流程
四 QQicmp代码分析
五 小结
六 QQicmp源代码
一、现状
随着Internet网络的普及,各个中大型公司均建立了自己的局域网络,并与Internet相连接,而公司内部人员上网的限制也逐渐成为一个大家关心的话题。目前最为流行的网络工具大多是基于IP协议的,而其中最主要的两个协议就是TCP和UDP协议。HTTP,FTP等上层协议均是建立在TCP协议之上了,而DNS,ICQ,TFTP等则是建立在UDP协议之上的。往往我们会遇到这样情况:公司禁止了UDP协议(很大一部分的网络通讯软件都是建立在UDP协议之上的),而仅开通了TCP协议。这样,我们就可以通过TCP协议来为我们转发UDP数据报,具体实现原理可以参看eyas的《突破TCP-IP过滤/防火墙进入内网》,里面详细讨论了如何实现TCP与UDP数据报之间的相互转发,在此就不多说了。现在进入正题,如何实现利用ICMP数据报突破网关的限制?
二、ICMP转发数据报原理
ICMP协议(Internet Control Messages Protocol, 网际控制报文协议)是一种多功能的协议,在网络上有很多用处,比如ICMP扫描,拒绝服务(DOS)攻击,隧道攻击,以及我们最常用到的PING程序。而现在就利用ICMP协议来为我们转送UPD/TCP数据(假设本机被禁止了UPD或TCP协议)。大家知道一般的防火墙都是过滤了来自外部主机的回送请求(Echo
Request)报文,也就是我们平时说的PING数据报,但为了让内部主机能够探测外部主机的当前状态,防火墙大都不会过滤回送应答(Echo Reply)数据报,而且ICMP报文可以在广域网上传送,这样我们就可以利用它来突破网关的种种限制。由于本地主机被禁止了UDP/TCP协议,但在网关上却没有被禁止,我们就可以先将UDP/TCP数据报以ICMP的形式发送到网关,然后网关再将它解码,构造成UDP/TCP数据报发送到我们的目的服务器;同样,服务器发送来的UDP/TCP数据报被网关所接收,网关将其解码后,以ICMP的形式发送到本地主机,本机再解码构包后发送到客户端程序,这样就实现了对网关限制的突破,一次发送/接收共需要两次解包和构包。本文主要针对使用ICMP协议来转发
UDP数据报的功能,并以OICQ为背景,至于利用ICMP协议来突破TCP的限制,也大同小异。
三、QQicmp工作流程
以下是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通信。
发送数据报时:首先QQicmp(l)在特定端口监听来自QQ客户端的UDP数据报,解码构包后以ICMP的形式发送到网关;QQicmp(g)在网关上监听来自QQicmp(l)的ICMP数据报,解码构包后以UDP的形式发送到腾讯服务器。
接收数据报时:首先QQicmp(g)在网关上接收来自腾讯服务器的UDP数据报,解码构包后以ICMP的形式发送到QQicmp(l);当QQicmp(l)接收到ICMP数据报后,同样解码构包,然后以UDP的形式发送到QQ客户端。
四、QQicmp代码分析
Win2000/xp都提供了自己构造数据报的功能,也就是我们可以自己定义发送IP数据报的各项内容,当然也可以监听通过主机的基于IP协议的各种数据报。为了发送ICMP数据报及接收所有的IP数据报,我们必须自定义数据报的格式及校验和的求解:ypedef 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,*PIPHEADER;
typedef struct icmpheader
{
unsigned char type; //ICMP类型: 0->回送应答 8->回送请求
unsigned char code; //代码
unsigned short checksum; //ICMP校验和
unsigned short id; //标识符
unsigned short seq; //序号
}ICMPHEADER,*PICMPHEADER;
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)的监听端口,当然你也可以保持默认的8000,这样QQicmp(l)就应该选在8000端口监听QQ客户端的数据。总之,QQ客户端里服务器端口应该和QQicmp(l)里所选的端口相同。同时,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); //并捆绑到指定地址,指定端口上
if(istbcs==0) //填充ICMP数据报头部
{
icmphdr.type=0; //类型:echo reply
icmphdr.code=0; //代码
icmphdr.checksum=0; //先将校验和设置为零
icmphdr.id=htons(65456); //序号
icmphdr.seq=htons(65456); //标志符,用以过滤数据报
memset(msgsend,0,sizeof(msgsend));
memcpy(msgsend,&icmphdr,sizeof(icmphdr));
istbcs =sizeof(icmphdr);
}
memcpy(msgsend istbcs,msgrecv,iret); //将接收到的UDP数据报的内容提取,放到即将发送的ICMP数据报内
icmphdr.checksum=checksum((USHORT *)&msgsend,ileft); //计算ICMP校验和
memcpy(msgsend,&icmphdr,sizeof(icmphdr)); //重新填充ICMP头部
iret=sendto(sock[0][1],msgsend,istbcs,0,(struct sockaddr *)&sin[0][3],addrlen); //发送ICMP数据报网关同时,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头文件。
iret=recvfrom(sock[1][0],msgrecv,sizeof(msgrecv),0,(struct sockaddr *)&temp1,&addrlen); //接收所有数据报
if(iret<=28) //文件过小
{
continue;
}
if((icmphdr->type!=0) || (icmphdr->code!=0) || ((icmphdr->id)!=htons(65456)) || ((icmphdr->seq)!=htons(65456)))
//不符合接收条件
{
continue;
}
memcpy(msgsend istbcs,msgrecv,iret); //将接收到的ICMP数据报的内容提取,准备以UDP的形式发送解包后,用UDP数据报将接收到的来自网关的ICMP数据发送到QQ客户端,
idx=28; //ICMP数据报的前20字节是IP头部,接着的8字节是ICMP头部
iret=sendto(sock[1][1],&msgsend[idx],ileft,0,(struct sockaddr *)&sin[1][3],addrlen); //发送到QQ客户端
我们创建了两个线程在两个方向(udpicmp,icmpudp)上接收并传送数据,如果某个线程出错,就重新创建该线程,而未出错的线程则保持不变,
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<
以上就是QQicmp(l)的工作原理,QQicmp(g)运行在网关上,虽然模式不同,但工作原理是一样的,只是数据报的流动方向有点差异。
五、小结
本文利用了ICMP协议来传输数据,但由于ICMP协议自身的原因,可靠性就不可能得到很好的保证。其实,你还可以用一些其他的方法来突破网关的限制,比如最近网上常谈到的ARP协议在某些情况下就可以使用,当然前提是你要获得网关的某些权限。以上谈到的各种方法,本质上都是利用其他未被禁止的协议来转发被禁止协议需要传送的数据,然后再用原本使用的协议将数据发送到目的主机。
六、附源代码
#include #include #include #define imaxsize 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);
}
int iaddrlen=sizeof(struct sockaddr_in);
SOCKET socki[2][2];
struct sockaddr_in sini[2][4],sag,sal,tempir,tempis;
DWORD WINAPI u2i(LPVOID num)
{
UNREFERENCED_PARAMETER(num);
char msgrecv[imaxsize]={0},msgsend[imaxsize]={0};
fd_set fdread,fdwrite;
int iret,ret,istbcs=0,ileft,idx=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((socki[0][1]=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP))==INVALID_SOCKET)
{
cout<0)
{
if(FD_ISSET(socki[0][0],&fdread))
{
iret=recvfrom(socki[0][0],msgrecv,sizeof(msgrecv),0,(struct sockaddr *)&tempir,&iaddrlen);
if(iret==SOCKET_ERROR)
{
cout<0)
{
if(sini[0][3].sin_addr.s_addr==htonl(0))
{
cout<0)
{
if(FD_ISSET(socki[1][0],&fdread))
{
iret=recvfrom(socki[1][0],msgrecv,sizeof(msgrecv),0,(struct sockaddr *)&tempis,&iaddrlen);
if(iret==SOCKET_ERROR)
{
coutcode!=0) || ((icmphdr->id)!=htons(65456)) || ((icmphdr->seq)!=htons(65456)))
{
break;
}
if((sini[1][0].sin_addr.s_addr!=htonl(0)) && (sini[1][0].sin_addr.s_addr!=tempis.sin_addr.s_addr))
break;
else if(sini[1][0].sin_addr.s_addr==htonl(0))
{
sini[1][0].sin_addr.s_addr=tempis.sin_addr.s_addr;
sini[0][3].sin_addr.s_addr=tempis.sin_addr.s_addr;
}
cout<0)
{
iret=sendto(socki[1][1],&msgsend[idx],ileft,0,(struct sockaddr *)&sini[1][3],iaddrlen);
if(iret==SOCKET_ERROR)
{
cout
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(!ilocal)
{
sini[0][0].sin_addr.s_addr=itarget.sin_addr.s_addr;
sini[0][0].sin_family=AF_INET;
sini[0][0].sin_port=htons(8000);
sini[0][1].sin_addr.s_addr=htonl(INADDR_ANY);
sini[0][1].sin_family=AF_INET;
sini[0][1].sin_port=itarget.sin_port ;
sini[0][2]=sal;
memset(&sini[0][3],0,iaddrlen);
sini[0][3].sin_family=AF_INET;
}
else
{
memset(&sini[0][0],0,iaddrlen);
sini[0][0].sin_family=AF_INET;
sini[0][0].sin_addr.s_addr=inet_addr("127.0.0.1");
sini[0][1].sin_addr.s_addr=htonl(INADDR_ANY);
sini[0][1].sin_family=AF_INET;
sini[0][1].sin_port=itarget.sin_port ;
sini[0][2]=sal;
sini[0][3].sin_addr.s_addr=itarget.sin_addr.s_addr;
sini[0][3].sin_family=AF_INET;
}
sini[1][0]=sini[0][3];
sini[1][1]=sini[0][2];
sini[1][2]=sini[0][1];
sini[1][3]=sini[0][0];
if((socki[0][0]=socket(AF_INET,SOCK_DGRAM,0))==INVALID_SOCKET)
{
cout<
(全文完)
下载本文示例代码
输入您的搜索字词
提交搜索表单
防火墙阻止tftp_再谈突破TCP-IP过滤/防火墙进入内网(icmp篇)相关推荐
- 使用 AIX TCP/IP 过滤功能设置防火墙
简介 一个 POWER 服务器具有承载上百个 LPAR 或分区(一个分区就是操作系统安装的一个独立实例)的处理能力,这意味着,虚拟化功能让您能在一个物理机器中拥有上百个服务器.如果这上百个服务器在处理 ...
- 详细谈电脑ip、域名、内网、外网、localhost、127.0.0.1、网关等通讯基础知识(易懂)
1. ip地址与域名的定义以及其关系 ip地址的定义: IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址. IP地址是IP协议提供的一种统一的地址格 ...
- 七月文章导读【TCP/IP相关】:解密 TCP/IP;什么是公网ip?什么是内网ip?为什么ip地址通常以192.168开头?
Table of Contents 解密 TCP/IP! TCP/IP是什么 网络分层 分层 封装 地址 端口 域名系统 分用 小结 什么是公网ip?什么是内网ip?为什么ip地址通常以192.168 ...
- 【转】:TCP/IP详解学习笔记(4)-ICMP协议,ping和Traceroute
TCP/IP详解学习笔记(4)-ICMP协议,ping和Traceroute 分类: TCP/IP详解学习笔记计算机网络2006-04-20 18:147970人阅读评论(1)收 ...
- 还不懂 TCP/IP 是啥?看这一篇就够了!!!
文章目录 TCP/IP 的历史背景 TCP/IP 标准 TCP/IP 协议簇 通信链路层 物理层 数据链路层 网络层 传输层 应用层 数据包的发送历程 数据包结构 数据包发送历程 我把自己以往的文章汇 ...
- Apache 2.4.7在CentOS6.4中安装配置反向代理解决单外网IP对应多个内网主机的方法实践
欢迎转载,转载时请保留全文及出处. Apache 2.4.7在CentOS6.4中安装配置反向代理解决单外网IP对应多个内网主机的方法实践 Apache安装 下载源程序(http://httpd.ap ...
- GSM模块的GPRS的TCP测试成功-校园网(内网穿透)映射
** GPRS的TCP测试成功-校园网(内网穿透)映射 ** 笔者是基于手上已有SIM800C的GSM模块进行测试,也是经过一番测试才获得成功的测试经历,特此分享. 首先GSM模块的硬件连接就不说了, ...
- 什么是 IP地址 NAT 公网 内网 端口
目录 什么是IPv4? 什么是NAT 公网 内网? 什么是IPv6? 什么是端口? 引言 随着网络信息技术的发展,互联网逐渐庞大,人们在网络上又建立了一个全新的生态环境. 正如每个人的身份证号上都写着 ...
- 推断给定的IP地址是否是内网IP
/*** 推断给定的IP地址是否是内网IP* * @author GaoHuanJie*/ public class Test{public boolean isInnerIP(String ipAd ...
- 防火墙阻止tftp_防火墙TFTP协议处理流程及TFTP ALG应用
一. 协议简介 TFTP ( Trivial File Transfer Protocol, 简单文件传输协议) 是 TCP/IP 协议族中的一个用来 在客户机与服务器之间进行简单文件传输的协议,提供 ...
最新文章
- VMware 虚拟机 无法将Ethernet0连接到虚拟网络VMnet0 问题
- mysql中字典值怎么添加_插入Python字典中的值,包括MySQL的键
- 网络爬虫相关程序学习(包含jar包等)---各大网站网络爬虫
- Ubuntu18.04安装opencv出现的一系列问题解决方法(持续更新~)
- Python编写只允许实例化一个对象的类
- 暴雪2013年内发布linux游戏,只因win8是场灾难?
- 【BZOJ 2957】 2957: 楼房重建 (线段树)
- hdu 4318 Power transmission 临接表 广搜 多校联合赛(二) 第九题
- 单溶水箱串级控制计算机控制,单容水箱液位控制系统
- 【Java架构师入门到精通】java分布式架构有哪些技术
- x509证书cer格式转pem格式
- onlyoffice 在线预览编辑office
- 《SEM长尾搜索营销策略解密》一一2.2 互联网里的长尾更具影响力
- RTA和RTB的区别
- 全网最全java Springboot对接微信公众号开发平台(可能是最全)!
- spark 实现K-means算法
- 杭州云栖·2050大会-团聚召集人手册
- 小巧高效的php框架,猿团YTFramework框架上线 让PHP开发更简单高效
- 计算机读不了硬盘分区,一分钟告诉你硬盘要不要分区,为什么
- 有符号二进制乘法及MATLAB有符号数16进制到2进制的转换问题
热门文章
- Leetcode. 回文字符串的分割和最少分割数
- iOS UI控件(2) UITextView
- mkdir命令(转)
- ubuntu 12.04 修改 grub 启动参数
- 【Vegas原创】windows2008配置、安装Exchange2007证书(for OWA,RPC over HTTP)
- 【jenkins 异常】org.eclipse.jgit.errors.InvalidObjectIdException: Invalid id xxxxx
- Spring事务原理分析(二)--@Transactional事务属性的解析
- concurrentHashMap扩容细节
- Web开发之Servlet
- mysql 索引优化分析