Compare commits

...

4 Commits

11 changed files with 173 additions and 87 deletions

View File

@ -25,11 +25,13 @@ Evan 增强版特性
* HTTP CONNECT方法支持自定义Host(常用于免流混淆) * HTTP CONNECT方法支持自定义Host(常用于免流混淆)
- 例子: gost -L http://:8888 -F http://evan.run:80?host=cdn.dingding.com -F socks5://127.0.0.1:1080 - 例子: gost -L http://:8888 -F http://evan.run:80?host=cdn.dingding.com -F socks5://127.0.0.1:1080
* 修改默认User Agent为Google默认浏览器的UA,修改代理默认UA为nginx * 修改默认User Agent为Google默认浏览器的UA,修改代理默认UA为nginx
* 兼容Android环境,针对Android环境下无法解析域名的问题(Android环境下默认使用114.114.114.114作为DNS,可使用启动参数-NS自定义) * ~~兼容Android环境,针对Android环境下无法解析域名的问题(Android环境下默认使用114.114.114.114作为DNS,可使用启动参数-NS自定义)~~
- Android使用NDK交叉编译可以解决DNS无法解析问题,默认114.114.114.114的配置已经移除,-NS参数保留,交叉编译参考buildAndroid.sh
- 例子: gost -L http://:8888 -F http://evan.run:80 -NS 114.114.114.114:53/udp - 例子: gost -L http://:8888 -F http://evan.run:80 -NS 114.114.114.114:53/udp
* 修复原版DNS解析的BUG * 修复原版DNS解析的BUG
* 修复原版websocket协议中path参数不解码后发送问题 * 修复原版websocket协议中path参数不解码后发送问题
* TLS握手指纹增强:使用chrome的TLS指纹特征来对抗TLS指纹识别检测 * TLS握手指纹增强:使用chrome的TLS指纹特征来对抗TLS指纹识别检测
* TLS握手指纹兼容性改进:由于Chrome的默认TLS ClientHello握手中默认的ALPN为H2+HTTP/1.1,而Go的默认握手请求中是不包含ALPN的,故会导致兼容性问题,可以使用```h2Alpn=false```选项强制关闭HTTP2的ALPN(握手请求降级为Chrome默认的WebSocket请求)
初步防检测防杀毒处理脚本 初步防检测防杀毒处理脚本
------ ------

27
buildAndroid.sh Normal file
View File

@ -0,0 +1,27 @@
NDK_VERSION_IF_MISSING=r23b
mkdir -p ndk
cd ndk
curl https://dl.google.com/android/repository/android-ndk-${NDK_VERSION_IF_MISSING}-linux.zip -L -o ndk.zip
unzip ndk.zip > /dev/null || exit $?
rm -f ndk.zip
[ ! -d android-ndk-${NDK_VERSION_IF_MISSING} ] && echo "Missing directory: android-ndk-${NDK_VERSION_IF_MISSING}" && exit 1
export ANDROID_NDK_ROOT=$PWD/android-ndk-${NDK_VERSION_IF_MISSING}
cd ..
fi
echo "ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT"
cd gost
CC=$(find $ANDROID_NDK_ROOT | grep 'armv7a-linux-androideabi23-clang$') \
GOOS="android" GOARCH="arm" CGO_ENABLED="1" \
go build -buildvcs=false -ldflags "-s -w" -a -o gost_android_arm
CC=$(find $ANDROID_NDK_ROOT | grep 'aarch64-linux-android23-clang$') \
GOOS="android" GOARCH="arm64" CGO_ENABLED="1" \
go build -buildvcs=false -ldflags "-s -w" -a -o gost_android_arm64
CC=$(find $ANDROID_NDK_ROOT | grep 'i686-linux-android23-clang$') \
GOOS="android" GOARCH="386" CGO_ENABLED="1" \
go build -buildvcs=false -ldflags "-s -w" -a -o gost_android_x86
CC=$(find $ANDROID_NDK_ROOT | grep 'x86_64-linux-android23-clang$') \
GOOS="android" GOARCH="amd64" CGO_ENABLED="1" \
go build -buildvcs=false -ldflags "-s -w" -a -o gost_android_x86_64

View File

@ -85,6 +85,7 @@ type DialOptions struct {
Chain *Chain Chain *Chain
Host string Host string
HeaderConfig map[string]string HeaderConfig map[string]string
H2Alpn bool
} }
// DialOption allows a common way to set DialOptions. // DialOption allows a common way to set DialOptions.
@ -118,6 +119,13 @@ func HeaderConfigDialOption(HeaderConfig map[string]string) DialOption {
} }
} }
// H2AlpnDialOption specifies is use HTTP2 in ALPN for TLS ClientHello
func H2AlpnDialOption(useH2Alpn bool) DialOption {
return func(opts *DialOptions) {
opts.H2Alpn = useH2Alpn
}
}
// HandshakeOptions describes the options for handshake. // HandshakeOptions describes the options for handshake.
type HandshakeOptions struct { type HandshakeOptions struct {
Addr string Addr string
@ -131,6 +139,7 @@ type HandshakeOptions struct {
KCPConfig *KCPConfig KCPConfig *KCPConfig
QUICConfig *QUICConfig QUICConfig *QUICConfig
SSHConfig *SSHConfig SSHConfig *SSHConfig
H2Alpn bool
} }
// HandshakeOption allows a common way to set HandshakeOptions. // HandshakeOption allows a common way to set HandshakeOptions.
@ -213,6 +222,13 @@ func SSHConfigHandshakeOption(config *SSHConfig) HandshakeOption {
} }
} }
// H2AlpnHandshakeOption specifies is use HTTP2 in ALPN for TLS ClientHello.
func H2AlpnHandshakeOption(useH2Alpn bool) HandshakeOption {
return func(opts *HandshakeOptions) {
opts.H2Alpn = useH2Alpn
}
}
// ConnectOptions describes the options for Connector.Connect. // ConnectOptions describes the options for Connector.Connect.
type ConnectOptions struct { type ConnectOptions struct {
Addr string Addr string

View File

@ -1,12 +1,10 @@
package main package main
import ( import (
"context"
"crypto/tls" "crypto/tls"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"net"
"net/http" "net/http"
"os" "os"
"runtime" "runtime"
@ -91,12 +89,6 @@ func main() {
if baseCfg.ExternalResolver != "" { if baseCfg.ExternalResolver != "" {
gost.DefaultExternalResolver = parseResolver(baseCfg.ExternalResolver) gost.DefaultExternalResolver = parseResolver(baseCfg.ExternalResolver)
gost.DefaultExternalResolver.Init() gost.DefaultExternalResolver.Init()
} else if os.Getenv("ANDROID_ROOT") != "" {
log.Logf("Android detected modify default DNS server to %v", gost.DefaultDNSServer)
net.DefaultResolver = &net.Resolver{Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{}
return d.DialContext(ctx, network, gost.DefaultDNSServer)
}, PreferGo: true}
} }
if err := start(); err != nil { if err := start(); err != nil {

View File

@ -291,10 +291,17 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
} }
h2AlpnStr := node.Get("h2Alpn")
h2Alpn := true
if h2AlpnStr != "" {
h2Alpn = node.GetBool("h2Alpn")
}
node.DialOptions = append(node.DialOptions, node.DialOptions = append(node.DialOptions,
gost.TimeoutDialOption(timeout), gost.TimeoutDialOption(timeout),
gost.HostDialOption(host), gost.HostDialOption(host),
gost.HeaderConfigDialOption(headerCfg), gost.HeaderConfigDialOption(headerCfg),
gost.H2AlpnDialOption(h2Alpn),
) )
node.ConnectOptions = []gost.ConnectOption{ node.ConnectOptions = []gost.ConnectOption{
@ -322,6 +329,7 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
gost.TimeoutHandshakeOption(timeout), gost.TimeoutHandshakeOption(timeout),
gost.RetryHandshakeOption(node.GetInt("retry")), gost.RetryHandshakeOption(node.GetInt("retry")),
gost.SSHConfigHandshakeOption(sshConfig), gost.SSHConfigHandshakeOption(sshConfig),
gost.H2AlpnHandshakeOption(h2Alpn),
} }
node.Client = &gost.Client{ node.Client = &gost.Client{

View File

@ -20,7 +20,7 @@ import (
) )
// Version is the gost version. // Version is the gost version.
const Version = "2.11.2-EvanMod-v1.2.2" const Version = "2.11.2-EvanMod-v1.2.3"
const ProxyAgent = "nginx" const ProxyAgent = "nginx"
// Debug is a flag that enables the debug log. // Debug is a flag that enables the debug log.

View File

@ -161,7 +161,7 @@ func (tr *http2Transporter) Dial(addr string, options ...DialOption) (net.Conn,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return wrapTLSClient(conn, cfg, timeout) return wrapTLSClient(conn, cfg, timeout, opts.H2Alpn)
}, },
} }
client = &http.Client{ client = &http.Client{
@ -242,7 +242,7 @@ func (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, err
if tr.tlsConfig == nil { if tr.tlsConfig == nil {
return conn, nil return conn, nil
} }
return wrapTLSClient(conn, cfg, timeout) return wrapTLSClient(conn, cfg, timeout, opts.H2Alpn)
}, },
} }
client = &http.Client{ client = &http.Client{

2
log.go
View File

@ -3,10 +3,12 @@ package gost
import ( import (
"fmt" "fmt"
"log" "log"
"os"
) )
func init() { func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetFlags(log.LstdFlags | log.Lshortfile)
log.SetOutput(os.Stdout)
} }
// LogLogger uses the standard log package as the logger // LogLogger uses the standard log package as the logger

View File

@ -1,2 +1,6 @@
sed -b -i s/\*gost./*evan./g $1 sed -b -i s/\*gost./*evan./g $1
sed -b -i s/]gost./]evan./g $1 sed -b -i s/]gost./]evan./g $1
sed -b -i s#ginuerzh/gost#evanevan/evan#g $1
sed -b -i s#go-gost#ev-evan#g $1
sed -b -i s#gost.#evan.#g $1
sed -b -i s#cmd/gost#cmd/evan#g $1

27
tls.go
View File

@ -36,7 +36,7 @@ func (tr *tlsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (
timeout = HandshakeTimeout timeout = HandshakeTimeout
} }
return wrapTLSClient(conn, opts.TLSConfig, timeout) return wrapTLSClient(conn, opts.TLSConfig, timeout, opts.H2Alpn)
} }
type mtlsTransporter struct { type mtlsTransporter struct {
@ -131,7 +131,7 @@ func (tr *mtlsTransporter) initSession(addr string, conn net.Conn, opts *Handsha
if opts.TLSConfig == nil { if opts.TLSConfig == nil {
opts.TLSConfig = &tls.Config{InsecureSkipVerify: true} opts.TLSConfig = &tls.Config{InsecureSkipVerify: true}
} }
conn, err := wrapTLSClient(conn, opts.TLSConfig, opts.Timeout) conn, err := wrapTLSClient(conn, opts.TLSConfig, opts.Timeout, opts.H2Alpn)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -268,7 +268,7 @@ func (l *mtlsListener) Close() error {
// //
// This code is taken from consul: // This code is taken from consul:
// https://github.com/hashicorp/consul/blob/master/tlsutil/config.go // https://github.com/hashicorp/consul/blob/master/tlsutil/config.go
func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration) (net.Conn, error) { func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration, h2Alpn bool) (net.Conn, error) {
var err error var err error
if timeout <= 0 { if timeout <= 0 {
@ -279,7 +279,26 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration)
defer conn.SetDeadline(time.Time{}) defer conn.SetDeadline(time.Time{})
//tlsConn := tls.Client(conn, tlsConfig) //tlsConn := tls.Client(conn, tlsConfig)
tlsConn := utls.UClient(conn, &utls.Config{InsecureSkipVerify: tlsConfig.InsecureSkipVerify, ServerName: tlsConfig.ServerName}, utls.HelloChrome_Auto) var tlsConn *utls.UConn
utlsConf := &utls.Config{InsecureSkipVerify: tlsConfig.InsecureSkipVerify, ServerName: tlsConfig.ServerName, ClientAuth: utls.ClientAuthType(tlsConfig.ClientAuth), ClientCAs: tlsConfig.ClientCAs, RootCAs: tlsConfig.RootCAs}
if len(tlsConfig.Certificates) > 0 {
for _, certificate := range tlsConfig.Certificates {
utlsConf.Certificates = append(utlsConf.Certificates, utls.Certificate{
Certificate: certificate.Certificate,
PrivateKey: certificate.PrivateKey,
OCSPStaple: certificate.OCSPStaple,
SignedCertificateTimestamps: certificate.SignedCertificateTimestamps,
Leaf: certificate.Leaf,
})
}
}
if h2Alpn {
tlsConn = utls.UClient(conn, utlsConf, utls.HelloChrome_Auto)
} else {
tlsConn = utls.UClient(conn, utlsConf, utls.HelloCustom)
tlsConn.ApplyPreset(newWsSpec())
}
// Otherwise perform handshake, but don't verify the domain // Otherwise perform handshake, but don't verify the domain
// //

60
ws.go
View File

@ -744,28 +744,8 @@ type websocketConn struct {
rb []byte rb []byte
} }
func websocketClientConn(url string, conn net.Conn, tlsConfig *tls.Config, options *WSOptions) (net.Conn, error) { func newWsSpec() *utls.ClientHelloSpec {
if options == nil { return &utls.ClientHelloSpec{
options = &WSOptions{}
}
timeout := options.HandshakeTimeout
if timeout <= 0 {
timeout = HandshakeTimeout
}
dialer := websocket.Dialer{
ReadBufferSize: options.ReadBufferSize,
WriteBufferSize: options.WriteBufferSize,
TLSClientConfig: tlsConfig,
HandshakeTimeout: timeout,
EnableCompression: options.EnableCompression,
NetDial: func(net, addr string) (net.Conn, error) {
return conn, nil
},
NetDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
client := utls.UClient(conn, &utls.Config{InsecureSkipVerify: tlsConfig.InsecureSkipVerify, ServerName: tlsConfig.ServerName}, utls.HelloCustom)
client.ApplyPreset(&utls.ClientHelloSpec{
CipherSuites: []uint16{ CipherSuites: []uint16{
utls.GREASE_PLACEHOLDER, utls.GREASE_PLACEHOLDER,
utls.TLS_AES_128_GCM_SHA256, utls.TLS_AES_128_GCM_SHA256,
@ -834,7 +814,43 @@ func websocketClientConn(url string, conn net.Conn, tlsConfig *tls.Config, optio
&utls.UtlsGREASEExtension{}, &utls.UtlsGREASEExtension{},
&utls.UtlsPaddingExtension{GetPaddingLen: utls.BoringPaddingStyle}, &utls.UtlsPaddingExtension{GetPaddingLen: utls.BoringPaddingStyle},
}, },
}
}
func websocketClientConn(url string, conn net.Conn, tlsConfig *tls.Config, options *WSOptions) (net.Conn, error) {
if options == nil {
options = &WSOptions{}
}
timeout := options.HandshakeTimeout
if timeout <= 0 {
timeout = HandshakeTimeout
}
dialer := websocket.Dialer{
ReadBufferSize: options.ReadBufferSize,
WriteBufferSize: options.WriteBufferSize,
TLSClientConfig: tlsConfig,
HandshakeTimeout: timeout,
EnableCompression: options.EnableCompression,
NetDial: func(net, addr string) (net.Conn, error) {
return conn, nil
},
NetDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
utlsConf := &utls.Config{InsecureSkipVerify: tlsConfig.InsecureSkipVerify, ServerName: tlsConfig.ServerName, ClientAuth: utls.ClientAuthType(tlsConfig.ClientAuth), ClientCAs: tlsConfig.ClientCAs, RootCAs: tlsConfig.RootCAs}
if len(tlsConfig.Certificates) > 0 {
for _, certificate := range tlsConfig.Certificates {
utlsConf.Certificates = append(utlsConf.Certificates, utls.Certificate{
Certificate: certificate.Certificate,
PrivateKey: certificate.PrivateKey,
OCSPStaple: certificate.OCSPStaple,
SignedCertificateTimestamps: certificate.SignedCertificateTimestamps,
Leaf: certificate.Leaf,
}) })
}
}
client := utls.UClient(conn, utlsConf, utls.HelloCustom)
client.ApplyPreset(newWsSpec())
err := client.Handshake() err := client.Handshake()
if err != nil { if err != nil {
return nil, err return nil, err