main
sheepbao 8 years ago
commit 22843aa300
  1. 2
      build.sh
  2. 71
      dump.go
  3. 84
      gomitmproxy.go
  4. 4
      readme.md
  5. 0
      src/doc/goproxy.png
  6. 0
      src/doc/proxy.png
  7. 41
      src/main/main.go
  8. 2
      src/vendor/color/tcolor.go
  9. 14
      src/vendor/config/config.go
  10. 2
      src/vendor/mitm/cache.go
  11. 100
      src/vendor/mitm/dump.go
  12. 9
      src/vendor/mitm/genKey.go
  13. 48
      src/vendor/mitm/gomitmproxy.go
  14. 2
      src/vendor/mitm/listener.go
  15. 99
      src/vendor/mitm/mitm.go
  16. 30
      src/vendor/mylog/my_log.go
  17. 17
      src/vendor/mylog/my_log_test.go
  18. 1
      src/vendor/mylog/test.log

@ -0,0 +1,2 @@
export GOPATH=`pwd`
go build -o bin/gomitmproxy src/main/*.go

@ -1,71 +0,0 @@
package main
import (
"bufio"
"bytes"
"compress/flate"
"compress/gzip"
"fmt"
"io/ioutil"
"math"
"net/http"
"strconv"
)
func httpDump(req *http.Request, resp *http.Response) {
defer resp.Body.Close()
var respStatusStr string
respStatus := resp.StatusCode
respStatusHeader := int(math.Floor(float64(respStatus / 100)))
switch respStatusHeader {
case 2:
respStatusStr = Green("<--" + strconv.Itoa(respStatus))
case 3:
respStatusStr = Yellow("<--" + strconv.Itoa(respStatus))
case 4:
respStatusStr = Magenta("<--" + strconv.Itoa(respStatus))
case 5:
respStatusStr = Red("<--" + strconv.Itoa(respStatus))
}
fmt.Println(Green("Request:"))
fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr)
for headerName, headerContext := range req.Header {
fmt.Printf("%s: %s\n", Blue(headerName), headerContext)
}
fmt.Println(Green("Response:"))
for headerName, headerContext := range resp.Header {
fmt.Printf("%s: %s\n", Blue(headerName), headerContext)
}
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
logger.Println("func httpDump read resp body err:", err)
} else {
acceptEncode := resp.Header["Content-Encoding"]
var respBodyBin bytes.Buffer
w := bufio.NewWriter(&respBodyBin)
w.Write(respBody)
w.Flush()
for _, compress := range acceptEncode {
switch compress {
case "gzip":
r, err := gzip.NewReader(&respBodyBin)
if err != nil {
logger.Println("gzip reader err:", err)
} else {
defer r.Close()
respBody, _ = ioutil.ReadAll(r)
}
break
case "deflate":
r := flate.NewReader(&respBodyBin)
defer r.Close()
respBody, _ = ioutil.ReadAll(r)
break
}
}
fmt.Printf("%s\n", string(respBody))
}
fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################"))
}

@ -1,84 +0,0 @@
// This example shows a proxy server that uses go-mitm to man-in-the-middle
// HTTPS connections opened with CONNECT requests
package main
import (
"flag"
"log"
"net/http"
"os"
"sync"
"time"
)
const (
Version = "1.1"
)
var (
wg sync.WaitGroup
)
var logFile *os.File
var logger *log.Logger
func main() {
var conf Cfg
conf.Port = flag.String("port", "8080", "Listen port")
conf.Raddr = flag.String("raddr", "", "Remote addr")
conf.Log = flag.String("log", "./error.log", "log file path")
conf.Monitor = flag.Bool("m", false, "monitor mode")
conf.Tls = flag.Bool("tls", false, "tls connect")
flag.Parse()
var err error
logFile, err = os.Create(*conf.Log)
if err != nil {
log.Fatalln("fail to create log file!")
}
logger = log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile)
wg.Add(1)
gomitmproxy(&conf)
wg.Wait()
}
func gomitmproxy(conf *Cfg) {
tlsConfig := NewTlsConfig("gomitmproxy-ca-pk.pem", "gomitmproxy-ca-cert.pem", "", "")
handler, err := InitConfig(conf, tlsConfig)
if err != nil {
logger.Fatalf("InitConfig error: %s", err)
}
server := &http.Server{
Addr: ":" + *conf.Port,
Handler: handler,
ReadTimeout: 1 * time.Hour,
WriteTimeout: 1 * time.Hour,
}
go func() {
log.Printf("proxy listening port:%s", *conf.Port)
if *conf.Tls {
log.Println("ListenAndServeTLS")
err = server.ListenAndServeTLS("gomitmproxy-ca-cert.pem", "gomitmproxy-ca-pk.pem")
} else {
log.Println("ListenAndServe")
err = server.ListenAndServe()
}
if err != nil {
logger.Fatalf("Unable to start HTTP proxy: %s", err)
}
wg.Done()
log.Printf("gomitmproxy stop!!!!")
}()
return
}

@ -40,7 +40,7 @@ gomitmproxy
gomitmproxy -m gomitmproxy -m
``` ```
![fetch http](https://raw.githubusercontent.com/sheepbao/gomitmproxy/develop/goproxy.png) ![fetch http](https://raw.githubusercontent.com/sheepbao/gomitmproxy/develop/doc/goproxy.png)
加 -m 参数,表示抓取http请求和响应 加 -m 参数,表示抓取http请求和响应
@ -59,7 +59,7 @@ gomitmproxy -m
``` ```
然后浏览器设置代理,ip为localhost,端口为8080,即可实现科学上网 然后浏览器设置代理,ip为localhost,端口为8080,即可实现科学上网
![proxy](https://raw.githubusercontent.com/sheepbao/gomitmproxy/master/proxy.png) ![proxy](https://raw.githubusercontent.com/sheepbao/gomitmproxy/develop/doc/proxy.png)
## 最后 ## 最后

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

@ -0,0 +1,41 @@
package main
import (
"config"
"flag"
"io"
"mitm"
"mylog"
"os"
"sync"
)
func main() {
var log io.WriteCloser
var err error
// cofig
conf := new(config.Cfg)
conf.Port = flag.String("port", "8080", "Listen port")
conf.Raddr = flag.String("raddr", "", "Remote addr")
conf.Log = flag.String("logFile", "", "log file path")
conf.Monitor = flag.Bool("m", false, "monitor mode")
conf.Tls = flag.Bool("tls", false, "tls connect")
flag.Parse()
// init log
if *conf.Log != "" {
log, err = os.Create(*conf.Log)
if err != nil {
mylog.Fatalln("fail to create log file!")
}
} else {
log = os.Stderr
}
mylog.SetLog(log)
// start mitm proxy
wg := new(sync.WaitGroup)
wg.Add(1)
mitm.Gomitmproxy(conf, wg)
wg.Wait()
}

@ -1,4 +1,4 @@
package main package color
import ( import (
"fmt" "fmt"

@ -1,16 +1,6 @@
package main package config
import ( import "crypto/tls"
"crypto/tls"
"time"
)
const (
ONE_DAY = 24 * time.Hour
TWO_WEEKS = ONE_DAY * 14
ONE_MONTH = 1
ONE_YEAR = 1
)
type Cfg struct { type Cfg struct {
Port *string Port *string

@ -1,6 +1,6 @@
// package cache implements a really primitive cache that associates expiring // package cache implements a really primitive cache that associates expiring
// values with string keys. This cache never clears itself out. // values with string keys. This cache never clears itself out.
package main package mitm
import ( import (
"sync" "sync"

@ -0,0 +1,100 @@
package mitm
import (
"bufio"
"bytes"
"color"
"compress/flate"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"math"
"mylog"
"net/http"
"strconv"
)
func httpDump(reqDump []byte, resp *http.Response) {
defer resp.Body.Close()
var respStatusStr string
respStatus := resp.StatusCode
respStatusHeader := int(math.Floor(float64(respStatus / 100)))
switch respStatusHeader {
case 2:
respStatusStr = color.Green("<--" + strconv.Itoa(respStatus))
case 3:
respStatusStr = color.Yellow("<--" + strconv.Itoa(respStatus))
case 4:
respStatusStr = color.Magenta("<--" + strconv.Itoa(respStatus))
case 5:
respStatusStr = color.Red("<--" + strconv.Itoa(respStatus))
}
fmt.Println(color.Green("Request:"), respStatusStr)
req, _ := ParseReq(reqDump)
fmt.Printf("%s %s %s\n", color.Blue(req.Method), req.Host+req.RequestURI, respStatusStr)
fmt.Printf("%s %s\n", color.Blue("RemoteAddr:"), req.RemoteAddr)
for headerName, headerContext := range req.Header {
fmt.Printf("%s: %s\n", color.Blue(headerName), headerContext)
}
if req.Method == "POST" {
fmt.Println(color.Green("POST Param:"))
err := req.ParseForm()
if err != nil {
mylog.Println("parseForm error:", err)
} else {
for k, v := range req.Form {
fmt.Printf("\t%s: %s\n", color.Blue(k), v)
}
}
}
fmt.Println(color.Green("Response:"))
for headerName, headerContext := range resp.Header {
fmt.Printf("%s: %s\n", color.Blue(headerName), headerContext)
}
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
mylog.Println("func httpDump read resp body err:", err)
} else {
acceptEncode := resp.Header["Content-Encoding"]
var respBodyBin bytes.Buffer
w := bufio.NewWriter(&respBodyBin)
w.Write(respBody)
w.Flush()
for _, compress := range acceptEncode {
switch compress {
case "gzip":
r, err := gzip.NewReader(&respBodyBin)
if err != nil {
mylog.Println("gzip reader err:", err)
} else {
defer r.Close()
respBody, _ = ioutil.ReadAll(r)
}
break
case "deflate":
r := flate.NewReader(&respBodyBin)
defer r.Close()
respBody, _ = ioutil.ReadAll(r)
break
}
}
fmt.Printf("%s\n", string(respBody))
}
fmt.Printf("%s%s%s\n", color.Black("####################"), color.Cyan("END"), color.Black("####################"))
}
func ParseReq(b []byte) (*http.Request, error) {
// func ReadRequest(b *bufio.Reader) (req *Request, err error) { return readRequest(b, deleteHostHeader) }
fmt.Println(string(b))
fmt.Println("-----------------------")
var buf io.ReadWriter
buf = new(bytes.Buffer)
buf.Write(b)
bufr := bufio.NewReader(buf)
return http.ReadRequest(bufr)
}

@ -1,5 +1,5 @@
// Package keyman provides convenience APIs around Go's built-in crypto APIs. // Package keyman provides convenience APIs around Go's built-in crypto APIs.
package main package mitm
import ( import (
"crypto/rand" "crypto/rand"
@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"mylog"
"net" "net"
"os" "os"
"time" "time"
@ -85,7 +86,7 @@ func (key *PrivateKey) WriteToFile(filename string) (err error) {
return fmt.Errorf("Unable to PEM encode private key: %s", err) return fmt.Errorf("Unable to PEM encode private key: %s", err)
} }
if err := keyOut.Close(); err != nil { if err := keyOut.Close(); err != nil {
logger.Printf("Unable to close file: %v", err) mylog.Printf("Unable to close file: %v", err)
} }
return return
} }
@ -236,7 +237,7 @@ func (cert *Certificate) WriteToFile(filename string) (err error) {
} }
defer func() { defer func() {
if err := certOut.Close(); err != nil { if err := certOut.Close(); err != nil {
logger.Printf("Unable to close file: %v", err) mylog.Printf("Unable to close file: %v", err)
} }
}() }()
return pem.Encode(certOut, cert.pemBlock()) return pem.Encode(certOut, cert.pemBlock())
@ -264,7 +265,7 @@ func (cert *Certificate) WriteToDERFile(filename string) (err error) {
} }
defer func() { defer func() {
if err := certOut.Close(); err != nil { if err := certOut.Close(); err != nil {
logger.Printf("Unable to close file: %v", err) mylog.Printf("Unable to close file: %v", err)
} }
}() }()
_, err = certOut.Write(cert.derBytes) _, err = certOut.Write(cert.derBytes)

@ -0,0 +1,48 @@
// This example shows a proxy server that uses go-mitm to man-in-the-middle
// HTTPS connections opened with CONNECT requests
package mitm
import (
"config"
"mylog"
"net/http"
"sync"
"time"
)
func Gomitmproxy(conf *config.Cfg, wg *sync.WaitGroup) {
tlsConfig := config.NewTlsConfig("gomitmproxy-ca-pk.pem", "gomitmproxy-ca-cert.pem", "", "")
handler, err := InitConfig(conf, tlsConfig)
if err != nil {
mylog.Fatalf("InitConfig error: %s", err)
}
server := &http.Server{
Addr: ":" + *conf.Port,
Handler: handler,
ReadTimeout: 1 * time.Hour,
WriteTimeout: 1 * time.Hour,
}
go func() {
mylog.Printf("Gomitmproxy Listening On: %s", *conf.Port)
if *conf.Tls {
mylog.Println("Listen And Serve HTTP TLS")
err = server.ListenAndServeTLS("gomitmproxy-ca-cert.pem", "gomitmproxy-ca-pk.pem")
} else {
mylog.Println("Listen And Serve HTTP")
err = server.ListenAndServe()
}
if err != nil {
mylog.Fatalf("Unable To Start HTTP proxy: %s", err)
}
wg.Done()
mylog.Printf("Gomitmproxy Stop!!!!")
}()
return
}

@ -1,4 +1,4 @@
package main package mitm
import ( import (
"io" "io"

@ -1,12 +1,14 @@
package main package mitm
import ( import (
"bufio" "bufio"
"config"
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"log" "log"
"mylog"
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
@ -17,9 +19,17 @@ import (
"time" "time"
) )
const (
Version = "1.1"
ONE_DAY = 24 * time.Hour
TWO_WEEKS = ONE_DAY * 14
ONE_MONTH = 1
ONE_YEAR = 1
)
type HandlerWrapper struct { type HandlerWrapper struct {
MyConfig *Cfg MyConfig *config.Cfg
tlsConfig *TlsConfig tlsConfig *config.TlsConfig
wrapped http.Handler wrapped http.Handler
pk *PrivateKey pk *PrivateKey
pkPem []byte pkPem []byte
@ -100,11 +110,20 @@ func (hw *HandlerWrapper) FakeCertForName(name string) (cert *tls.Certificate, e
func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.Request) { func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.Request) {
req.Header.Del("Proxy-Connection") req.Header.Del("Proxy-Connection")
req.Header.Set("Connection", "Keep-Alive") req.Header.Set("Connection", "Keep-Alive")
var reqDump []byte
var err error
ch := make(chan bool)
// handle connection // handle connection
go func() {
reqDump, err = httputil.DumpRequestOut(req, true)
ch <- true
}()
if err != nil {
mylog.Println("DumpRequest error ", err)
}
connIn, _, err := resp.(http.Hijacker).Hijack() connIn, _, err := resp.(http.Hijacker).Hijack()
if err != nil { if err != nil {
logger.Println("hijack error:", err) mylog.Println("hijack error:", err)
} }
defer connIn.Close() defer connIn.Close()
@ -120,18 +139,20 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R
connOut, err := net.DialTimeout("tcp", host, time.Second*30) connOut, err := net.DialTimeout("tcp", host, time.Second*30)
if err != nil { if err != nil {
logger.Println("dial to", host, "error:", err)
mylog.Println("dial to", host, "error:", err)
return return
} }
if err = req.Write(connOut); err != nil { if err = req.Write(connOut); err != nil {
logger.Println("send to server error", err)
mylog.Println("send to server error", err)
return return
} }
respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) respOut, err = http.ReadResponse(bufio.NewReader(connOut), req)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
logger.Println("read response error:", err) mylog.Println("read response error:", err)
} }
} else { } else {
@ -140,19 +161,21 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R
} }
connOut, err := tls.Dial("tcp", host, hw.tlsConfig.ServerTLSConfig) connOut, err := tls.Dial("tcp", host, hw.tlsConfig.ServerTLSConfig)
if err != nil { if err != nil {
logger.Panicln("tls dial to", host, "error:", err)
}
if err = req.Write(connOut); err != nil {
mylog.Println("tls dial to", host, "error:", err)
return return
} }
if err = req.Write(connOut); err != nil { if err = req.Write(connOut); err != nil {
logger.Println("send to server error", err) mylog.Println("send to server error", err)
return return
} }
respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) respOut, err = http.ReadResponse(bufio.NewReader(connOut), req)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
logger.Println("read response error:", err) mylog.Println("read response error:", err)
} }
} }
@ -164,18 +187,20 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R
respDump, err := httputil.DumpResponse(respOut, true) respDump, err := httputil.DumpResponse(respOut, true)
if err != nil { if err != nil {
logger.Println("respDump error:", err) mylog.Println("respDump error:", err)
} }
_, err = connIn.Write(respDump) _, err = connIn.Write(respDump)
if err != nil { if err != nil {
logger.Println("connIn write error:", err) mylog.Println("connIn write error:", err)
} }
if *hw.MyConfig.Monitor { if *hw.MyConfig.Monitor {
go httpDump(req, respOut) <-ch
go httpDump(reqDump, respOut)
} else {
<-ch
} }
} }
func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
@ -226,7 +251,7 @@ func (hw *HandlerWrapper) InterceptHTTPs(resp http.ResponseWriter, req *http.Req
go func() { go func() {
err = http.Serve(listener, handler) err = http.Serve(listener, handler)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
logger.Printf("Error serving mitm'ed connection: %s", err) mylog.Printf("Error serving mitm'ed connection: %s", err)
} }
}() }()
@ -237,12 +262,12 @@ func (hw *HandlerWrapper) Forward(resp http.ResponseWriter, req *http.Request, r
connIn, _, err := resp.(http.Hijacker).Hijack() connIn, _, err := resp.(http.Hijacker).Hijack()
connOut, err := net.Dial("tcp", raddr) connOut, err := net.Dial("tcp", raddr)
if err != nil { if err != nil {
logger.Println("dial tcp error", err) mylog.Println("dial tcp error", err)
} }
err = connectProxyServer(connOut, raddr) err = connectProxyServer(connOut, raddr)
if err != nil { if err != nil {
logger.Println("connectProxyServer error:", err) mylog.Println("connectProxyServer error:", err)
} }
if req.Method == "CONNECT" { if req.Method == "CONNECT" {
@ -250,24 +275,24 @@ func (hw *HandlerWrapper) Forward(resp http.ResponseWriter, req *http.Request, r
"Proxy-Agent: gomitmproxy/" + Version + "\r\n\r\n") "Proxy-Agent: gomitmproxy/" + Version + "\r\n\r\n")
_, err := connIn.Write(b) _, err := connIn.Write(b)
if err != nil { if err != nil {
logger.Println("Write Connect err:", err) mylog.Println("Write Connect err:", err)
return return
} }
} else { } else {
req.Header.Del("Proxy-Connection") req.Header.Del("Proxy-Connection")
req.Header.Set("Connection", "Keep-Alive") req.Header.Set("Connection", "Keep-Alive")
if err = req.Write(connOut); err != nil { if err = req.Write(connOut); err != nil {
logger.Println("send to server err", err) mylog.Println("send to server err", err)
return return
} }
} }
err = Transport(connIn, connOut) err = Transport(connIn, connOut)
if err != nil { if err != nil {
log.Println("trans error ", err) mylog.Println("trans error ", err)
} }
} }
func InitConfig(conf *Cfg, tlsConfig *TlsConfig) (*HandlerWrapper, error) { func InitConfig(conf *config.Cfg, tlsConfig *config.TlsConfig) (*HandlerWrapper, error) {
hw := &HandlerWrapper{ hw := &HandlerWrapper{
MyConfig: conf, MyConfig: conf,
tlsConfig: tlsConfig, tlsConfig: tlsConfig,
@ -288,6 +313,14 @@ func copyTlsConfig(template *tls.Config) *tls.Config {
return tlsConfig return tlsConfig
} }
func copyHTTPRequest(template *http.Request) *http.Request {
req := &http.Request{}
if template != nil {
*req = *template
}
return req
}
func respBadGateway(resp http.ResponseWriter, msg string) { func respBadGateway(resp http.ResponseWriter, msg string) {
log.Println(msg) log.Println(msg)
resp.WriteHeader(502) resp.WriteHeader(502)
@ -316,7 +349,6 @@ func MyCopy(src io.Reader, dst io.Writer, ch chan<- error) {
} }
func connectProxyServer(conn net.Conn, addr string) error { func connectProxyServer(conn net.Conn, addr string) error {
req := &http.Request{ req := &http.Request{
Method: "CONNECT", Method: "CONNECT",
URL: &url.URL{Host: addr}, URL: &url.URL{Host: addr},
@ -340,3 +372,22 @@ func connectProxyServer(conn net.Conn, addr string) error {
} }
return nil return nil
} }
/*func ReadNotDrain(r *http.Request) (content []byte, err error) {
content, err = ioutil.ReadAll(r.Body)
r.Body = io.ReadCloser(bytes.NewBuffer(content))
return
}
func ParsePostValues(req *http.Request) (url.Values, error) {
c, err := ReadNotDrain(req)
if err != nil {
return nil, err
}
values, err := url.ParseQuery(string(c))
if err != nil {
return nil, err
}
return values, nil
}
*/

@ -0,0 +1,30 @@
package mylog
import "log"
import "io"
var logger *log.Logger
func SetLog(l io.WriteCloser) {
logger = log.New(l, "[gomitmproxy]", log.LstdFlags)
}
func Fatalf(format string, v ...interface{}) {
logger.Fatalf(format, v)
}
func Fatalln(v ...interface{}) {
logger.Fatalln(v)
}
func Printf(format string, v ...interface{}) {
logger.Printf(format, v)
}
func Println(v ...interface{}) {
logger.Println(v)
}
func Panicln(v ...interface{}) {
logger.Panicln(v)
}

@ -0,0 +1,17 @@
package mylog
import (
"log"
"os"
"testing"
)
func TestMyLog(t *testing.T) {
logFile, err := os.Create("test.log")
if err != nil {
log.Fatalln("fail to create log file!")
}
logger := log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile)
SetLog(logger)
Println("log test")
}

@ -0,0 +1 @@
[gomitmproxy]2017/04/03 00:00:25 /Users/bao/program/go/gowork/gomitmproxy/src/vendor/mylog/my_log.go:20: [log test]
Loading…
Cancel
Save