Просмотр исходного кода

Support obfuscated2

tags/v2.0.0-rc1
9seconds 5 лет назад
Родитель
Сommit
58335b3e59

+ 37
- 0
mtglib/internal/obfuscated2/client_handshake.go Просмотреть файл

@@ -0,0 +1,37 @@
1
+package obfuscated2
2
+
3
+import (
4
+	"crypto/cipher"
5
+	"crypto/subtle"
6
+	"encoding/hex"
7
+	"fmt"
8
+)
9
+
10
+// Connection Type secure. We support only fake tls.
11
+var clientHandshakeMagic = []byte{0xdd, 0xdd, 0xdd, 0xdd}
12
+
13
+func ClientHandshake(secret []byte, handshakeFrame *HandhakeFrame) (int16, cipher.Stream, cipher.Stream, error) {
14
+	decHasher := acquireSha256Hasher()
15
+	defer releaseSha256Hasher(decHasher)
16
+
17
+	decHasher.Write(handshakeFrame.key()) // nolint: errcheck
18
+	decHasher.Write(secret)               // nolint: errcheck
19
+	decryptor := makeAesCtr(decHasher.Sum(nil), handshakeFrame.iv())
20
+
21
+	encHasher := acquireSha256Hasher()
22
+	defer releaseSha256Hasher(encHasher)
23
+
24
+	invertedFrame := handshakeFrame.invert()
25
+	encHasher.Write(invertedFrame.key()) // nolint: errcheck
26
+	encHasher.Write(secret)               // nolint: errcheck
27
+	encryptor := makeAesCtr(encHasher.Sum(nil), invertedFrame.iv())
28
+
29
+	decryptedFrame := HandhakeFrame{}
30
+	decryptor.XORKeyStream(decryptedFrame.data[:], handshakeFrame.data[:])
31
+
32
+	if magic := decryptedFrame.magic(); subtle.ConstantTimeCompare(clientHandshakeMagic, magic) != 1 {
33
+		return 0, nil, nil, fmt.Errorf("unsupported connection type: %s", hex.EncodeToString(magic))
34
+	}
35
+
36
+	return decryptedFrame.dc(), encryptor, decryptor, nil
37
+}

+ 33
- 0
mtglib/internal/obfuscated2/conn.go Просмотреть файл

@@ -0,0 +1,33 @@
1
+package obfuscated2
2
+
3
+import (
4
+	"crypto/cipher"
5
+	"net"
6
+)
7
+
8
+type Conn struct {
9
+	net.Conn
10
+
11
+	Encryptor cipher.Stream
12
+	Decryptor cipher.Stream
13
+
14
+	writeBuf []byte
15
+}
16
+
17
+func (c *Conn) Read(p []byte) (int, error) {
18
+	n, err := c.Conn.Read(p)
19
+	if err != nil {
20
+		return n, err // nolint: wrapcheck
21
+	}
22
+
23
+	c.Decryptor.XORKeyStream(p, p[:n])
24
+
25
+	return n, nil
26
+}
27
+
28
+func (c *Conn) Write(p []byte) (int, error) {
29
+	c.writeBuf = append(c.writeBuf[:0], p...)
30
+	c.Encryptor.XORKeyStream(c.writeBuf, c.writeBuf)
31
+
32
+	return c.Conn.Write(c.writeBuf)
33
+}

+ 78
- 0
mtglib/internal/obfuscated2/frame.go Просмотреть файл

@@ -1 +1,79 @@
1 1
 package obfuscated2
2
+
3
+import (
4
+	"encoding/binary"
5
+	"fmt"
6
+	"io"
7
+)
8
+
9
+const (
10
+	handshakeFrameLen = 64
11
+
12
+	handshakeFrameLenKey   = 32
13
+	handshakeFrameLenIV    = 16
14
+	handshakeFrameLenMagic = 4
15
+	handshakeFrameLenDC    = 2
16
+
17
+	handshakeFrameOffsetStart = 8
18
+	handshakeFrameOffsetKey   = handshakeFrameOffsetStart
19
+	handshakeFrameOffsetIV    = handshakeFrameOffsetKey + handshakeFrameLenKey
20
+	handshakeFrameOffsetMagic = handshakeFrameOffsetIV + handshakeFrameLenIV
21
+	handshakeFrameOffsetDC    = handshakeFrameOffsetMagic + handshakeFrameLenMagic
22
+	handshakeFrameOffsetEnd   = handshakeFrameOffsetDC + handshakeFrameLenDC
23
+)
24
+
25
+// A structure of obfuscated2 handshake frame is following:
26
+//
27
+//    [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd].
28
+//
29
+//    - 8 bytes of noise
30
+//    - 32 bytes of AES Key
31
+//    - 16 bytes of AES IV
32
+//    - 4 bytes of 'magic' - this has some settings like a connection type
33
+//    - 2 bytes of 'DC'. DC is little endian int16
34
+//    - 2 bytes of noise
35
+type HandhakeFrame struct {
36
+	data [handshakeFrameLen]byte
37
+}
38
+
39
+func (f *HandhakeFrame) Fingerprint() []byte {
40
+	return f.data[handshakeFrameOffsetStart:handshakeFrameOffsetEnd]
41
+}
42
+
43
+func (f *HandhakeFrame) dc() int16 {
44
+	data := f.data[handshakeFrameOffsetDC:handshakeFrameOffsetEnd]
45
+
46
+	return int16(binary.LittleEndian.Uint16(data))
47
+}
48
+
49
+func (f *HandhakeFrame) key() []byte {
50
+	return f.data[handshakeFrameLenKey:handshakeFrameOffsetIV]
51
+}
52
+
53
+func (f *HandhakeFrame) iv() []byte {
54
+	return f.data[handshakeFrameOffsetIV:handshakeFrameOffsetMagic]
55
+}
56
+
57
+func (f *HandhakeFrame) magic() []byte {
58
+	return f.data[handshakeFrameOffsetMagic:handshakeFrameOffsetDC]
59
+}
60
+
61
+func (f *HandhakeFrame) invert() *HandhakeFrame {
62
+	newFrame := &HandhakeFrame{}
63
+
64
+	for i, v := range f.data {
65
+		newFrame.data[handshakeFrameLen-1-i] = v
66
+	}
67
+
68
+	return newFrame
69
+}
70
+
71
+func ReadHandshakeFrame(reader io.Reader) (*HandhakeFrame, error) {
72
+	frame := &HandhakeFrame{}
73
+
74
+	if _, err := io.ReadFull(reader, frame.data[:]); err != nil {
75
+		return nil, fmt.Errorf("cannot read frame data: %w", err)
76
+	}
77
+
78
+	return frame, nil
79
+}

+ 22
- 0
mtglib/internal/obfuscated2/pools.go Просмотреть файл

@@ -0,0 +1,22 @@
1
+package obfuscated2
2
+
3
+import (
4
+	"crypto/sha256"
5
+	"hash"
6
+	"sync"
7
+)
8
+
9
+var sha256HasherPool = sync.Pool{
10
+	New: func() interface{} {
11
+		return sha256.New()
12
+	},
13
+}
14
+
15
+func acquireSha256Hasher() hash.Hash {
16
+	return sha256HasherPool.Get().(hash.Hash)
17
+}
18
+
19
+func releaseSha256Hasher(h hash.Hash) {
20
+	h.Reset()
21
+	sha256HasherPool.Put(h)
22
+}

+ 15
- 0
mtglib/internal/obfuscated2/utils.go Просмотреть файл

@@ -0,0 +1,15 @@
1
+package obfuscated2
2
+
3
+import (
4
+	"crypto/aes"
5
+	"crypto/cipher"
6
+)
7
+
8
+func makeAesCtr(key, iv []byte) cipher.Stream {
9
+	block, err := aes.NewCipher(key)
10
+	if err != nil {
11
+		panic(err)
12
+	}
13
+
14
+	return cipher.NewCTR(block, iv)
15
+}

+ 39
- 7
mtglib/proxy.go Просмотреть файл

@@ -8,6 +8,7 @@ import (
8 8
 	"sync"
9 9
 	"time"
10 10
 
11
+	"github.com/9seconds/mtg/v2/mtglib/internal/obfuscated2"
11 12
 	"github.com/panjf2000/ants/v2"
12 13
 )
13 14
 
@@ -17,13 +18,12 @@ type Proxy struct {
17 18
 	streamWaitGroup sync.WaitGroup
18 19
 	workerPool      *ants.PoolWithFunc
19 20
 
20
-	secret             Secret
21
-	network            Network
22
-	timeAttackDetector TimeAttackDetector
23
-	antiReplayCache    AntiReplayCache
24
-	ipBlocklist        IPBlocklist
25
-	eventStream        EventStream
26
-	logger             Logger
21
+	secret          Secret
22
+	network         Network
23
+	antiReplayCache AntiReplayCache
24
+	ipBlocklist     IPBlocklist
25
+	eventStream     EventStream
26
+	logger          Logger
27 27
 }
28 28
 
29 29
 func (p *Proxy) ServeConn(conn net.Conn) {
@@ -49,6 +49,12 @@ func (p *Proxy) ServeConn(conn net.Conn) {
49 49
 		})
50 50
 		ctx.logger.Info("Stream has been finished")
51 51
 	}()
52
+
53
+	if err := p.doObfuscated2Handshake(ctx); err != nil {
54
+		p.logger.InfoError("obfuscated2 handshake is failed", err)
55
+
56
+		return
57
+	}
52 58
 }
53 59
 
54 60
 func (p *Proxy) Serve(listener net.Listener) error {
@@ -88,6 +94,32 @@ func (p *Proxy) Shutdown() {
88 94
 	p.workerPool.Release()
89 95
 }
90 96
 
97
+func (p *Proxy) doObfuscated2Handshake(ctx *streamContext) error {
98
+	handshakeFrame, err := obfuscated2.ReadHandshakeFrame(ctx.clientConn)
99
+	if err != nil {
100
+		return fmt.Errorf("cannot read handshake frame: %w", err)
101
+	}
102
+
103
+	dc, encryptor, decryptor, err := obfuscated2.ClientHandshake(p.secret.Key[:], handshakeFrame)
104
+	if err != nil {
105
+		return fmt.Errorf("cannot process client handshake: %w", err)
106
+	}
107
+
108
+	if dc < 0 {
109
+		dc = -dc
110
+	}
111
+
112
+	ctx.dc = int(dc)
113
+	ctx.logger = ctx.logger.BindInt("dc", ctx.dc)
114
+	ctx.clientConn = &obfuscated2.Conn{
115
+		Conn:      ctx.clientConn,
116
+		Encryptor: encryptor,
117
+		Decryptor: decryptor,
118
+	}
119
+
120
+	return nil
121
+}
122
+
91 123
 func NewProxy(opts ProxyOpts) (*Proxy, error) {
92 124
 	switch {
93 125
 	case opts.Network == nil:

+ 1
- 0
mtglib/stream_context.go Просмотреть файл

@@ -13,6 +13,7 @@ type streamContext struct {
13 13
 	ctxCancel  context.CancelFunc
14 14
 	clientConn net.Conn
15 15
 	connID     string
16
+	dc         int
16 17
 	logger     Logger
17 18
 }
18 19
 

Загрузка…
Отмена
Сохранить