mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-12-24 13:27:56 +08:00
修订文档,示例,代码;添加quic的嗅探代码
This commit is contained in:
@@ -42,8 +42,13 @@ tcp/udp(以及fullcone)/unix domain socket, tls(包括客户端证书验证), uT
|
||||
为了不吓跑小白,本 README 把安装、使用方式 放在了前面,如果你要直接阅读本作的技术介绍部分,点击跳转 -> [创新点](#创新点)
|
||||
|
||||
|
||||
|
||||
## 安装方式:
|
||||
|
||||
另外,对觉得本作安装很复杂的人,我再强调一遍,本作对标的是 v2ray和xray等内核,不是对标的“安装脚本”,**本作是个内核**,再说一遍。内核能支持各种交互模式已经很强大了好不好。
|
||||
|
||||
你见哪个内核项目负责人自己上来就提供完整一键脚本的?都是其他人帮着提供的,我这么忙哪有时间研究一键脚本。有需求的你可以写一个然后提PR啊。
|
||||
|
||||
### 下载安装
|
||||
|
||||
如果是 linux服务器,可以参考我的一篇指导文章 [install.md](docs/install.md)
|
||||
|
||||
196
advLayer/quic/sniff.go
Normal file
196
advLayer/quic/sniff.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/e1732a364fed/v2ray_simple/tlsLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||
"github.com/lucas-clemente/quic-go/quicvarint"
|
||||
"github.com/marten-seemann/qtls"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
const (
|
||||
versionDraft29 uint32 = 0xff00001d
|
||||
version1 uint32 = 0x1
|
||||
)
|
||||
|
||||
var (
|
||||
quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}
|
||||
quicSalt = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
|
||||
initialSuite = &qtls.CipherSuiteTLS13{
|
||||
ID: tls.TLS_AES_128_GCM_SHA256,
|
||||
KeyLen: 16,
|
||||
AEAD: qtls.AEADAESGCMTLS13,
|
||||
Hash: crypto.SHA256,
|
||||
}
|
||||
)
|
||||
|
||||
//来自v2ray, 实际上就是先嗅探quic,之后再嗅探tls层。也就是说quic握手包是套在tls握手包外面的。
|
||||
func SniffQUIC(b []byte) (sni string) {
|
||||
buffer := bytes.NewBuffer(b)
|
||||
typeByte, err := buffer.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
isLongHeader := typeByte&0x80 > 0
|
||||
if !isLongHeader || typeByte&0x40 == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
vb, err := buffer.ReadBytes(4)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
versionNumber := binary.BigEndian.Uint32(vb)
|
||||
|
||||
if versionNumber != 0 && typeByte&0x40 == 0 {
|
||||
return
|
||||
} else if versionNumber != versionDraft29 && versionNumber != version1 {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeByte&0x30)>>4 != 0x0 {
|
||||
return
|
||||
}
|
||||
|
||||
var destConnID []byte
|
||||
var l byte
|
||||
if l, err = buffer.ReadByte(); err != nil {
|
||||
return
|
||||
} else if destConnID = buffer.Next(int(l)); len(destConnID) != int(l) {
|
||||
return
|
||||
}
|
||||
|
||||
if l, err := buffer.ReadByte(); err != nil {
|
||||
return
|
||||
} else if tmpBs := buffer.Next(int(l)); len(tmpBs) != int(l) {
|
||||
return
|
||||
}
|
||||
|
||||
tokenLen, err := quicvarint.Read(buffer)
|
||||
if err != nil || tokenLen > uint64(len(b)) {
|
||||
return
|
||||
}
|
||||
|
||||
if tmpBs := buffer.Next(int(tokenLen)); len(tmpBs) != int(tokenLen) {
|
||||
return
|
||||
}
|
||||
|
||||
packetLen, err := quicvarint.Read(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hdrLen := len(b) - int(buffer.Len())
|
||||
|
||||
origPNBytes := make([]byte, 4)
|
||||
copy(origPNBytes, b[hdrLen:hdrLen+4])
|
||||
|
||||
var salt []byte
|
||||
if versionNumber == version1 {
|
||||
salt = quicSalt
|
||||
} else {
|
||||
salt = quicSaltOld
|
||||
}
|
||||
initialSecret := hkdf.Extract(crypto.SHA256.New, destConnID, salt)
|
||||
secret := hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size())
|
||||
hpKey := hkdfExpandLabel(initialSuite.Hash, secret, []byte{}, "quic hp", initialSuite.KeyLen)
|
||||
block, err := aes.NewCipher(hpKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
wholeCacheBs := utils.GetPacket()
|
||||
defer utils.PutPacket(wholeCacheBs)
|
||||
|
||||
cache := bytes.NewBuffer(wholeCacheBs)
|
||||
|
||||
mask := cache.Next(int(block.BlockSize()))
|
||||
block.Encrypt(mask, b[hdrLen+4:hdrLen+4+16])
|
||||
b[0] ^= mask[0] & 0xf
|
||||
for i := range b[hdrLen : hdrLen+4] {
|
||||
b[hdrLen+i] ^= mask[i+1]
|
||||
}
|
||||
packetNumberLength := b[0]&0x3 + 1
|
||||
if packetNumberLength != 1 {
|
||||
return
|
||||
}
|
||||
var packetNumber uint32
|
||||
{
|
||||
n, err := buffer.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
packetNumber = uint32(n)
|
||||
}
|
||||
|
||||
if packetNumber != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
extHdrLen := hdrLen + int(packetNumberLength)
|
||||
copy(b[extHdrLen:hdrLen+4], origPNBytes[packetNumberLength:])
|
||||
data := b[extHdrLen : int(packetLen)+hdrLen]
|
||||
|
||||
key := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16)
|
||||
iv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12)
|
||||
cipher := qtls.AEADAESGCMTLS13(key, iv)
|
||||
nonce := cache.Next(int(cipher.NonceSize()))
|
||||
binary.BigEndian.PutUint64(nonce[len(nonce)-8:], uint64(packetNumber))
|
||||
decrypted, err := cipher.Open(b[extHdrLen:extHdrLen], nonce, data, b[:extHdrLen])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buffer = bytes.NewBuffer(decrypted)
|
||||
frameType, err := buffer.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if frameType != 0x6 {
|
||||
// not crypto frame
|
||||
return
|
||||
}
|
||||
if _, err := quicvarint.Read(buffer); err != nil {
|
||||
return
|
||||
}
|
||||
dataLen, err := quicvarint.Read(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if dataLen > uint64(buffer.Len()) {
|
||||
return
|
||||
}
|
||||
frameData := buffer.Next(int(dataLen))
|
||||
if len(frameData) != int(dataLen) {
|
||||
return
|
||||
}
|
||||
|
||||
cs := tlsLayer.ComSniff{Isclient: true}
|
||||
cs.CommonDetect(frameData, true, true)
|
||||
|
||||
return cs.SniffedServerName
|
||||
}
|
||||
|
||||
func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte {
|
||||
b := make([]byte, 3, 3+6+len(label)+1+len(context))
|
||||
binary.BigEndian.PutUint16(b, uint16(length))
|
||||
b[2] = uint8(6 + len(label))
|
||||
b = append(b, []byte("tls13 ")...)
|
||||
b = append(b, []byte(label)...)
|
||||
b = b[:3+6+len(label)+1]
|
||||
b[3+6+len(label)] = uint8(len(context))
|
||||
b = append(b, context...)
|
||||
|
||||
out := make([]byte, length)
|
||||
n, err := hkdf.Expand(hash.New, secret, b).Read(out)
|
||||
if err != nil || n != length {
|
||||
panic("quic: HKDF-Expand-Label invocation failed unexpectedly")
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -16,7 +16,7 @@ key = "cert.key"
|
||||
[[fallback]]
|
||||
|
||||
from = ["vlesstls"]
|
||||
alpn = ["http/1.1"]
|
||||
alpn = ["http/1.1"] # 注意, 你的回落的协议要明确指出 alpn, 比如你要回落到vless+ws, 那么你对应的客户端的ws的dial部分 要配置好 这个 http/1.1 作为 alpn, 你不指出那么显然就没alpn, 当然就连不上,这不怨vs。
|
||||
dest = "@vlessws" # dest 也可以写成 这种形式, 直接匹配tag, 可防止笔误。
|
||||
|
||||
[[fallback]]
|
||||
|
||||
3
go.mod
3
go.mod
@@ -9,6 +9,7 @@ require (
|
||||
github.com/gobwas/ws v1.1.0
|
||||
github.com/lucas-clemente/quic-go v0.0.0-00010101000000-000000000000
|
||||
github.com/manifoldco/promptui v0.9.0
|
||||
github.com/marten-seemann/qtls v0.10.0
|
||||
github.com/miekg/dns v1.1.48
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible
|
||||
github.com/oschwald/maxminddb-golang v1.9.0
|
||||
@@ -19,6 +20,7 @@ require (
|
||||
github.com/yl2chen/cidranger v1.0.2
|
||||
go.uber.org/atomic v1.9.0
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
|
||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150
|
||||
@@ -41,7 +43,6 @@ require (
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.10 // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@@ -73,6 +73,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
@@ -125,6 +126,8 @@ github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||
github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc=
|
||||
github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc=
|
||||
@@ -313,6 +316,7 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
@@ -413,5 +417,7 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
||||
Reference in New Issue
Block a user