通常防火墙默认是允许用户使用Ping一个网络地址的,而我们知道Ping的过程其实是发送和接收ICMP报文的过程。我们看一下ICMP报文结构:
ICMP 8 | 代码 8
校 验 和 16
标 识 符 16
序 列 码 16
当类型为8时是一个echo请求报文,当类型为0时是一个echo应答报文。Ping的时候发送的类型为8 接收的响应为0,所以防火墙对ICMP类型为0的报文是没有防的。这样我们就可以从这里着手做一些东西了。
先看代码,分为服务端和客户端。
- //ICMP Server.cpp
- #include <afxwin.h>
- #include <winsock2.h>
- #include <stdio.h>
- #include <urlmon.h>
- #include <tlhelp32.h>
- #pragma comment(lib, "Urlmon.lib")
- #pragma comment(lib, "ws2_32.lib")
- #define STATUS_FAILED 0xFFFF
- #define MAX_PACKET 256
- #define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
- #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)
- /* The IP header */
- typedef struct iphdr
- {
- unsigned char h_verlen; //4位首部长度+IP版本号,4表示IPV4
- unsigned char tos; //8位服务类型TOS
- unsigned short total_len; //16位总长度(字节)
- unsigned short ident; //16位标识
- unsigned short frag_and_flags; //3位标志位
- unsigned char ttl; //8位生存时间 TTL
- unsigned char proto; //8位协议 (TCP, UDP 或其他)
- unsigned short checksum; //16位IP首部校验和
- unsigned int sourceIP; //32位源IP地址
- unsigned int destIP; //32位目的IP地址
- }IpHeader;
- //定义ICMP首部
- typedef struct _ihdr
- {
- BYTE i_type; //8位类型
- BYTE i_code; //8位代码
- USHORT i_cksum; //16位校验和
- USHORT i_id; //识别号(一般用进程号作为识别号)
- USHORT i_seq; //报文序列号
- ULONG timestamp; //时间戳
- }IcmpHeader;
- char buffer[2048] = {0};//回传数据缓冲
- void decode_resp(char *,int ,struct sockaddr_in *);//ICMP解包函数
- void fill_icmp_data(char * icmp_data); //icmp报文填充
- void sendICMP(void); //发送
- char *ICMP_DEST_IP;
- USHORT checksum(USHORT *buffer, int size); //校验位计算
- int main(int argc,char *argv[])
- {
- char *icmp_data;
- int bread,datasize,retval;
- SOCKET sockRaw = (SOCKET)NULL;
- WSADATA wsaData;
- int timeout = 2000;
- char *recvbuf;
- DWORD dwBufferLen[10] ;
- DWORD dwBufferInLen = 1 ;
- DWORD dwBytesReturned = 0 ;
- struct sockaddr_in from, local;
- int fromlen = sizeof(struct sockaddr_in);
- if ((retval = WSAStartup(MAKEWORD(2,1),&wsaData)) != 0)
- {
- printf("WSAStartup failed: %s/n",retval);
- ExitProcess(STATUS_FAILED);
- }
- //获取本机IP地址
- char FAR name[MAX_PATH];
- gethostname(name, MAX_PATH);
- struct hostent FAR * pHostent;
- pHostent = (struct hostent * )malloc(sizeof(struct hostent));
- pHostent = gethostbyname(name);
- sockRaw = WSASocket (AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED);
- if (sockRaw == INVALID_SOCKET)
- {
- printf("WSASocket() failed: %s/n",WSAGetLastError());
- ExitProcess(STATUS_FAILED);
- }
- bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));
- if(bread == SOCKET_ERROR)
- goto EXIT_SEGMENT;
- datasize=0;
- datasize += sizeof(IcmpHeader);
- icmp_data =(char*)xmalloc(MAX_PACKET);
- recvbuf = (char*)xmalloc(MAX_PACKET);
- if (!icmp_data)
- {
- goto EXIT_SEGMENT;;
- }
- memset(icmp_data,0,MAX_PACKET);
- local.sin_family=AF_INET;
- local.sin_port= 0;
- local.sin_addr.S_un.S_addr = *(ULONG*)pHostent->h_addr_list[0];
- if(bind(sockRaw, (struct sockaddr*)&local,sizeof(struct sockaddr)) == SOCKET_ERROR)
- {
- printf("bind errcode = %d/n", WSAGetLastError());
- goto EXIT_SEGMENT;
- }
- //设置SOCK_RAW为SIO_RCVALL,以便接收所有的IP包
- if(WSAIoctl(sockRaw,SIO_RCVALL,&dwBufferInLen, /
- sizeof(dwBufferInLen),&dwBufferLen, /
- sizeof(dwBufferLen),&dwBytesReturned , NULL , NULL ) == SOCKET_ERROR)
- {
- printf("WSAIoctl errcode = %d/n", WSAGetLastError());
- goto EXIT_SEGMENT;
- }
- for(;;)
- {
- bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,&fromlen);
- if (bread == SOCKET_ERROR)
- {
- if (WSAGetLastError() == WSAETIMEDOUT)
- continue;
- printf("recv failed, errcode = %d/n", WSAGetLastError());
- break;
- }
- decode_resp(recvbuf,bread,&from);
- Sleep(200);
- memset(recvbuf,0,sizeof(recvbuf));
- }
- EXIT_SEGMENT:
- if (sockRaw != INVALID_SOCKET) closesocket(sockRaw);
- WSACleanup();
- return 0;
- }
- //显示接收内容,并回传
- void decode_resp(char *buf, int nBytes,struct sockaddr_in *from)
- {
- IpHeader *iphdr;
- IcmpHeader *icmphdr;
- unsigned short iphdrlen;
- iphdr = (IpHeader *)buf;
- buf[nBytes] = 0;
- //屏蔽高四位version
- iphdrlen = ((iphdr->h_verlen) & 0x0F)<<2 ;
- icmphdr = (IcmpHeader*)(buf + iphdrlen);
- if(icmphdr->i_seq==1234)//密码正确则输出数据段
- {
- ICMP_DEST_IP=inet_ntoa(from->sin_addr);//取得ICMP包的源地址
- printf("recv %d bytes from %s: %s/n", nBytes, ICMP_DEST_IP, buf + iphdrlen + 12);
- memset(buffer, 0x00, sizeof(buffer));
- strcpy(buffer, "received ");
- strcat(buffer, buf + iphdrlen + 12);
- sendICMP();
- }
- }
- //计算校验值
- USHORT checksum(USHORT *buffer, int size)
- {
- unsigned long cksum=0;
- while(size >1)
- {
- cksum+=*buffer++;
- size -=sizeof(USHORT);
- }
- if(size )
- {
- cksum += *(UCHAR*)buffer;
- }
- cksum = (cksum >> 16) + (cksum & 0xffff);
- cksum += (cksum >>16);
- return (USHORT)(~cksum);
- }
- //填充ICMP报文
- void fill_icmp_data(char * icmp_data)
- {
- IcmpHeader *icmp_hdr;
- icmp_hdr = (IcmpHeader*)icmp_data;
- icmp_hdr->i_type = 0;
- icmp_hdr->i_code = 0;
- icmp_hdr->i_id = (USHORT) GetCurrentProcessId();
- icmp_hdr->i_cksum = 0;
- icmp_hdr->i_seq =4321;
- icmp_hdr->timestamp = GetTickCount(); //设置时间戳
- memcpy(icmp_data + 12,buffer,strlen(buffer));
- printf("echo back: %s/n", buffer);
- }
- //发送报文
- void sendICMP(void)
- {
- SOCKET skt = (SOCKET)NULL;
- struct sockaddr_in dest;
- int bread,datasize,bwrote;
- int timeout = 1000;
- char *icmp_data;
- if((skt=WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED))
- ==INVALID_SOCKET) ExitProcess(STATUS_FAILED);
- if((bread=setsockopt(skt,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,sizeof(timeout)))==SOCKET_ERROR)
- goto EXIT_SEGMENT;
- //设置发送超时
- memset(&dest,0,sizeof(dest));
- dest.sin_family = AF_INET;
- dest.sin_addr.s_addr = inet_addr(ICMP_DEST_IP);
- datasize=strlen(buffer);
- datasize+=sizeof(IcmpHeader);
- icmp_data=(char*)xmalloc(MAX_PACKET);
- if(!icmp_data) goto EXIT_SEGMENT;
- memset(icmp_data,0,MAX_PACKET);
- //填充ICMP报文
- fill_icmp_data(icmp_data);
- //计算校验和
- ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);
- //发送报文
- bwrote=sendto(skt,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest));
- if (bwrote == SOCKET_ERROR)
- {
- if (WSAGetLastError() == WSAETIMEDOUT) printf("Timed out/n");
- printf("sendto failed:%d",WSAGetLastError());
- goto EXIT_SEGMENT;
- }
- printf("Send Packet to %s Success!/n", ICMP_DEST_IP);
- EXIT_SEGMENT:
- if (skt != INVALID_SOCKET) closesocket(skt);
- memset(buffer,0,sizeof(buffer));
- Sleep(200);
- }
- #include <winsock2.h>
- #include <stdio.h>
- #include <stdlib.h>
- #pragma comment(lib,"ws2_32.lib")
- char SendMsg[256];
- //IP报文头
- typedef struct iphdr
- {
- unsigned char h_verlen; //4位首部长度+IP版本号,4表示IPV4
- unsigned char tos; //8位服务类型TOS
- unsigned short total_len; //16位总长度(字节)
- unsigned short ident; //16位标识
- unsigned short frag_and_flags; //3位标志位
- unsigned char ttl; //8位生存时间 TTL
- unsigned char proto; //8位协议 (TCP, UDP 或其他)
- unsigned short checksum; //16位IP首部校验和
- unsigned int sourceIP; //32位源IP地址
- unsigned int destIP; //32位目的IP地址
- }IpHeader;
- //ICMP报文头
- typedef struct _ihdr
- {
- BYTE i_type; //8位类型
- BYTE i_code; //8位代码
- USHORT i_cksum; //16位校验和
- USHORT i_id; //识别号(一般用进程号作为识别号)
- USHORT i_seq; //报文序列号
- ULONG timestamp; //时间截
- } IcmpHeader;
- #define STATUS_FAILED 0xFFFF
- #define MAX_PACKET 256
- #define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
- void fill_icmp_data(char *, int);
- USHORT checksum(USHORT *, int);
- void decode_resp(char *,int ,struct sockaddr_in *);//ICMP解包函数
- void usage();
- int main(int argc, char *argv[])
- {
- char *ICMP_DEST_IP; //目标主机的IP
- char *recvbuf;
- if(argc!=2)
- {
- usage();
- return 0;
- }
- ICMP_DEST_IP=argv[1];//取得目标主机IP
- WSADATA wsaData;
- SOCKET sockRaw;
- struct sockaddr_in dest,from;
- int datasize;
- int fromlen=sizeof(from);
- char *icmp_data;
- if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
- {
- fprintf(stderr, "WSAStartup failed: %d/n", GetLastError());
- ExitProcess(STATUS_FAILED);
- }
- sockRaw=socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
- int timeout=1000;
- setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout));
- timeout=1000;
- setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout));
- memset(&dest,0,sizeof(dest));
- dest.sin_addr.s_addr=inet_addr(ICMP_DEST_IP);
- dest.sin_family=AF_INET;
- usage();
- for(;;)
- {
- memset(SendMsg, 0x00, sizeof(SendMsg));
- fgets(SendMsg,1024,stdin);//取得命令行,保存在SendMsg数组中
- if(!strcmp(SendMsg,"Q/n")||!strcmp(SendMsg,"q/n"))ExitProcess(0);
- if(!strcmp(SendMsg,"/n"))continue;
- SendMsg[strlen(SendMsg)-1] = 0;
- datasize = strlen(SendMsg);
- datasize +=sizeof(IcmpHeader);
- printf("ICMP packet size is %d",datasize);
- icmp_data= (char*)xmalloc(MAX_PACKET);
- recvbuf= (char *)xmalloc(MAX_PACKET);
- memset(icmp_data,0, MAX_PACKET);
- fill_icmp_data(icmp_data, datasize);
- ((IcmpHeader *)icmp_data)->i_cksum=0;
- ((IcmpHeader *)icmp_data)->i_cksum=checksum((USHORT *)icmp_data, datasize);
- int bwrote=sendto(sockRaw, icmp_data, datasize, 0, (struct sockaddr *) &dest, sizeof(dest));
- if (bwrote == SOCKET_ERROR)
- {
- if (WSAGetLastError() == WSAETIMEDOUT) printf("Timed out/n");
- fprintf(stderr,"sendto failed: %d/n",WSAGetLastError());
- }
- if (bwrote<datasize ) //没有把所有的数据发送出去,也出错了。
- {
- if (sockRaw != INVALID_SOCKET) closesocket(sockRaw);
- WSACleanup();
- return 0;
- }
- printf("/nSend Packet to %s Success!/n",argv[1]);
- memset(recvbuf,0,MAX_PACKET);
- int bread=recvfrom(sockRaw, recvbuf, MAX_PACKET, 0, (struct sockaddr *) &from, &fromlen);
- if(bread == SOCKET_ERROR)
- {
- if(WSAGetLastError() == WSAETIMEDOUT)
- {
- printf("timed out/n");
- continue;
- }
- break;
- }
- decode_resp(recvbuf, bread, &from);
- }//end for
- if (sockRaw != INVALID_SOCKET) closesocket(sockRaw);
- WSACleanup();
- return 0;
- }
- USHORT checksum(USHORT *buffer, int size)
- {
- unsigned long cksum=0;
- while(size > 1)
- {
- cksum+=*buffer++;
- size-=sizeof(USHORT);
- }
- if(size)
- {
- cksum+=*(UCHAR *)buffer;
- }
- cksum=(cksum >> 16) + (cksum & 0xffff);
- cksum+=(cksum >> 16);
- return(USHORT) (~cksum);
- }
- void fill_icmp_data(char *icmp_data, int datasize)
- {
- IcmpHeader *icmp_hdr;
- char *datapart;
- icmp_hdr= (IcmpHeader *)icmp_data;
- icmp_hdr->i_type=0;
- icmp_hdr->i_code=0;
- icmp_hdr->i_id=(USHORT)GetCurrentProcessId();
- icmp_hdr->timestamp =GetTickCount();
- icmp_hdr->i_seq=1234;
- datapart=icmp_data + sizeof(IcmpHeader);
- memcpy(datapart,SendMsg,sizeof(SendMsg));
- }
- void usage()
- {
- printf("Usage: icmpclient RemoteIP/n");
- printf("Ctrl+C or Q/q to Quite/n/n");
- }
- //显示回显内容
- void decode_resp(char *buf, int bytes,struct sockaddr_in *from)
- {
- IpHeader *iphdr;
- IcmpHeader *icmphdr;
- unsigned short iphdrlen;
- iphdr = (IpHeader *)buf;
- //屏蔽高4位version
- iphdrlen = ((iphdr->h_verlen) & 0x0F)<<2;
- icmphdr = (IcmpHeader*)(buf + iphdrlen);
- if(icmphdr->i_seq == 4321)//密码正确则输出数据段
- {
- printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));
- printf(" IcmpType %d",icmphdr->i_type);
- printf(" IcmpCode %d",icmphdr->i_code);
- printf("/n");
- printf("recv: %s/n",buf+iphdrlen+12);
- }
- }
可以将Server端程序在另一电脑上运行,而Client端在本地运行,也可以在同一机器上运行。
运行完成后服务端等待数据到来,客户端通过输入可以向Server发送数据,服务端收到后解包进行回复,并回到等待接收数据状态,客户端接收服务端的响应,输出信息后继续等待用户输入。
这种方式的绕过其实是利用现有的“通行证”带一些自定义的东西,应该可以满足一些想法了呵呵...