diff --git a/.gitignore b/.gitignore index 1a884eb..bd72ed2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +FiddlerCore4 + + # User-specific files *.rsuser *.suo diff --git a/BarrageGrab/Appsetting.cs b/BarrageGrab/Appsetting.cs index 6b144c3..d114af1 100644 --- a/BarrageGrab/Appsetting.cs +++ b/BarrageGrab/Appsetting.cs @@ -11,12 +11,14 @@ namespace BarrageGrab { private static readonly Appsetting ins = new Appsetting(); + public static Appsetting Instanse { get => Get(); } public Appsetting() { FilterProcess = AppSettings["filterProcess"].Trim().Split(','); WsProt = int.Parse(AppSettings["wsListenPort"]); - PrintBarrage = AppSettings["printBarrage"] == "on"; + PrintBarrage = AppSettings["printBarrage"].ToLower() == "true"; + ProxyPort = int.Parse(AppSettings["proxPort"]); } public static Appsetting Get() @@ -38,5 +40,10 @@ namespace BarrageGrab /// 控制台打印消息开关 /// public bool PrintBarrage { get; private set; } + + /// + /// 代理端口 + /// + public int ProxyPort { get; private set; } = 8827; } } diff --git a/BarrageGrab/JsonEntity/BarrageMessages.cs b/BarrageGrab/JsonEntity/BarrageMessages.cs index e0005f8..484144b 100644 --- a/BarrageGrab/JsonEntity/BarrageMessages.cs +++ b/BarrageGrab/JsonEntity/BarrageMessages.cs @@ -132,7 +132,7 @@ /// /// 抖币价格 /// - public int GiamondCount { get; set; } + public int DiamondCount { get; set; } } /// diff --git a/BarrageGrab/Proxy/FiddlerProxy.cs b/BarrageGrab/Proxy/FiddlerProxy.cs new file mode 100644 index 0000000..afadd4c --- /dev/null +++ b/BarrageGrab/Proxy/FiddlerProxy.cs @@ -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 +{ + /// + /// FiddlerCore 代理程序 + /// + [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; + } + } +} diff --git a/BarrageGrab/Proxy/ISystemProxy.cs b/BarrageGrab/Proxy/ISystemProxy.cs new file mode 100644 index 0000000..9cccac0 --- /dev/null +++ b/BarrageGrab/Proxy/ISystemProxy.cs @@ -0,0 +1,40 @@ +using System; +using Fiddler; +using System.Reflection; + +namespace BarrageGrab.Proxy +{ + internal interface ISystemProxy : IDisposable + { + event EventHandler OnWebSocketData; + + /// + /// 开始监听 + /// + void Start(); + } + + public class WsMessageEventArgs + { + /// + /// 消息体 + /// + public byte[] Payload { get; set; } + + /// + /// 进程ID + /// + public int ProcessID { get; set; } + + /// + /// 进程名 + /// + public string ProcessName { get; set; } + + /// + /// 域名 + /// + public string HostName { get; set; } + + } +} \ No newline at end of file diff --git a/BarrageGrab/Proxy/SystemProxy.cs b/BarrageGrab/Proxy/SystemProxy.cs new file mode 100644 index 0000000..8c847e5 --- /dev/null +++ b/BarrageGrab/Proxy/SystemProxy.cs @@ -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/ + + /// + /// 接收到websocket消息事件 + /// + public event EventHandler OnWebSocketData; + + /// + /// 代理端口 + /// + 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}"); + } + + /// + /// 关闭系统代理 + /// + 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}"); + } + } +} diff --git a/BarrageGrab/Proxy/TitaniumProxy.cs b/BarrageGrab/Proxy/TitaniumProxy.cs new file mode 100644 index 0000000..4b8938b --- /dev/null +++ b/BarrageGrab/Proxy/TitaniumProxy.cs @@ -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 hostlist = new List(); + + 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); + + + } + + /// + /// 释放资源,关闭系统代理 + /// + override public void Dispose() + { + proxyServer.Stop(); + proxyServer.Dispose(); + CloseSystemProxy(); + } + + /// + /// 启动监听 + /// + override public void Start() + { + proxyServer.Start(); + proxyServer.SetAsSystemHttpProxy(explicitEndPoint); + proxyServer.SetAsSystemHttpsProxy(explicitEndPoint); + } + } +} diff --git a/BarrageGrab/WssBarrageGrab.cs b/BarrageGrab/WssBarrageGrab.cs index 8d2a961..33287ea 100644 --- a/BarrageGrab/WssBarrageGrab.cs +++ b/BarrageGrab/WssBarrageGrab.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using BarrageGrab.ProtoEntity; using ProtoBuf; using ColorConsole; +using BarrageGrab.Proxy; namespace BarrageGrab { @@ -16,7 +17,8 @@ namespace BarrageGrab /// public class WssBarrageGrab : IDisposable { - FiddlerProxy proxy = new FiddlerProxy(); + //ISystemProxy proxy = new FiddlerProxy(); + ISystemProxy proxy = new TitaniumProxy(); Appsetting appsetting = Appsetting.Get(); ConsoleWriter console = new ConsoleWriter(); @@ -94,13 +96,14 @@ namespace BarrageGrab return outBuffer.ToArray(); } - private void Proxy_OnWebSocketData(object sender, FiddlerProxy.WsMessageEventArgs e) + private void Proxy_OnWebSocketData(object sender, WsMessageEventArgs e) { if (!appsetting.FilterProcess.Contains(e.ProcessName)) return; - var buff = e.Message.PayloadData; + var buff = e.Payload; if (buff.Length == 0) return; if (buff[0] != 0x08) return; + try { var enty = Serializer.Deserialize(new ReadOnlyMemory(buff)); @@ -109,6 +112,7 @@ namespace BarrageGrab //检测包格式 if (!enty.Headers.Any(a => a.Key == "compress_type" && a.Value == "gzip")) return; + //解压gzip var odata = enty.Payload; var decomp = Decompress(odata); diff --git a/BarrageGrab/WssBarrageService.cs b/BarrageGrab/WssBarrageService.cs index 5fa5724..e46a2ec 100644 --- a/BarrageGrab/WssBarrageService.cs +++ b/BarrageGrab/WssBarrageService.cs @@ -23,7 +23,7 @@ namespace BarrageGrab ConsoleWriter console = new ConsoleWriter(); WssBarrageGrab grab = new WssBarrageGrab(); Appsetting Appsetting = Appsetting.Get(); - + public WssBarrageService() { var socket = new WebSocketServer($"ws://127.0.0.1:{Appsetting.WsProt}"); @@ -96,7 +96,7 @@ namespace BarrageGrab { RoomId = e.Common.roomId, Content = e.Common.Describe, - GiamondCount = e.Gift.diamondCount, + DiamondCount = e.Gift.diamondCount, GiftCount = e.repeatCount, GiftId = e.giftId, GiftName = e.Gift.Name, diff --git a/BarrageGrab/WssBarrageService.csproj b/BarrageGrab/WssBarrageService.csproj index 2e0ec19..32b5ed1 100644 --- a/BarrageGrab/WssBarrageService.csproj +++ b/BarrageGrab/WssBarrageService.csproj @@ -50,18 +50,24 @@ ..\packages\FiddlerCore.4.6.2.0\lib\net40\BCMakeCert.dll + + ..\packages\Portable.BouncyCastle.1.8.8\lib\net40\BouncyCastle.Crypto.dll + + + ..\packages\BrotliSharpLib.0.3.3\lib\net451\BrotliSharpLib.dll + ..\packages\FiddlerCore.4.6.2.0\lib\net40\CertMaker.dll ..\packages\ColorConsole.1.0.1\lib\net40\ColorConsole.dll - - ..\packages\FiddlerCore.4.6.2.0\lib\net40\FiddlerCore4.dll - ..\packages\Fleck.1.2.0\lib\net45\Fleck.dll + + ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll @@ -90,19 +96,33 @@ ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Security.AccessControl.5.0.0\lib\net461\System.Security.AccessControl.dll + + + ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\Titanium.Web.Proxy.3.2.0\lib\net461\Titanium.Web.Proxy.dll + + + + - diff --git a/BarrageGrab/packages.config b/BarrageGrab/packages.config index 4665f8b..24a24dc 100644 --- a/BarrageGrab/packages.config +++ b/BarrageGrab/packages.config @@ -1,9 +1,12 @@  + + + @@ -11,4 +14,8 @@ + + + + \ No newline at end of file diff --git a/BarrageService.sln b/BarrageService.sln index c4ba32d..e55db5d 100644 --- a/BarrageService.sln +++ b/BarrageService.sln @@ -5,16 +5,32 @@ VisualStudioVersion = 17.2.32408.312 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WssBarrageService", "BarrageGrab\WssBarrageService.csproj", "{A07BB1DA-D5C2-4EEA-9F3F-E42AADB9C1D6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FiddlerCore4", "FiddlerCore4\FiddlerCore4.csproj", "{6E137484-17DC-4AB2-A1BB-2E6E050A9F54}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A07BB1DA-D5C2-4EEA-9F3F-E42AADB9C1D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A07BB1DA-D5C2-4EEA-9F3F-E42AADB9C1D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A07BB1DA-D5C2-4EEA-9F3F-E42AADB9C1D6}.Debug|x86.ActiveCfg = Debug|Any CPU + {A07BB1DA-D5C2-4EEA-9F3F-E42AADB9C1D6}.Debug|x86.Build.0 = Debug|Any CPU {A07BB1DA-D5C2-4EEA-9F3F-E42AADB9C1D6}.Release|Any CPU.ActiveCfg = Release|Any CPU {A07BB1DA-D5C2-4EEA-9F3F-E42AADB9C1D6}.Release|Any CPU.Build.0 = Release|Any CPU + {A07BB1DA-D5C2-4EEA-9F3F-E42AADB9C1D6}.Release|x86.ActiveCfg = Release|Any CPU + {A07BB1DA-D5C2-4EEA-9F3F-E42AADB9C1D6}.Release|x86.Build.0 = Release|Any CPU + {6E137484-17DC-4AB2-A1BB-2E6E050A9F54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6E137484-17DC-4AB2-A1BB-2E6E050A9F54}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E137484-17DC-4AB2-A1BB-2E6E050A9F54}.Debug|x86.ActiveCfg = Debug|Any CPU + {6E137484-17DC-4AB2-A1BB-2E6E050A9F54}.Debug|x86.Build.0 = Debug|Any CPU + {6E137484-17DC-4AB2-A1BB-2E6E050A9F54}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6E137484-17DC-4AB2-A1BB-2E6E050A9F54}.Release|Any CPU.Build.0 = Release|Any CPU + {6E137484-17DC-4AB2-A1BB-2E6E050A9F54}.Release|x86.ActiveCfg = Release|Any CPU + {6E137484-17DC-4AB2-A1BB-2E6E050A9F54}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE