Compare commits
15 Commits
8e8649db83
...
master
Author | SHA1 | Date | |
---|---|---|---|
2a7946a5c9 | |||
040fdfb7ef | |||
ce9f312a77 | |||
91ac6f7e17 | |||
a6bf1c0e7d | |||
bd2cf3865d | |||
a064f72104 | |||
c2ab6cbe5f | |||
a983817b8d | |||
116970c654 | |||
d276407530 | |||
0a7d7dd290 | |||
f426b7b2cc | |||
c4c6f1c4ff | |||
1c49851d13 |
79
README.md
79
README.md
@ -1,11 +1,11 @@
|
||||
# Shadow-TLS
|
||||
### TLS伪装代理 -- 包装任意TCP连接为真正合法域名的TLS连接
|
||||
## 基本原理
|
||||
与服务端连接后,服务端会请求指定合法的HTTPS域名(例如www.apple.com)并转发TLS握手流量,与客户端TLS握手成功后后续将转发实际的TCP流量,对审计设备(防火墙/上网行为管理软件/零信任网关)而言你是访问一个合法且是真实证书的HTTPS网站.
|
||||
给审计设备表演一个访问指定网站的TLS握手,在后续的加密流量中传输自定义的数据,由于握手的证书是真实的证书,对审计设备(防火墙/上网行为分析/零信任网关)而言本次TCP连接是访问指定网站的HTTPS流量
|
||||
|
||||
## 使用场景
|
||||
- 在有域名白名单的情况下需要将流量转发出去
|
||||
- 对抗网络审计设备的审查
|
||||
- 审计设备会验证TLS证书合法性,自签证书无法通过审计设备的场景
|
||||
|
||||
## 使用方法
|
||||
- 服务端示例:
|
||||
@ -19,13 +19,76 @@
|
||||
./shadowtls client -l 0.0.0.0:11222 -s 145.142.63.32:443 -d www.apple.com
|
||||
```
|
||||
|
||||
## 功能特性
|
||||
- TLS连接多路复用,减少TLS握手次数
|
||||
## 探测防御
|
||||
- 设置参数密码-p 可开启探测防御功能,服务端检测到非被客户端发起的请求后将会作为标准的SNI代理服务器,转发用于伪装源站的所有流量,对主动探测者而言这台服务器是指定网站的官方服务器
|
||||
- 若被大量请求可能造成服务器产生大量流量,注意风控
|
||||
- 引入utls组件,伪装TLS ClientHello指纹为Chrome 102版本,进一步对抗探测
|
||||
|
||||
## 安全性特别说明
|
||||
- 包装的TCP流量没有加密,如有需求请加密后再转发
|
||||
- 多路复用特性使用了smux的框架,有协议特征,有更进一步需求需修改源码二次加密
|
||||
- TLS后续流量没有进行Application Data封装,深层次的协议分析可以发现此特征
|
||||
## 流量加密
|
||||
- 设置加密密钥参数-k 可启用流量加密,密钥长度必须为16,24或32个字符
|
||||
|
||||
## 已知限制
|
||||
- 若审计软件会对HTTPS证书进行替换使用审计软件自签的证书,本工具将不再适用
|
||||
|
||||
## 使用Nginx将本服务和其他443端口服务并存
|
||||
- 编译带有stream和stream_ssl_preread模块的nginx
|
||||
- 参考以下配置
|
||||
```nginx
|
||||
stream {
|
||||
map $ssl_preread_server_name $backend_pool {
|
||||
www.apple.com shadow;
|
||||
defalut local_server;
|
||||
}
|
||||
|
||||
upstream shadow{
|
||||
server 127.0.2.1:2443;
|
||||
}
|
||||
upstream local_server{
|
||||
server 127.0.2.1:8443;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443;
|
||||
ssl_preread on;
|
||||
proxy_bind $remote_addr transparent; # 加了这个才能传递客户端IP
|
||||
proxy_pass $backend_pool;
|
||||
proxy_connect_timeout 15s;
|
||||
proxy_timeout 15s;
|
||||
proxy_next_upstream_timeout 15s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
http {
|
||||
|
||||
...
|
||||
|
||||
server {
|
||||
listen 127.0.2.1:8443 ssl http2;
|
||||
server_name file.evan.run;
|
||||
charset utf-8;
|
||||
|
||||
ssl_certificate cert.crt;
|
||||
ssl_certificate_key private.key;
|
||||
|
||||
port_in_redirect off; #重要:阻止nginx重定向到此Server listen的端口
|
||||
|
||||
location / {
|
||||
root /root/file;
|
||||
autoindex on;
|
||||
autoindex_exact_size off;
|
||||
autoindex_localtime on;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
- 添加策略路由:
|
||||
```shell
|
||||
ip rule add from 127.0.2.1 lookup 61
|
||||
ip route add local 0.0.0.0/0 dev lo table 61
|
||||
```
|
||||
|
||||
## 特别说明
|
||||
- 感谢v2ex网友ihciah的思路灵感.
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"shadowTLS/shadow"
|
||||
)
|
||||
@ -17,6 +18,10 @@ var (
|
||||
Use: "client",
|
||||
Short: "Client mode",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if !verifyAndParseEncryptionKey() {
|
||||
fmt.Println("[Client] Invalid encryption key length")
|
||||
return
|
||||
}
|
||||
client := shadow.NewClient(clientParams.ListenAddr, clientParams.ServerAddr, clientParams.SNI)
|
||||
client.Start()
|
||||
},
|
||||
|
11
cmd/root.go
11
cmd/root.go
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"shadowTLS/shadow"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -15,6 +16,8 @@ var (
|
||||
|
||||
func init() {
|
||||
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
||||
rootCmd.PersistentFlags().StringVarP(&shadow.HandshakePassword, "password", "p", "", "Password for probe resist.Probe resist will be disabled if password is empty.")
|
||||
rootCmd.PersistentFlags().StringVarP(&shadow.Key, "key", "k", "", "Encryption key for AES-CBC.Encryption will be enabled if key is set.Length of key must be 16,24 or 32.")
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
@ -23,3 +26,11 @@ func Execute() {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyAndParseEncryptionKey() bool {
|
||||
if shadow.Key != "" && len(shadow.Key) != 16 && len(shadow.Key) != 24 && len(shadow.Key) != 32 {
|
||||
return false
|
||||
}
|
||||
shadow.EncryptKey = []byte(shadow.Key)
|
||||
return true
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"shadowTLS/shadow"
|
||||
)
|
||||
@ -17,6 +18,10 @@ var (
|
||||
Use: "server",
|
||||
Short: "Server mode",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if !verifyAndParseEncryptionKey() {
|
||||
fmt.Println("[Server] Invalid encryption key length")
|
||||
return
|
||||
}
|
||||
server := shadow.NewServer(serverParams.ListenAddr, serverParams.TargetAddr, serverParams.FakeAddr)
|
||||
server.Start()
|
||||
},
|
||||
|
6
go.mod
6
go.mod
@ -3,11 +3,15 @@ module shadowTLS
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/refraction-networking/utls v1.1.3
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/xtaci/smux v1.5.16
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
||||
)
|
||||
|
20
go.sum
20
go.sum
@ -1,12 +1,28 @@
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/refraction-networking/utls v1.1.3 h1:K9opY+iKxcGvHOBG2019wFEVtsNFh0f5WqHyc2i3iU0=
|
||||
github.com/refraction-networking/utls v1.1.3/go.mod h1:+D89TUtA8+NKVFj1IXWr0p3tSdX1+SqUB7rL0QnGqyg=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk=
|
||||
github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211111160137-58aab5ef257a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
10
main.go
10
main.go
@ -1,8 +1,14 @@
|
||||
package main
|
||||
|
||||
import "shadowTLS/cmd"
|
||||
import (
|
||||
"math/rand"
|
||||
"shadowTLS/cmd"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
func main() {
|
||||
|
||||
cmd.Execute()
|
||||
}
|
||||
|
36
shadow/aes_util.go
Normal file
36
shadow/aes_util.go
Normal file
@ -0,0 +1,36 @@
|
||||
package shadow
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
func AesEncryptCBC(origData []byte, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
blockSize := block.BlockSize()
|
||||
origData = pkcs5Padding(origData, blockSize)
|
||||
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
|
||||
encrypted := make([]byte, len(origData))
|
||||
blockMode.CryptBlocks(encrypted, origData)
|
||||
return encrypted
|
||||
}
|
||||
func AesDecryptCBC(encrypted []byte, key []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
blockSize := block.BlockSize()
|
||||
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
blockMode.CryptBlocks(decrypted, encrypted)
|
||||
decrypted = pkcs5UnPadding(decrypted)
|
||||
return decrypted
|
||||
}
|
||||
func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
func pkcs5UnPadding(origData []byte) []byte {
|
||||
length := len(origData)
|
||||
unpadding := int(origData[length-1])
|
||||
return origData[:(length - unpadding)]
|
||||
}
|
@ -2,8 +2,10 @@ package shadow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/refraction-networking/utls"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
@ -22,8 +24,6 @@ func NewClient(listenAddress string, serverAddress string, fakeAddressSNI string
|
||||
}
|
||||
|
||||
func (c *Client) Start() {
|
||||
bridge := NewTLSBridge(c.ServerAddress, c.FakeAddressSNI)
|
||||
|
||||
listen, err := net.Listen("tcp", c.ListenAddress)
|
||||
if err != nil {
|
||||
fmt.Printf("[Client] Start client error: %v\n", err)
|
||||
@ -37,15 +37,65 @@ func (c *Client) Start() {
|
||||
fmt.Printf("[Client] accept error: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
stream := bridge.GetStream()
|
||||
if stream == nil {
|
||||
conn.Close()
|
||||
fmt.Println("[Client] connect to server error")
|
||||
continue
|
||||
}
|
||||
fmt.Printf("[Client] New TCP connection: %v <-> %v \n", conn.LocalAddr().String(), conn.RemoteAddr().String())
|
||||
go io.Copy(conn, stream)
|
||||
go io.Copy(stream, conn)
|
||||
go handlerClient(conn, c.ServerAddress, c.FakeAddressSNI)
|
||||
}
|
||||
}
|
||||
|
||||
func handlerClient(conn net.Conn, serverAddress string, fakeAddressSNI string) {
|
||||
config := &tls.Config{
|
||||
ServerName: fakeAddressSNI,
|
||||
}
|
||||
if HandshakePassword != "" {
|
||||
config.Rand = RandReaderObj
|
||||
}
|
||||
|
||||
rawConn, err := net.DialTimeout("tcp", serverAddress, time.Second*5)
|
||||
if err != nil {
|
||||
fmt.Printf("[Client] Dial server error: %v\n", err)
|
||||
return
|
||||
}
|
||||
dial := tls.UClient(rawConn, config, tls.HelloChrome_102)
|
||||
err = dial.Handshake()
|
||||
if err != nil {
|
||||
fmt.Printf("[Client] Handshake error: %v\n", err)
|
||||
return
|
||||
}
|
||||
//dial.GetUnderlyingConn().SetDeadline(time.Now())
|
||||
//dial.GetUnderlyingConn().SetDeadline(time.Time{})
|
||||
|
||||
p := &PackAppData{Conn: dial.GetUnderlyingConn()}
|
||||
|
||||
defer p.Close()
|
||||
defer conn.Close()
|
||||
exitCh := make(chan int, 1)
|
||||
|
||||
go MyCopy(conn, p, exitCh)
|
||||
go MyCopy(p, conn, exitCh)
|
||||
<-exitCh
|
||||
}
|
||||
|
||||
func MyCopy(src io.ReadWriteCloser, dst io.ReadWriteCloser, ch chan int) {
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if er != nil {
|
||||
if er == io.EOF {
|
||||
break
|
||||
} else {
|
||||
fmt.Printf("Read err: %v\n", er)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if ew != nil {
|
||||
fmt.Printf("Write error:%v\n", ew)
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
fmt.Printf("Write less then buffered \n")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
ch <- 1
|
||||
}
|
||||
|
@ -1,28 +1,19 @@
|
||||
package shadow
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
utls "github.com/refraction-networking/utls"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestName(t *testing.T) {
|
||||
dial, err := tls.DialWithDialer(&net.Dialer{
|
||||
Timeout: time.Second * 5,
|
||||
}, "tcp", "www.baidu.com:443", &tls.Config{
|
||||
ServerName: "www.baidu.com",
|
||||
})
|
||||
|
||||
err = dial.Handshake()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
time.Sleep(time.Minute)
|
||||
}
|
||||
|
||||
func TestName2(t *testing.T) {
|
||||
func TestHandshake(t *testing.T) {
|
||||
dial, err := tls.DialWithDialer(&net.Dialer{
|
||||
Timeout: time.Second * 5,
|
||||
}, "tcp", "evan.run:443", &tls.Config{
|
||||
@ -35,3 +26,163 @@ func TestName2(t *testing.T) {
|
||||
}
|
||||
time.Sleep(time.Minute)
|
||||
}
|
||||
|
||||
func TestMd5(t *testing.T) {
|
||||
key := "Passwd"
|
||||
passwd := []byte(key)
|
||||
|
||||
buf := make([]byte, 32)
|
||||
srcCode := md5.Sum(RandomByte(16))
|
||||
copy(buf[0:], srcCode[0:])
|
||||
buffer := bytes.NewBuffer(srcCode[:])
|
||||
|
||||
sum := md5.Sum(passwd)
|
||||
buffer.Write(sum[:])
|
||||
|
||||
hash := md5.Sum(buffer.Bytes())
|
||||
copy(buf[16:], hash[0:])
|
||||
fmt.Println(buf)
|
||||
|
||||
vBuf := make([]byte, 32)
|
||||
copy(vBuf, buf[0:16])
|
||||
verifyBuf := bytes.NewBuffer(vBuf)
|
||||
verifyBuf.Write(sum[:])
|
||||
|
||||
verifyHash := md5.Sum(buffer.Bytes())
|
||||
if bytes.Equal(verifyHash[:], buf[16:32]) {
|
||||
fmt.Println("GOOD")
|
||||
}
|
||||
if VerifyKey(buf, key) {
|
||||
fmt.Println("VerifyKey GOOD")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAes(t *testing.T) {
|
||||
key := []byte("1234567812345678")
|
||||
data := []byte("AVC")
|
||||
|
||||
e := AesEncryptCBC(data, key)
|
||||
d := AesDecryptCBC(e, key)
|
||||
|
||||
fmt.Println(string(d))
|
||||
}
|
||||
|
||||
func TestTLSFingerprint(t *testing.T) {
|
||||
|
||||
transport := http.Transport{
|
||||
DialTLS: func(network, adr string) (net.Conn, error) {
|
||||
dial, err := net.Dial(network, adr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wrapTLSClient(dial, time.Second*5)
|
||||
},
|
||||
}
|
||||
client := http.Client{
|
||||
Transport: &transport,
|
||||
CheckRedirect: nil,
|
||||
Jar: nil,
|
||||
Timeout: 0,
|
||||
}
|
||||
get, err := client.Get("https://client.tlsfingerprint.io:8443/")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
all, err := ioutil.ReadAll(get.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(string(all))
|
||||
|
||||
}
|
||||
|
||||
func wrapTLSClient(conn net.Conn, timeout time.Duration) (net.Conn, error) {
|
||||
var err error
|
||||
|
||||
conn.SetDeadline(time.Now().Add(timeout))
|
||||
defer conn.SetDeadline(time.Time{})
|
||||
|
||||
tlsConn := utls.UClient(conn, &utls.Config{ServerName: "client.tlsfingerprint.io"}, utls.HelloCustom)
|
||||
//fingerprinter := &utls.Fingerprinter{}
|
||||
//generatedSpec, err := fingerprinter.FingerprintClientHello([]byte{0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x0c, 0x81, 0xa3, 0x5c, 0x8b, 0x44, 0xf7, 0x74, 0x77, 0x7a, 0x51, 0x0f, 0x6f, 0xf4, 0xef, 0xb2, 0xb0, 0x40, 0x15, 0x8e, 0x66, 0xeb, 0xbe, 0x84, 0x6e, 0x18, 0x4b, 0x41, 0x2d, 0x6c, 0xb1, 0x97, 0x20, 0x85, 0x63, 0x63, 0x8b, 0xa6, 0x08, 0x50, 0xd2, 0xbe, 0xd9, 0xd3, 0x15, 0x8a, 0xbe, 0xdb, 0x62, 0xef, 0x39, 0x01, 0x7b, 0xdb, 0xd7, 0xe9, 0x78, 0xc0, 0x8d, 0x3d, 0x32, 0xbe, 0x8d, 0xfc, 0xef, 0x00, 0x20, 0x6a, 0x6a, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0x01, 0x00, 0x01, 0x93, 0x7a, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x13, 0x00, 0x00, 0x10, 0x73, 0x61, 0x6e, 0x6b, 0x75, 0x61, 0x69, 0x2e, 0x65, 0x76, 0x61, 0x6e, 0x2e, 0x72, 0x75, 0x6e, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0xea, 0xea, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0b, 0x00, 0x09, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x12, 0x00, 0x10, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x29, 0xea, 0xea, 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x20, 0x0c, 0x4d, 0x88, 0xfa, 0x97, 0xa0, 0x2e, 0xbe, 0xac, 0x9a, 0xae, 0x1d, 0xae, 0x00, 0x2d, 0xd0, 0x57, 0x40, 0x8f, 0x06, 0xcb, 0x31, 0xf3, 0x8e, 0x7d, 0xec, 0x93, 0xfb, 0xd7, 0x95, 0x0a, 0x40, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x07, 0x06, 0xea, 0xea, 0x03, 0x04, 0x03, 0x03, 0x00, 0x1b, 0x00, 0x03, 0x02, 0x00, 0x02, 0x44, 0x69, 0x00, 0x05, 0x00, 0x03, 0x02, 0x68, 0x32, 0xda, 0xda, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00, 0xca, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
//tlsConn.ApplyPreset(generatedSpec)
|
||||
spec := &utls.ClientHelloSpec{
|
||||
CipherSuites: []uint16{
|
||||
utls.GREASE_PLACEHOLDER,
|
||||
utls.TLS_AES_128_GCM_SHA256,
|
||||
utls.TLS_AES_256_GCM_SHA384,
|
||||
utls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
utls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
utls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
utls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
utls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
utls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
utls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
utls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
utls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
utls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
utls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
utls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
utls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
CompressionMethods: []byte{
|
||||
0x00, // compressionNone
|
||||
},
|
||||
Extensions: []utls.TLSExtension{
|
||||
&utls.UtlsGREASEExtension{},
|
||||
&utls.SNIExtension{},
|
||||
&utls.UtlsExtendedMasterSecretExtension{},
|
||||
&utls.RenegotiationInfoExtension{Renegotiation: utls.RenegotiateOnceAsClient},
|
||||
&utls.SupportedCurvesExtension{[]utls.CurveID{
|
||||
utls.GREASE_PLACEHOLDER,
|
||||
utls.X25519,
|
||||
utls.CurveP256,
|
||||
utls.CurveP384,
|
||||
}},
|
||||
&utls.SupportedPointsExtension{SupportedPoints: []byte{
|
||||
0x00, // pointFormatUncompressed
|
||||
}},
|
||||
&utls.SessionTicketExtension{},
|
||||
&utls.ALPNExtension{AlpnProtocols: []string{"http/1.1"}},
|
||||
&utls.StatusRequestExtension{},
|
||||
&utls.SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []utls.SignatureScheme{
|
||||
utls.ECDSAWithP256AndSHA256,
|
||||
utls.PSSWithSHA256,
|
||||
utls.PKCS1WithSHA256,
|
||||
utls.ECDSAWithP384AndSHA384,
|
||||
utls.PSSWithSHA384,
|
||||
utls.PKCS1WithSHA384,
|
||||
utls.PSSWithSHA512,
|
||||
utls.PKCS1WithSHA512,
|
||||
}},
|
||||
&utls.SCTExtension{},
|
||||
&utls.KeyShareExtension{[]utls.KeyShare{
|
||||
{Group: utls.CurveID(utls.GREASE_PLACEHOLDER), Data: []byte{0}},
|
||||
{Group: utls.X25519},
|
||||
}},
|
||||
&utls.PSKKeyExchangeModesExtension{[]uint8{
|
||||
utls.PskModeDHE,
|
||||
}},
|
||||
&utls.SupportedVersionsExtension{[]uint16{
|
||||
utls.GREASE_PLACEHOLDER,
|
||||
VersionTLS13,
|
||||
VersionTLS12,
|
||||
}},
|
||||
&utls.UtlsCompressCertExtension{[]utls.CertCompressionAlgo{
|
||||
utls.CertCompressionBrotli,
|
||||
}},
|
||||
&utls.ApplicationSettingsExtension{SupportedProtocols: []string{"h2"}},
|
||||
&utls.UtlsGREASEExtension{},
|
||||
&utls.UtlsPaddingExtension{GetPaddingLen: utls.BoringPaddingStyle},
|
||||
},
|
||||
}
|
||||
tlsConn.ApplyPreset(spec)
|
||||
if err = tlsConn.Handshake(); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
tlsConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tlsConn, err
|
||||
}
|
||||
|
8
shadow/global.go
Normal file
8
shadow/global.go
Normal file
@ -0,0 +1,8 @@
|
||||
package shadow
|
||||
|
||||
var (
|
||||
HandshakePassword = ""
|
||||
RandReaderObj = &RandReader{}
|
||||
EncryptKey = []byte{}
|
||||
Key = ""
|
||||
)
|
94
shadow/packer.go
Normal file
94
shadow/packer.go
Normal file
@ -0,0 +1,94 @@
|
||||
package shadow
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
AppDataHeader = []byte{0x17, 0x3, 0x3}
|
||||
HeaderLength = len(AppDataHeader)
|
||||
)
|
||||
|
||||
type PackAppData struct {
|
||||
Conn net.Conn
|
||||
}
|
||||
|
||||
func (m PackAppData) Read(p []byte) (n int, err error) {
|
||||
|
||||
buf := make([]byte, 48*1024+HeaderLength+2)
|
||||
|
||||
headRead, err := io.ReadAtLeast(m.Conn, buf[0:HeaderLength+2], HeaderLength+2)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
fmt.Printf("Read header error: %v\n", err)
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
if headRead < HeaderLength+2 {
|
||||
return 0, errors.New("Read header failed")
|
||||
}
|
||||
if bytes.Equal(buf[0:HeaderLength], AppDataHeader) {
|
||||
payLoadLength := int(binary.BigEndian.Uint16(buf[HeaderLength : HeaderLength+2]))
|
||||
sum := 0
|
||||
for sum < payLoadLength {
|
||||
r, e := m.Conn.Read(buf[HeaderLength+2+sum : HeaderLength+2+payLoadLength])
|
||||
if e != nil {
|
||||
if e == io.EOF {
|
||||
break
|
||||
} else {
|
||||
return 0, e
|
||||
}
|
||||
}
|
||||
sum += r
|
||||
}
|
||||
if len(EncryptKey) > 0 {
|
||||
encryptedData := buf[HeaderLength+2 : HeaderLength+2+sum]
|
||||
decrypted := AesDecryptCBC(encryptedData, EncryptKey)
|
||||
copy(p[0:], decrypted)
|
||||
sum = len(decrypted)
|
||||
} else {
|
||||
copy(p[0:], buf[HeaderLength+2:HeaderLength+2+sum])
|
||||
}
|
||||
return sum, err
|
||||
} else {
|
||||
fmt.Printf("Invalid header")
|
||||
return 0, errors.New("invalid header")
|
||||
}
|
||||
}
|
||||
|
||||
func (m PackAppData) Write(p []byte) (n int, err error) {
|
||||
var sendData []byte
|
||||
if len(EncryptKey) > 0 {
|
||||
sendData = AesEncryptCBC(p, EncryptKey)
|
||||
} else {
|
||||
sendData = p
|
||||
}
|
||||
|
||||
lenNum := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(lenNum, uint16(len(sendData)))
|
||||
|
||||
packetBuf := bytes.NewBuffer(AppDataHeader)
|
||||
packetBuf.Write(lenNum)
|
||||
packetBuf.Write(sendData)
|
||||
|
||||
write, err := m.Conn.Write(packetBuf.Bytes())
|
||||
if len(EncryptKey) > 0 {
|
||||
if write != packetBuf.Len() {
|
||||
write = 0
|
||||
} else {
|
||||
write = len(p)
|
||||
}
|
||||
} else {
|
||||
write = write - HeaderLength - 2
|
||||
}
|
||||
return write, err
|
||||
}
|
||||
|
||||
func (m PackAppData) Close() error {
|
||||
return m.Conn.Close()
|
||||
}
|
49
shadow/rand.go
Normal file
49
shadow/rand.go
Normal file
@ -0,0 +1,49 @@
|
||||
package shadow
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"io"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
type RandReader struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (r RandReader) Read(p []byte) (n int, err error) {
|
||||
buf := make([]byte, 32)
|
||||
randBytes := md5.Sum(RandomByte(16))
|
||||
copy(buf[0:], randBytes[:])
|
||||
|
||||
preHashData := bytes.NewBuffer(randBytes[:])
|
||||
sum := md5.Sum([]byte(HandshakePassword))
|
||||
preHashData.Write(sum[:])
|
||||
hash := md5.Sum(preHashData.Bytes())
|
||||
copy(buf[16:], hash[:])
|
||||
copy(p, buf)
|
||||
return 32, nil
|
||||
}
|
||||
|
||||
func RandomByte(size int) []byte {
|
||||
buf := make([]byte, size)
|
||||
for i := 0; i < size; i++ {
|
||||
buf[i] = byte(rand.Intn(255))
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func VerifyKey(p []byte, key string) bool {
|
||||
if len(p) != 32 {
|
||||
return false
|
||||
}
|
||||
buf := make([]byte, 16)
|
||||
copy(buf, p[0:16])
|
||||
buffer := bytes.NewBuffer(buf)
|
||||
sum := md5.Sum([]byte(key))
|
||||
buffer.Write(sum[:])
|
||||
|
||||
hash := md5.Sum(buffer.Bytes())
|
||||
|
||||
return bytes.Equal(hash[:], p[16:])
|
||||
}
|
@ -2,7 +2,6 @@ package shadow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/xtaci/smux"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
@ -52,53 +51,61 @@ func handler(conn net.Conn, targetAddress string, fakeAddress string) {
|
||||
}
|
||||
waitCh := make(chan int, 1)
|
||||
|
||||
go processHandshake(conn, fakeConn, waitCh)
|
||||
go processHandshake(fakeConn, conn, waitCh)
|
||||
go processHandshake(conn, fakeConn, waitCh, "client")
|
||||
go processHandshake(fakeConn, conn, waitCh, "server")
|
||||
|
||||
<-waitCh
|
||||
|
||||
//Clean up previous buffered data
|
||||
conn.SetDeadline(time.Now())
|
||||
conn.SetDeadline(time.Time{})
|
||||
fakeConn.Close()
|
||||
|
||||
//Process real tcp connection
|
||||
session, err := smux.Server(conn, nil)
|
||||
realConnection, err := net.Dial("tcp", targetAddress)
|
||||
if err != nil {
|
||||
fmt.Printf("[Server] smux error: %v\n", err)
|
||||
fmt.Printf("[Server] Dial target error : %v\n", err)
|
||||
return
|
||||
}
|
||||
for {
|
||||
stream, err := session.AcceptStream()
|
||||
if err != nil {
|
||||
fmt.Printf("[Server] AcceptStream error: %v\n", err)
|
||||
break
|
||||
}
|
||||
go handlerMux(stream, targetAddress)
|
||||
return
|
||||
}
|
||||
|
||||
p := &PackAppData{Conn: conn}
|
||||
|
||||
defer p.Close()
|
||||
defer realConnection.Close()
|
||||
exit := make(chan int, 1)
|
||||
|
||||
go MyCopy(p, realConnection, exit)
|
||||
go MyCopy(realConnection, p, exit)
|
||||
<-exit
|
||||
}
|
||||
|
||||
func processHandshake(src net.Conn, dst net.Conn, waitCh chan int) {
|
||||
func processHandshake(src net.Conn, dst net.Conn, waitCh chan int, srcType string) {
|
||||
buf := make([]byte, 32*1024)
|
||||
verifyPass := false
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
|
||||
header := ParseAndVerifyTLSHeader(buf[0:nr])
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if header != nil && header.Type == ChangeCipherSpec {
|
||||
//fmt.Println(header.toString())
|
||||
fmt.Println("[Server] handshake complete")
|
||||
if header.ChangeCipherSpecNext == AppData {
|
||||
dst.Close()
|
||||
waitCh <- 1
|
||||
} else {
|
||||
src.Close()
|
||||
waitCh <- 1
|
||||
return
|
||||
if srcType == "client" {
|
||||
header := ParseAndVerifyTLSHeader(buf[0:nr])
|
||||
if header != nil {
|
||||
if header != nil && header.Type == Handshake && header.HandshakeType == ClientHello && !verifyPass {
|
||||
verifyPass = VerifyKey(header.Rand, HandshakePassword)
|
||||
}
|
||||
if header.Type == ChangeCipherSpec {
|
||||
if HandshakePassword != "" && !verifyPass {
|
||||
fmt.Println("[Server] Probe detected,pass through all traffic.")
|
||||
} else {
|
||||
fmt.Println("[Server] handshake complete")
|
||||
waitCh <- 1
|
||||
break
|
||||
}
|
||||
}
|
||||
//fmt.Println(header.toString())
|
||||
}
|
||||
}
|
||||
if nw < 0 || nr < nw {
|
||||
nw = 0
|
||||
if ew == nil {
|
||||
@ -121,19 +128,4 @@ func processHandshake(src net.Conn, dst net.Conn, waitCh chan int) {
|
||||
break
|
||||
}
|
||||
}
|
||||
waitCh <- 1
|
||||
}
|
||||
|
||||
func handlerMux(conn *smux.Stream, targetAddress string) {
|
||||
|
||||
realConnection, err := net.Dial("tcp", targetAddress)
|
||||
if err != nil {
|
||||
fmt.Printf("[Server] Dial target error : %v\n", err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go io.Copy(realConnection, conn)
|
||||
go io.Copy(conn, realConnection)
|
||||
}
|
||||
|
@ -1,75 +0,0 @@
|
||||
package shadow
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/xtaci/smux"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TLSBridge struct {
|
||||
session *smux.Session
|
||||
locker sync.Mutex
|
||||
serverAddress string
|
||||
fakeAddressSNI string
|
||||
}
|
||||
|
||||
func NewTLSBridge(serverAddress string, fakeAddressSNI string) *TLSBridge {
|
||||
t := &TLSBridge{
|
||||
session: nil,
|
||||
locker: sync.Mutex{},
|
||||
serverAddress: serverAddress,
|
||||
fakeAddressSNI: fakeAddressSNI,
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *TLSBridge) dial() error {
|
||||
if t.session != nil {
|
||||
t.session.Close()
|
||||
}
|
||||
dial, err := tls.DialWithDialer(&net.Dialer{
|
||||
Timeout: time.Second * 5,
|
||||
}, "tcp", t.serverAddress, &tls.Config{
|
||||
ServerName: t.fakeAddressSNI,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dial.Handshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dial.NetConn().SetDeadline(time.Now())
|
||||
dial.NetConn().SetDeadline(time.Time{})
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
session, err := smux.Client(dial.NetConn(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//force openStream to prevent first connection problem
|
||||
session.OpenStream()
|
||||
t.session = session
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TLSBridge) GetStream() *smux.Stream {
|
||||
t.locker.Lock()
|
||||
defer t.locker.Unlock()
|
||||
|
||||
if t.session == nil {
|
||||
err := t.dial()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
openStream, err := t.session.OpenStream()
|
||||
if err != nil {
|
||||
t.session.Close()
|
||||
t.session = nil
|
||||
}
|
||||
return openStream
|
||||
|
||||
}
|
@ -19,6 +19,10 @@ const (
|
||||
|
||||
ServerHello = 2
|
||||
ClientHello = 1
|
||||
Certificate = 11
|
||||
ServerKeyExchange = 12
|
||||
ServerHelloDone = 14
|
||||
EncryptedHandshake = 99
|
||||
)
|
||||
|
||||
type TLSHeader struct {
|
||||
@ -27,6 +31,7 @@ type TLSHeader struct {
|
||||
Length int
|
||||
HandshakeType uint8
|
||||
ChangeCipherSpecNext uint8
|
||||
Rand []byte
|
||||
}
|
||||
|
||||
func (t *TLSHeader) toString() string {
|
||||
@ -88,12 +93,17 @@ func ParseAndVerifyTLSHeader(data []byte) *TLSHeader {
|
||||
if header.Type == Handshake {
|
||||
header.HandshakeType = data[5]
|
||||
//Check Handshake type
|
||||
if header.HandshakeType != ServerHello && header.HandshakeType != ClientHello {
|
||||
return nil
|
||||
if header.HandshakeType != ServerHello && header.HandshakeType != ClientHello && header.HandshakeType != Certificate && header.HandshakeType != ServerKeyExchange && header.HandshakeType != ServerHelloDone {
|
||||
header.HandshakeType = EncryptedHandshake
|
||||
}
|
||||
if header.HandshakeType == ClientHello {
|
||||
header.Rand = data[11:43]
|
||||
}
|
||||
}
|
||||
if header.Type == ChangeCipherSpec {
|
||||
if len(data) > 6 {
|
||||
header.ChangeCipherSpecNext = data[6]
|
||||
}
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
Reference in New Issue
Block a user