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" "os" "os/exec" "os/signal" "path/filepath" "runtime" "strings" "syscall" "time" "golang.org/x/sys/windows/registry" ) type Config struct { DomainsOfInterest []string Proxy struct { Port int } Dump struct { OutputDir string } } type ProxyServer struct { config *Config tlsConfig *tls.Config 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") flag.Parse() // Set console to UTF-8 on Windows to prevent garbled text if runtime.GOOS == "windows" { setConsoleUTF8() } 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) } // Create output directory if err := os.MkdirAll(config.Dump.OutputDir, 0755); err != nil { log.Fatalf("Failed to create output directory: %v", err) } // Create proxy server proxy, err := NewProxyServer(config) if err != nil { log.Fatalf("Failed to create proxy server: %v", err) } // Install CA certificate if err := proxy.installCACert(); err != nil { log.Printf("Failed to install CA certificate: %v", err) } else { 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) // Verify proxy settings were applied if err := proxy.verifyProxySettings(); err != nil { fmt.Printf("⚠️ Warning: Could not verify proxy settings: %v\n", err) } else { fmt.Printf("✓ Proxy settings verified in Windows registry\n") } // Show current proxy configuration for debugging proxy.showProxyConfiguration() // Start proxy server serverStarted := make(chan error, 1) go func() { fmt.Printf("🚀 Starting proxy server on port %d...\n", config.Proxy.Port) if err := proxy.Start(); err != nil && err != http.ErrServerClosed { serverStarted <- err return } serverStarted <- nil }() // Wait for server to start or fail fmt.Printf("⏳ Waiting for server to start...\n") select { case err := <-serverStarted: if err != nil { log.Fatalf("❌ Failed to start proxy server: %v", err) } 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") } // Run full connectivity test if requested if *testConnectivity { proxyAddr := fmt.Sprintf("127.0.0.1:%d", config.Proxy.Port) testProxyConnectivity(proxyAddr) } // Wait for interrupt signal sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) <-sigChan fmt.Println("\n🛑 Shutting down proxy server...") proxy.Shutdown() fmt.Println("✓ Proxy server closed") } func loadConfig(filename string) (*Config, error) { return parseConfig(filename) } func NewProxyServer(config *Config) (*ProxyServer, error) { // Load hardcoded P12 certificate tlsConfig, err := loadHardcodedCertificate() if err != nil { return nil, fmt.Errorf("failed to load certificate: %v", err) } proxy := &ProxyServer{ config: config, tlsConfig: tlsConfig, } // 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), } return proxy, nil } func (p *ProxyServer) Start() error { fmt.Printf("🔧 Starting HTTP server on %s\n", p.server.Addr) // Test if port is available listener, err := net.Listen("tcp", p.server.Addr) if err != nil { 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) // Start serving with our listener return p.server.Serve(listener) } func (p *ProxyServer) Shutdown() { // Restore system proxy settings if err := p.restoreSystemProxy(); err != nil { log.Printf("Failed to restore system proxy: %v", err) } else { fmt.Println("✓ System proxy restored") } // Close HTTP server ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() 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) 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) for name, values := range req.Header { for _, value := range values { fmt.Fprintf(file, "%s: %s\n", name, value) } } fmt.Fprintf(file, "\n") // Write request body if len(reqBody) > 0 { fmt.Fprintf(file, "%s\n", string(reqBody)) } // Write response information fmt.Fprintf(file, "\n=== RESPONSE ===\n") fmt.Fprintf(file, "%s\n", string(respBody)) } 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) file, err := os.Create(filepath) if err != nil { log.Printf("Failed to create HTTPS dump file: %v", err) return } defer file.Close() file.Write(data) } func (p *ProxyServer) isDomainOfInterest(host string) bool { // Remove port number if colonIndex := strings.Index(host, ":"); colonIndex != -1 { host = host[:colonIndex] } for _, domain := range p.config.DomainsOfInterest { if strings.Contains(host, domain) { return true } } 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("==========================================") } func sanitizeFilename(filename string) string { // Replace unsafe characters replacer := strings.NewReplacer( ":", "_", "/", "_", "\\", "_", "*", "_", "?", "_", "\"", "_", "<", "_", ">", "_", "|", "_", ) return replacer.Replace(filename) } func (p *ProxyServer) setSystemProxy() error { // Get current proxy settings k, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE) if err != nil { return err } defer k.Close() // Read current ProxyServer settings currentProxy, _, err := k.GetStringValue("ProxyServer") if err == nil { p.originalProxy = currentProxy } // Set new proxy k, err = registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.SET_VALUE) if err != nil { return err } defer k.Close() proxyAddr := fmt.Sprintf("127.0.0.1:%d", p.config.Proxy.Port) // Enable proxy if err := k.SetDWordValue("ProxyEnable", 1); err != nil { return err } // Set proxy server address for BOTH HTTP and HTTPS // Format: "http=proxy:port;https=proxy:port" or just "proxy:port" for both if err := k.SetStringValue("ProxyServer", proxyAddr); err != nil { return err } // Ensure no proxy bypass for local addresses if err := k.SetStringValue("ProxyOverride", ""); err != nil { // This is not critical, continue anyway fmt.Printf("Warning: Could not set ProxyOverride: %v\n", err) } // Refresh IE settings and notify system return p.refreshIESettings() } func (p *ProxyServer) restoreSystemProxy() error { k, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.SET_VALUE) if err != nil { return err } defer k.Close() // Disable proxy if err := k.SetDWordValue("ProxyEnable", 0); err != nil { return err } // Restore original proxy settings if p.originalProxy != "" { if err := k.SetStringValue("ProxyServer", p.originalProxy); err != nil { return err } } // Refresh IE settings return p.refreshIESettings() } func (p *ProxyServer) refreshIESettings() error { // Multiple methods to ensure Windows recognizes the proxy change // Method 1: Reset WinHTTP proxy cmd1 := exec.Command("cmd", "/c", "netsh winhttp reset proxy") if err := cmd1.Run(); err != nil { fmt.Printf("Warning: netsh winhttp reset failed: %v\n", err) } // Method 2: Import IE proxy settings to WinHTTP cmd2 := exec.Command("cmd", "/c", "netsh winhttp import proxy source=ie") if err := cmd2.Run(); err != nil { fmt.Printf("Warning: netsh winhttp import failed: %v\n", err) } // Method 3: Force refresh Internet Options cmd3 := exec.Command("cmd", "/c", "rundll32.exe inetcpl.cpl,ClearMyTracksByProcess 8") if err := cmd3.Run(); err != nil { fmt.Printf("Warning: Internet Options refresh failed: %v\n", err) } return nil } func (p *ProxyServer) verifyProxySettings() error { k, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE) if err != nil { return fmt.Errorf("failed to open registry key: %v", err) } defer k.Close() // Check if proxy is enabled proxyEnable, _, err := k.GetIntegerValue("ProxyEnable") if err != nil { return fmt.Errorf("failed to read ProxyEnable: %v", err) } if proxyEnable != 1 { return fmt.Errorf("proxy is not enabled (ProxyEnable = %d)", proxyEnable) } // Check proxy server setting proxyServer, _, err := k.GetStringValue("ProxyServer") if err != nil { return fmt.Errorf("failed to read ProxyServer: %v", err) } expectedProxy := fmt.Sprintf("127.0.0.1:%d", p.config.Proxy.Port) if proxyServer != expectedProxy { return fmt.Errorf("proxy server mismatch: expected %s, got %s", expectedProxy, proxyServer) } return nil } func (p *ProxyServer) showProxyConfiguration() { 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) return } defer k.Close() if proxyEnable, _, err := k.GetIntegerValue("ProxyEnable"); err == nil { fmt.Printf(" ProxyEnable: %d\n", proxyEnable) } if proxyServer, _, err := k.GetStringValue("ProxyServer"); err == nil { fmt.Printf(" ProxyServer: %s\n", proxyServer) } if proxyOverride, _, err := k.GetStringValue("ProxyOverride"); err == nil { fmt.Printf(" ProxyOverride: %s\n", proxyOverride) } // Show WinHTTP proxy settings fmt.Printf(" WinHTTP Proxy Settings:\n") cmd := exec.Command("cmd", "/c", "netsh winhttp show proxy") if output, err := cmd.CombinedOutput(); err == nil { fmt.Printf(" %s\n", string(output)) } else { fmt.Printf(" ❌ Could not get WinHTTP proxy settings: %v\n", err) } } func (p *ProxyServer) installCACert() error { // Use hardcoded CA certificate data certData := []byte(getHardcodedCACert()) // Parse certificate var cert *x509.Certificate var err error // Try parsing DER format directly cert, err = x509.ParseCertificate(certData) if err != nil { // Try parsing PEM format block, _ := pem.Decode(certData) if block == nil { return fmt.Errorf("unable to parse certificate file") } cert, err = x509.ParseCertificate(block.Bytes) if err != nil { return err } } // Write certificate to temporary file tempFile, err := os.CreateTemp("", "ca_cert_*.crt") if err != nil { return err } tempFileName := tempFile.Name() defer os.Remove(tempFileName) if err := pem.Encode(tempFile, &pem.Block{ Type: "CERTIFICATE", Bytes: cert.Raw, }); err != nil { tempFile.Close() return err } // Close the file before certutil accesses it to avoid sharing violation tempFile.Close() // Use certutil to install certificate to trusted root certificate authorities cmd := exec.Command("certutil", "-addstore", "-f", "Root", tempFileName) output, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("failed to install certificate: %v, output: %s", err, string(output)) } return nil } // setConsoleUTF8 sets the Windows console to UTF-8 code page to prevent garbled text func setConsoleUTF8() { // Set console input and output code page to UTF-8 (65001) cmd := exec.Command("cmd", "/c", "chcp 65001 >nul") 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...") // 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) return } // Create HTTP client with proxy transport := &http.Transport{ Proxy: http.ProxyURL(proxyURL), TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } client := &http.Client{ Transport: transport, Timeout: 30 * time.Second, } // Test HTTP request fmt.Println("🌐 Testing HTTP request...") testHTTPRequest(client, "http://httpbin.org/ip") // Test HTTPS request fmt.Println("🔒 Testing HTTPS request...") testHTTPSRequest(client, "https://httpbin.org/ip") // Test redirects (should be transparent) fmt.Println("🔄 Testing HTTP redirect...") testRedirectTransparency(client, "http://httpbin.org/redirect/1") // Test domain that might cause issues 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) return } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("❌ Failed to read HTTP response: %v\n", err) return } 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) return } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("❌ Failed to read HTTPS response: %v\n", err) return } fmt.Printf("✅ HTTPS test successful: %s\n", resp.Status) fmt.Printf(" Response: %s\n", string(body)[:min(len(body), 200)]) } func testRedirectTransparency(client *http.Client, testURL string) { // Create a client that doesn't follow redirects to test transparency testClient := &http.Client{ Transport: client.Transport, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, Timeout: client.Timeout, } resp, err := testClient.Get(testURL) if err != nil { 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) } else { fmt.Printf("⚠️ Expected redirect, got: %s (this may be normal)\n", resp.Status) } } func min(a, b int) int { if a < b { return a } return b }