From 3fb67f065ab1a4558d16833746e953ea35081d54 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Fri, 20 Jan 2023 23:36:08 -0500 Subject: [PATCH] Add fingerprint xray_random (#1540) * Add fingerprint xray_random xray_random means to pick a random uTLS fingerprint at the core startup This way, the fingerprint is stable for a user for some days. While there is no identifiable signature for the whole xray community * Fingerprint "random" refine Exclude old fingerprint from RNG --- transport/internet/grpc/dial.go | 2 +- transport/internet/http/dialer.go | 2 +- transport/internet/tcp/dialer.go | 2 +- transport/internet/tls/tls.go | 66 ++++++++++++++++++++------ transport/internet/websocket/dialer.go | 2 +- 5 files changed, 56 insertions(+), 18 deletions(-) diff --git a/transport/internet/grpc/dial.go b/transport/internet/grpc/dial.go index 9836d93a..04f81e34 100644 --- a/transport/internet/grpc/dial.go +++ b/transport/internet/grpc/dial.go @@ -122,7 +122,7 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in if tlsConfig != nil { var transportCredential credentials.TransportCredentials - if fingerprint, exists := tls.Fingerprints[tlsConfig.Fingerprint]; exists { + if fingerprint, exists := tls.GetFingerprint(ctx, tlsConfig.Fingerprint); exists { transportCredential = tls.NewGrpcUtls(tlsConfig.GetTLSConfig(), fingerprint) } else { // Fallback to normal gRPC TLS transportCredential = credentials.NewTLS(tlsConfig.GetTLSConfig()) diff --git a/transport/internet/http/dialer.go b/transport/internet/http/dialer.go index 5c4cbdfd..6fef71c5 100644 --- a/transport/internet/http/dialer.go +++ b/transport/internet/http/dialer.go @@ -75,7 +75,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in } var cn tls.Interface - if fingerprint, ok := tls.Fingerprints[tlsConfigs.Fingerprint]; ok { + if fingerprint, ok := tls.GetFingerprint(ctx, tlsConfigs.Fingerprint); ok { cn = tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn) } else { cn = tls.Client(pconn, tlsConfig).(*tls.Conn) diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index 296c7d8d..b08fd4b2 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -22,7 +22,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { tlsConfig := config.GetTLSConfig(tls.WithDestination(dest)) - if fingerprint, ok := tls.Fingerprints[config.Fingerprint]; ok { + if fingerprint, ok := tls.GetFingerprint(ctx, config.Fingerprint); ok { conn = tls.UClient(conn, tlsConfig, fingerprint) if err := conn.(*tls.UConn).Handshake(); err != nil { return nil, err diff --git a/transport/internet/tls/tls.go b/transport/internet/tls/tls.go index 392df808..9baf4054 100644 --- a/transport/internet/tls/tls.go +++ b/transport/internet/tls/tls.go @@ -1,17 +1,23 @@ package tls import ( + "context" + "crypto/rand" "crypto/tls" + "math/big" utls "github.com/refraction-networking/utls" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" ) //go:generate go run github.com/xtls/xray-core/common/errors/errorgen var _ buf.Writer = (*Conn)(nil) +var XrayRandom *utls.ClientHelloID + type Conn struct { *tls.Conn } @@ -111,29 +117,63 @@ func copyConfig(c *tls.Config) *utls.Config { } } +func GetFingerprint(ctx context.Context, config string) (*utls.ClientHelloID, bool) { + if XrayRandom == nil { + // lazy init + for k, v := range FingerprintsForRNG { + Fingerprints[k] = v + } + big, err := rand.Int(rand.Reader, big.NewInt(int64(len(FingerprintsForRNG)))) + if err != nil { + newError("failed to generate xray random fingerprint").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + var i = int(big.Int64()) + count := 0 + for k, v := range FingerprintsForRNG { + if count == i { + newError("xray random fingerprint: ", k).WriteToLog(session.ExportIDToError(ctx)) + XrayRandom = v + break + } + count++ + } + } + if config == "random" { + return XrayRandom, true + } + fingerprint, ok := Fingerprints[config] + return fingerprint, ok +} + var Fingerprints = map[string]*utls.ClientHelloID{ "chrome": &utls.HelloChrome_Auto, "firefox": &utls.HelloFirefox_Auto, "safari": &utls.HelloSafari_Auto, "randomized": &utls.HelloRandomized, // This is a bit lame, but it seems there is no good way to reflect variables from Golang package - "hellogolang": &utls.HelloGolang, - "hellorandomized": &utls.HelloRandomized, - "hellorandomizedalpn": &utls.HelloRandomizedALPN, - "hellorandomizednoalpn": &utls.HelloRandomizedNoALPN, + // We don't RNG for go, randomized, or fingerprints that is more than 4 years old + "hellogolang": &utls.HelloGolang, + "hellorandomized": &utls.HelloRandomized, + "hellorandomizedalpn": &utls.HelloRandomizedALPN, + "hellorandomizednoalpn": &utls.HelloRandomizedNoALPN, + "hellofirefox_55": &utls.HelloFirefox_55, + "hellofirefox_56": &utls.HelloFirefox_56, + "hellofirefox_63": &utls.HelloFirefox_63, + "hellofirefox_65": &utls.HelloFirefox_65, + "hellochrome_58": &utls.HelloChrome_58, + "hellochrome_62": &utls.HelloChrome_62, + "hellochrome_70": &utls.HelloChrome_70, + "hellochrome_72": &utls.HelloChrome_72, + "helloios_11_1": &utls.HelloIOS_11_1, + "hello360_7_5": &utls.Hello360_7_5, +} + +var FingerprintsForRNG = map[string]*utls.ClientHelloID{ "hellofirefox_auto": &utls.HelloFirefox_Auto, - "hellofirefox_55": &utls.HelloFirefox_55, - "hellofirefox_56": &utls.HelloFirefox_56, - "hellofirefox_63": &utls.HelloFirefox_63, - "hellofirefox_65": &utls.HelloFirefox_65, "hellofirefox_99": &utls.HelloFirefox_99, "hellofirefox_102": &utls.HelloFirefox_102, "hellofirefox_105": &utls.HelloFirefox_105, "hellochrome_auto": &utls.HelloChrome_Auto, - "hellochrome_58": &utls.HelloChrome_58, - "hellochrome_62": &utls.HelloChrome_62, - "hellochrome_70": &utls.HelloChrome_70, - "hellochrome_72": &utls.HelloChrome_72, "hellochrome_83": &utls.HelloChrome_83, "hellochrome_87": &utls.HelloChrome_87, "hellochrome_96": &utls.HelloChrome_96, @@ -141,7 +181,6 @@ var Fingerprints = map[string]*utls.ClientHelloID{ "hellochrome_102": &utls.HelloChrome_102, "hellochrome_106_shuffle": &utls.HelloChrome_106_Shuffle, "helloios_auto": &utls.HelloIOS_Auto, - "helloios_11_1": &utls.HelloIOS_11_1, "helloios_12_1": &utls.HelloIOS_12_1, "helloios_13": &utls.HelloIOS_13, "helloios_14": &utls.HelloIOS_14, @@ -152,7 +191,6 @@ var Fingerprints = map[string]*utls.ClientHelloID{ "hellosafari_auto": &utls.HelloSafari_Auto, "hellosafari_16_0": &utls.HelloSafari_16_0, "hello360_auto": &utls.Hello360_Auto, - "hello360_7_5": &utls.Hello360_7_5, "hello360_11_0": &utls.Hello360_11_0, "helloqq_auto": &utls.HelloQQ_Auto, "helloqq_11_1": &utls.HelloQQ_11_1, diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index a8f71264..a0ac6811 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -86,7 +86,7 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in protocol = "wss" tlsConfig := config.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1")) dialer.TLSClientConfig = tlsConfig - if fingerprint, exists := tls.Fingerprints[config.Fingerprint]; exists { + if fingerprint, exists := tls.GetFingerprint(ctx, config.Fingerprint); exists { dialer.NetDialTLSContext = func(_ context.Context, _, addr string) (gonet.Conn, error) { // Like the NetDial in the dialer pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)