网络安全之tcp阻截引擎

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络安全之tcp阻截引擎 相关的知识,希望对你有一定的参考价值。

1、tcp协议的缺陷

任何协议都有缺陷,tcp协议同样如此,tcp协议的三次握手是有一个不能轻易发现的缺陷,这个缺陷容易引起旁路中断,引起中间人攻击,可以让中间人篡改数据包,Man-in-the-MiddleAttack,我们先看下图,看懂了,就可以继续了。

多层关系
    实际上,这个缺陷就是在服务器和客户机真正建立连接之前,我们可以发送RST包,先告诉服务器断开,然后我们模拟服务器给客户端发包,直接串入到他们的网络中间。
    在TCP层,tcp的意思是传输控制协议,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG. 对于我们日常的分析有用的就是前面的五个字段,SYN,建立连接,FIN包的意思是finish 关闭连接,ACK是回答响应,而RST表示我异常中断,链接重置,PSH才是真正的数据传送。对于我们来说,关键的截断就在于RST 和 FIN,我们选取RST包。

2 、抓包

2.1 抓包方式1 交换机镜像

    这种方式是直接从交换机镜像端口复制数据,可以抓该交换机口路径下的所有包,缺点是不能抓所有包。在网络安全中,为了方便对一个或多个网络接口的流量进行分析(如IDS 入侵检测、网络分析仪等),可以通过配置交换机或路由器来把一个或多个端口(VLAN)的数据转发到某一个端口,这就是端口镜像,来实现对网络的监听。

2.2 抓包方式2 网桥

    在路由器和交换机之间串入网桥,抓所有包,这种方式是可以抓所有包,缺点是流量更大。通常的网桥做法,可以使用两个网卡,在两个网卡之间既可以用旁路方式,也可以使用串入方式,在传输层这一层,既可以抓取tcp包,也可以抓取udp包,这取决于需求。

2.3 抓包方式3 网关

    网关方式是比较安全的方式,一般是纯软件旁路方式抓包。旁路方式抓包是不能阻截udp的,除非我们动用arp 协议,在网络中下毒arp,让所有主机误认为我们发放病毒的机器是网关,这种方式不可取,本身具有arp防火墙的主机是不会受干扰的。

为了方便演示,显然我们制作一个网关,在网关上旁路抓包是比较安全的,下面是建立RAW SOCKET,抓取所有流经网关的原始数据包,然后一层一层往下走,代码其实是适应各种环境的,无论交换机镜像,还是旁路抓包都是这么写,网桥的方式不一样,可以通过linux内核模块流经处理的方式发送到用户态去分析,这里暂时不讨论这种方式。一下是旁路方式抓包。

if((m_helperSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==SOCKET_ERROR)
		{		
			return 0;
		}

		if(setsockopt(m_helperSocket, IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof(optval))==SOCKET_ERROR)
		{		
			return 0;
		}
		if((m_sniffSocket = socket(AF_INET, SOCK_RAW, IPPROTO_IP))==SOCKET_ERROR)
		{		
			return 0;
		}
		PIP_ADAPTER_INFO pAdapterInfo = m_pAdapterInfo;		
		u_long in = 0;
		do 
		{
			if (strcmp (in_szSourceDevice, pAdapterInfo->AdapterName ) == 0)
			{
				break;
			}
			in++;
			pAdapterInfo = pAdapterInfo->Next;    // Progress through
		}
		while(pAdapterInfo);  
		

		struct sockaddr_in src;
		memset(&src, 0, sizeof(src));
		src.sin_addr.S_un.S_addr = inet_addr (pAdapterInfo->IpAddressList.IpAddress.String);		
		src.sin_family      = AF_INET;
		src.sin_port        = 0;
		if (bind(m_sniffSocket,(struct sockaddr *)&src,sizeof(src)) == SOCKET_ERROR)
		{			
			return 0;
		}

		int j=1;

		if (WSAIoctl(m_sniffSocket, SIO_RCVALL, &j, sizeof(j), 0, 0, &in,0, 0) == SOCKET_ERROR)
		{			
			return 0;
		}		

3、建立线程,查询流经数据包

一个最大的IP包为64K,因为一个包是由两个字节来表示的,建立64K数据包缓存,下面只是样例写法,实际上需要建立缓存链表,逐次分析每个包。

3.1 抓取IP

int ThreadHandler(void * in_pParam)
{
		
 //    libnet_t *LibnetHandle; 
	//char *DestAddr = "192.168.1.178";
 //   char *SourAddr = "192.168.1.1";
	
	
	//LibnetHandle = libnet_init(LIBNET_LINK, g_DevName, NULL);
 //   if (LibnetHandle == NULL)
 //   {
 //      
 //      return -1;
 //   }
 //   unsigned int DestAddrNumber = libnet_name2addr4(LibnetHandle, DestAddr, LIBNET_RESOLVE);
 //   unsigned int SourAddrNumber = libnet_name2addr4(LibnetHandle, SourAddr, LIBNET_RESOLVE);

	cprocess* _this = (cprocess*)in_pParam;	

	int  res = 0;
	char *pkt_data = (char *)malloc(65536); //64K的缓存!	
	char					m_pLogString[256];		
	Packet					p;
	
	if (pkt_data == NULL)
	{		
		return 0;
	}

	SetEvent(_this->m_hThrdReadyEvent);
	
	// Capture packet
	string strName;
	do
	{
       //#define GET_IP(x,buf) sprintf(buf,"%d,%d,%d,%d", x&0xFF, (x>>8)&0xFF, (x>>16)&0xFF, (x>>24)&0xFF)
		res = recvfrom(_this->m_sniffSocket,pkt_data,65536,0,0,0); //
		if(res > 0)	
		{			
			ZeroMemory(&p, sizeof (Packet));
			unsigned int Number = _this->DecodeIP((u_int8_t*)pkt_data, res, &p);
			if (p.banned == 1)
			{
				_this->FilterHttpRequestXP(&p);
				char ip_string_src[17];
				char ip_string_dst[17];
				memcpy (ip_string_src, inet_ntoa(p.iph->ip_src), 17);
				memcpy (ip_string_dst, inet_ntoa(p.iph->ip_dst), 17);

				sprintf (m_pLogString,
					"关键词 \\'%s\\' 被侦测从 %s 到 %s. 暗影联盟日志", 
					p.matched, ip_string_src, ip_string_dst);
				_this->m_pFilterLog->AddLog(m_pLogString);
			}
		}
	} 
	while (res > 0);
	free(pkt_data);
	return 1;
}

3.2 解析IP包

unsigned int  DecodeIP(u_int8_t * pkt, const u_int32_t len, Packet * p)
{
	u_int32_t ip_len;			/* length from the start of the ip hdr to the pkt end   ip包长度*/
	u_int32_t hlen;             /* ip header length  ip包头长度 一个IP包如tcp包即使只有一个字节的应用层数据也是要大于40字节的 */
	/* lay the IP struct over the raw data  */
	p->iph = (IPHdr *) pkt;
	/* 小于一个IP包头的长度立刻退出 */
	if(len < IP_HEADER_LEN)
	{		
		p->iph = NULL;
		return -1;
	}
	if(IP_VER(p->iph) != 4)
	{
		p->iph = NULL;
		return -1;
	}
	/* set the IP datagram length */
	ip_len = ntohs(p->iph->ip_len);
	/* set the IP header length */
	hlen = IP_HLEN(p->iph) << 2;
	/* header length sanity check */
	if(hlen < IP_HEADER_LEN)
	{
		p->iph = NULL;
		return -1;
	}

	//抓包后分析的包长度
	if (ip_len > len) 
	{
		 ip_len = len;
	}

	if(ip_len < hlen)
	{
		 p->iph = NULL;
		 return -1;
	}

	/* test for IP options */
	p->ip_options_len = hlen - IP_HEADER_LEN;

	if(p->ip_options_len > 0)
	{
		p->ip_options_data = pkt + IP_HEADER_LEN;		
	}


	/* set the remaining packet length */
	ip_len -= hlen;

	/* check for fragmented packets */
	p->frag_offset = ntohs(p->iph->ip_off);

	/* 
	* get the values of the reserved, more 
	* fragments and don't fragment flags 
	*/
	p->rf = (u_int8_t)((p->frag_offset & 0x8000) >> 15);
	p->df = (u_int8_t)((p->frag_offset & 0x4000) >> 14);
	p->mf = (u_int8_t)((p->frag_offset & 0x2000) >> 13);

	/* mask off the high bits in the fragment offset field */
	p->frag_offset &= 0x1FFF;

	if(p->frag_offset || p->mf)
	{
		/* set the packet fragment flag */
		p->frag_flag = 1;	
	}

	/* if this packet isn't a fragment  如果不是一个分片代码*/
	if(!(p->frag_flag))
	{
		if(p->iph->ip_src.S_un.S_addr == g_SelfIPAddress)
		//iphdr *pip = (iphdr *) pkt;
		{
			//这个包是从本地发出去的ip包
			g_Direction = 0;
		}
		else
		{
			if(p->iph->ip_dst.S_un.S_addr == g_SelfIPAddress)
			{
				//这个包是个进来的ip包
				g_Direction = 1;
			}
			else g_Direction = -1;//内网地址
		}
		
		//2013-02-01 注释,做家长行为管理,不进行协议分析
        if(g_Direction >=0)
		{
			//unsigned int t = time(NULL);
			//return Analyze_Protocol(pip,t,g_Direction);
		}
		//2009-04-7 先进行协议分析,以下屏蔽
		//2013-02-01 又重新打开,做家长行为管理
		/* Decode only TCP headers */
		if (p->iph->ip_proto == IPPROTO_TCP)
		{
			DecodeTCP(pkt + hlen, ip_len, p);
		}

	}
	else
	{
		/* set the payload pointer and payload size */
		p->data = pkt + hlen;
		p->dsize = (u_short) ip_len;
		return -1;
	}


	return -1;
}

3.3 解析tcp

///解析tcp包,pkt传进来的是ip包

void DecodeTCP(u_int8_t * pkt, const u_int32_t len, Packet * p)
{
	u_int32_t hlen;            /* TCP header length */
	
	if(len < 20)
	{
		  p->tcph = NULL;
		  return;
	}

	/* lay TCP on top of the data cause there is enough of it! */
	p->tcph = (TCPHdr *) pkt;

	/* multiply the payload offset value by 4 */
	hlen = TCP_OFFSET(p->tcph) << 2;

	if(hlen < 20)
	{
		p->tcph = NULL;
		return;
	}

	if(hlen > len)
	{
		p->tcph = NULL;
		return;
	}

	/* if options are present, decode them */
	p->tcp_options_len = hlen - 20;

	if(p->tcp_options_len > 0)
	{	
		p->tcp_options_data = pkt + 20;	
	}


	/* set the data pointer and size */
	p->data = (u_int8_t *) (pkt + hlen);

	if(hlen < len)
	{
		p->dsize = (u_short)(len - hlen);
	}
	else
	{
		p->dsize = 0;
	}
	
	if (p->tcph->th_flags & TH_ACK && p->tcph->th_flags & TH_PSH)
	{
		if(p->tcph->th_dport != htons(80) &&
			p->tcph->th_dport != htons(443) )
			return ;


		DecodeHTTP(p->data, p->dsize, p);

	}
	
	return;
}

上面的的流程就是一层一层往下解析,我们还要做的就是建立数据特征向量,分析数据,保存数据,同样可以完成数据回放。

4 、包分析和阻断流程

分析阻断

5、IP 校验算法

在发送IP包的过程中,需要用到校验算法


unsigned short CalcIPSum(unsigned short * w, int blen)
{
	unsigned int cksum;

	/* IP must be >= 20 bytes */
	cksum  = w[0];
	cksum += w[1];
	cksum += w[2];
	cksum += w[3];
	cksum += w[4];
	cksum += w[5];
	cksum += w[6];
	cksum += w[7];
	cksum += w[8];
	cksum += w[9];

	blen  -= 20;
	w     += 10;

	while( blen ) /* IP-hdr must be an integral number of 4 byte words */
	{
		cksum += w[0];
		cksum += w[1];
		w     += 2;
		blen  -= 4;
	}

	cksum  = (cksum >> 16) + (cksum & 0x0000ffff);
	cksum += (cksum >> 16);

	return (unsigned short) (~cksum);
}

6、制作界面

制作一个简单的界面,从网卡上抓包,这里其实做实验可以从本地做,如果做好了,可以做一个网关,局域网中的所有网络主机填写网关地址为本网关,就可以从网卡上以原始数据包方式抓取所有包。例如linux和windows server系列是可以抓包和发送原始数据包的,但是windows7 和 10 这种系列没有权限发送原始数据包,只能抓取,ok,原理已经清楚了,我们一步一步建立这样的界面和程序。
界面
下一节我们继续探讨网络中抓到包以后如何把包解析出来。

以上是关于网络安全之tcp阻截引擎 的主要内容,如果未能解决你的问题,请参考以下文章

计算机网络—网络原理之TCP/IP协议

Laravel之视图和Blade模板引擎

网络编程套接字之三TCP

网络骇客入门之TCP并发网页服务器

Python 之 Socket编程(TCP/UDP)

异常和TCP通讯