修订文档,示例,代码;添加quic的嗅探代码

This commit is contained in:
e1732a364fed
2022-05-10 21:36:49 +08:00
parent 72d9e97053
commit 4e4aeb63f9
6 changed files with 211 additions and 3 deletions

View File

@@ -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
View 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
}

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -32,7 +32,7 @@ type iicsZapWriter struct {
}
func (zw *iicsZapWriter) setid(id uint32) {
zw.assignedFields[0] = zap.Uint32("id", id)
zw.assignedFields[0] = zap.Uint32("connid", id)
zw.id = id
}