大家好,欢迎来到IT知识分享网。
1.背景
自己想了解一下Https如何透明,于是找到了这个项目,但是需要自己把流量定位到8080端口。windows是支持代理流量的,常见的代理方式是修改internet setting(某不愿透露姓名的哥儿说SSR也是通过这种方式代理本地的流量),如下图所示。
接着简单分析一下这个代理服务器是如何实现。通过ProMon不难发现,iexplore是通过修改注册表的方式实现的。
那么我们怎么通过API来指定自己的代理服务器,这里通过栈回溯,发现使用的是WININET.dll下的InternetSetOptionW。
这里自己假装去找一下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的降级),这里不谈,主要是自己水平有限。
最后成功透明化了流量。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/23428.html