diff --git a/.gitignore b/.gitignore index 196a142..db3f412 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,6 @@ # Go workspace file go.work - -traffic_dumps/ \ No newline at end of file +*_dumps/ +out/ +log diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..919c755 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "goproxy" + ] +} \ No newline at end of file diff --git a/config.go b/config.go index 632bc52..0e9e95d 100644 --- a/config.go +++ b/config.go @@ -1,21 +1,14 @@ package main import ( - "bufio" "fmt" "os" - "strconv" - "strings" + + "github.com/BurntSushi/toml" ) -// Simple TOML parser, only handles basic formats we need +// parseConfig parses the TOML configuration file using BurntSushi/toml func parseConfig(filename string) (*Config, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - config := &Config{ DomainsOfInterest: []string{}, } @@ -23,79 +16,41 @@ func parseConfig(filename string) (*Config, error) { // Set default values config.Proxy.Port = 8080 config.Dump.OutputDir = "traffic_dumps" + config.Dump.DOIDir = "interest_dumps" - scanner := bufio.NewScanner(file) - var currentSection string - - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - - // Skip empty lines and comments - if line == "" || strings.HasPrefix(line, "#") { - continue - } - - // Check if it's a section title - if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { - currentSection = strings.Trim(line, "[]") - continue - } - - // Parse key-value pairs - parts := strings.SplitN(line, "=", 2) - if len(parts) != 2 { - continue - } - - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - - // Handle array values - if strings.HasPrefix(value, "[") && strings.HasSuffix(value, "]") { - arrayStr := strings.Trim(value, "[]") - if key == "domains_of_interest" { - if arrayStr != "" { - items := strings.Split(arrayStr, ",") - for _, item := range items { - item = strings.TrimSpace(item) - item = strings.Trim(item, "\"") - if item != "" { - config.DomainsOfInterest = append(config.DomainsOfInterest, item) - } - } - } - } - continue - } - - // Handle string values - value = strings.Trim(value, "\"") - - // Set values based on current section - switch currentSection { - case "proxy": - switch key { - case "port": - if port, err := strconv.Atoi(value); err == nil { - config.Proxy.Port = port - } - } - case "dump": - switch key { - case "output_dir": - config.Dump.OutputDir = value - } - } + // Parse the TOML file + _, err := toml.DecodeFile(filename, config) + if err != nil { + return nil, fmt.Errorf("failed to parse config file %s: %v", filename, err) } - if err := scanner.Err(); err != nil { - return nil, err + // Create dump directories if they don't exist + if err := createDumpDirectories(config); err != nil { + return nil, fmt.Errorf("failed to create dump directories: %v", err) } return config, nil } -func (c *Config) String() string { - return fmt.Sprintf("Config{DomainsOfInterest: %v, Proxy: {Port: %d}, Dump: {OutputDir: %s}}", - c.DomainsOfInterest, c.Proxy.Port, c.Dump.OutputDir) +// createDumpDirectories creates the dump directories if they don't exist +func createDumpDirectories(config *Config) error { + directories := []string{ + config.Dump.OutputDir, + config.Dump.DOIDir, + } + + for _, dir := range directories { + if dir != "" { + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %v", dir, err) + } + } + } + + return nil +} + +func (c *Config) String() string { + return fmt.Sprintf("Config{DomainsOfInterest: %v, Proxy: {Port: %d}, Dump: {OutputDir: %s, DOI_dir: %s}}", + c.DomainsOfInterest, c.Proxy.Port, c.Dump.OutputDir, c.Dump.DOIDir) } diff --git a/config.toml b/config.toml index 95bab7b..3509b4a 100644 --- a/config.toml +++ b/config.toml @@ -1,10 +1,7 @@ # Domains of interest configuration # Requests and responses for these domains will be printed to stdout domains_of_interest = [ - "example.com", - "httpbin.org", - "api.github.com", - "www.google.com" + "amemv.com" ] # Proxy server configuration @@ -14,3 +11,4 @@ port = 8080 # Traffic dump configuration [dump] output_dir = "traffic_dumps" +DOI_dir = "interest_dumps" \ No newline at end of file diff --git a/go.mod b/go.mod index 5ec5887..8b1f28b 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,14 @@ module mitm go 1.25 require ( - golang.org/x/sys v0.15.0 + github.com/elazarl/goproxy v1.7.2 + golang.org/x/sys v0.30.0 software.sslmate.com/src/go-pkcs12 v0.4.0 ) -require golang.org/x/crypto v0.17.0 // indirect +require ( + github.com/BurntSushi/toml v1.5.0 + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/text v0.22.0 // indirect +) diff --git a/go.sum b/go.sum index caca033..eb0f595 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,22 @@ -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/main.go b/main.go index 30df443..f71c879 100644 --- a/main.go +++ b/main.go @@ -3,17 +3,13 @@ package main import ( "bytes" "context" - "crypto/rand" - "crypto/rsa" "crypto/tls" "crypto/x509" - "crypto/x509/pkix" "encoding/pem" "flag" "fmt" "io" "log" - "math/big" "net" "net/http" "net/url" @@ -26,29 +22,29 @@ import ( "syscall" "time" + "github.com/elazarl/goproxy" "golang.org/x/sys/windows/registry" ) type Config struct { - DomainsOfInterest []string + DomainsOfInterest []string `toml:"domains_of_interest"` Proxy struct { - Port int - } + Port int `toml:"port"` + } `toml:"proxy"` Dump struct { - OutputDir string - } + OutputDir string `toml:"output_dir"` + DOIDir string `toml:"DOI_dir"` + } `toml:"dump"` } type ProxyServer struct { config *Config tlsConfig *tls.Config + proxy *goproxy.ProxyHttpServer server *http.Server originalProxy string } -// testMode can be set via build flags for UTF-8 testing -var testMode string - func main() { // Parse command line flags var testConnectivity = flag.Bool("test", false, "Test proxy connectivity") @@ -61,34 +57,22 @@ func main() { fmt.Println("Starting MITM proxy server...") - // If in test mode, output UTF-8 test characters and exit - if testMode == "true" { - fmt.Println("========================================") - fmt.Println("UTF-8 Encoding Test") - fmt.Println("========================================") - fmt.Println("Testing Unicode characters:") - fmt.Println("English: Hello World") - fmt.Println("Chinese: 你好世界") - fmt.Println("Japanese: こんにちは世界") - fmt.Println("Korean: 안녕하세요 세계") - fmt.Println("Symbols: ✓ ✗ ★ ♥ ◆ ▲") - fmt.Println("") - fmt.Println("If you can see the above characters correctly,") - fmt.Println("UTF-8 encoding is working properly in the MITM proxy.") - fmt.Println("========================================") - return - } - // Load configuration config, err := loadConfig("config.toml") if err != nil { log.Fatalf("Failed to load configuration: %v", err) } + log.Println(config.String()) - // Create output directory + // Create output directories if err := os.MkdirAll(config.Dump.OutputDir, 0755); err != nil { log.Fatalf("Failed to create output directory: %v", err) } + if config.Dump.DOIDir != "" { + if err := os.MkdirAll(config.Dump.DOIDir, 0755); err != nil { + log.Fatalf("Failed to create DOI directory: %v", err) + } + } // Create proxy server proxy, err := NewProxyServer(config) @@ -100,20 +84,20 @@ func main() { if err := proxy.installCACert(); err != nil { log.Printf("Failed to install CA certificate: %v", err) } else { - fmt.Println("✓ CA certificate installed successfully") + fmt.Println("CA certificate installed successfully") } // Set system proxy if err := proxy.setSystemProxy(); err != nil { log.Fatalf("Failed to set system proxy: %v", err) } - fmt.Printf("✓ System proxy set to 127.0.0.1:%d\n", config.Proxy.Port) + fmt.Printf("System proxy set to 127.0.0.1:%d\n", config.Proxy.Port) // Verify proxy settings were applied if err := proxy.verifyProxySettings(); err != nil { - fmt.Printf("⚠️ Warning: Could not verify proxy settings: %v\n", err) + fmt.Printf("Warning: Could not verify proxy settings: %v\n", err) } else { - fmt.Printf("✓ Proxy settings verified in Windows registry\n") + fmt.Printf("Proxy settings verified in Windows registry\n") } // Show current proxy configuration for debugging @@ -122,7 +106,7 @@ func main() { // Start proxy server serverStarted := make(chan error, 1) go func() { - fmt.Printf("🚀 Starting proxy server on port %d...\n", config.Proxy.Port) + fmt.Printf("Starting proxy server on port %d...\n", config.Proxy.Port) if err := proxy.Start(); err != nil && err != http.ErrServerClosed { serverStarted <- err return @@ -131,24 +115,15 @@ func main() { }() // Wait for server to start or fail - fmt.Printf("⏳ Waiting for server to start...\n") + fmt.Printf("Waiting for server to start...\n") select { case err := <-serverStarted: if err != nil { - log.Fatalf("❌ Failed to start proxy server: %v", err) + log.Fatalf("Failed to start proxy server: %v", err) } - fmt.Printf("✅ Proxy server successfully started on port %d\n", config.Proxy.Port) + fmt.Printf("Proxy server successfully started on port %d\n", config.Proxy.Port) case <-time.After(5 * time.Second): - fmt.Printf("✅ Proxy server appears to be starting (no immediate errors)\n") - } - - // Test basic connectivity first - fmt.Printf("🔍 Testing basic proxy connectivity...\n") - if err := testBasicConnectivity(config.Proxy.Port); err != nil { - fmt.Printf("⚠️ Basic connectivity test failed: %v\n", err) - fmt.Printf("💡 Try running diagnose_proxy.bat for detailed diagnostics\n") - } else { - fmt.Printf("✅ Basic proxy connectivity test passed\n") + fmt.Printf("Proxy server appears to be starting (no immediate errors)\n") } // Run full connectivity test if requested @@ -162,9 +137,9 @@ func main() { signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) <-sigChan - fmt.Println("\n🛑 Shutting down proxy server...") + fmt.Println("\nShutting down proxy server...") proxy.Shutdown() - fmt.Println("✓ Proxy server closed") + fmt.Println("Proxy server closed") } func loadConfig(filename string) (*Config, error) { @@ -172,31 +147,128 @@ func loadConfig(filename string) (*Config, error) { } func NewProxyServer(config *Config) (*ProxyServer, error) { - // Load hardcoded P12 certificate + // Load hardcoded P12 certificate for MITM tlsConfig, err := loadHardcodedCertificate() if err != nil { return nil, fmt.Errorf("failed to load certificate: %v", err) } - proxy := &ProxyServer{ + // Create goproxy instance + goProxy := goproxy.NewProxyHttpServer() + goProxy.Verbose = true + + ps := &ProxyServer{ config: config, tlsConfig: tlsConfig, + proxy: goProxy, } - // Create HTTP server with enhanced logging - // IMPORTANT: Use a custom handler that properly handles CONNECT requests - proxy.server = &http.Server{ - Addr: fmt.Sprintf(":%d", config.Proxy.Port), - Handler: proxy, // Use the proxy itself as the handler - // Add some server configuration for better debugging - ErrorLog: log.New(os.Stdout, "HTTP-SERVER: ", log.LstdFlags), + // Configure MITM for HTTPS traffic + ps.setupMITM() + + // Setup request/response handlers + ps.setupHandlers() + + // Create HTTP server + ps.server = &http.Server{ + Addr: fmt.Sprintf(":%d", config.Proxy.Port), + Handler: ps.proxy, + ErrorLog: log.New(os.Stdout, "GOPROXY-SERVER: ", log.LstdFlags), } - return proxy, nil + return ps, nil +} + +// setupMITM configures HTTPS MITM using goproxy +func (p *ProxyServer) setupMITM() { + // Set the CA certificate for MITM + if len(p.tlsConfig.Certificates) > 0 { + cert := p.tlsConfig.Certificates[0] + goproxy.GoproxyCa = cert + goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: goproxy.TLSConfigFromCA(&cert)} + } + + // Enable MITM for all HTTPS connections + p.proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) +} + +// setupHandlers configures request/response handlers for logging and filtering +func (p *ProxyServer) setupHandlers() { + // Add health check endpoint + p.proxy.OnRequest().DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + if r.Host == "127.0.0.1:"+fmt.Sprintf("%d", p.config.Proxy.Port) && r.URL.Path == "/proxy-health" { + return r, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusOK, "MITM Proxy is running") + } + return r, nil + }) + + // Log all HTTP requests and capture request body + p.proxy.OnRequest().DoFunc(func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + timestamp := time.Now().Format("20060102T15:04:05.000000") + fmt.Printf("[%s][INFO][Interest=%v] HTTP Request: %s %s from %s\n", timestamp, p.isDomainOfInterest(r.Host), r.Method, r.URL.String(), r.RemoteAddr) + + // Read request body once and recreate it for both dumping and forwarding + if r.Body != nil { + reqBody, err := io.ReadAll(r.Body) + if err != nil { + log.Printf("Failed to read request body: %v", err) + return r, nil + } + r.Body.Close() + + // Recreate the request body so it can be forwarded to the server + r.Body = io.NopCloser(bytes.NewReader(reqBody)) + r.ContentLength = int64(len(reqBody)) + + // Store request body in context for later use in response handler + if len(reqBody) > 0 { + ctx.UserData = reqBody + } + } + + return r, nil + }) + + // Log all HTTP responses and dump traffic + p.proxy.OnResponse().DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx) *http.Response { + timestamp := time.Now().Format("20060102T15:04:05.000000") + if r != nil { + fmt.Printf("[%s][INFO][Interest=%v] HTTP Response: %s %s\n", timestamp, p.isDomainOfInterest(ctx.Req.Host), r.Status, ctx.Req.URL.String()) + + // Get request body from context (if available) + var reqBody []byte + if ctx.UserData != nil { + if body, ok := ctx.UserData.([]byte); ok { + reqBody = body + } + } + + // Read response body once and recreate it for both dumping and returning + if r.Body != nil { + respBody, err := io.ReadAll(r.Body) + if err != nil { + log.Printf("Failed to read response body: %v", err) + return r + } + r.Body.Close() + + // Recreate the response body so it can be returned to client + r.Body = io.NopCloser(bytes.NewReader(respBody)) + r.ContentLength = int64(len(respBody)) + + // Dump traffic to file with both request and response bodies + p.dumpHTTPTrafficWithBodies(ctx.Req, r, reqBody, respBody) + } else { + // No response body, but may have request body + p.dumpHTTPTrafficWithBodies(ctx.Req, r, reqBody, nil) + } + } + return r + }) } func (p *ProxyServer) Start() error { - fmt.Printf("🔧 Starting HTTP server on %s\n", p.server.Addr) + fmt.Printf("Starting HTTP server on %s\n", p.server.Addr) // Test if port is available listener, err := net.Listen("tcp", p.server.Addr) @@ -204,7 +276,7 @@ func (p *ProxyServer) Start() error { return fmt.Errorf("failed to bind to port %s: %v", p.server.Addr, err) } - fmt.Printf("✅ Successfully bound to port %s\n", p.server.Addr) + fmt.Printf("Successfully bound to port %s\n", p.server.Addr) // Start serving with our listener return p.server.Serve(listener) @@ -215,7 +287,7 @@ func (p *ProxyServer) Shutdown() { if err := p.restoreSystemProxy(); err != nil { log.Printf("Failed to restore system proxy: %v", err) } else { - fmt.Println("✓ System proxy restored") + fmt.Println("System proxy restored") } // Close HTTP server @@ -224,480 +296,9 @@ func (p *ProxyServer) Shutdown() { p.server.Shutdown(ctx) } -func (p *ProxyServer) createTLSConfigForHost(host string) *tls.Config { - // Create a TLS config that generates certificates on-demand - config := &tls.Config{ - InsecureSkipVerify: true, - GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - // Generate a certificate specifically for this host - return p.generateCertificateForHost(info.ServerName) - }, - } - return config -} - -// generateCertificateForHost creates a certificate for a specific hostname -func (p *ProxyServer) generateCertificateForHost(hostname string) (*tls.Certificate, error) { - // Use the CA certificate and key from our base config - if len(p.tlsConfig.Certificates) == 0 { - return nil, fmt.Errorf("no CA certificate available") - } - - caCert := p.tlsConfig.Certificates[0] - - // Parse the CA certificate - caCertParsed, err := x509.ParseCertificate(caCert.Certificate[0]) - if err != nil { - return nil, fmt.Errorf("failed to parse CA certificate: %v", err) - } - - // Generate a new private key for the host certificate - hostPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - return nil, fmt.Errorf("failed to generate host private key: %v", err) - } - - // Create certificate template for the specific host - template := x509.Certificate{ - SerialNumber: big.NewInt(time.Now().UnixNano()), - Subject: pkix.Name{ - Organization: []string{"MITM Proxy"}, - Country: []string{"US"}, - CommonName: hostname, - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(365 * 24 * time.Hour), - // Proper key usage for server certificate - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - }, - BasicConstraintsValid: true, - IsCA: false, - } - - // Add the hostname to DNS names - template.DNSNames = []string{hostname} - - // If hostname is an IP address, add it to IP addresses - if ip := net.ParseIP(hostname); ip != nil { - template.IPAddresses = []net.IP{ip} - } - - // Generate the certificate signed by our CA - certDER, err := x509.CreateCertificate(rand.Reader, &template, caCertParsed, &hostPrivateKey.PublicKey, caCert.PrivateKey) - if err != nil { - return nil, fmt.Errorf("failed to create certificate for %s: %v", hostname, err) - } - - // Create the TLS certificate - cert := &tls.Certificate{ - Certificate: [][]byte{certDER}, - PrivateKey: hostPrivateKey, - } - - return cert, nil -} - -// ServeHTTP implements http.Handler interface to properly handle all requests including CONNECT -func (p *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Handle health check endpoint - if r.URL.Path == "/proxy-health" { - fmt.Printf("🏥 Health check request from %s\n", r.RemoteAddr) - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusOK) - w.Write([]byte("MITM Proxy is running")) - return - } - - // Handle all other requests through our main handler - p.handleHTTP(w, r) -} - -func (p *ProxyServer) handleHTTP(w http.ResponseWriter, r *http.Request) { - // Log all incoming requests for debugging with timestamp - timestamp := time.Now().Format("15:04:05") - fmt.Printf("[%s] 📥 INCOMING: %s %s %s from %s\n", timestamp, r.Method, r.Host, r.URL.Path, r.RemoteAddr) - fmt.Printf("[%s] 📋 Headers: %d headers received\n", timestamp, len(r.Header)) - - // Log ALL headers for debugging proxy issues - for name, values := range r.Header { - for _, value := range values { - fmt.Printf("[%s] 📋 Header: %s: %s\n", timestamp, name, value) - } - } - - // Log the complete URL being requested - fmt.Printf("[%s] 🌐 Full URL: %s\n", timestamp, r.URL.String()) - - // Check if this is actually an HTTPS request being sent as HTTP - if r.URL.Scheme == "https" { - fmt.Printf("[%s] ⚠️ HTTPS URL received as HTTP request - browser may not be using CONNECT\n", timestamp) - } - - if r.Method == http.MethodConnect { - fmt.Printf("[%s] 🔐 Processing HTTPS CONNECT for %s\n", timestamp, r.Host) - p.handleHTTPS(w, r) - return - } - - // Handle HTTP request - fmt.Printf("[%s] 🌐 Processing HTTP request for %s\n", timestamp, r.Host) - p.handleHTTPRequest(w, r) -} - -func (p *ProxyServer) handleHTTPS(w http.ResponseWriter, r *http.Request) { - timestamp := time.Now().Format("15:04:05") - fmt.Printf("[%s] 🔐 HTTPS CONNECT: Starting for %s from %s\n", timestamp, r.Host, r.RemoteAddr) - - // Get the hijacker before sending any response - hijacker, ok := w.(http.Hijacker) - if !ok { - fmt.Printf("[%s] ❌ HTTPS CONNECT: Hijacking not supported for %s\n", timestamp, r.Host) - http.Error(w, "Hijacking not supported", http.StatusInternalServerError) - return - } - fmt.Printf("[%s] ✅ HTTPS CONNECT: Hijacker obtained for %s\n", timestamp, r.Host) - - // Hijack the connection first - clientConn, _, err := hijacker.Hijack() - if err != nil { - fmt.Printf("[%s] ❌ HTTPS CONNECT: Hijack failed for %s: %v\n", timestamp, r.Host, err) - http.Error(w, err.Error(), http.StatusServiceUnavailable) - return - } - defer func() { - fmt.Printf("[%s] 🔌 HTTPS CONNECT: Closing client connection for %s\n", timestamp, r.Host) - clientConn.Close() - }() - fmt.Printf("[%s] ✅ HTTPS CONNECT: Connection hijacked for %s\n", timestamp, r.Host) - - // Send 200 Connection established response manually - connectResponse := "HTTP/1.1 200 Connection established\r\n\r\n" - written, err := clientConn.Write([]byte(connectResponse)) - if err != nil { - fmt.Printf("[%s] ❌ HTTPS CONNECT: Failed to send response to %s: %v\n", timestamp, r.Host, err) - return - } - fmt.Printf("[%s] ✅ HTTPS CONNECT: Sent %d bytes response to %s\n", timestamp, written, r.Host) - - // Give the client a moment to process the CONNECT response - time.Sleep(100 * time.Millisecond) - - // Try MITM first - if it fails, fall back to transparent proxy - fmt.Printf("[%s] 🔍 HTTPS CONNECT: Attempting MITM for %s\n", timestamp, r.Host) - if !p.attemptMITM(clientConn, r.Host) { - fmt.Printf("[%s] ⚠️ HTTPS CONNECT: MITM failed for %s, falling back to transparent proxy\n", timestamp, r.Host) - p.handleHTTPSTransparent(clientConn, r.Host) - } -} - -// attemptMITM tries to perform MITM decryption, returns true if successful -func (p *ProxyServer) attemptMITM(clientConn net.Conn, host string) bool { - timestamp := time.Now().Format("15:04:05") - fmt.Printf("[%s] 🔍 MITM: Attempting MITM for %s\n", timestamp, host) - - // First establish connection to target server - fmt.Printf("[%s] 🌐 MITM: Connecting to target %s\n", timestamp, host) - destConn, err := tls.Dial("tcp", host, &tls.Config{ - InsecureSkipVerify: true, - }) - if err != nil { - fmt.Printf("[%s] ❌ MITM: Cannot connect to target %s with TLS: %v\n", timestamp, host, err) - return false - } - defer destConn.Close() - fmt.Printf("[%s] ✅ MITM: Connected to target %s\n", timestamp, host) - - // Create TLS config for client connection - fmt.Printf("[%s] 🔧 MITM: Creating TLS config for client connection to %s\n", timestamp, host) - tlsConfig := p.createTLSConfigForHost(host) - - // Wrap client connection with our certificate - fmt.Printf("[%s] 🔐 MITM: Wrapping client connection with our certificate for %s\n", timestamp, host) - tlsClientConn := tls.Server(clientConn, tlsConfig) - defer tlsClientConn.Close() - - // Set handshake timeout - fmt.Printf("[%s] ⏰ MITM: Setting TLS handshake timeout for %s\n", timestamp, host) - tlsClientConn.SetDeadline(time.Now().Add(10 * time.Second)) - - // Try TLS handshake with client - fmt.Printf("[%s] 🤝 MITM: Starting TLS handshake with client for %s\n", timestamp, host) - if err := tlsClientConn.Handshake(); err != nil { - fmt.Printf("[%s] ❌ MITM: TLS handshake failed for %s: %v\n", timestamp, host, err) - return false - } - - // Clear deadline after successful handshake - tlsClientConn.SetDeadline(time.Time{}) - fmt.Printf("[%s] ✅ MITM: TLS handshake successful for %s - decrypting traffic\n", timestamp, host) - - // Create channels for bidirectional copying - done := make(chan struct{}, 2) - - // Copy data bidirectionally with decryption - go func() { - defer func() { - fmt.Printf("[%s] 📤 MITM: Response copying finished for %s\n", timestamp, host) - done <- struct{}{} - }() - p.copyAndDumpDecrypted(destConn, tlsClientConn, host, "response") - }() - - go func() { - defer func() { - fmt.Printf("[%s] 📥 MITM: Request copying finished for %s\n", timestamp, host) - done <- struct{}{} - }() - p.copyAndDumpDecrypted(tlsClientConn, destConn, host, "request") - }() - - // Wait for one direction to close - <-done - fmt.Printf("[%s] 🔚 MITM: Connection to %s closed\n", timestamp, host) - return true -} - -// handleHTTPSTransparent handles transparent HTTPS proxy (when TLS decryption fails) -func (p *ProxyServer) handleHTTPSTransparent(clientConn net.Conn, host string) { - timestamp := time.Now().Format("15:04:05") - fmt.Printf("[%s] 🔄 TRANSPARENT: Starting transparent proxy for %s\n", timestamp, host) - - // Set connection timeout - clientConn.SetDeadline(time.Now().Add(30 * time.Second)) - defer clientConn.SetDeadline(time.Time{}) - - // Establish connection to target server - fmt.Printf("[%s] 🌐 TRANSPARENT: Connecting to target %s\n", timestamp, host) - destConn, err := net.DialTimeout("tcp", host, 10*time.Second) - if err != nil { - fmt.Printf("[%s] ❌ TRANSPARENT: Failed to connect to target server %s: %v\n", timestamp, host, err) - return - } - defer destConn.Close() - fmt.Printf("[%s] ✅ TRANSPARENT: Connected to target %s\n", timestamp, host) - - // Set timeout for destination connection - destConn.SetDeadline(time.Now().Add(30 * time.Second)) - defer destConn.SetDeadline(time.Time{}) - - fmt.Printf("[%s] 🔗 TRANSPARENT: Established transparent proxy connection to %s\n", timestamp, host) - - // Create channels to handle connection closing - done := make(chan struct{}, 2) - - // Transparently proxy encrypted data - go func() { - defer func() { - fmt.Printf("[%s] 📤 TRANSPARENT: Response copying finished for %s\n", timestamp, host) - done <- struct{}{} - }() - p.copyAndDump(destConn, clientConn, host, "response") - }() - - go func() { - defer func() { - fmt.Printf("[%s] 📥 TRANSPARENT: Request copying finished for %s\n", timestamp, host) - done <- struct{}{} - }() - p.copyAndDump(clientConn, destConn, host, "request") - }() - - // Wait for one direction to close, then close both connections - <-done - fmt.Printf("[%s] 🔚 TRANSPARENT: Proxy connection to %s closed\n", timestamp, host) -} - -func (p *ProxyServer) handleHTTPRequest(w http.ResponseWriter, r *http.Request) { - // Build the complete target URL - targetURL := r.URL - if targetURL.Scheme == "" { - targetURL.Scheme = "http" - } - if targetURL.Host == "" { - targetURL.Host = r.Host - } - - // Read request body for dumping - var reqBody []byte - if r.Body != nil { - var err error - reqBody, err = io.ReadAll(r.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - r.Body = io.NopCloser(bytes.NewReader(reqBody)) - } - - // Create new request with exact same properties - proxyReq, err := http.NewRequest(r.Method, targetURL.String(), bytes.NewReader(reqBody)) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // Copy ALL headers exactly as-is (completely transparent) - proxyReq.Header = make(http.Header) - for name, values := range r.Header { - proxyReq.Header[name] = values - } - - // Create a completely transparent HTTP client - client := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, // For MITM purposes - }, - DisableKeepAlives: false, - DisableCompression: true, // Keep original compression - MaxIdleConns: 100, - IdleConnTimeout: 90 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - }, - // IMPORTANT: Don't interfere with redirects - let client handle them - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - Timeout: 30 * time.Second, - } - - // Make the request - resp, err := client.Do(proxyReq) - if err != nil { - http.Error(w, err.Error(), http.StatusServiceUnavailable) - return - } - defer resp.Body.Close() - - // Read response body for dumping - respBody, err := io.ReadAll(resp.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // Dump traffic for inspection - p.dumpTraffic(r, reqBody, respBody, r.Host) - - // Print traffic if domain is of interest - if p.isDomainOfInterest(r.Host) { - p.printTraffic(r, reqBody, respBody) - } - - // Copy ALL response headers exactly as-is (completely transparent) - for name, values := range resp.Header { - w.Header()[name] = values - } - - // Write the exact response status and body - w.WriteHeader(resp.StatusCode) - w.Write(respBody) -} - -func (p *ProxyServer) copyAndDump(dst io.Writer, src io.Reader, host, direction string) { - buffer := make([]byte, 32768) // Increased buffer size - var allData []byte - totalBytes := 0 - - for { - n, err := src.Read(buffer) - if n > 0 { - data := buffer[:n] - written, writeErr := dst.Write(data) - if writeErr != nil { - fmt.Printf("Write error in %s direction for %s: %v\n", direction, host, writeErr) - break - } - if written != n { - fmt.Printf("Incomplete write in %s direction for %s: wrote %d of %d bytes\n", direction, host, written, n) - } - allData = append(allData, data...) - totalBytes += n - } - if err != nil { - if err != io.EOF { - fmt.Printf("Read error in %s direction for %s: %v\n", direction, host, err) - } - break - } - } - - // Dump encrypted HTTPS traffic - if len(allData) > 0 { - p.dumpHTTPSTraffic(host, direction+"_encrypted", allData) - - // If domain is of interest, print to stdout - if p.isDomainOfInterest(host) { - fmt.Printf("\n=== HTTPS %s Encrypted Traffic: %s ===\n", direction, host) - fmt.Printf("Data length: %d bytes\n", totalBytes) - // Display first 256 bytes of encrypted data (hexadecimal format) - displayLength := len(allData) - if displayLength > 256 { - displayLength = 256 - } - fmt.Printf("Encrypted data content (first %d bytes): %x\n", displayLength, allData[:displayLength]) - fmt.Println("==========================================") - } - } -} - -// copyAndDumpDecrypted copies and dumps decrypted data -func (p *ProxyServer) copyAndDumpDecrypted(dst io.Writer, src io.Reader, host, direction string) { - buffer := make([]byte, 32768) // Increased buffer size - var allData []byte - totalBytes := 0 - - for { - n, err := src.Read(buffer) - if n > 0 { - data := buffer[:n] - written, writeErr := dst.Write(data) - if writeErr != nil { - fmt.Printf("Write error in decrypted %s direction for %s: %v\n", direction, host, writeErr) - break - } - if written != n { - fmt.Printf("Incomplete write in decrypted %s direction for %s: wrote %d of %d bytes\n", direction, host, written, n) - } - allData = append(allData, data...) - totalBytes += n - } - if err != nil { - if err != io.EOF { - fmt.Printf("Read error in decrypted %s direction for %s: %v\n", direction, host, err) - } - break - } - } - - // Dump decrypted HTTPS traffic - if len(allData) > 0 { - p.dumpHTTPSTraffic(host, direction+"_decrypted", allData) - - // If domain is of interest, print to stdout - if p.isDomainOfInterest(host) { - fmt.Printf("\n=== HTTPS %s Decrypted Traffic: %s ===\n", direction, host) - fmt.Printf("Data length: %d bytes\n", totalBytes) - // Display complete decrypted data (limit to first 2KB for readability) - displayLength := len(allData) - if displayLength > 2048 { - displayLength = 2048 - } - fmt.Printf("Decrypted data content (first %d bytes): %s\n", displayLength, string(allData[:displayLength])) - fmt.Println("==========================================") - } - } -} - -func (p *ProxyServer) dumpTraffic(req *http.Request, reqBody, respBody []byte, host string) { - timestamp := time.Now().Format("20060102_150405") - filename := fmt.Sprintf("%s_%s.txt", timestamp, sanitizeFilename(host)) - filepath := filepath.Join(p.config.Dump.OutputDir, filename) - - file, err := os.Create(filepath) +// dumpHTTPTraffic dumps HTTP request and response to file +func (p *ProxyServer) dumpHTTPTraffic(req *http.Request, resp *http.Response) { + file, err := os.Create(p.getFilePath(req)) if err != nil { log.Printf("Failed to create dump file: %v", err) return @@ -708,6 +309,49 @@ func (p *ProxyServer) dumpTraffic(req *http.Request, reqBody, respBody []byte, h fmt.Fprintf(file, "=== REQUEST ===\n") fmt.Fprintf(file, "%s %s %s\n", req.Method, req.URL.String(), req.Proto) fmt.Fprintf(file, "Host: %s\n", req.Host) + + // Write all request headers + for name, values := range req.Header { + for _, value := range values { + fmt.Fprintf(file, "%s: %s\n", name, value) + } + } + fmt.Fprintf(file, "\n") + + // Note: Request body handling is done in the request handler to avoid consuming it twice + + // Write response information + if resp != nil { + fmt.Fprintf(file, "\n=== RESPONSE ===\n") + fmt.Fprintf(file, "%s %s\n", resp.Proto, resp.Status) + + // Write all response headers + for name, values := range resp.Header { + for _, value := range values { + fmt.Fprintf(file, "%s: %s\n", name, value) + } + } + fmt.Fprintf(file, "\n") + + // Note: Response body will be handled in the response handler to avoid consuming it + } +} + +// dumpHTTPTrafficWithBodies dumps HTTP request and response with both bodies to file +func (p *ProxyServer) dumpHTTPTrafficWithBodies(req *http.Request, resp *http.Response, reqBody []byte, respBody []byte) { + file, err := os.Create(p.getFilePath(req)) + if err != nil { + log.Printf("Failed to create dump file: %v", err) + return + } + defer file.Close() + + // Write request information + fmt.Fprintf(file, "=== REQUEST ===\n") + fmt.Fprintf(file, "%s %s %s\n", req.Method, req.URL.String(), req.Proto) + fmt.Fprintf(file, "Host: %s\n", req.Host) + + // Write all request headers for name, values := range req.Header { for _, value := range values { fmt.Fprintf(file, "%s: %s\n", name, value) @@ -721,25 +365,35 @@ func (p *ProxyServer) dumpTraffic(req *http.Request, reqBody, respBody []byte, h } // Write response information - fmt.Fprintf(file, "\n=== RESPONSE ===\n") - fmt.Fprintf(file, "%s\n", string(respBody)) -} + if resp != nil { + fmt.Fprintf(file, "\n=== RESPONSE ===\n") + fmt.Fprintf(file, "%s %s\n", resp.Proto, resp.Status) -func (p *ProxyServer) dumpHTTPSTraffic(host, direction string, data []byte) { - timestamp := time.Now().Format("20060102_150405") - filename := fmt.Sprintf("%s_%s_%s.bin", timestamp, sanitizeFilename(host), direction) - filepath := filepath.Join(p.config.Dump.OutputDir, filename) + // Write all response headers + for name, values := range resp.Header { + for _, value := range values { + fmt.Fprintf(file, "%s: %s\n", name, value) + } + } + fmt.Fprintf(file, "\n") - file, err := os.Create(filepath) - if err != nil { - log.Printf("Failed to create HTTPS dump file: %v", err) - return + // Write response body + if len(respBody) > 0 { + fmt.Fprintf(file, "%s\n", string(respBody)) + } } - defer file.Close() - - file.Write(data) } +func (p *ProxyServer) getFilePath(req *http.Request) string { + timestamp := time.Now().Format("20060102.150405.000000000") + filename := fmt.Sprintf("%s_%s.txt", timestamp, sanitizeFilename(req.URL.String())) + if p.isDomainOfInterest(req.Host) { + return filepath.Join(p.config.Dump.DOIDir, filename) + } + return filepath.Join(p.config.Dump.OutputDir, filename) +} + +// Keep the isDomainOfInterest method as it's still needed func (p *ProxyServer) isDomainOfInterest(host string) bool { // Remove port number if colonIndex := strings.Index(host, ":"); colonIndex != -1 { @@ -754,30 +408,7 @@ func (p *ProxyServer) isDomainOfInterest(host string) bool { return false } -func (p *ProxyServer) printTraffic(req *http.Request, reqBody, respBody []byte) { - fmt.Printf("\n=== Traffic from Domain of Interest: %s ===\n", req.Host) - fmt.Printf("Request: %s %s\n", req.Method, req.URL.String()) - - // Print request headers - fmt.Println("Request Headers:") - for name, values := range req.Header { - for _, value := range values { - fmt.Printf(" %s: %s\n", name, value) - } - } - - // Print complete request body - if len(reqBody) > 0 { - fmt.Printf("Request Body (%d bytes): %s\n", len(reqBody), string(reqBody)) - } - - // Print complete response body - if len(respBody) > 0 { - fmt.Printf("Response Body (%d bytes): %s\n", len(respBody), string(respBody)) - } - fmt.Println("==========================================") -} - +// sanitizeFilename replaces unsafe characters for file names func sanitizeFilename(filename string) string { // Replace unsafe characters replacer := strings.NewReplacer( @@ -791,7 +422,11 @@ func sanitizeFilename(filename string) string { ">", "_", "|", "_", ) - return replacer.Replace(filename) + s := replacer.Replace(filename) + if len(s) > 80 { + return s[:80] + } + return s } func (p *ProxyServer) setSystemProxy() error { @@ -916,12 +551,12 @@ func (p *ProxyServer) verifyProxySettings() error { } func (p *ProxyServer) showProxyConfiguration() { - fmt.Printf("🔍 Current Proxy Configuration:\n") + fmt.Printf("Current Proxy Configuration:\n") // Show Windows proxy settings k, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE) if err != nil { - fmt.Printf(" ❌ Could not read proxy settings: %v\n", err) + fmt.Printf("Could not read proxy settings: %v\n", err) return } defer k.Close() @@ -1006,43 +641,14 @@ func setConsoleUTF8() { cmd.Run() } -// testBasicConnectivity tests if the proxy server is responding to basic HTTP requests -func testBasicConnectivity(port int) error { - client := &http.Client{ - Timeout: 5 * time.Second, - } - - healthURL := fmt.Sprintf("http://127.0.0.1:%d/proxy-health", port) - resp, err := client.Get(healthURL) - if err != nil { - return fmt.Errorf("failed to connect to proxy health endpoint: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("proxy health check returned status %d", resp.StatusCode) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read health check response: %v", err) - } - - if !strings.Contains(string(body), "MITM Proxy is running") { - return fmt.Errorf("unexpected health check response: %s", string(body)) - } - - return nil -} - // testProxyConnectivity tests the proxy with simple HTTP and HTTPS requests func testProxyConnectivity(proxyAddr string) { - fmt.Println("🧪 Testing proxy connectivity...") + fmt.Println("Testing proxy connectivity...") // Create proxy URL proxyURL, err := url.Parse(fmt.Sprintf("http://%s", proxyAddr)) if err != nil { - fmt.Printf("❌ Failed to parse proxy URL: %v\n", err) + fmt.Printf("Failed to parse proxy URL: %v\n", err) return } @@ -1059,55 +665,55 @@ func testProxyConnectivity(proxyAddr string) { } // Test HTTP request - fmt.Println("🌐 Testing HTTP request...") + fmt.Println("Testing HTTP request...") testHTTPRequest(client, "http://httpbin.org/ip") // Test HTTPS request - fmt.Println("🔒 Testing HTTPS request...") + fmt.Println("Testing HTTPS request...") testHTTPSRequest(client, "https://httpbin.org/ip") // Test redirects (should be transparent) - fmt.Println("🔄 Testing HTTP redirect...") + fmt.Println("Testing HTTP redirect...") testRedirectTransparency(client, "http://httpbin.org/redirect/1") // Test domain that might cause issues - fmt.Println("🌍 Testing Google...") + fmt.Println("Testing Google...") testHTTPSRequest(client, "https://www.google.com") } func testHTTPRequest(client *http.Client, testURL string) { resp, err := client.Get(testURL) if err != nil { - fmt.Printf("❌ HTTP test failed: %v\n", err) + fmt.Printf("HTTP test failed: %v\n", err) return } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - fmt.Printf("❌ Failed to read HTTP response: %v\n", err) + fmt.Printf("Failed to read HTTP response: %v\n", err) return } - fmt.Printf("✅ HTTP test successful: %s\n", resp.Status) + fmt.Printf("HTTP test successful: %s\n", resp.Status) fmt.Printf(" Response: %s\n", string(body)[:min(len(body), 200)]) } func testHTTPSRequest(client *http.Client, testURL string) { resp, err := client.Get(testURL) if err != nil { - fmt.Printf("❌ HTTPS test failed: %v\n", err) + fmt.Printf("HTTPS test failed: %v\n", err) return } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - fmt.Printf("❌ Failed to read HTTPS response: %v\n", err) + fmt.Printf("Failed to read HTTPS response: %v\n", err) return } - fmt.Printf("✅ HTTPS test successful: %s\n", resp.Status) + fmt.Printf("HTTPS test successful: %s\n", resp.Status) fmt.Printf(" Response: %s\n", string(body)[:min(len(body), 200)]) } @@ -1123,16 +729,16 @@ func testRedirectTransparency(client *http.Client, testURL string) { resp, err := testClient.Get(testURL) if err != nil { - fmt.Printf("❌ Redirect transparency test failed: %v\n", err) + fmt.Printf("Redirect transparency test failed: %v\n", err) return } defer resp.Body.Close() if resp.StatusCode >= 300 && resp.StatusCode < 400 { location := resp.Header.Get("Location") - fmt.Printf("✅ Redirect transparency test successful: %s -> %s\n", resp.Status, location) + fmt.Printf("Redirect transparency test successful: %s -> %s\n", resp.Status, location) } else { - fmt.Printf("⚠️ Expected redirect, got: %s (this may be normal)\n", resp.Status) + fmt.Printf("Expected redirect, got: %s (this may be normal)\n", resp.Status) } } diff --git a/proxy_log.txt b/proxy_log.txt deleted file mode 100644 index 9d2e5e7..0000000 --- a/proxy_log.txt +++ /dev/null @@ -1,67 +0,0 @@ -Starting MITM proxy server... -2025/08/20 01:14:22 Failed to install CA certificate: failed to install certificate: exit status 0x80070005, output: 使用选择的选项需要管理员权限。使用管理员命令提示来完成这些任务。 -Root "受信任的根证书颁发机构" -无法打开证书存储。 -CertUtil: -addstore 失败: 0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED) -CertUtil: 拒绝访问。 -Warning: netsh winhttp reset failed: exit status 1 -Warning: netsh winhttp import failed: exit status 1 -鉁?System proxy set to 127.0.0.1:8080 -鉁?Proxy settings verified in Windows registry -馃攳 Current Proxy Configuration: - ProxyEnable: 1 - ProxyServer: 127.0.0.1:8080 - ProxyOverride: - WinHTTP Proxy Settings: - -Current WinHTTP proxy settings: - - Direct access (no proxy server). - - -鈴?Waiting for server to start... -馃殌 Starting proxy server on port 8080... -馃敡 Starting HTTP server on :8080 -鉁?Successfully bound to port :8080 -[01:14:23] 馃摜 INCOMING: GET ipv6.msftconnecttest.com /connecttest.txt from 127.0.0.1:53610 -[01:14:23] 馃搵 Headers: 2 headers received -[01:14:23] 馃搵 Header: Connection: Close -[01:14:23] 馃搵 Header: User-Agent: Microsoft NCSI -[01:14:23] 馃寪 Full URL: http://ipv6.msftconnecttest.com/connecttest.txt -[01:14:23] 馃寪 Processing HTTP request for ipv6.msftconnecttest.com -[01:14:25] 馃摜 INCOMING: GET crl3.digicert.com /DigiCertAssuredIDRootCA.crl from 127.0.0.1:53613 -[01:14:25] 馃搵 Headers: 6 headers received -[01:14:25] 馃搵 Header: User-Agent: Microsoft-CryptoAPI/10.0 -[01:14:25] 馃搵 Header: Cache-Control: max-age = 6311 -[01:14:25] 馃搵 Header: Proxy-Connection: Keep-Alive -[01:14:25] 馃搵 Header: Accept: */* -[01:14:25] 馃搵 Header: If-Modified-Since: Wed, 13 Aug 2025 21:15:04 GMT -[01:14:25] 馃搵 Header: If-None-Match: "689d0058-435" -[01:14:25] 馃寪 Full URL: http://crl3.digicert.com/DigiCertAssuredIDRootCA.crl -[01:14:25] 馃寪 Processing HTTP request for crl3.digicert.com -[01:14:25] 馃摜 INCOMING: GET crl3.digicert.com /DigiCertGlobalRootCA.crl from 127.0.0.1:53613 -[01:14:25] 馃搵 Headers: 6 headers received -[01:14:25] 馃搵 Header: Cache-Control: max-age = 6311 -[01:14:25] 馃搵 Header: Proxy-Connection: Keep-Alive -[01:14:25] 馃搵 Header: Accept: */* -[01:14:25] 馃搵 Header: If-Modified-Since: Wed, 13 Aug 2025 21:15:07 GMT -[01:14:25] 馃搵 Header: If-None-Match: "689d005b-30b" -[01:14:25] 馃搵 Header: User-Agent: Microsoft-CryptoAPI/10.0 -[01:14:25] 馃寪 Full URL: http://crl3.digicert.com/DigiCertGlobalRootCA.crl -[01:14:25] 馃寪 Processing HTTP request for crl3.digicert.com -[01:14:25] 馃摜 INCOMING: GET crl4.digicert.com /DigiCertHighAssuranceEVRootCA.crl from 127.0.0.1:53613 -[01:14:25] 馃搵 Headers: 6 headers received -[01:14:25] 馃搵 Header: If-Modified-Since: Wed, 13 Aug 2025 21:15:07 GMT -[01:14:25] 馃搵 Header: If-None-Match: "689d005b-2e4" -[01:14:25] 馃搵 Header: User-Agent: Microsoft-CryptoAPI/10.0 -[01:14:25] 馃搵 Header: Cache-Control: max-age = 3862 -[01:14:25] 馃搵 Header: Proxy-Connection: Keep-Alive -[01:14:25] 馃搵 Header: Accept: */* -[01:14:25] 馃寪 Full URL: http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl -[01:14:25] 馃寪 Processing HTTP request for crl4.digicert.com -鉁?Proxy server appears to be starting (no immediate errors) -馃攳 Testing basic proxy connectivity... -馃彞 Health check request from 127.0.0.1:53625 -鉁?Basic proxy connectivity test passed -馃彞 Health check request from 127.0.0.1:53670 -exit status 1