Created
June 29, 2026 19:12
-
-
Save secdev02/0052f1ed9d395f1b8dc2ca6df79b77f9 to your computer and use it in GitHub Desktop.
GLM-5.2:cloud ollama - Create TLS Intercepting Proxy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Start-TlsDebugger.ps1 | |
| # A Burp/Fiddler-style HTTP(S) debugging proxy that runs entirely from PowerShell, | |
| # using New-SelfSignedCertificate for all cert generation (no COM, no NuGet). | |
| # | |
| # Wildcard certs: one *.example.com cert is reused for every host in that zone. | |
| # | |
| # TLS 1.3: handled automatically via SslProtocols.None -> SChannel picks the best | |
| # protocol the OS and peer support (TLS 1.3 on Win11/Server 2022, else TLS 1.2). | |
| # | |
| # Switch: -Debug (built-in common parameter provided by [CmdletBinding()]) | |
| [CmdletBinding()] | |
| param( | |
| [int] $Port = 8888, | |
| [string]$CaPfxPath = "$PSScriptRoot\tlsdebugger-ca.pfx", | |
| [string]$CaPassword = "tlsdebugger", | |
| [int] $LeafCertDays = 730, | |
| [switch]$SkipRootInstall, | |
| [switch]$SkipTls13Enable | |
| ) | |
| $ErrorActionPreference = 'Stop' | |
| # --------------------------------------------------------- | |
| # 0. Encourage OS-default TLS selection (enables TLS 1.3) | |
| # --------------------------------------------------------- | |
| [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::SystemDefault | |
| if (-not $SkipTls13Enable) { | |
| $reg = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols' | |
| try { | |
| foreach ($side in 'Client','Server') { | |
| $p = Join-Path $reg "TLS 1.3\$side" | |
| if (-not (Test-Path $p)) { New-Item -Path $p -Force -ErrorAction SilentlyContinue | Out-Null } | |
| Set-ItemProperty -Path $p -Name 'Enabled' -Value 1 -Type DWord -ErrorAction SilentlyContinue | |
| Set-ItemProperty -Path $p -Name 'DisabledByDefault' -Value 0 -Type DWord -ErrorAction SilentlyContinue | |
| } | |
| } catch { } | |
| } | |
| # ========================================================= | |
| # 1. CA CERT - load from PFX or generate via New-SelfSignedCertificate | |
| # ========================================================= | |
| $secureCaPw = ConvertTo-SecureString -String $CaPassword -AsPlainText -Force | |
| $caCert = $null | |
| if (Test-Path $CaPfxPath) { | |
| Write-Host "[*] Loading CA from $CaPfxPath ..." -ForegroundColor Cyan | |
| try { | |
| $caCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2( | |
| $CaPfxPath, | |
| $secureCaPw, | |
| [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor | |
| [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::UserKeySet -bor | |
| [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable | |
| ) | |
| if (-not $caCert.HasPrivateKey) { $caCert = $null } | |
| } catch { | |
| Write-Warning "Failed to load CA PFX: $($_.Exception.Message). Will regenerate." | |
| $caCert = $null | |
| } | |
| } | |
| if (-not $caCert) { | |
| Write-Host "[*] Generating new self-signed Root CA via New-SelfSignedCertificate ..." -ForegroundColor Yellow | |
| $caCert = New-SelfSignedCertificate ` | |
| -Subject "CN=TLS Debugger Root CA,O=TLS Debugger" ` | |
| -DnsName "TLS Debugger Root CA" ` | |
| -CertStoreLocation Cert:\CurrentUser\My ` | |
| -NotAfter (Get-Date).AddYears(10) ` | |
| -KeyAlgorithm RSA ` | |
| -KeyLength 2048 ` | |
| -KeyUsage CertSign, CRLSign ` | |
| -TextExtension "2.5.29.19={text}ca=1&pathlength=10" | |
| [IO.File]::WriteAllBytes($CaPfxPath, $caCert.Export( | |
| [System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx, | |
| $secureCaPw)) | |
| Write-Host "[*] Saved CA to $CaPfxPath" -ForegroundColor Green | |
| } | |
| # Re-import into the user's My store if not already there (so -Signer works later). | |
| $myStore = New-Object System.Security.Cryptography.X509Certificates.X509Store( | |
| [System.Security.Cryptography.X509Certificates.StoreName]::My, | |
| [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser) | |
| $myStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) | |
| $found = $myStore.Certificates.Find( | |
| [System.Security.Cryptography.X509Certificates.X509FindType]::FindByThumbprint, | |
| $caCert.Thumbprint, $false) | |
| if ($found.Count -eq 0) { | |
| Write-Host "[*] Re-importing CA private key into CurrentUser\My ..." -ForegroundColor Yellow | |
| $myStore.Add($caCert) | |
| # Refresh $caCert to one that has a usable private key handle in this process | |
| $caCert = $myStore.Certificates | Where-Object { $_.Thumbprint -eq $caCert.Thumbprint } | Select-Object -First 1 | |
| } | |
| $myStore.Close() | |
| # ========================================================= | |
| # 2. Install CA public cert to Trusted Root if requested | |
| # ========================================================= | |
| if (-not $SkipRootInstall) { | |
| $rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store( | |
| [System.Security.Cryptography.X509Certificates.StoreName]::Root, | |
| [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser) | |
| $rootStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) | |
| $alreadyThere = $rootStore.Certificates.Find( | |
| [System.Security.Cryptography.X509Certificates.X509FindType]::FindByThumbprint, | |
| $caCert.Thumbprint, $false) | |
| if ($alreadyThere.Count -eq 0) { | |
| Write-Host "[*] Installing CA into CurrentUser\Trusted Root Certification Authorities ..." -ForegroundColor Yellow | |
| # Public-only copy of the cert (no private key into Root store) | |
| $publicOnly = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList (,$caCert.RawData) | |
| $rootStore.Add($publicOnly) | |
| } else { | |
| Write-Host "[*] CA already in Trusted Root (thumbprint $($caCert.Thumbprint))" -ForegroundColor DarkGray | |
| } | |
| $rootStore.Close() | |
| } | |
| # ========================================================= | |
| # 3. Dedicated Runspace for cert generation | |
| # ========================================================= | |
| Write-Host "[*] Creating dedicated PowerShell runspace for cert generation ..." -ForegroundColor Cyan | |
| $certRunspace = [runspacefactory]::CreateRunspace() | |
| $certRunspace.ApartmentState = 'STA' | |
| $certRunspace.ThreadOptions = 'ReuseThread' | |
| $certRunspace.Open() | |
| $certRunspace.SessionStateProxy.SetVariable('caCert', $caCert) | |
| $certRunspace.SessionStateProxy.SetVariable('leafDays', $LeafCertDays) | |
| # ========================================================= | |
| # C# SOURCE | |
| # ========================================================= | |
| $src = @' | |
| using System; | |
| using System.Collections.Concurrent; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Net; | |
| using System.Net.Security; | |
| using System.Net.Sockets; | |
| using System.Management.Automation; | |
| using System.Management.Automation.Runspaces; | |
| using System.Security.Authentication; | |
| using System.Security.Cryptography; | |
| using System.Security.Cryptography.X509Certificates; | |
| using System.Text; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| namespace TlsDebugger | |
| { | |
| public static class Proxy | |
| { | |
| public static int Port = 8888; | |
| public static string CaPfxPath = "tlsdebugger-ca.pfx"; | |
| public static string CaPassword = "tlsdebugger"; | |
| public static int LeafCertDays = 730; | |
| public static bool SkipRootInstall = false; | |
| public static bool VerboseBodies = true; | |
| public static bool DebugMode = false; | |
| // Set from PowerShell: | |
| public static X509Certificate2 CaCert; // the CA cert (with private key) | |
| public static Runspace CertRunspace; // dedicated PS runspace for minting leaves | |
| private static readonly SslProtocols _clientProtocols = SslProtocols.None; | |
| private static readonly SslProtocols _serverProtocols = SslProtocols.None; | |
| // Local C# cache mirroring the PS cache; keyed by wildcard name. | |
| private static readonly ConcurrentDictionary<string, X509Certificate2> _certCache = | |
| new ConcurrentDictionary<string, X509Certificate2>(); | |
| private static readonly object _certLock = new object(); | |
| // Inline PowerShell script used by GetOrMintCertFor. | |
| // $caCert and $leafDays are pre-loaded into the runspace. | |
| private const string LeafMintScript = @" | |
| param([string]$wildcard, [string]$hostname) | |
| $cert = New-SelfSignedCertificate ` | |
| -DnsName $wildcard, $hostname ` | |
| -Signer $caCert ` | |
| -CertStoreLocation Cert:\CurrentUser\My ` | |
| -NotAfter (Get-Date).AddDays($leafDays) ` | |
| -KeyAlgorithm RSA -KeyLength 2048 ` | |
| -TextExtension '2.5.29.37={text}1.3.6.1.5.5.7.3.1' | |
| return $cert | |
| "; | |
| public static void Run() | |
| { | |
| if (CaCert == null) throw new Exception("CaCert not set on Proxy."); | |
| if (CertRunspace == null) throw new Exception("CertRunspace not set on Proxy."); | |
| Log("INFO", "CA subject : " + CaCert.Subject, ConsoleColor.Green); | |
| Log("INFO", "CA thumbprint : " + CaCert.Thumbprint, ConsoleColor.Green); | |
| Log("INFO", "Leaf cert days : " + LeafCertDays, ConsoleColor.Green); | |
| Log("INFO", "Protocol policy : OS-default (SslProtocols.None) -> TLS 1.3 / 1.2", ConsoleColor.Green); | |
| var listener = new TcpListener(IPAddress.Loopback, Port); | |
| listener.Start(); | |
| Log("INFO", "Listening on 127.0.0.1:" + Port + " (Ctrl+C to stop)", ConsoleColor.Green); | |
| while (true) | |
| { | |
| var client = listener.AcceptTcpClient(); | |
| Task.Run(() => HandleClient(client)); | |
| } | |
| } | |
| // ================================================= | |
| // LEAF CERT + WILDCARD | |
| // ================================================= | |
| private static string ComputeWildcard(string hostname) | |
| { | |
| var labels = hostname.Split('.'); | |
| if (labels.Length <= 2) return hostname; | |
| return "*." + string.Join(".", labels, 1, labels.Length - 1); | |
| } | |
| private static X509Certificate2 GetOrMintCertFor(string hostname) | |
| { | |
| string wildcard = ComputeWildcard(hostname); | |
| DebugLog("GetOrMintCertFor: host=" + hostname + " wildcard=" + wildcard); | |
| X509Certificate2 cached; | |
| if (_certCache.TryGetValue(wildcard, out cached)) | |
| { | |
| DebugLog(" cache HIT for " + wildcard); | |
| return cached; | |
| } | |
| lock (_certLock) | |
| { | |
| if (_certCache.TryGetValue(wildcard, out cached)) return cached; | |
| Log("CERT", "Minting new leaf cert for " + wildcard + " (SAN: " + wildcard + ", " + hostname + ")", ConsoleColor.DarkYellow); | |
| try | |
| { | |
| using (var ps = PowerShell.Create()) | |
| { | |
| ps.Runspace = CertRunspace; | |
| ps.AddScript(LeafMintScript) | |
| .AddArgument(wildcard) | |
| .AddArgument(hostname); | |
| var results = ps.Invoke(); | |
| if (ps.HadErrors) | |
| { | |
| foreach (var err in ps.Streams.Error) | |
| Log("ERR", "PS error: " + err.ToString(), ConsoleColor.Red); | |
| throw new Exception("PowerShell cert generation failed (see errors above)."); | |
| } | |
| if (results == null || results.Count == 0) | |
| throw new Exception("PowerShell returned no cert object."); | |
| var leaf = results[0].BaseObject as X509Certificate2; | |
| if (leaf == null) throw new Exception("Returned object was not an X509Certificate2."); | |
| if (!leaf.HasPrivateKey) throw new Exception("Returned leaf cert has no private key."); | |
| _certCache[wildcard] = leaf; | |
| DebugLog(" leaf cert minted OK, thumbprint=" + leaf.Thumbprint); | |
| return leaf; | |
| } | |
| } | |
| catch (Exception ex) | |
| { | |
| Log("ERR", "Cert generation for " + wildcard + " failed: " + ex.Message, ConsoleColor.Red); | |
| if (ex.InnerException != null) Log("ERR", " inner: " + ex.InnerException.Message, ConsoleColor.Red); | |
| throw; | |
| } | |
| } | |
| } | |
| // ================================================= | |
| // CONNECTION HANDLING | |
| // ================================================= | |
| private static async Task HandleClient(TcpClient client) | |
| { | |
| var ns = client.GetStream(); | |
| var lb = new LineBufferedReader(ns); | |
| try | |
| { | |
| string reqLine = lb.ReadLine(); | |
| if (reqLine == null) return; | |
| var sp = reqLine.Split(' '); | |
| if (sp.Length < 3) return; | |
| string method = sp[0]; | |
| string target = sp[1]; | |
| var headers = new List<KeyValuePair<string,string>>(); | |
| string h; | |
| while ((h = lb.ReadLine()) != null && h.Length > 0) | |
| { | |
| int i = h.IndexOf(':'); | |
| if (i > 0) headers.Add(new KeyValuePair<string,string>(h.Substring(0,i).Trim(), h.Substring(i+1).Trim())); | |
| } | |
| if (method.Equals("CONNECT", StringComparison.OrdinalIgnoreCase)) | |
| { | |
| string host = target.Split(':')[0]; | |
| int port = target.Contains(":") ? int.Parse(target.Split(':')[1]) : 443; | |
| await HandleConnect(client, ns, lb, host, port); | |
| } | |
| else | |
| { | |
| await HandlePlainHttp(client, ns, lb, method, target, headers, reqLine); | |
| } | |
| } | |
| catch (Exception ex) { Log("ERR", ex.Message, ConsoleColor.Red); } | |
| finally { try { client.Close(); } catch {} } | |
| } | |
| private static async Task HandlePlainHttp(TcpClient client, NetworkStream ns, LineBufferedReader lb, | |
| string method, string target, List<KeyValuePair<string,string>> headers, string reqLine) | |
| { | |
| var uri = new Uri(target); | |
| string host = uri.Host; | |
| int port = uri.Port == -1 ? 80 : uri.Port; | |
| int contentLen = GetContentLength(headers); | |
| byte[] leftover = lb.Leftover(); | |
| byte[] body = new byte[Math.Max(0, contentLen - leftover.Length)]; | |
| if (body.Length > 0) | |
| { | |
| int r = 0; while (r < body.Length) { int x = await ns.ReadAsync(body, r, body.Length - r); if (x <= 0) break; r += x; } | |
| } | |
| byte[] fullBody; | |
| if (contentLen > 0) | |
| { | |
| fullBody = new byte[contentLen]; | |
| Array.Copy(leftover, 0, fullBody, 0, leftover.Length); | |
| if (body.Length > 0) Array.Copy(body, 0, fullBody, leftover.Length, body.Length); | |
| } | |
| else fullBody = leftover; | |
| Log("REQ", method + " " + target + " (" + fullBody.Length + "B body)", ConsoleColor.Cyan); | |
| string rel = uri.PathAndQuery; if (string.IsNullOrEmpty(rel)) rel = "/"; | |
| var upstream = new TcpClient(host, port); | |
| var us = upstream.GetStream(); | |
| byte[] headerBytes = Encoding.ASCII.GetBytes(reqLine.Replace(target, rel) + "\r\n" + | |
| string.Join("\r\n", headers.ConvertAll(kv => kv.Key + ": " + kv.Value)) + "\r\n\r\n"); | |
| await us.WriteAsync(headerBytes, 0, headerBytes.Length); | |
| await us.WriteAsync(fullBody, 0, fullBody.Length); | |
| var ular = new LineBufferedReader(us); | |
| await RelayResponse(ular, ns, target); | |
| upstream.Close(); | |
| } | |
| private static async Task HandleConnect(TcpClient client, NetworkStream ns, LineBufferedReader lb, | |
| string host, int port) | |
| { | |
| byte[] ok = Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n"); | |
| await ns.WriteAsync(ok, 0, ok.Length); | |
| X509Certificate2 leaf = GetOrMintCertFor(host); | |
| var sslServer = new SslStream(ns, false); | |
| try | |
| { | |
| await sslServer.AuthenticateAsServerAsync(leaf, false, _serverProtocols, false); | |
| Log("TLS", " (client<->proxy) protocol = " + sslServer.SslProtocol, ConsoleColor.DarkCyan); | |
| } | |
| catch (Exception ex) | |
| { | |
| Log("TLS", "Server handshake to client failed for " + host + ": " + ex.Message, ConsoleColor.Red); | |
| return; | |
| } | |
| var clb = new LineBufferedReader(sslServer); | |
| TcpClient upstream; | |
| try { upstream = new TcpClient(host, port); } | |
| catch (Exception ex) | |
| { | |
| Log("UP", "Could not connect to " + host + ":" + port + " - " + ex.Message, ConsoleColor.Red); | |
| return; | |
| } | |
| var sslUpstream = new SslStream(upstream.GetStream(), false, | |
| (s, c, ch, e) => true, null); | |
| try | |
| { | |
| await sslUpstream.AuthenticateAsClientAsync(host, null, _clientProtocols, false); | |
| Log("TLS", " (proxy<->" + host + ") protocol = " + sslUpstream.SslProtocol, ConsoleColor.DarkCyan); | |
| } | |
| catch (Exception ex) | |
| { | |
| Log("TLS", "Upstream handshake to " + host + " failed: " + ex.Message, ConsoleColor.Red); | |
| upstream.Close(); return; | |
| } | |
| var ulb = new LineBufferedReader(sslUpstream); | |
| while (true) | |
| { | |
| string reqLine; | |
| try { reqLine = clb.ReadLine(); } catch { break; } | |
| if (reqLine == null) break; | |
| var sp = reqLine.Split(' '); | |
| if (sp.Length < 3) break; | |
| string method = sp[0]; string path = sp[1]; | |
| var headers = new List<KeyValuePair<string,string>>(); | |
| string h; | |
| while ((h = clb.ReadLine()) != null && h.Length > 0) | |
| { | |
| int i = h.IndexOf(':'); | |
| if (i > 0) headers.Add(new KeyValuePair<string,string>(h.Substring(0,i).Trim(), h.Substring(i+1).Trim())); | |
| } | |
| int contentLen = GetContentLength(headers); | |
| byte[] leftover = clb.Leftover(); | |
| byte[] body = new byte[Math.Max(0, contentLen - leftover.Length)]; | |
| int read = 0; | |
| while (read < body.Length) | |
| { | |
| int r = await sslServer.ReadAsync(body, read, body.Length - read); | |
| if (r <= 0) break; | |
| read += r; | |
| } | |
| byte[] fullBody = new byte[contentLen]; | |
| Array.Copy(leftover, 0, fullBody, 0, leftover.Length); | |
| if (body.Length > 0) Array.Copy(body, 0, fullBody, leftover.Length, body.Length); | |
| Log("REQ", "https://" + host + path + " [" + method + "] " + fullBody.Length + "B", ConsoleColor.Cyan); | |
| byte[] headerBytes = Encoding.ASCII.GetBytes(reqLine + "\r\n" + | |
| string.Join("\r\n", headers.ConvertAll(kv => kv.Key + ": " + kv.Value)) + "\r\n\r\n"); | |
| await sslUpstream.WriteAsync(headerBytes, 0, headerBytes.Length); | |
| await sslUpstream.WriteAsync(fullBody, 0, fullBody.Length); | |
| bool keepGoing = await RelayResponse(ulb, sslServer, "https://" + host + path); | |
| if (!keepGoing) break; | |
| } | |
| upstream.Close(); | |
| } | |
| private static async Task<bool> RelayResponse(LineBufferedReader upstreamLb, Stream clientStream, string reqTarget) | |
| { | |
| string statusLine = upstreamLb.ReadLine(); | |
| if (statusLine == null) return false; | |
| var respHeaders = new List<KeyValuePair<string,string>>(); | |
| string h; | |
| while ((h = upstreamLb.ReadLine()) != null && h.Length > 0) | |
| { | |
| int i = h.IndexOf(':'); | |
| if (i > 0) respHeaders.Add(new KeyValuePair<string,string>(h.Substring(0,i).Trim(), h.Substring(i+1).Trim())); | |
| } | |
| int contentLen = -1; bool chunked = false; | |
| foreach (var kv in respHeaders) | |
| { | |
| if (kv.Key.Equals("Content-Length", StringComparison.OrdinalIgnoreCase)) int.TryParse(kv.Value, out contentLen); | |
| if (kv.Key.Equals("Transfer-Encoding", StringComparison.OrdinalIgnoreCase) && | |
| kv.Value.IndexOf("chunked", StringComparison.OrdinalIgnoreCase) >= 0) chunked = true; | |
| } | |
| byte[] head = Encoding.ASCII.GetBytes(statusLine + "\r\n" + | |
| string.Join("\r\n", respHeaders.ConvertAll(kv => kv.Key + ": " + kv.Value)) + "\r\n\r\n"); | |
| await clientStream.WriteAsync(head, 0, head.Length); | |
| Log("RSP", reqTarget + " -> " + statusLine.Substring(statusLine.IndexOf(' ')+1).Trim(), ConsoleColor.Magenta); | |
| byte[] leftover = upstreamLb.Leftover(); | |
| if (leftover.Length > 0) await clientStream.WriteAsync(leftover, 0, leftover.Length); | |
| int logged = 0; int logCap = 256; var sb = new StringBuilder(); | |
| if (chunked) | |
| { | |
| while (true) | |
| { | |
| string cs = upstreamLb.ReadLine(); | |
| if (cs == null) return false; | |
| var csBytes = Encoding.ASCII.GetBytes(cs + "\r\n"); | |
| await clientStream.WriteAsync(csBytes, 0, csBytes.Length); | |
| int sz = ParseChunkSize(cs); | |
| if (sz == 0) | |
| { | |
| string tr = upstreamLb.ReadLine(); | |
| if (tr != null) { var t = Encoding.ASCII.GetBytes(tr + "\r\n"); await clientStream.WriteAsync(t, 0, t.Length); } | |
| break; | |
| } | |
| byte[] chunk = new byte[sz + 2]; | |
| int got = upstreamLb.Read(chunk, 0, sz + 2); | |
| await clientStream.WriteAsync(chunk, 0, got); | |
| if (logged < logCap) AppendSnippet(sb, chunk, ref logged, logCap); | |
| } | |
| } | |
| else if (contentLen >= 0) | |
| { | |
| int total = contentLen - leftover.Length; | |
| byte[] buf = new byte[8192]; | |
| while (total > 0) | |
| { | |
| int want = Math.Min(buf.Length, total); | |
| int got = upstreamLb.Read(buf, 0, want); | |
| if (got <= 0) break; | |
| await clientStream.WriteAsync(buf, 0, got); | |
| if (logged < logCap) AppendSnippet(sb, buf, ref logged, logCap); | |
| total -= got; | |
| } | |
| } | |
| else | |
| { | |
| byte[] buf = new byte[8192]; | |
| while (true) | |
| { | |
| int got; | |
| try { got = upstreamLb.Read(buf, 0, buf.Length); } catch { break; } | |
| if (got <= 0) break; | |
| await clientStream.WriteAsync(buf, 0, got); | |
| if (logged < logCap) AppendSnippet(sb, buf, ref logged, logCap); | |
| } | |
| } | |
| if (VerboseBodies && sb.Length > 0) Log("BODY", sb.ToString(), ConsoleColor.DarkGray); | |
| return true; | |
| } | |
| // ================================================= | |
| // HELPERS | |
| // ================================================= | |
| private static int GetContentLength(List<KeyValuePair<string,string>> h) | |
| { | |
| foreach (var kv in h) | |
| if (kv.Key.Equals("Content-Length", StringComparison.OrdinalIgnoreCase)) | |
| { | |
| int v; if (int.TryParse(kv.Value, out v)) return v; | |
| } | |
| return 0; | |
| } | |
| private static int ParseChunkSize(string line) | |
| { | |
| int i = line.IndexOf(';'); if (i >= 0) line = line.Substring(0, i); | |
| int v; int.TryParse(line.Trim(), System.Globalization.NumberStyles.HexNumber, null, out v); | |
| return v; | |
| } | |
| private static void AppendSnippet(StringBuilder sb, byte[] buf, ref int logged, int cap) | |
| { | |
| if (logged >= cap) return; | |
| int take = Math.Min(buf.Length, cap - logged); | |
| string s = Encoding.UTF8.GetString(buf, 0, take); | |
| foreach (char c in s) | |
| { | |
| if (c == '\n') sb.Append("\\n"); | |
| else if (c == '\r') sb.Append("\\r"); | |
| else if (c == '\t') sb.Append("\\t"); | |
| else if (c < 0x20) sb.Append('.'); | |
| else sb.Append(c); | |
| } | |
| logged += take; | |
| } | |
| private static void Log(string tag, string msg, ConsoleColor color) | |
| { | |
| lock (typeof(Proxy)) | |
| { | |
| var prev = Console.ForegroundColor; | |
| Console.ForegroundColor = color; | |
| Console.WriteLine("[" + tag.PadRight(4) + "] " + msg); | |
| Console.ForegroundColor = prev; | |
| } | |
| } | |
| public static void DebugLog(string msg) | |
| { | |
| if (!DebugMode) return; | |
| lock (typeof(Proxy)) | |
| { | |
| var prev = Console.ForegroundColor; | |
| Console.ForegroundColor = ConsoleColor.DarkGray; | |
| Console.WriteLine("[DBG ] " + msg); | |
| Console.ForegroundColor = prev; | |
| } | |
| } | |
| private sealed class LineBufferedReader | |
| { | |
| private readonly Stream _s; | |
| private readonly byte[] _buf = new byte[16384]; | |
| private int _head = 0, _tail = 0; | |
| public LineBufferedReader(Stream s) { _s = s; } | |
| public string ReadLine() | |
| { | |
| var sb = new StringBuilder(); | |
| while (true) | |
| { | |
| if (_head >= _tail) | |
| { | |
| _head = 0; | |
| _tail = _s.Read(_buf, 0, _buf.Length); | |
| if (_tail <= 0) return sb.Length > 0 ? sb.ToString() : null; | |
| } | |
| byte b = _buf[_head++]; | |
| if (b == '\n') | |
| { | |
| if (sb.Length > 0 && sb[sb.Length - 1] == '\r') sb.Remove(sb.Length - 1, 1); | |
| return sb.ToString(); | |
| } | |
| sb.Append((char)b); | |
| } | |
| } | |
| public byte[] Leftover() | |
| { | |
| if (_head >= _tail) return new byte[0]; | |
| var r = new byte[_tail - _head]; | |
| Array.Copy(_buf, _head, r, 0, r.Length); | |
| _head = _tail = 0; | |
| return r; | |
| } | |
| public int Read(byte[] buf, int off, int count) | |
| { | |
| int total = 0; | |
| if (_head < _tail) | |
| { | |
| int n = Math.Min(count, _tail - _head); | |
| Array.Copy(_buf, _head, buf, off, n); | |
| _head += n; off += n; count -= n; total += n; | |
| } | |
| while (count > 0) | |
| { | |
| int r = _s.Read(buf, off, count); | |
| if (r <= 0) break; | |
| off += r; count -= r; total += r; | |
| } | |
| return total; | |
| } | |
| } | |
| } | |
| } | |
| '@ | |
| # ========================================================= | |
| # COMPILE & RUN | |
| # ========================================================= | |
| $cp = New-Object System.CodeDom.Compiler.CompilerParameters | |
| $cp.ReferencedAssemblies.Add('System.dll') | Out-Null | |
| $cp.ReferencedAssemblies.Add('System.Core.dll') | Out-Null | |
| $cp.ReferencedAssemblies.Add('System.Net.dll') | Out-Null | |
| $cp.ReferencedAssemblies.Add([powershell].Assembly.Location) | Out-Null # System.Management.Automation | |
| $cp.GenerateInMemory = $true | |
| $cp.WarningLevel = 3 | |
| $cp.TreatWarningsAsErrors = $false | |
| Write-Host "[*] Compiling C# debugger against .NET Framework 4.8 ..." -ForegroundColor Cyan | |
| Add-Type -TypeDefinition $src -Language CSharp -CompilerParameters $cp -ErrorAction Stop | |
| [TlsDebugger.Proxy]::Port = $Port | |
| [TlsDebugger.Proxy]::CaPfxPath = $CaPfxPath | |
| [TlsDebugger.Proxy]::CaPassword = $CaPassword | |
| [TlsDebugger.Proxy]::LeafCertDays = $LeafCertDays | |
| [TlsDebugger.Proxy]::SkipRootInstall = [bool]$SkipRootInstall.IsPresent | |
| [TlsDebugger.Proxy]::DebugMode = ($DebugPreference -ne [System.Management.Automation.ActionPreference]::SilentlyContinue) | |
| [TlsDebugger.Proxy]::CaCert = $caCert | |
| [TlsDebugger.Proxy]::CertRunspace = $certRunspace | |
| Write-Host "" | |
| Write-Host "=================== TLS DEBUGGER ===================" -ForegroundColor White | |
| Write-Host " HTTP/HTTPS Proxy on 127.0.0.1:$Port" -ForegroundColor White | |
| Write-Host " CA file : $CaPfxPath" -ForegroundColor White | |
| Write-Host " CA password : $CaPassword" -ForegroundColor White | |
| Write-Host " CA subject : $($caCert.Subject)" -ForegroundColor White | |
| Write-Host " Wildcard mints: one cert per parent domain" -ForegroundColor White | |
| Write-Host " Cert engine : New-SelfSignedCertificate via dedicated Runspace" -ForegroundColor White | |
| Write-Host " TLS policy : SslProtocols.None (OS picks 1.3 or 1.2)" -ForegroundColor White | |
| Write-Host " Debug logging: $(if ($DebugPreference -ne 'SilentlyContinue') {'ON'} else {'OFF (use -Debug to enable)'})" -ForegroundColor White | |
| Write-Host " Root install: $(if ($SkipRootInstall) {'no'} else {'CurrentUser\Root'})" -ForegroundColor White | |
| Write-Host "====================================================" -ForegroundColor White | |
| Write-Host "" | |
| [TlsDebugger.Proxy]::Run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment