mic.git


mic

~ proxy for JA4-TLS fingerprinting evasion
git clone https://github.com/djnnvx/mic.git
log | files | refs | archive | README | LICENSE

ja4s_capture_test.go (1441B)

      1 //go:build integration
      2 
      3 package proxy
      4 
      5 import (
      6 	"bytes"
      7 	"net"
      8 	"sync"
      9 
     10 	"github.com/djnnvx/mic/fingerprint"
     11 )
     12 
     13 // teeWriteConn tees every Write into a buffer and emits the JA4S once the
     14 // buffer holds a complete ServerHello.
     15 type teeWriteConn struct {
     16 	net.Conn
     17 	mu   sync.Mutex
     18 	buf  bytes.Buffer
     19 	ch   chan<- string
     20 	sent bool
     21 }
     22 
     23 func (t *teeWriteConn) Write(p []byte) (int, error) {
     24 	t.mu.Lock()
     25 	if !t.sent {
     26 		// In client-front MitM the proxy writes a plaintext "HTTP/1.1 200" line on
     27 		// this conn first, so only collect from the first TLS handshake record.
     28 		if t.buf.Len() > 0 || (len(p) >= 2 && p[0] == 0x16 && p[1] == 0x03) {
     29 			t.buf.Write(p)
     30 			if ja4s, err := captureToJA4S(t.buf.Bytes()); err == nil {
     31 				select {
     32 				case t.ch <- ja4s:
     33 				default:
     34 				}
     35 				t.sent = true
     36 			}
     37 		}
     38 	}
     39 	t.mu.Unlock()
     40 	return t.Conn.Write(p)
     41 }
     42 
     43 func captureToJA4S(raw []byte) (string, error) {
     44 	sh, err := fingerprint.ParseServerHello(raw)
     45 	if err != nil {
     46 		return "", err
     47 	}
     48 	return fingerprint.ComputeJA4S(sh), nil
     49 }
     50 
     51 // captureJA4SListener wraps each accepted conn so the ServerHello mic writes
     52 // back is captured and emitted on the returned channel.
     53 func captureJA4SListener(ln net.Listener, p *Proxy, h Handler) <-chan string {
     54 	ch := make(chan string, 16)
     55 	go func() {
     56 		for {
     57 			conn, err := ln.Accept()
     58 			if err != nil {
     59 				return
     60 			}
     61 			wrapped := &teeWriteConn{Conn: conn, ch: ch}
     62 			go h(wrapped, p)
     63 		}
     64 	}()
     65 	return ch
     66 }