mic~ proxy for JA4-TLS fingerprinting evasion |
| git clone https://github.com/djnnvx/mic.git |
| log | files | refs | archive | README | LICENSE |
main.go (2129B)
1 // probe prints the JA4 hash tlsinfo.me observes for each mic profile, to 2 // populate the fingerprint table. 3 package main 4 5 import ( 6 "bufio" 7 "context" 8 "encoding/json" 9 "fmt" 10 "io" 11 "maps" 12 "net" 13 "net/http" 14 "slices" 15 16 utls "github.com/bogdanfinn/utls" 17 "golang.org/x/net/http2" 18 19 "github.com/djnnvx/mic/fingerprint" 20 ) 21 22 func dialUTLS(ctx context.Context, id utls.ClientHelloID) (*utls.UConn, error) { 23 tcpConn, err := (&net.Dialer{}).DialContext(ctx, "tcp", "tlsinfo.me:443") 24 if err != nil { 25 return nil, err 26 } 27 uconn := utls.UClient(tcpConn, &utls.Config{ServerName: "tlsinfo.me"}, id, false, false, false) 28 if err := uconn.Handshake(); err != nil { 29 tcpConn.Close() 30 return nil, err 31 } 32 return uconn, nil 33 } 34 35 func decodeJA4(body io.Reader) (string, error) { 36 var result struct { 37 JA4 string `json:"ja4"` 38 } 39 if err := json.NewDecoder(body).Decode(&result); err != nil { 40 return "", err 41 } 42 return result.JA4, nil 43 } 44 45 func probe(id utls.ClientHelloID) (string, error) { 46 ctx := context.Background() 47 uconn, err := dialUTLS(ctx, id) 48 if err != nil { 49 return "", err 50 } 51 defer uconn.Close() 52 53 if uconn.ConnectionState().NegotiatedProtocol == "h2" { 54 // Reuse the handshaked uconn. A Transport redial would measure a different handshake. 55 cc, err := (&http2.Transport{}).NewClientConn(uconn) 56 if err != nil { 57 return "", err 58 } 59 req, _ := http.NewRequest("GET", "https://tlsinfo.me/json", nil) 60 resp, err := cc.RoundTrip(req) 61 if err != nil { 62 return "", err 63 } 64 defer resp.Body.Close() 65 return decodeJA4(resp.Body) 66 } 67 68 req, _ := http.NewRequest("GET", "/json", nil) 69 req.Host = "tlsinfo.me" 70 req.Header.Set("Connection", "close") 71 if err := req.Write(uconn); err != nil { 72 return "", err 73 } 74 resp, err := http.ReadResponse(bufio.NewReader(uconn), req) 75 if err != nil { 76 return "", err 77 } 78 defer resp.Body.Close() 79 return decodeJA4(resp.Body) 80 } 81 82 func main() { 83 for _, name := range slices.Sorted(maps.Keys(fingerprint.NameTable)) { 84 ja4, err := probe(fingerprint.NameTable[name]) 85 if err != nil { 86 fmt.Printf("%-22s ERROR: %v\n", name, err) 87 continue 88 } 89 fmt.Printf("%-22s %s\n", name, ja4) 90 } 91 }