first commit

This commit is contained in:
juneflbjpz 2024-09-13 23:27:03 +08:00
commit d787bce159
9 changed files with 441 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
lagran
.idea
.DS_Store
*.DS_Store

49
InitParams.go Normal file
View File

@ -0,0 +1,49 @@
package main
import (
"flag"
)
var Port string
var SaEnable bool
var WindowSa uint16
var AEnable bool
var WindowA uint16
var PaEnable bool
var WindowPa uint16
var FaEnable bool
var WindowFa uint16
var Debug bool
var Daemon bool
var Forever bool
func InitParams() (string, bool, uint16, bool, uint16, bool, uint16, bool, uint16, bool, bool, bool) {
flag.StringVar(&Port, "p", "443", "The port of geneva, multi ports should be like \"80,443\"")
flag.BoolVar(&SaEnable, "sa", true, "Enable to handle the packet when TCP flag SYN and ACK are 1.")
var wsa int
flag.IntVar(&wsa, "wsa", 4, "The window size of packet when TCP flag SYN and ACK are 1, available only when param -sa is true")
flag.BoolVar(&AEnable, "a", true, "Enable to handle the packet when TCP flag ACK is 1")
var wa int
flag.IntVar(&wa, "wa", 4, "The window size of packet when TCP flag ACK is 1, available only when param -a is true")
flag.BoolVar(&PaEnable, "pa", true, "Enable to handle the packet when TCP flag PSH and ACK are 1.")
var wpa int
flag.IntVar(&wpa, "wpa", 4, "The window size of packet when TCP flag PSH and ACK are 1, available only when param -pa is true")
flag.BoolVar(&FaEnable, "fa", true, "Enable to handle the packet when TCP flag FIN and ACK are 1.")
var wfa int
flag.IntVar(&wfa, "wfa", 4, "The window size of packet when TCP flag FIN and ACK are 1, available only when param -fa is true")
flag.BoolVar(&Debug, "debug", false, "Debug mode.")
flag.BoolVar(&Daemon, "daemon", false, "Run in daemon")
flag.BoolVar(&Forever, "forever", false, "Run forever")
flag.Parse()
WindowSa = uint16(wsa)
WindowA = uint16(wa)
WindowPa = uint16(wpa)
WindowFa = uint16(wfa)
return Port, SaEnable, WindowSa, AEnable, WindowA, PaEnable, WindowPa, FaEnable, WindowFa, Debug, Daemon, Forever
}

11
README.md Normal file
View File

@ -0,0 +1,11 @@
- 本小程序主要用来实现Geneva的以下四条规则还可以自定义端口、需要修改的window size的值。
```
"[TCP:flags:SA]-tamper{TCP:window:replace:0}-|"
"[TCP:flags:A]-tamper{TCP:window:replace:0}-|"
"[TCP:flags:PA]-tamper{TCP:window:replace:0}-|"
"[TCP:flags:FA]-tamper{TCP:window:replace:0}-|"
```
- 默认四条规则全部开启,具体使用方法参考`./lagran -h`。
- 例如开启第一条规则并设置window为2同时关闭2、3、4条规则
```./lagran -debug -p 80 -sa=true -wsa 2 -a=false -pa=false -fa=false```
- 注意本小程序依赖libpcap-dev、libnetfilter-queue-dev、iptables等使用之前请先安装。

42
common.go Normal file
View File

@ -0,0 +1,42 @@
package main
import (
"fmt"
"log"
"os"
"os/exec"
"strconv"
)
func getProcessOwner() string {
stdout, err := exec.Command("ps", "-o", "user=", "-p", strconv.Itoa(os.Getpid())).Output()
if err != nil {
log.Fatalln(err)
}
return string(stdout)
}
func StripSlice(slice []string, element string) []string {
for i := 0; i < len(slice); {
if slice[i] == element && i != len(slice)-1 {
slice = append(slice[:i], slice[i+1:]...)
} else if slice[i] == element && i == len(slice)-1 {
slice = slice[:i]
} else {
i++
}
}
return slice
}
func SubProcess(args []string) *exec.Cmd {
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
fmt.Fprintf(os.Stderr, "[-] Error: %s\n", err)
}
return cmd
}

21
go.mod Normal file
View File

@ -0,0 +1,21 @@
module lagran
go 1.18
require (
github.com/coreos/go-iptables v0.6.0
github.com/florianl/go-nfqueue v1.3.1
github.com/google/gopacket v1.1.19
github.com/panjf2000/ants/v2 v2.5.0
)
require (
github.com/google/go-cmp v0.5.7 // indirect
github.com/josharian/native v1.0.0 // indirect
github.com/mdlayher/netlink v1.6.0 // indirect
github.com/mdlayher/socket v0.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect
)

55
go.sum Normal file
View File

@ -0,0 +1,55 @@
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/florianl/go-nfqueue v1.3.1 h1:khQ9fYCrjbu5CF8dZF55G2RTIEIQRI0Aj5k3msJR6Gw=
github.com/florianl/go-nfqueue v1.3.1/go.mod h1:aHWbgkhryJxF5XxYvJ3oRZpdD4JP74Zu/hP1zuhja+M=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
github.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI=
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/panjf2000/ants/v2 v2.5.0 h1:1rWGWSnxCsQBga+nQbA4/iY6VMeNoOIAM0ZWh9u3q2Q=
github.com/panjf2000/ants/v2 v2.5.0/go.mod h1:cU93usDlihJZ5CfRGNDYsiBYvoilLvBF5Qp/BT2GNRE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 h1:XDXtA5hveEEV8JB2l7nhMTp3t3cHp9ZpwcdjqyEWLlo=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=

30
iptables.go Normal file
View File

@ -0,0 +1,30 @@
package main
import (
"fmt"
"github.com/coreos/go-iptables/iptables"
"os"
)
func SetIptable(sport string) {
ipt, err := iptables.New()
if err != nil {
fmt.Printf("Iptabels new error:%v", err)
os.Exit(1)
}
_ = ipt.AppendUnique("filter", "OUTPUT", "-p", "tcp", "-m", "multiport", "--sport", sport, "--tcp-flags", "SYN,RST,ACK,FIN,PSH", "SYN,ACK", "-j", "NFQUEUE", "--queue-balance", "1000:1127")
_ = ipt.AppendUnique("filter", "OUTPUT", "-p", "tcp", "-m", "multiport", "--sport", sport, "--tcp-flags", "SYN,RST,ACK,FIN,PSH", "ACK", "-j", "NFQUEUE", "--queue-balance", "2000:2127")
_ = ipt.AppendUnique("filter", "OUTPUT", "-p", "tcp", "-m", "multiport", "--sport", sport, "--tcp-flags", "SYN,RST,ACK,FIN,PSH", "PSH,ACK", "-j", "NFQUEUE", "--queue-balance", "3000:3127")
_ = ipt.AppendUnique("filter", "OUTPUT", "-p", "tcp", "-m", "multiport", "--sport", sport, "--tcp-flags", "SYN,RST,ACK,FIN,PSH", "FIN,ACK", "-j", "NFQUEUE", "--queue-balance", "4000:4127")
}
func UnsetIptable(sport string) {
ipt, err := iptables.New()
if err != nil {
fmt.Printf("Iptabels new error:%v", err)
os.Exit(1)
}
_ = ipt.Delete("filter", "OUTPUT", "-p", "tcp", "-m", "multiport", "--sport", sport, "--tcp-flags", "SYN,RST,ACK,FIN,PSH", "SYN,ACK", "-j", "NFQUEUE", "--queue-balance", "1000:1127")
_ = ipt.Delete("filter", "OUTPUT", "-p", "tcp", "-m", "multiport", "--sport", sport, "--tcp-flags", "SYN,RST,ACK,FIN,PSH", "ACK", "-j", "NFQUEUE", "--queue-balance", "2000:2127")
_ = ipt.Delete("filter", "OUTPUT", "-p", "tcp", "-m", "multiport", "--sport", sport, "--tcp-flags", "SYN,RST,ACK,FIN,PSH", "PSH,ACK", "-j", "NFQUEUE", "--queue-balance", "3000:3127")
_ = ipt.Delete("filter", "OUTPUT", "-p", "tcp", "-m", "multiport", "--sport", sport, "--tcp-flags", "SYN,RST,ACK,FIN,PSH", "FIN,ACK", "-j", "NFQUEUE", "--queue-balance", "4000:4127")
}

89
main.go Normal file
View File

@ -0,0 +1,89 @@
package main
import (
"fmt"
"github.com/panjf2000/ants/v2"
"io/ioutil"
"log"
"os"
"sync"
)
const (
DAEMON = "daemon"
FOREVER = "forever"
)
func init() {
if getProcessOwner() != "root\n" {
log.Fatalln("Please run this program with root.")
}
}
func main() {
InitParams()
if !Debug {
log.SetOutput(ioutil.Discard)
}
if Daemon {
SubProcess(StripSlice(os.Args, "-"+DAEMON))
fmt.Printf("[*] Daemon running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
os.Exit(0)
} else if Forever {
for {
cmd := SubProcess(StripSlice(os.Args, "-"+FOREVER))
fmt.Printf("[*] Forever running in PID: %d PPID: %d\n", os.Getpid(), os.Getppid())
cmd.Wait()
}
os.Exit(0)
} else {
UnsetIptable(Port)
SetIptable(Port)
var wg sync.WaitGroup
p1, _ := ants.NewPoolWithFunc(512, func(i interface{}) {
packetHandle(i.(int))
wg.Done()
})
defer p1.Release()
// Submit tasks one by one.
log.Println("Starting Task p1")
for i := 1000; i < 1512; i++ {
wg.Add(1)
_ = p1.Invoke(int(i))
}
p2, _ := ants.NewPoolWithFunc(1512, func(i interface{}) {
packetHandle(i.(int))
wg.Done()
})
defer p2.Release()
// Submit tasks one by one.
log.Println("Starting Task p2")
for i := 2000; i < 2512; i++ {
wg.Add(1)
_ = p2.Invoke(int(i))
}
p3, _ := ants.NewPoolWithFunc(512, func(i interface{}) {
packetHandle(i.(int))
wg.Done()
})
defer p3.Release()
// Submit tasks one by one.
log.Println("Starting Task p3")
for i := 3000; i < 3512; i++ {
wg.Add(1)
_ = p3.Invoke(int(i))
}
p4, _ := ants.NewPoolWithFunc(512, func(i interface{}) {
packetHandle(i.(int))
wg.Done()
})
defer p4.Release()
// Submit tasks one by one.
log.Println("Starting Task p4")
for i := 4000; i < 4512; i++ {
wg.Add(1)
_ = p4.Invoke(int(i))
}
wg.Wait()
}
}

140
packet.go Normal file
View File

@ -0,0 +1,140 @@
package main
import (
"context"
"fmt"
"github.com/florianl/go-nfqueue"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"log"
"net"
"regexp"
"strings"
"time"
)
func packetHandle(queueNum int) {
config := nfqueue.Config{
NfQueue: uint16(queueNum),
MaxPacketLen: 0xFFFF,
MaxQueueLen: 0xFF,
Copymode: nfqueue.NfQnlCopyPacket,
WriteTimeout: 200 * time.Millisecond,
}
nf, err := nfqueue.Open(&config)
if err != nil {
fmt.Println("could not open nfqueue socket:", err)
return
}
defer nf.Close()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
fn := func(a nfqueue.Attribute) int {
id := *a.PacketID
var srcIP net.IP
var dstIP net.IP
log.Printf("PKT[%03d]\t\n", id)
packet := gopacket.NewPacket(*a.Payload, layers.LayerTypeIPv4, gopacket.Default)
// Get the IP layer from this packet
if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil {
// Get actual IP data from this layer
ip, _ := ipLayer.(*layers.IPv4)
srcIP = ip.SrcIP
dstIP = ip.DstIP
log.Printf("%15s > %-15s \n", srcIP, dstIP)
}
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
tcp, _ := tcpLayer.(*layers.TCP)
ports := strings.Split(Port, ",")
reg := regexp.MustCompile(`\d+`)
sport := reg.FindString(tcp.SrcPort.String())
var matchedPort bool = false
for _, port := range ports {
if port == sport {
matchedPort = true
break
}
}
if matchedPort {
var ok1 bool = SaEnable && tcp.SYN && tcp.ACK
var ok2 bool = AEnable && tcp.ACK && !tcp.PSH && !tcp.FIN && !tcp.SYN && !tcp.RST
var ok3 bool = PaEnable && tcp.PSH && tcp.ACK
var ok4 bool = FaEnable && tcp.FIN && tcp.ACK
var windowSize uint16
if ok1 || ok2 || ok3 || ok4 {
if ok1 {
windowSize = WindowSa
log.Println("Handle SYN=1 and ACK=1")
}
if ok2 {
windowSize = WindowA
log.Println("Handle ACK=1")
}
if ok3 {
windowSize = WindowPa
log.Println("Handle PSH=1 and ACK=1")
}
if ok4 {
windowSize = WindowFa
log.Println("Handle FIN=1 and ACK=1")
}
packet.TransportLayer().(*layers.TCP).Window = windowSize
err := packet.TransportLayer().(*layers.TCP).SetNetworkLayerForChecksum(packet.NetworkLayer())
if err != nil {
log.Fatalf("SetNetworkLayerForChecksum error: %v", err)
}
buffer := gopacket.NewSerializeBuffer()
options := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
if err := gopacket.SerializePacket(buffer, options, packet); err != nil {
log.Fatalf("SerializePacket error: %v", err)
}
packetBytes := buffer.Bytes()
log.Printf("Set TCP window size to %d", windowSize)
err = nf.SetVerdictModPacket(id, nfqueue.NfAccept, packetBytes)
if err != nil {
log.Fatalf("SetVerdictModified error: %v", err)
}
return 0
}
err := nf.SetVerdict(id, nfqueue.NfAccept)
if err != nil {
log.Fatalf("SetVerdictModified error: %v", err)
}
return 0
}
err := nf.SetVerdict(id, nfqueue.NfAccept)
if err != nil {
log.Fatalf("SetVerdictModified error: %v", err)
}
return 0
}
err := nf.SetVerdict(id, nfqueue.NfAccept)
if err != nil {
log.Fatalf("SetVerdictModified error: %v", err)
}
return 0
}
// Register your function to listen on nflqueue queue 100
err = nf.RegisterWithErrorFunc(ctx, fn, func(e error) int {
if e != nil {
log.Fatalln("RegisterWithErrorFunc Error:", e)
}
return 0
})
if err != nil {
fmt.Println(err)
return
}
<-ctx.Done()
}