将代理内核换为 Titanium.Web.Proxy ,解决了高内存,高cpu 占用的问题

This commit is contained in:
小白猿
2023-03-04 14:52:04 +08:00
parent dc5dddc3d6
commit 1190e3c031
12 changed files with 456 additions and 11 deletions
+116
View File
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using ColorConsole;
using Fiddler;
using Microsoft.Win32;
namespace BarrageGrab.Proxy
{
/// <summary>
/// FiddlerCore 代理程序
/// </summary>
[Obsolete("由于框架存在内存泄漏,已弃用,请使用TitaniumProxy)", false)]
internal class FiddlerProxy : SystemProxy
{
bool disposed = false;
ConsoleWriter console = new ConsoleWriter();
public FiddlerProxy()
{
InitFiddler();
}
//初始化Fiddler
private void InitFiddler()
{
//这个名字随便
FiddlerApplication.SetAppDisplayName("FiddlerCore");
FiddlerApplication.OnWebSocketMessage += FiddlerApplication_OnWebSocketMessage;
//FiddlerApplication.OnReadResponseBuffer += OnReadResponseBuffer;
//设置pac规则,匹配douyin且排除douyincdn的host走代理,其余的直连
FiddlerApplication.Prefs.SetStringPref("fiddler.proxy.pacfile.text", $"if (/douyin(?!cdn)/.test(host)) {{return 'PROXY 127.0.0.1:{base.ProxyPort}' }} return 'DIRECT'");
X509Certificate2 oRootCert;
if (null == CertMaker.GetRootCertificate())
{
CertMaker.createRootCert();
oRootCert = CertMaker.GetRootCertificate();
X509Store certStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadWrite);
try
{
certStore.Add(oRootCert);
}
finally
{
certStore.Close();
}
}
else
{
oRootCert = CertMaker.GetRootCertificate();
}
//信任证书
CertMaker.trustRootCert();
FiddlerApplication.oDefaultClientCertificate = oRootCert;
//忽略服务器证书错误
CONFIG.IgnoreServerCertErrors = true;
CONFIG.bEnableIPv6 = false;
//CONFIG.bCaptureCONNECT = true;
//CONFIG.bUseEventLogForExceptions = false;
//CONFIG.bAutoProxyLogon = false;
//CONFIG.bMITM_HTTPS = false;
////CONFIG.SetNoDecryptList("*");
}
private void FiddlerApplication_OnWebSocketMessage(object sender, WebSocketMessageEventArgs e)
{
var msg = e.oWSM;
FieldInfo field = msg.GetType().GetField("_wsOwner", BindingFlags.NonPublic | BindingFlags.Instance);
WebSocket obj = (WebSocket)field.GetValue(msg);
field = obj.GetType().GetField("oCP", BindingFlags.NonPublic | BindingFlags.Instance);
var sessionField = obj.GetType().GetField("_mySession", BindingFlags.NonPublic | BindingFlags.Instance);
ClientPipe ocp = (ClientPipe)field.GetValue(obj);
Session session = (Session)sessionField.GetValue(obj);
this.SendWebSocketData(new WsMessageEventArgs()
{
Payload = msg.PayloadData,
ProcessID = ocp.LocalProcessID,
ProcessName = ocp.LocalProcessName,
HostName = session.hostname,
});
}
public override void Start()
{
FiddlerApplication.Startup(Appsetting.Instanse.ProxyPort, FiddlerCoreStartupFlags.Default);
console.WriteLine("Fiddler代理已启动", ConsoleColor.Green);
}
public override void Dispose()
{
if (disposed) return;
FiddlerApplication.Shutdown();
CloseSystemProxy();
console.WriteLine("Fiddler代理已关闭");
disposed = true;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
using System;
using Fiddler;
using System.Reflection;
namespace BarrageGrab.Proxy
{
internal interface ISystemProxy : IDisposable
{
event EventHandler<WsMessageEventArgs> OnWebSocketData;
/// <summary>
/// 开始监听
/// </summary>
void Start();
}
public class WsMessageEventArgs
{
/// <summary>
/// 消息体
/// </summary>
public byte[] Payload { get; set; }
/// <summary>
/// 进程ID
/// </summary>
public int ProcessID { get; set; }
/// <summary>
/// 进程名
/// </summary>
public string ProcessName { get; set; }
/// <summary>
/// 域名
/// </summary>
public string HostName { get; set; }
}
}
+103
View File
@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
using System.Diagnostics;
namespace BarrageGrab.Proxy
{
internal abstract class SystemProxy : ISystemProxy
{
//弹幕服务域名
protected string[] barrageWsHostNames = new string[]
{
"webcast3-ws-web-hl.douyin.com",
"webcast3-ws-web-lf.douyin.com",
"frontier-im.douyin.com"
};
//https://live.douyin.com/webcast/gift/list/
/// <summary>
/// 接收到websocket消息事件
/// </summary>
public event EventHandler<WsMessageEventArgs> OnWebSocketData;
/// <summary>
/// 代理端口
/// </summary>
public int ProxyPort { get => Appsetting.Instanse.ProxyPort; }
public abstract void Dispose();
public abstract void Start();
protected void SendWebSocketData(WsMessageEventArgs args)
{
OnWebSocketData?.Invoke(this, args);
}
protected string GetProcessName(int processID)
{
var process = Process.GetProcessById(processID);
if (process != null)
{
return process.ProcessName;
}
return $"<{processID}>";
}
// 生成自签名证书
protected X509Certificate2 EnsureCertificate(string subjectName)
{
// 添加证书到证书存储
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
//判断证书是否已经存在
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, subjectName, false);
if (certificates.Count > 0)
{
return certificates[0];
}
// 创建证书请求
var request = new CertificateRequest($"CN={subjectName}", RSA.Create(2048), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// 添加扩展属性
request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
// 用自己的私钥签名并生成证书
var certificate = request.CreateSelfSigned(DateTimeOffset.Now.AddDays(-1), DateTimeOffset.Now.AddDays(365));
store.Add(certificate);
store.Close();
return certificate;
}
//注册系统代理
protected void RegisterSystemProxy()
{
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
registry.SetValue("ProxyEnable", 1);
registry.SetValue("ProxyServer", $"http=localhost:{ProxyPort};https=localhost:{ProxyPort}");
}
/// <summary>
/// 关闭系统代理
/// </summary>
protected void CloseSystemProxy()
{
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
registry.SetValue("ProxyEnable", 0);
registry.SetValue("ProxyServer", $"http=localhost:{ProxyPort};https=localhost:{ProxyPort}");
}
}
}
+129
View File
@@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
using Titanium.Web.Proxy;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.StreamExtended.Network;
namespace BarrageGrab.Proxy
{
internal class TitaniumProxy : SystemProxy
{
ProxyServer proxyServer = null;
ExplicitProxyEndPoint explicitEndPoint = null;
public TitaniumProxy()
{
//注册系统代理
//RegisterSystemProxy();
proxyServer = new ProxyServer();
//var cert = EnsureCertificate("localhost");
proxyServer.ReuseSocket = false;
proxyServer.EnableConnectionPool = true;
proxyServer.ForwardToUpstreamGateway = true;
proxyServer.CertificateManager.SaveFakeCertificates = true;
proxyServer.ServerCertificateValidationCallback += ProxyServer_ServerCertificateValidationCallback; ;
proxyServer.BeforeResponse += ProxyServer_BeforeResponse;
explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any,ProxyPort, true);
explicitEndPoint.BeforeTunnelConnectRequest += ExplicitEndPoint_BeforeTunnelConnectRequest;
proxyServer.AddEndPoint(explicitEndPoint);
}
private Task ProxyServer_ServerCertificateValidationCallback(object sender, CertificateValidationEventArgs e)
{
// set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == SslPolicyErrors.None)
{
e.IsValid = true;
}
return Task.CompletedTask;
}
private async Task ProxyServer_BeforeResponse(object sender, SessionEventArgs e)
{
string hostname = e.HttpClient.Request.RequestUri.Host;
if (!barrageWsHostNames.Contains(hostname)) return;
if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Websocket)
{
e.DataReceived += WebSocket_DataReceived;
}
}
//List<string> hostlist = new List<string>();
private async Task ExplicitEndPoint_BeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
string hostname = e.HttpClient.Request.RequestUri.Host;
if (!barrageWsHostNames.Contains(hostname))
{
e.DecryptSsl = false;
}
}
private async void WebSocket_DataReceived(object sender, DataEventArgs e)
{
var args = (SessionEventArgs)sender;
string hostname = args.HttpClient.Request.RequestUri.Host;
var processid = args.HttpClient.ProcessId.Value;
var frames = args.WebSocketDecoderReceive.Decode(e.Buffer, e.Offset, e.Count).ToList();
//Console.WriteLine(frames.Count);
foreach (var frame in frames)
{
//Console.WriteLine(frame.GetText());
base.SendWebSocketData(new WsMessageEventArgs()
{
ProcessID = processid,
HostName = hostname,
Payload = frame.Data.ToArray(),
ProcessName = base.GetProcessName(processid)
});
}
//Console.WriteLine("获取到弹幕信息:" + hostname);
}
/// <summary>
/// 释放资源,关闭系统代理
/// </summary>
override public void Dispose()
{
proxyServer.Stop();
proxyServer.Dispose();
CloseSystemProxy();
}
/// <summary>
/// 启动监听
/// </summary>
override public void Start()
{
proxyServer.Start();
proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
}
}
}