WinDivert+MITM实现https流量透明

WinDivert+MITM实现https流量透明1.背景自己想了解一下Https如何透明,于是找到了这个项目,但是需要自己把流量定位到8080端口。windows是支持代理流量的,常见的代理方式是修改internetsetting(某不愿透露姓名的哥儿说SSR也是通过这种方式代理本地的流量),如下图所示。接着简单分析一下这个代理服务器是如何实现。通过ProMon不难发现,iexplore是通过修改注册表的方式实现的。那么我们怎么通过API来指定自己的代理服务器,这里通过栈回溯,发现使用的是WININET.dll下的InternetS

大家好,欢迎来到IT知识分享网。

1.背景

自己想了解一下Https如何透明,于是找到了这个项目,但是需要自己把流量定位到8080端口。windows是支持代理流量的,常见的代理方式是修改internet setting(某不愿透露姓名的哥儿说SSR也是通过这种方式代理本地的流量),如下图所示。

WinDivert+MITM实现https流量透明

接着简单分析一下这个代理服务器是如何实现。通过ProMon不难发现,iexplore是通过修改注册表的方式实现的。

WinDivert+MITM实现https流量透明

那么我们怎么通过API来指定自己的代理服务器,这里通过栈回溯,发现使用的是WININET.dll下的InternetSetOptionW。

WinDivert+MITM实现https流量透明

这里自己假装去找一下MSDN的定义,了解一下怎么使用这个API

2.如何自己转发流量

上述的方式可以代理当前计算机产生的流量,mitmproxy也能成功的实现https的透明化,但是有几个问题需要了解和注意:

1、修改windows代理的方式比较简单,但是也容易被其他人修改。

2、https透明需要本地安装根证书的。

3、怎么让流量走到我们设置的代理服务器。

在对上述问题有一个基本了解之后,开始查找资料。

3.使用WinDivert进行端口重定向

对https有点了解的朋友都知道,https使用的是443端口,那么我们只需要转发自己的443端口到代理服务器的端口不就行了吗?是的就是这么简单。下面是r3使用WinDivert转发端口的代码

#include <winsock2.h>
#include <windows.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <map>
#include "windivert.h"

#define MAXBUF          WINDIVERT_MTU_MAX

//结构体使用的是 https://github.com/zliu-fd/WinDivertProxy
struct EndPoint {
	UINT32 addr;
	USHORT port;
	bool operator<(const EndPoint& ep) const { return (addr < ep.addr || (addr == ep.addr && port < ep.port)); }
	bool operator==(const EndPoint& ep) const { return (addr == ep.addr && port == ep.port); }
	bool operator>(const EndPoint& ep) const { return (addr > ep.addr || (addr == ep.addr && port > ep.port)); }
	EndPoint() { }
	EndPoint(UINT32 _addr, USHORT _port)
	{
		addr = _addr;
		port = _port;
	}
};

std::string ConvertIP(UINT32 addr)
{
	in_addr in_addr;
	in_addr.S_un.S_addr = addr;
	char* pAddr = inet_ntoa(in_addr);
	std::string ipaddr(pAddr);
	return ipaddr;
}

void LogRedirect(UINT32 srcAddr, USHORT srcPort, UINT32 proxyAddr, USHORT proxyPort, UINT32 dstAddr, USHORT dstPort, int direction) 
{
	if (direction == 0)
	{
		std::cout << "O Redirect ";
		std::cout << "[" << ConvertIP(srcAddr) << ":" << ntohs(srcPort) << "  " << ConvertIP(dstAddr) << ":" << ntohs(dstPort) << "]";
		std::cout << " -> [" << ConvertIP(srcAddr) << ":" << ntohs(srcPort) << "  " << ConvertIP(proxyAddr) << ":" << ntohs(proxyPort) << "]" << std::endl;
	}
	else if (direction == 1)
	{
		std::cout << "I Received ";
		std::cout << "[" << ConvertIP(proxyAddr) << ":" << ntohs(proxyPort) << "  " << ConvertIP(dstAddr) << ":" << ntohs(dstPort) << "]";
		std::cout << " -> [" << ConvertIP(srcAddr) << ":" << ntohs(srcPort) << "  " << ConvertIP(dstAddr) << ":" << ntohs(dstPort) << "]" << std::endl;
	}
	else 
	{
		std::cout << "X Error ";
	}
}

std::map<EndPoint, EndPoint> ClientToServerMap;

int main()
{
	PVOID payload = NULL;
	UINT payload_len = NULL;
	UINT packet_len = NULL;
	unsigned char packet[MAXBUF];
	WINDIVERT_ADDRESS addr = { 0 };
	PWINDIVERT_IPHDR ip_header = NULL;
	PWINDIVERT_TCPHDR tcp_header = NULL;

	//代理服务器,不支持本地
	UINT32 ProxyAddr = inet_addr("");
	USHORT ProxyPort = htons(8080);

	//发往443端口和从8080端口返回的数据都会被拦截下来
	HANDLE handle = WinDivertOpen(
		"(outbound and tcp.DstPort == 443) or (inbound and tcp.SrcPort == 8080)",
		WINDIVERT_LAYER_NETWORK, 0, 0);

	if (handle == INVALID_HANDLE_VALUE)
		return 0;

	while (1)
	{
		//这里一直在接收数据包
		if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len, &addr))
		{
			printf("failed to read packet (%d) \n", GetLastError());
			continue;
		}

		WinDivertHelperParsePacket(packet, packet_len, &ip_header, NULL, NULL, NULL, NULL, &tcp_header, NULL, &payload, &payload_len, NULL, NULL);
		if (ip_header == NULL || tcp_header == NULL)
		{
			printf("failed to parse packet (%d) \n", GetLastError());
			continue;
		}

		if (addr.Outbound)//出去的数据
		{
			if(tcp_header->DstPort == htons(443))
			{
				EndPoint srcEndPoint(ip_header->SrcAddr, tcp_header->SrcPort);
				EndPoint dstEndPoint(ip_header->DstAddr, tcp_header->DstPort);
				ClientToServerMap[srcEndPoint] = dstEndPoint;

				LogRedirect(ip_header->SrcAddr, tcp_header->SrcPort, ProxyAddr, ProxyPort, ip_header->DstAddr, tcp_header->DstPort, 0);

				//指定到代理服务器
				ip_header->DstAddr = ProxyAddr;
				tcp_header->DstPort = ProxyPort;
			}
		}
		else//进来的数据
		{
			if (tcp_header->SrcPort == ProxyPort)
			{
				//接收到代理服务器发送的数据
				EndPoint dstEndPoint(ip_header->DstAddr, tcp_header->DstPort);

				if (ClientToServerMap.find(dstEndPoint) != ClientToServerMap.end())
				{
					EndPoint originalDstEP = ClientToServerMap[dstEndPoint];
					ip_header->SrcAddr = originalDstEP.addr;
					tcp_header->SrcPort = originalDstEP.port;

					LogRedirect(ip_header->SrcAddr, tcp_header->SrcPort, ProxyAddr, ProxyPort, ip_header->DstAddr, tcp_header->DstPort, 1);
				}

			}
		}

		WinDivertHelperCalcChecksums(packet, packet_len, &addr, 0);
		UINT writeLen = NULL;
		if (!WinDivertSend(handle, packet, packet_len, &writeLen, &addr))
		{
			printf("failed to send packet (%d) \n", GetLastError());
			continue;
		}

	}

	return 0;
}

这里需要注意的是不能使用本地的IP地址,因为443端口的数据会被全部转发到8080,导致本地的代理服务器的https请求也发不出去,形成一个闭环。

还有就是根证书问题,需要在需要https透明的主机上安装,建议使用静默安装。

证书管理器工具 (Certmgr.exe) | Microsoft Docs

当然https还有很多需要注意的细节(比如为什么要安装CA、https通信又是如何保证安全的、Http1和Http2又有说明区别、MITM又是怎么实现中间人的、SSLStrip如何实现https的降级),这里不谈,主要是自己水平有限。

最后成功透明化了流量。

WinDivert+MITM实现https流量透明

 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/23428.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信