王朝网络
分享
 
 
 

再谈突破TCP-IP过滤/防火墙进入内网(icmp篇)

王朝vc·作者佚名  2006-01-17
宽屏版  字体: |||超大  

再谈突破TCP-IP过滤/防火墙进入内网(icmp篇)

作者:TOo2y

一 现状

二 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客户端 <--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通信。

发送数据报时:首先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(size0) //各位求和

{

cksum+=*buffer++;

size-=sizeof(unsigned short);

}

if(size)

cksum+=*(unsigned char *)buffer;

cksum=(cksum16)+(cksum & 0xffff); //移位,位与运算

cksum+=(cksum16);

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(irettype!=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客户端

我们创建了两个线程在两个方向(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)运行在网关上,虽然模式不同,但工作原理是一样的,只是数据报的流动方向有点差异。

五、小结

本文利用了ICMP协议来传输数据,但由于ICMP协议自身的原因,可靠性就不可能得到很好的保证。其实,你还可以用一些其他的方法来突破网关的限制,比如最近网上常谈到的ARP协议在某些情况下就可以使用,当然前提是你要获得网关的某些权限。以上谈到的各种方法,本质上都是利用其他未被禁止的协议来转发被禁止协议需要传送的数据,然后再用原本使用的协议将数据发送到目的主机。

六、附源代码

#include <iostream.h

#include <winsock2.h

#include <mstcpip.h

#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(size0)

{

cksum+=*buffer++;

size-=sizeof(unsigned short);

}

if(size)

cksum+=*(unsigned char *)buffer;

cksum=(cksum16)+(cksum & 0xffff);

cksum+=(cksum16);

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<<"Socket socki[0][1] Error: "<<GetLastError()<<endl;

return -1;

}

if(bind(socki[0][1],(struct sockaddr *)&sini[0][2],iaddrlen)==SOCKET_ERROR)

{

cout<<"Bind socki[0][1] Error: "<<GetLastError()<<endl;

return -1;

}

while(1)

{

FD_ZERO(&fdread);

FD_ZERO(&fdwrite);

FD_SET(socki[0][0],&fdread);

FD_SET(socki[0][1],&fdwrite);

if((ret=select(0,&fdread,&fdwrite,NULL,NULL))==SOCKET_ERROR)

{

cout<<"Select in thread 0 Error: "<<GetLastError()<<endl;

break;

}

if(ret0)

{

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<<"\nRecvfrom socki[0][0] Error: "<<GetLastError()<<endl;

break;

}

else if(iret==0)

{

break;

}

if(tempir.sin_port!=sini[0][0].sin_port)

{

sini[0][0].sin_port=tempir.sin_port;

sini[1][3].sin_port=tempir.sin_port;

}

cout<<"\nThread 0 Recv "<<iret<<" bytes from\t"<<inet_ntoa(tempir.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(socki[0][1],&fdwrite))

{

ileft=istbcs;

idx=0;

while(ileft0)

{

if(sini[0][3].sin_addr.s_addr==htonl(0))

{

cout<<"sini[0][3].sin_addr.s_addr==htonl(0)"<<endl;

istbcs=0;

memset(msgsend,0,sizeof(msgsend));

break;

}

iret=sendto(socki[0][1],&msgsend[idx],ileft,0,(struct sockaddr *)&sini[0][3],iaddrlen);

if(iret==SOCKET_ERROR)

{

cout<<"Sendto socki[0][1] Error: "<<GetLastError()<<endl;

break;

}

else if(iret==0)

break;

cout<<"Thread 0 send "<<iret<<" bytes to \t"<<inet_ntoa(sini[0][3].sin_addr)<<endl;

ileft-=iret;

idx+=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[imaxsize]={0},msgsend[imaxsize]={0};

int ret,iret,idx,istbcs=0,ileft;

DWORD dwbufferlen[10];

DWORD dwbufferinlen=1;

DWORD dwbytesreturned=0;

struct ipheader *iphdr;

struct icmpheader *icmphdr;

if((socki[1][0]=socket(AF_INET,SOCK_RAW,IPPROTO_IP))==INVALID_SOCKET)

{

cout<<"Socket socki[1][0] Error: "<<GetLastError()<<endl;

return -1;

}

if(bind(socki[1][0],(struct sockaddr *)&sini[1][1],iaddrlen)==SOCKET_ERROR)

{

cout<<"Bind socki[1][0] Error: "<<GetLastError()<<endl;

return -1;

}

WSAIoctl(socki[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(socki[1][0],&fdread);

FD_SET(socki[1][1],&fdwrite);

if((ret=select(0,&fdread,&fdwrite,NULL,NULL))==SOCKET_ERROR)

{

cout<<"Select in thread 1 Error: "<<GetLastError()<<endl;

break;

}

if(ret0)

{

if(FD_ISSET(socki[1][0],&fdread))

{

iret=recvfrom(socki[1][0],msgrecv,sizeof(msgrecv),0,(struct sockaddr *)&tempis,&iaddrlen);

if(iret==SOCKET_ERROR)

{

cout<<"Recvfrom socki[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((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<<"\nThread 1 Recv "<<iret<<" bytes from \t"<<inet_ntoa(tempis.sin_addr)<<endl;

memcpy(msgsend+istbcs,msgrecv,iret);

istbcs+=iret;

memset(msgrecv,0,sizeof(msgrecv));

}

else if(FD_ISSET(socki[1][1],&fdwrite))

{

ileft=istbcs-28;

idx=28;

while(ileft0)

{

iret=sendto(socki[1][1],&msgsend[idx],ileft,0,(struct sockaddr *)&sini[1][3],iaddrlen);

if(iret==SOCKET_ERROR)

{

cout<<"Sendto socki[1][1] Error: "<<GetLastError()<<endl;

break;

}

else if(iret==0)

break;

cout<<"Thread 1 send "<<iret<<" bytes to \t"<<inet_ntoa(sini[1][3].sin_addr)<<endl;

ileft-=iret;

idx+=iret;

}

istbcs=0;

memset(msgsend,0,sizeof(msgsend));

}

Sleep(20);

}

}

return 0;

}

void QQicmp(struct sockaddr_in itarget,BOOL ilocal)

{

HANDLE ithreads[2];

DWORD ithreadid[2];

struct hostent *hp;

char cname[100];

int dwret,log;

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(ipnum1)

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<<"Socket socki[0][0] Error: "<<GetLastError()<<endl;

return ;

}

if(bind(socki[0][0],(struct sockaddr *)&sini[0][1],iaddrlen)==SOCKET_ERROR)

{

cout<<"Bind socki[0][0] Error: "<<GetLastError()<<endl;

return ;

}

socki[1][1]=socki[0][0];

cout<<"\n正常工作中..."<<endl;

ithreads[0]=CreateThread(NULL,0,u2i,(LPVOID)0,NULL,&ithreadid[0]);

ithreads[1]=CreateThread(NULL,0,i2u,(LPVOID)1,NULL,&ithreadid[1]);

while(1)

{

dwret=WaitForMultipleObjects(2,ithreads,false,INFINITE);

if(dwret==WAIT_FAILED)

{

cout<<"WaitForMultipleObjects Error: "<<GetLastError()<<endl;

return ;

}

log=dwret-WAIT_OBJECT_0;

if(log==0)

{

CloseHandle(ithreads[0]);

closesocket(socki[0][1]);

ithreads[0]=CreateThread(NULL,0,u2i,(LPVOID)0,NULL,&ithreadid[0]);

}

else if(log==1)

{

CloseHandle(ithreads[1]);

closesocket(socki[1][0]);

ithreads[1]=CreateThread(NULL,0,i2u,(LPVOID)1,NULL,&ithreadid[1]);

}

else

{

for(int no1=0;no1<2;no1++)

{

CloseHandle(ithreads[no1]);

for(int no2=0;no2<2;no2++)

closesocket(socki[no1][no2]);

}

}

}

return ;

}

(全文完)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有