├── divert ├── divert.go ├── windivert_hash.c ├── windivert_device.h ├── windivert.h └── windivert.c ├── WinDivert ├── arm64 │ └── WinDivert.dll ├── x64 │ └── WinDivert.dll └── x86 │ └── WinDivert.dll ├── header ├── header_doc.go └── checksum.go ├── go.mod ├── memmod ├── memmod_doc.go ├── memmod_windows_386.go ├── memmod_windows_amd64.go ├── memmod_windows_arm.go ├── memmod_windows_arm64.go ├── memmod_windows_32.go ├── memmod_windows_64.go ├── syscall_windows_64.go ├── syscall_windows_32.go ├── syscall_windows.go └── memmod_windows.go ├── go.sum ├── divert_embedded_go.go ├── example └── version-info │ └── main.go ├── README.md ├── .gitignore ├── divert_embedded_go_386.go ├── divert_embedded_go_amd64.go ├── divert_embedded_go_arm64.go ├── divert_embedded_rsrc.go ├── .github └── workflows │ └── go.yml ├── divert_embedded_64.go ├── divert_dll_64.go ├── divert_embedded_32.go ├── divert_dll_32.go ├── const_other.go ├── divert_dll.go ├── const.go ├── device.go ├── divert_cgo.go ├── const_cgo.go ├── divert_embedded.go ├── addr.go ├── error.go └── divert.go /divert/divert.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | -------------------------------------------------------------------------------- /WinDivert/arm64/WinDivert.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgk/divert-go/HEAD/WinDivert/arm64/WinDivert.dll -------------------------------------------------------------------------------- /WinDivert/x64/WinDivert.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgk/divert-go/HEAD/WinDivert/x64/WinDivert.dll -------------------------------------------------------------------------------- /WinDivert/x86/WinDivert.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imgk/divert-go/HEAD/WinDivert/x86/WinDivert.dll -------------------------------------------------------------------------------- /header/header_doc.go: -------------------------------------------------------------------------------- 1 | // 2022-02-04 2 | // https://github.com/google/gvisor/tree/go/pkg/tcpip/header 3 | package header 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/imgk/divert-go 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.1 6 | 7 | require golang.org/x/sys v0.31.0 8 | -------------------------------------------------------------------------------- /memmod/memmod_doc.go: -------------------------------------------------------------------------------- 1 | // 2022-02-04 2 | // https://github.com/WireGuard/wireguard-windows/tree/master/driver/memmod 3 | package memmod 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 2 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 3 | -------------------------------------------------------------------------------- /divert_embedded_go.go: -------------------------------------------------------------------------------- 1 | //go:build windows && divert_embed && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | import ( 6 | "embed" 7 | ) 8 | 9 | //go:embed WinDivert 10 | var f embed.FS 11 | -------------------------------------------------------------------------------- /memmod/memmod_windows_386.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package memmod 7 | 8 | const imageFileProcess = IMAGE_FILE_MACHINE_I386 9 | -------------------------------------------------------------------------------- /memmod/memmod_windows_amd64.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package memmod 7 | 8 | const imageFileProcess = IMAGE_FILE_MACHINE_AMD64 9 | -------------------------------------------------------------------------------- /memmod/memmod_windows_arm.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package memmod 7 | 8 | const imageFileProcess = IMAGE_FILE_MACHINE_ARMNT 9 | -------------------------------------------------------------------------------- /memmod/memmod_windows_arm64.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package memmod 7 | 8 | const imageFileProcess = IMAGE_FILE_MACHINE_ARM64 9 | -------------------------------------------------------------------------------- /example/version-info/main.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (amd64 || 386 || arm64) 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/imgk/divert-go" 9 | ) 10 | 11 | func main() { 12 | ver, err := divert.GetVersionInfo() 13 | if err != nil { 14 | panic(err) 15 | } 16 | fmt.Println(ver) 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go bindings of [WinDivert](https://github.com/basil00/Divert) 2 | 3 | ## Feature 4 | 5 | + Support WinDivert 2.x 6 | + Optional CGO support to remove dependence of WinDivert.dll, use `-tags="divert_cgo"` 7 | + Support loading dll from rsrc data, use `-tags="divert_rsrc"` 8 | 9 | More details about WinDivert please refer https://www.reqrypt.org/windivert-doc.html. 10 | -------------------------------------------------------------------------------- /memmod/memmod_windows_32.go: -------------------------------------------------------------------------------- 1 | //go:build (windows && 386) || (windows && arm) 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package memmod 9 | 10 | func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr { 11 | return 0 12 | } 13 | 14 | func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) { 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | 6 | # Icon must end with two \r 7 | # Icon 8 | 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | .com.apple.timemachine.donotpresent 21 | 22 | # Directories potentially created on remote AFP share 23 | .AppleDB 24 | .AppleDesktop 25 | Network Trash Folder 26 | Temporary Items 27 | .apdisk -------------------------------------------------------------------------------- /divert_embedded_go_386.go: -------------------------------------------------------------------------------- 1 | //go:build windows && divert_embed && 386 2 | 3 | package divert 4 | 5 | import ( 6 | "fmt" 7 | "sync/atomic" 8 | "unsafe" 9 | 10 | "golang.org/x/sys/windows" 11 | 12 | "github.com/imgk/divert-go/memmod" 13 | ) 14 | 15 | func (d *lazyDLL) Load() error { 16 | if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil { 17 | return nil 18 | } 19 | d.mu.Lock() 20 | defer d.mu.Unlock() 21 | if d.module != nil { 22 | return nil 23 | } 24 | 25 | data, err := f.ReadFile("WinDivert/x86/WinDivert.dll") 26 | if err != nil { 27 | return fmt.Errorf("load dll error: %w", err) 28 | } 29 | module, err := memmod.LoadLibrary(data) 30 | if err != nil { 31 | return fmt.Errorf("Unable to load library: %w", err) 32 | } 33 | d.Base = windows.Handle(module.BaseAddr()) 34 | 35 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module)) 36 | if d.onLoad != nil { 37 | d.onLoad(d) 38 | } 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /divert_embedded_go_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && divert_embed && amd64 2 | 3 | package divert 4 | 5 | import ( 6 | "fmt" 7 | "sync/atomic" 8 | "unsafe" 9 | 10 | "golang.org/x/sys/windows" 11 | 12 | "github.com/imgk/divert-go/memmod" 13 | ) 14 | 15 | func (d *lazyDLL) Load() error { 16 | if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil { 17 | return nil 18 | } 19 | d.mu.Lock() 20 | defer d.mu.Unlock() 21 | if d.module != nil { 22 | return nil 23 | } 24 | 25 | data, err := f.ReadFile("WinDivert/x64/WinDivert.dll") 26 | if err != nil { 27 | return fmt.Errorf("load dll error: %w", err) 28 | } 29 | module, err := memmod.LoadLibrary(data) 30 | if err != nil { 31 | return fmt.Errorf("Unable to load library: %w", err) 32 | } 33 | d.Base = windows.Handle(module.BaseAddr()) 34 | 35 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module)) 36 | if d.onLoad != nil { 37 | d.onLoad(d) 38 | } 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /divert_embedded_go_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && divert_embed && arm64 2 | 3 | package divert 4 | 5 | import ( 6 | "fmt" 7 | "sync/atomic" 8 | "unsafe" 9 | 10 | "golang.org/x/sys/windows" 11 | 12 | "github.com/imgk/divert-go/memmod" 13 | ) 14 | 15 | func (d *lazyDLL) Load() error { 16 | if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil { 17 | return nil 18 | } 19 | d.mu.Lock() 20 | defer d.mu.Unlock() 21 | if d.module != nil { 22 | return nil 23 | } 24 | 25 | data, err := f.ReadFile("WinDivert/arm64/WinDivert.dll") 26 | if err != nil { 27 | return fmt.Errorf("load dll error: %w", err) 28 | } 29 | module, err := memmod.LoadLibrary(data) 30 | if err != nil { 31 | return fmt.Errorf("Unable to load library: %w", err) 32 | } 33 | d.Base = windows.Handle(module.BaseAddr()) 34 | 35 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module)) 36 | if d.onLoad != nil { 37 | d.onLoad(d) 38 | } 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /memmod/memmod_windows_64.go: -------------------------------------------------------------------------------- 1 | //go:build (windows && amd64) || (windows && arm64) 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package memmod 9 | 10 | import ( 11 | "fmt" 12 | 13 | "golang.org/x/sys/windows" 14 | ) 15 | 16 | func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr { 17 | return uintptr(opthdr.ImageBase & 0xffffffff00000000) 18 | } 19 | 20 | func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) { 21 | for (module.codeBase >> 32) < ((module.codeBase + alignedImageSize) >> 32) { 22 | node := &addressList{ 23 | next: module.blockedMemory, 24 | address: module.codeBase, 25 | } 26 | module.blockedMemory = node 27 | module.codeBase, err = windows.VirtualAlloc(0, 28 | alignedImageSize, 29 | windows.MEM_RESERVE|windows.MEM_COMMIT, 30 | windows.PAGE_READWRITE) 31 | if err != nil { 32 | return fmt.Errorf("Error allocating memory block: %w", err) 33 | } 34 | } 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /divert_embedded_rsrc.go: -------------------------------------------------------------------------------- 1 | //go:build windows && divert_rsrc && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | import ( 6 | "fmt" 7 | "sync/atomic" 8 | "unsafe" 9 | 10 | "golang.org/x/sys/windows" 11 | 12 | "github.com/imgk/divert-go/memmod" 13 | ) 14 | 15 | func (d *lazyDLL) Load() error { 16 | if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil { 17 | return nil 18 | } 19 | d.mu.Lock() 20 | defer d.mu.Unlock() 21 | if d.module != nil { 22 | return nil 23 | } 24 | 25 | const ourModule windows.Handle = 0 26 | resInfo, err := windows.FindResource(ourModule, d.Name, windows.RT_RCDATA) 27 | if err != nil { 28 | return fmt.Errorf("Unable to find \"%v\" RCDATA resource: %w", d.Name, err) 29 | } 30 | data, err := windows.LoadResourceData(ourModule, resInfo) 31 | if err != nil { 32 | return fmt.Errorf("Unable to load resource: %w", err) 33 | } 34 | module, err := memmod.LoadLibrary(data) 35 | if err != nil { 36 | return fmt.Errorf("Unable to load library: %w", err) 37 | } 38 | d.Base = windows.Handle(module.BaseAddr()) 39 | 40 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module)) 41 | if d.onLoad != nil { 42 | d.onLoad(d) 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ dev ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: ^1.18beta2 20 | id: go 21 | 22 | - name: Check out code into the Go module directory 23 | uses: actions/checkout@v2 24 | 25 | - name: Get dependencies 26 | run: | 27 | go get -v -t -d ./... 28 | if [ -f Gopkg.toml ]; then 29 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 30 | dep ensure 31 | fi 32 | - name: Build Windows-amd64-dll 33 | run: env CGO_ENABLED=0 GOARCH=amd64 GOOS=windows go build -v -trimpath -ldflags="-s -w" -tags="" ./... 34 | 35 | - name: Build Windows-386-dll 36 | run: env CGO_ENABLED=0 GOARCH=386 GOOS=windows go build -v -trimpath -ldflags="-s -w" -tags="" ./... 37 | 38 | - name: Build Windows-amd64-embed 39 | run: env CGO_ENABLED=0 GOARCH=amd64 GOOS=windows go build -v -trimpath -ldflags="-s -w" -tags="divert_embedded" ./... 40 | 41 | - name: Build Windows-386-embed 42 | run: env CGO_ENABLED=0 GOARCH=386 GOOS=windows go build -v -trimpath -ldflags="-s -w" -tags="divert_embedded" ./... 43 | 44 | - name: Test 45 | run: go test -v ./... 46 | -------------------------------------------------------------------------------- /divert_embedded_64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (divert_rsrc || divert_embed) && (amd64 || arm64) 2 | 3 | package divert 4 | 5 | import ( 6 | "runtime" 7 | "sync" 8 | "unsafe" 9 | 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | func open(filter string, layer Layer, priority int16, flags uint64) (h *Handle, err error) { 14 | if priority < PriorityLowest || priority > PriorityHighest { 15 | return nil, errPriority 16 | } 17 | 18 | filterPtr, err := windows.BytePtrFromString(filter) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | runtime.LockOSThread() 24 | hd, _, err := winDivertOpen.Call(uintptr(unsafe.Pointer(filterPtr)), uintptr(layer), uintptr(priority), uintptr(flags)) 25 | runtime.UnlockOSThread() 26 | 27 | if windows.Handle(hd) == windows.InvalidHandle { 28 | return nil, Error(err.(windows.Errno)) 29 | } 30 | 31 | rEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 32 | wEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 33 | 34 | return &Handle{ 35 | Mutex: sync.Mutex{}, 36 | Handle: windows.Handle(hd), 37 | rOverlapped: windows.Overlapped{ 38 | HEvent: rEvent, 39 | }, 40 | wOverlapped: windows.Overlapped{ 41 | HEvent: wEvent, 42 | }, 43 | }, nil 44 | } 45 | 46 | // CalcChecksums is ... 47 | func CalcChecksums(buffer []byte, address *Address, flags uint64) bool { 48 | re, _, err := winDivertCalcChecksums.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(address)), uintptr(flags)) 49 | if err != nil { 50 | return false 51 | } 52 | return re != 0 53 | } 54 | -------------------------------------------------------------------------------- /divert_dll_64.go: -------------------------------------------------------------------------------- 1 | //go:build windows && !divert_cgo && !divert_rsrc && !divert_embed && (amd64 || arm64) 2 | 3 | package divert 4 | 5 | import ( 6 | "runtime" 7 | "sync" 8 | "unsafe" 9 | 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | func open(filter string, layer Layer, priority int16, flags uint64) (h *Handle, err error) { 14 | if priority < PriorityLowest || priority > PriorityHighest { 15 | return nil, errPriority 16 | } 17 | 18 | filterPtr, err := windows.BytePtrFromString(filter) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | runtime.LockOSThread() 24 | hd, _, err := winDivertOpen.Call(uintptr(unsafe.Pointer(filterPtr)), uintptr(layer), uintptr(priority), uintptr(flags)) 25 | runtime.UnlockOSThread() 26 | 27 | if windows.Handle(hd) == windows.InvalidHandle { 28 | return nil, Error(err.(windows.Errno)) 29 | } 30 | 31 | rEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 32 | wEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 33 | 34 | return &Handle{ 35 | Mutex: sync.Mutex{}, 36 | Handle: windows.Handle(hd), 37 | rOverlapped: windows.Overlapped{ 38 | HEvent: rEvent, 39 | }, 40 | wOverlapped: windows.Overlapped{ 41 | HEvent: wEvent, 42 | }, 43 | }, nil 44 | } 45 | 46 | // CalcChecksums is ... 47 | func CalcChecksums(buffer []byte, address *Address, flags uint64) bool { 48 | re, _, err := winDivertCalcChecksums.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(address)), uintptr(flags)) 49 | if err != nil { 50 | return false 51 | } 52 | return re != 0 53 | } 54 | -------------------------------------------------------------------------------- /divert_embedded_32.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (divert_rsrc || divert_embed) && 386 2 | 3 | package divert 4 | 5 | import ( 6 | "runtime" 7 | "sync" 8 | "unsafe" 9 | 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | func open(filter string, layer Layer, priority int16, flags uint64) (h *Handle, err error) { 14 | if priority < PriorityLowest || priority > PriorityHighest { 15 | return nil, errPriority 16 | } 17 | 18 | filterPtr, err := windows.BytePtrFromString(filter) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | runtime.LockOSThread() 24 | // 386: Shadow panics on Windows 32-bit system, as `flags` is `uint64` while `uintptr` is `uint32` for 32-bit system. 25 | hd, _, err := winDivertOpen.Call(uintptr(unsafe.Pointer(filterPtr)), uintptr(layer), uintptr(priority), uintptr(flags), 0) 26 | runtime.UnlockOSThread() 27 | 28 | if windows.Handle(hd) == windows.InvalidHandle { 29 | return nil, Error(err.(windows.Errno)) 30 | } 31 | 32 | rEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 33 | wEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 34 | 35 | return &Handle{ 36 | Mutex: sync.Mutex{}, 37 | Handle: windows.Handle(hd), 38 | rOverlapped: windows.Overlapped{ 39 | HEvent: rEvent, 40 | }, 41 | wOverlapped: windows.Overlapped{ 42 | HEvent: wEvent, 43 | }, 44 | }, nil 45 | } 46 | 47 | // CalcChecksums is ... 48 | func CalcChecksums(buffer []byte, address *Address, flags uint64) bool { 49 | re, _, err := winDivertCalcChecksums.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(address)), uintptr(flags), 0) 50 | if err != nil { 51 | return false 52 | } 53 | return re != 0 54 | } 55 | -------------------------------------------------------------------------------- /divert_dll_32.go: -------------------------------------------------------------------------------- 1 | //go:build windows && !divert_cgo && !divert_rsrc && !divert_embed && 386 2 | 3 | package divert 4 | 5 | import ( 6 | "runtime" 7 | "sync" 8 | "unsafe" 9 | 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | func open(filter string, layer Layer, priority int16, flags uint64) (h *Handle, err error) { 14 | if priority < PriorityLowest || priority > PriorityHighest { 15 | return nil, errPriority 16 | } 17 | 18 | filterPtr, err := windows.BytePtrFromString(filter) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | runtime.LockOSThread() 24 | // 386: Shadow panics on Windows 32-bit system, as `flags` is `uint64` while `uintptr` is `uint32` for 32-bit system. 25 | hd, _, err := winDivertOpen.Call(uintptr(unsafe.Pointer(filterPtr)), uintptr(layer), uintptr(priority), uintptr(flags), 0) 26 | runtime.UnlockOSThread() 27 | 28 | if windows.Handle(hd) == windows.InvalidHandle { 29 | return nil, Error(err.(windows.Errno)) 30 | } 31 | 32 | rEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 33 | wEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 34 | 35 | return &Handle{ 36 | Mutex: sync.Mutex{}, 37 | Handle: windows.Handle(hd), 38 | rOverlapped: windows.Overlapped{ 39 | HEvent: rEvent, 40 | }, 41 | wOverlapped: windows.Overlapped{ 42 | HEvent: wEvent, 43 | }, 44 | }, nil 45 | } 46 | 47 | // CalcChecksums is ... 48 | func CalcChecksums(buffer []byte, address *Address, flags uint64) bool { 49 | re, _, err := winDivertCalcChecksums.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(address)), uintptr(flags), 0) 50 | if err != nil { 51 | return false 52 | } 53 | return re != 0 54 | } 55 | -------------------------------------------------------------------------------- /const_other.go: -------------------------------------------------------------------------------- 1 | //go:build windows && !divert_cgo && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | const ( 6 | LayerNetwork Layer = 0 7 | LayerNetworkForward Layer = 1 8 | LayerFlow Layer = 2 9 | LayerSocket Layer = 3 10 | LayerReflect Layer = 4 11 | //LayerEthernet Layer = 5 12 | ) 13 | 14 | const ( 15 | EventNetworkPacket Event = 0 16 | EventFlowEstablished Event = 1 17 | EventFlowDeleted Event = 2 18 | EventSocketBind Event = 3 19 | EventSocketConnect Event = 4 20 | EventSocketListen Event = 5 21 | EventSocketAccept Event = 6 22 | EventSocketClose Event = 7 23 | EventReflectOpen Event = 8 24 | EventReflectClose Event = 9 25 | //EventEthernetFrame Event = 10 26 | ) 27 | 28 | const ( 29 | ShutdownRecv Shutdown = 0 30 | ShutdownSend Shutdown = 1 31 | ShutdownBoth Shutdown = 2 32 | ) 33 | 34 | const ( 35 | QueueLength Param = 0 36 | QueueTime Param = 1 37 | QueueSize Param = 2 38 | VersionMajor Param = 3 39 | VersionMinor Param = 4 40 | ) 41 | 42 | const ( 43 | FlagDefault = 0x0000 44 | FlagSniff = 0x0001 45 | FlagDrop = 0x0002 46 | FlagRecvOnly = 0x0004 47 | FlagSendOnly = 0x0008 48 | FlagNoInstall = 0x0010 49 | FlagFragments = 0x0020 50 | ) 51 | 52 | const ( 53 | PriorityDefault = 0 54 | PriorityHighest = 3000 55 | PriorityLowest = -3000 56 | QueueLengthDefault = 4096 57 | QueueLengthMin = 32 58 | QueueLengthMax = 16384 59 | QueueTimeDefault = 2000 60 | QueueTimeMin = 100 61 | QueueTimeMax = 16000 62 | QueueSizeDefault = 4194304 63 | QueueSizeMin = 65535 64 | QueueSizeMax = 33554432 65 | ) 66 | 67 | const ( 68 | ChecksumDefault = 0 69 | NoIPChecksum = 1 70 | NoICMPChecksum = 2 71 | NoICMPV6Checksum = 4 72 | NoTCPChecksum = 8 73 | NoUDPChecksum = 16 74 | ) 75 | 76 | const ( 77 | BatchMax = 0xff 78 | MTUMax = 40 + 0xffff 79 | ) 80 | -------------------------------------------------------------------------------- /divert_dll.go: -------------------------------------------------------------------------------- 1 | //go:build windows && !divert_cgo && !divert_rsrc && !divert_embed && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | import ( 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | var ( 14 | winDivert = (*windows.DLL)(nil) 15 | winDivertOpen = (*windows.Proc)(nil) 16 | winDivertCalcChecksums = (*windows.Proc)(nil) 17 | ) 18 | 19 | // Open is ... 20 | func Open(filter string, layer Layer, priority int16, flags uint64) (h *Handle, err error) { 21 | once.Do(func() { 22 | dll, er := windows.LoadDLL("WinDivert.dll") 23 | if er != nil { 24 | err = er 25 | return 26 | } 27 | winDivert = dll 28 | 29 | proc, er := winDivert.FindProc("WinDivertOpen") 30 | if er != nil { 31 | err = er 32 | return 33 | } 34 | winDivertOpen = proc 35 | 36 | proc, er = winDivert.FindProc("WinDivertHelperCalcChecksums") 37 | if er != nil { 38 | err = er 39 | return 40 | } 41 | winDivertCalcChecksums = proc 42 | 43 | vers := map[string]struct{}{ 44 | "2.0": {}, 45 | "2.1": {}, 46 | "2.2": {}, 47 | } 48 | ver, er := func() (ver string, err error) { 49 | h, err := open("false", LayerNetwork, PriorityDefault, FlagDefault) 50 | if err != nil { 51 | return 52 | } 53 | defer func() { 54 | err = h.Close() 55 | }() 56 | 57 | major, err := h.GetParam(VersionMajor) 58 | if err != nil { 59 | return 60 | } 61 | 62 | minor, err := h.GetParam(VersionMinor) 63 | if err != nil { 64 | return 65 | } 66 | 67 | ver = strings.Join([]string{strconv.Itoa(int(major)), strconv.Itoa(int(minor))}, ".") 68 | return 69 | }() 70 | if er != nil { 71 | err = er 72 | return 73 | } 74 | if _, ok := vers[ver]; !ok { 75 | err = fmt.Errorf("unsupported windivert version: %v", ver) 76 | } 77 | }) 78 | if err != nil { 79 | return 80 | } 81 | 82 | return open(filter, layer, priority, flags) 83 | } 84 | -------------------------------------------------------------------------------- /const.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | type Layer int 6 | 7 | func (l Layer) String() string { 8 | switch l { 9 | case LayerNetwork: 10 | return "WINDIVERT_LAYER_NETWORK" 11 | case LayerNetworkForward: 12 | return "WINDIVERT_LAYER_NETWORK_FORWARD" 13 | case LayerFlow: 14 | return "WINDIVERT_LAYER_FLOW" 15 | case LayerSocket: 16 | return "WINDIVERT_LAYER_SOCKET" 17 | case LayerReflect: 18 | return "WINDIVERT_LAYER_REFLECT" 19 | //case LayerEthernet: 20 | // return "WINDIVERT_LAYER_ETHERNET" 21 | default: 22 | return "" 23 | } 24 | } 25 | 26 | type Event int 27 | 28 | func (e Event) String() string { 29 | switch e { 30 | case EventNetworkPacket: 31 | return "WINDIVERT_EVENT_NETWORK_PACKET" 32 | case EventFlowEstablished: 33 | return "WINDIVERT_EVENT_FLOW_ESTABLISHED" 34 | case EventFlowDeleted: 35 | return "WINDIVERT_EVENT_FLOW_DELETED" 36 | case EventSocketBind: 37 | return "WINDIVERT_EVENT_SOCKET_BIND" 38 | case EventSocketConnect: 39 | return "WINDIVERT_EVENT_SOCKET_CONNECT" 40 | case EventSocketListen: 41 | return "WINDIVERT_EVENT_SOCKET_LISTEN" 42 | case EventSocketAccept: 43 | return "WINDIVERT_EVENT_SOCKET_ACCEPT" 44 | case EventSocketClose: 45 | return "WINDIVERT_EVENT_SOCKET_CLOSE" 46 | case EventReflectOpen: 47 | return "WINDIVERT_EVENT_REFLECT_OPEN" 48 | case EventReflectClose: 49 | return "WINDIVERT_EVENT_REFLECT_CLOSE" 50 | //case EventEthernetFrame: 51 | // return "WINDIVERT_EVENT_ETHERNET_FRAME" 52 | default: 53 | return "" 54 | } 55 | } 56 | 57 | type Shutdown int 58 | 59 | func (s Shutdown) String() string { 60 | switch s { 61 | case ShutdownRecv: 62 | return "WINDIVERT_SHUTDOWN_RECV" 63 | case ShutdownSend: 64 | return "WINDIVERT_SHUTDOWN_SEND" 65 | case ShutdownBoth: 66 | return "WINDIVERT_SHUTDOWN_BOTH" 67 | default: 68 | return "" 69 | } 70 | } 71 | 72 | type Param int 73 | 74 | func (p Param) String() string { 75 | switch p { 76 | case QueueLength: 77 | return "WINDIVERT_PARAM_QUEUE_LENGTH" 78 | case QueueTime: 79 | return "WINDIVERT_PARAM_QUEUE_TIME" 80 | case QueueSize: 81 | return "WINDIVERT_PARAM_QUEUE_SIZE" 82 | case VersionMajor: 83 | return "WINDIVERT_PARAM_VERSION_MAJOR" 84 | case VersionMinor: 85 | return "WINDIVERT_PARAM_VERSION_MINOR" 86 | default: 87 | return "" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /device.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | type CtlCode uint32 6 | 7 | const ( 8 | METHOD_IN_DIRECT = 1 9 | METHOD_OUT_DIRECT = 2 10 | ) 11 | 12 | const ( 13 | FILE_READ_DATA = 1 14 | FILE_WRITE_DATA = 2 15 | ) 16 | 17 | const FILE_DEVICE_NETWORK = 0x00000012 18 | 19 | const ( 20 | ioCtlInitialize = CtlCode(((FILE_DEVICE_NETWORK) << 16) | ((FILE_READ_DATA | FILE_WRITE_DATA) << 14) | ((0x921) << 2) | (METHOD_OUT_DIRECT)) 21 | ioCtlStartup = CtlCode(((FILE_DEVICE_NETWORK) << 16) | ((FILE_READ_DATA | FILE_WRITE_DATA) << 14) | ((0x922) << 2) | (METHOD_IN_DIRECT)) 22 | ioCtlRecv = CtlCode(((FILE_DEVICE_NETWORK) << 16) | ((FILE_READ_DATA) << 14) | ((0x923) << 2) | (METHOD_OUT_DIRECT)) 23 | ioCtlSend = CtlCode(((FILE_DEVICE_NETWORK) << 16) | ((FILE_READ_DATA | FILE_WRITE_DATA) << 14) | ((0x924) << 2) | (METHOD_IN_DIRECT)) 24 | ioCtlSetParam = CtlCode(((FILE_DEVICE_NETWORK) << 16) | ((FILE_READ_DATA | FILE_WRITE_DATA) << 14) | ((0x925) << 2) | (METHOD_IN_DIRECT)) 25 | ioCtlGetParam = CtlCode(((FILE_DEVICE_NETWORK) << 16) | ((FILE_READ_DATA) << 14) | ((0x926) << 2) | (METHOD_OUT_DIRECT)) 26 | ioCtlShutdown = CtlCode(((FILE_DEVICE_NETWORK) << 16) | ((FILE_READ_DATA | FILE_WRITE_DATA) << 14) | ((0x927) << 2) | (METHOD_IN_DIRECT)) 27 | ) 28 | 29 | func (c CtlCode) String() string { 30 | switch c { 31 | case ioCtlInitialize: 32 | return "IOCTL_WINDIVERT_INITIALIZE" 33 | case ioCtlStartup: 34 | return "IOCTL_WINDIVERT_STARTUP" 35 | case ioCtlRecv: 36 | return "IOCTL_WINDIVERT_RECV" 37 | case ioCtlSend: 38 | return "IOCTL_WINDIVERT_SEND" 39 | case ioCtlSetParam: 40 | return "IOCTL_WINDIVERT_SET_PARAM" 41 | case ioCtlGetParam: 42 | return "IOCTL_WINDIVERT_GET_PARAM" 43 | case ioCtlShutdown: 44 | return "IOCTL_WINDIVERT_SHUTDOWN" 45 | default: 46 | return "" 47 | } 48 | } 49 | 50 | type ioCtl struct { 51 | b1, b2, b3, b4 uint32 52 | } 53 | 54 | type recv struct { 55 | Addr uint64 56 | AddrLenPtr uint64 57 | } 58 | 59 | type send struct { 60 | Addr uint64 61 | AddrLen uint64 62 | } 63 | 64 | type initialize struct { 65 | Layer uint32 66 | Priority uint32 67 | Flags uint64 68 | } 69 | 70 | type startup struct { 71 | Flags uint64 72 | _ uint64 73 | } 74 | 75 | type shutdown struct { 76 | How uint32 77 | _ uint32 78 | _ uint64 79 | } 80 | 81 | type getParam struct { 82 | Param uint32 83 | _ uint32 84 | Value uint64 85 | } 86 | 87 | type setParam struct { 88 | Value uint64 89 | Param uint32 90 | _ uint32 91 | } 92 | -------------------------------------------------------------------------------- /divert_cgo.go: -------------------------------------------------------------------------------- 1 | //go:build windows && divert_cgo && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | // #cgo CFLAGS: -I${SRCDIR}/divert -Wno-implicit-function-declaration -Wno-int-conversion -Wno-incompatible-pointer-types -Wno-unused-value 6 | // #define WINDIVERTEXPORT static 7 | // #include "windivert.c" 8 | import "C" 9 | 10 | import ( 11 | "fmt" 12 | "runtime" 13 | "strconv" 14 | "strings" 15 | "sync" 16 | "unsafe" 17 | 18 | "golang.org/x/sys/windows" 19 | ) 20 | 21 | // Open is ... 22 | func Open(filter string, layer Layer, priority int16, flags uint64) (h *Handle, err error) { 23 | once.Do(func() { 24 | vers := map[string]struct{}{ 25 | "2.0": {}, 26 | "2.1": {}, 27 | "2.2": {}, 28 | } 29 | ver, er := func() (ver string, err error) { 30 | h, err := open("false", LayerNetwork, PriorityDefault, FlagDefault) 31 | if err != nil { 32 | return 33 | } 34 | defer func() { 35 | err = h.Close() 36 | }() 37 | 38 | major, err := h.GetParam(VersionMajor) 39 | if err != nil { 40 | return 41 | } 42 | 43 | minor, err := h.GetParam(VersionMinor) 44 | if err != nil { 45 | return 46 | } 47 | 48 | ver = strings.Join([]string{strconv.Itoa(int(major)), strconv.Itoa(int(minor))}, ".") 49 | return 50 | }() 51 | if er != nil { 52 | err = er 53 | return 54 | } 55 | if _, ok := vers[ver]; !ok { 56 | err = fmt.Errorf("unsupported windivert version: %v", ver) 57 | } 58 | }) 59 | if err != nil { 60 | return 61 | } 62 | 63 | return open(filter, layer, priority, flags) 64 | } 65 | 66 | func open(filter string, layer Layer, priority int16, flags uint64) (h *Handle, err error) { 67 | if priority < PriorityLowest || priority > PriorityHighest { 68 | return nil, errPriority 69 | } 70 | 71 | runtime.LockOSThread() 72 | hd := C.WinDivertOpen(C.CString(filter), C.WINDIVERT_LAYER(layer), C.int16_t(priority), C.uint64_t(flags)) 73 | runtime.UnlockOSThread() 74 | 75 | if hd == C.HANDLE(C.INVALID_HANDLE_VALUE) { 76 | return nil, Error(C.GetLastError()) 77 | } 78 | 79 | rEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 80 | wEvent, _ := windows.CreateEvent(nil, 0, 0, nil) 81 | 82 | return &Handle{ 83 | Mutex: sync.Mutex{}, 84 | Handle: windows.Handle(uintptr(unsafe.Pointer(hd))), 85 | rOverlapped: windows.Overlapped{ 86 | HEvent: rEvent, 87 | }, 88 | wOverlapped: windows.Overlapped{ 89 | HEvent: wEvent, 90 | }, 91 | }, nil 92 | } 93 | 94 | // CalcChecksums is ... 95 | func CalcChecksums(buffer []byte, address *Address, flags uint64) bool { 96 | re := C.WinDivertHelperCalcChecksums(unsafe.Pointer(&buffer[0]), C.UINT(len(buffer)), (*C.WINDIVERT_ADDRESS)(unsafe.Pointer(address)), C.uint64_t(flags)) 97 | return re == C.TRUE 98 | } 99 | -------------------------------------------------------------------------------- /const_cgo.go: -------------------------------------------------------------------------------- 1 | //go:build windows && divert_cgo && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | // #cgo CFLAGS: -I${SRCDIR}/divert -Wno-incompatible-pointer-types 6 | // #include "windivert.h" 7 | import "C" 8 | 9 | const ( 10 | LayerNetwork = Layer(C.WINDIVERT_LAYER_NETWORK) 11 | LayerNetworkForward = Layer(C.WINDIVERT_LAYER_NETWORK_FORWARD) 12 | LayerFlow = Layer(C.WINDIVERT_LAYER_FLOW) 13 | LayerSocket = Layer(C.WINDIVERT_LAYER_SOCKET) 14 | LayerReflect = Layer(C.WINDIVERT_LAYER_REFLECT) 15 | //LayerEthernet = Layer(C.WINDIVERT_LAYER_ETHERNET) 16 | ) 17 | 18 | const ( 19 | EventNetworkPacket = Event(C.WINDIVERT_EVENT_NETWORK_PACKET) 20 | EventFlowEstablished = Event(C.WINDIVERT_EVENT_FLOW_ESTABLISHED) 21 | EventFlowDeleted = Event(C.WINDIVERT_EVENT_FLOW_DELETED) 22 | EventSocketBind = Event(C.WINDIVERT_EVENT_SOCKET_BIND) 23 | EventSocketConnect = Event(C.WINDIVERT_EVENT_SOCKET_CONNECT) 24 | EventSocketListen = Event(C.WINDIVERT_EVENT_SOCKET_LISTEN) 25 | EventSocketAccept = Event(C.WINDIVERT_EVENT_SOCKET_ACCEPT) 26 | EventSocketClose = Event(C.WINDIVERT_EVENT_SOCKET_CLOSE) 27 | EventReflectOpen = Event(C.WINDIVERT_EVENT_REFLECT_OPEN) 28 | EventReflectClose = Event(C.WINDIVERT_EVENT_REFLECT_CLOSE) 29 | //EventEthernetFrame = Event(C.WINDIVERT_EVENT_ETHERNET_FRAME) 30 | ) 31 | 32 | const ( 33 | ShutdownRecv = Shutdown(C.WINDIVERT_SHUTDOWN_RECV) 34 | ShutdownSend = Shutdown(C.WINDIVERT_SHUTDOWN_SEND) 35 | ShutdownBoth = Shutdown(C.WINDIVERT_SHUTDOWN_BOTH) 36 | ) 37 | 38 | const ( 39 | QueueLength = Param(C.WINDIVERT_PARAM_QUEUE_LENGTH) 40 | QueueTime = Param(C.WINDIVERT_PARAM_QUEUE_TIME) 41 | QueueSize = Param(C.WINDIVERT_PARAM_QUEUE_SIZE) 42 | VersionMajor = Param(C.WINDIVERT_PARAM_VERSION_MAJOR) 43 | VersionMinor = Param(C.WINDIVERT_PARAM_VERSION_MINOR) 44 | ) 45 | 46 | const ( 47 | FlagDefault = uint64(0) 48 | FlagSniff = uint64(C.WINDIVERT_FLAG_SNIFF) 49 | FlagDrop = uint64(C.WINDIVERT_FLAG_DROP) 50 | FlagRecvOnly = uint64(C.WINDIVERT_FLAG_RECV_ONLY) 51 | FlagSendOnly = uint64(C.WINDIVERT_FLAG_SEND_ONLY) 52 | FlagNoInstall = uint64(C.WINDIVERT_FLAG_NO_INSTALL) 53 | FlagFragments = uint64(C.WINDIVERT_FLAG_FRAGMENTS) 54 | ) 55 | 56 | const ( 57 | PriorityDefault = int16(0) 58 | PriorityHighest = int16(C.WINDIVERT_PRIORITY_HIGHEST) 59 | PriorityLowest = int16(C.WINDIVERT_PRIORITY_LOWEST) 60 | QueueLengthDefault = uint64(C.WINDIVERT_PARAM_QUEUE_LENGTH_DEFAULT) 61 | QueueLengthMin = uint64(C.WINDIVERT_PARAM_QUEUE_LENGTH_MIN) 62 | QueueLengthMax = uint64(C.WINDIVERT_PARAM_QUEUE_LENGTH_MAX) 63 | QueueTimeDefault = uint64(C.WINDIVERT_PARAM_QUEUE_TIME_DEFAULT) 64 | QueueTimeMin = uint64(C.WINDIVERT_PARAM_QUEUE_TIME_MIN) 65 | QueueTimeMax = uint64(C.WINDIVERT_PARAM_QUEUE_TIME_MAX) 66 | QueueSizeDefault = uint64(C.WINDIVERT_PARAM_QUEUE_SIZE_DEFAULT) 67 | QueueSizeMin = uint64(C.WINDIVERT_PARAM_QUEUE_SIZE_MIN) 68 | QueueSizeMax = uint64(C.WINDIVERT_PARAM_QUEUE_SIZE_MAX) 69 | ) 70 | 71 | const ( 72 | ChecksumDefault = uint64(0) 73 | NoIPChecksum = uint64(C.WINDIVERT_HELPER_NO_IP_CHECKSUM) 74 | NoICMPChecksum = uint64(C.WINDIVERT_HELPER_NO_ICMP_CHECKSUM) 75 | NoICMPV6Checksum = uint64(C.WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM) 76 | NoTCPChecksum = uint64(C.WINDIVERT_HELPER_NO_TCP_CHECKSUM) 77 | NoUDPChecksum = uint64(C.WINDIVERT_HELPER_NO_UDP_CHECKSUM) 78 | ) 79 | 80 | const ( 81 | BatchMax = int(C.WINDIVERT_BATCH_MAX) 82 | MTUMax = int(C.WINDIVERT_MTU_MAX) 83 | ) 84 | -------------------------------------------------------------------------------- /divert_embedded.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (divert_rsrc || divert_embed) && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | import ( 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | "sync" 10 | "sync/atomic" 11 | "syscall" 12 | "unsafe" 13 | 14 | "golang.org/x/sys/windows" 15 | 16 | "github.com/imgk/divert-go/memmod" 17 | ) 18 | 19 | var ( 20 | winDivert = (*lazyDLL)(nil) 21 | winDivertOpen = (*lazyProc)(nil) 22 | winDivertCalcChecksums = (*lazyProc)(nil) 23 | ) 24 | 25 | // Open is ... 26 | func Open(filter string, layer Layer, priority int16, flags uint64) (h *Handle, err error) { 27 | once.Do(func() { 28 | dll := newLazyDLL("WinDivert.dll", nil) 29 | if er := dll.Load(); er != nil { 30 | err = er 31 | return 32 | } 33 | winDivert = dll 34 | 35 | proc := winDivert.NewProc("WinDivertOpen") 36 | if er := proc.Find(); er != nil { 37 | err = er 38 | return 39 | } 40 | winDivertOpen = proc 41 | 42 | proc = winDivert.NewProc("WinDivertHelperCalcChecksums") 43 | if er := proc.Find(); er != nil { 44 | err = er 45 | return 46 | } 47 | winDivertCalcChecksums = proc 48 | 49 | vers := map[string]struct{}{ 50 | "2.0": {}, 51 | "2.1": {}, 52 | "2.2": {}, 53 | } 54 | ver, er := func() (ver string, err error) { 55 | h, err := open("false", LayerNetwork, PriorityDefault, FlagDefault) 56 | if err != nil { 57 | return 58 | } 59 | defer func() { 60 | err = h.Close() 61 | }() 62 | 63 | major, err := h.GetParam(VersionMajor) 64 | if err != nil { 65 | return 66 | } 67 | 68 | minor, err := h.GetParam(VersionMinor) 69 | if err != nil { 70 | return 71 | } 72 | 73 | ver = strings.Join([]string{strconv.Itoa(int(major)), strconv.Itoa(int(minor))}, ".") 74 | return 75 | }() 76 | if er != nil { 77 | err = er 78 | return 79 | } 80 | if _, ok := vers[ver]; !ok { 81 | err = fmt.Errorf("unsupported windivert version: %v", ver) 82 | } 83 | }) 84 | if err != nil { 85 | return 86 | } 87 | 88 | return open(filter, layer, priority, flags) 89 | } 90 | 91 | type lazyDLL struct { 92 | Name string 93 | Base windows.Handle 94 | mu sync.Mutex 95 | module *memmod.Module 96 | onLoad func(d *lazyDLL) 97 | } 98 | 99 | func (p *lazyProc) nameToAddr() (uintptr, error) { 100 | return p.dll.module.ProcAddressByName(p.Name) 101 | } 102 | 103 | func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL { 104 | return &lazyDLL{Name: name, onLoad: onLoad} 105 | } 106 | 107 | func (d *lazyDLL) NewProc(name string) *lazyProc { 108 | return &lazyProc{dll: d, Name: name} 109 | } 110 | 111 | type lazyProc struct { 112 | Name string 113 | mu sync.Mutex 114 | dll *lazyDLL 115 | addr uintptr 116 | } 117 | 118 | func (p *lazyProc) Find() error { 119 | if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil { 120 | return nil 121 | } 122 | p.mu.Lock() 123 | defer p.mu.Unlock() 124 | if p.addr != 0 { 125 | return nil 126 | } 127 | 128 | err := p.dll.Load() 129 | if err != nil { 130 | return fmt.Errorf("Error loading %v DLL: %w", p.dll.Name, err) 131 | } 132 | addr, err := p.nameToAddr() 133 | if err != nil { 134 | return fmt.Errorf("Error getting %v address: %w", p.Name, err) 135 | } 136 | 137 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr)) 138 | return nil 139 | } 140 | 141 | func (p *lazyProc) Addr() uintptr { 142 | err := p.Find() 143 | if err != nil { 144 | panic(err) 145 | } 146 | return p.addr 147 | } 148 | 149 | func (p *lazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) { 150 | return syscall.SyscallN(p.Addr(), a...) 151 | } 152 | -------------------------------------------------------------------------------- /memmod/syscall_windows_64.go: -------------------------------------------------------------------------------- 1 | //go:build (windows && amd64) || (windows && arm64) 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package memmod 9 | 10 | // Optional header format 11 | type IMAGE_OPTIONAL_HEADER struct { 12 | Magic uint16 13 | MajorLinkerVersion uint8 14 | MinorLinkerVersion uint8 15 | SizeOfCode uint32 16 | SizeOfInitializedData uint32 17 | SizeOfUninitializedData uint32 18 | AddressOfEntryPoint uint32 19 | BaseOfCode uint32 20 | ImageBase uintptr 21 | SectionAlignment uint32 22 | FileAlignment uint32 23 | MajorOperatingSystemVersion uint16 24 | MinorOperatingSystemVersion uint16 25 | MajorImageVersion uint16 26 | MinorImageVersion uint16 27 | MajorSubsystemVersion uint16 28 | MinorSubsystemVersion uint16 29 | Win32VersionValue uint32 30 | SizeOfImage uint32 31 | SizeOfHeaders uint32 32 | CheckSum uint32 33 | Subsystem uint16 34 | DllCharacteristics uint16 35 | SizeOfStackReserve uintptr 36 | SizeOfStackCommit uintptr 37 | SizeOfHeapReserve uintptr 38 | SizeOfHeapCommit uintptr 39 | LoaderFlags uint32 40 | NumberOfRvaAndSizes uint32 41 | DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY 42 | } 43 | 44 | const IMAGE_ORDINAL_FLAG uintptr = 0x8000000000000000 45 | 46 | type IMAGE_LOAD_CONFIG_DIRECTORY struct { 47 | Size uint32 48 | TimeDateStamp uint32 49 | MajorVersion uint16 50 | MinorVersion uint16 51 | GlobalFlagsClear uint32 52 | GlobalFlagsSet uint32 53 | CriticalSectionDefaultTimeout uint32 54 | DeCommitFreeBlockThreshold uint64 55 | DeCommitTotalFreeThreshold uint64 56 | LockPrefixTable uint64 57 | MaximumAllocationSize uint64 58 | VirtualMemoryThreshold uint64 59 | ProcessAffinityMask uint64 60 | ProcessHeapFlags uint32 61 | CSDVersion uint16 62 | DependentLoadFlags uint16 63 | EditList uint64 64 | SecurityCookie uint64 65 | SEHandlerTable uint64 66 | SEHandlerCount uint64 67 | GuardCFCheckFunctionPointer uint64 68 | GuardCFDispatchFunctionPointer uint64 69 | GuardCFFunctionTable uint64 70 | GuardCFFunctionCount uint64 71 | GuardFlags uint32 72 | CodeIntegrity IMAGE_LOAD_CONFIG_CODE_INTEGRITY 73 | GuardAddressTakenIatEntryTable uint64 74 | GuardAddressTakenIatEntryCount uint64 75 | GuardLongJumpTargetTable uint64 76 | GuardLongJumpTargetCount uint64 77 | DynamicValueRelocTable uint64 78 | CHPEMetadataPointer uint64 79 | GuardRFFailureRoutine uint64 80 | GuardRFFailureRoutineFunctionPointer uint64 81 | DynamicValueRelocTableOffset uint32 82 | DynamicValueRelocTableSection uint16 83 | Reserved2 uint16 84 | GuardRFVerifyStackPointerFunctionPointer uint64 85 | HotPatchTableOffset uint32 86 | Reserved3 uint32 87 | EnclaveConfigurationPointer uint64 88 | VolatileMetadataPointer uint64 89 | GuardEHContinuationTable uint64 90 | GuardEHContinuationCount uint64 91 | GuardXFGCheckFunctionPointer uint64 92 | GuardXFGDispatchFunctionPointer uint64 93 | GuardXFGTableDispatchFunctionPointer uint64 94 | CastGuardOsDeterminedFailureMode uint64 95 | } 96 | -------------------------------------------------------------------------------- /memmod/syscall_windows_32.go: -------------------------------------------------------------------------------- 1 | //go:build (windows && 386) || (windows && arm) 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package memmod 9 | 10 | // Optional header format 11 | type IMAGE_OPTIONAL_HEADER struct { 12 | Magic uint16 13 | MajorLinkerVersion uint8 14 | MinorLinkerVersion uint8 15 | SizeOfCode uint32 16 | SizeOfInitializedData uint32 17 | SizeOfUninitializedData uint32 18 | AddressOfEntryPoint uint32 19 | BaseOfCode uint32 20 | BaseOfData uint32 21 | ImageBase uintptr 22 | SectionAlignment uint32 23 | FileAlignment uint32 24 | MajorOperatingSystemVersion uint16 25 | MinorOperatingSystemVersion uint16 26 | MajorImageVersion uint16 27 | MinorImageVersion uint16 28 | MajorSubsystemVersion uint16 29 | MinorSubsystemVersion uint16 30 | Win32VersionValue uint32 31 | SizeOfImage uint32 32 | SizeOfHeaders uint32 33 | CheckSum uint32 34 | Subsystem uint16 35 | DllCharacteristics uint16 36 | SizeOfStackReserve uintptr 37 | SizeOfStackCommit uintptr 38 | SizeOfHeapReserve uintptr 39 | SizeOfHeapCommit uintptr 40 | LoaderFlags uint32 41 | NumberOfRvaAndSizes uint32 42 | DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY 43 | } 44 | 45 | const IMAGE_ORDINAL_FLAG uintptr = 0x80000000 46 | 47 | type IMAGE_LOAD_CONFIG_DIRECTORY struct { 48 | Size uint32 49 | TimeDateStamp uint32 50 | MajorVersion uint16 51 | MinorVersion uint16 52 | GlobalFlagsClear uint32 53 | GlobalFlagsSet uint32 54 | CriticalSectionDefaultTimeout uint32 55 | DeCommitFreeBlockThreshold uint32 56 | DeCommitTotalFreeThreshold uint32 57 | LockPrefixTable uint32 58 | MaximumAllocationSize uint32 59 | VirtualMemoryThreshold uint32 60 | ProcessHeapFlags uint32 61 | ProcessAffinityMask uint32 62 | CSDVersion uint16 63 | DependentLoadFlags uint16 64 | EditList uint32 65 | SecurityCookie uint32 66 | SEHandlerTable uint32 67 | SEHandlerCount uint32 68 | GuardCFCheckFunctionPointer uint32 69 | GuardCFDispatchFunctionPointer uint32 70 | GuardCFFunctionTable uint32 71 | GuardCFFunctionCount uint32 72 | GuardFlags uint32 73 | CodeIntegrity IMAGE_LOAD_CONFIG_CODE_INTEGRITY 74 | GuardAddressTakenIatEntryTable uint32 75 | GuardAddressTakenIatEntryCount uint32 76 | GuardLongJumpTargetTable uint32 77 | GuardLongJumpTargetCount uint32 78 | DynamicValueRelocTable uint32 79 | CHPEMetadataPointer uint32 80 | GuardRFFailureRoutine uint32 81 | GuardRFFailureRoutineFunctionPointer uint32 82 | DynamicValueRelocTableOffset uint32 83 | DynamicValueRelocTableSection uint16 84 | Reserved2 uint16 85 | GuardRFVerifyStackPointerFunctionPointer uint32 86 | HotPatchTableOffset uint32 87 | Reserved3 uint32 88 | EnclaveConfigurationPointer uint32 89 | VolatileMetadataPointer uint32 90 | GuardEHContinuationTable uint32 91 | GuardEHContinuationCount uint32 92 | GuardXFGCheckFunctionPointer uint32 93 | GuardXFGDispatchFunctionPointer uint32 94 | GuardXFGTableDispatchFunctionPointer uint32 95 | CastGuardOsDeterminedFailureMode uint32 96 | } 97 | -------------------------------------------------------------------------------- /addr.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | import "unsafe" 6 | 7 | // Ethernet is ... 8 | type Ethernet struct { 9 | InterfaceIndex uint32 10 | SubInterfaceIndex uint32 11 | _ [7]uint64 12 | } 13 | 14 | // Network is ... 15 | // The WINDIVERT_LAYER_NETWORK and WINDIVERT_LAYER_NETWORK_FORWARD layers allow the user 16 | // application to capture/block/inject network packets passing to/from (and through) the 17 | // local machine. Due to technical limitations, process ID information is not available 18 | // at these layers. 19 | type Network struct { 20 | InterfaceIndex uint32 21 | SubInterfaceIndex uint32 22 | _ [7]uint64 23 | } 24 | 25 | // Socket is ... 26 | // The WINDIVERT_LAYER_SOCKET layer can capture or block events corresponding to socket 27 | // operations, such as bind(), connect(), listen(), etc., or the termination of socket 28 | // operations, such as a TCP socket disconnection. Unlike the flow layer, most socket-related 29 | // events can be blocked. However, it is not possible to inject new or modified socket events. 30 | // Process ID information (of the process responsible for the socket operation) is available 31 | // at this layer. Due to technical limitations, this layer cannot capture events that occurred 32 | // before the handle was opened. 33 | type Socket struct { 34 | EndpointID uint64 35 | ParentEndpointID uint64 36 | ProcessID uint32 37 | LocalAddress [16]uint8 38 | RemoteAddress [16]uint8 39 | LocalPort uint16 40 | RemotePort uint16 41 | Protocol uint8 42 | _ [3]uint8 43 | _ uint32 44 | } 45 | 46 | // Flow is ... 47 | // The WINDIVERT_LAYER_FLOW layer captures information about network flow establishment/deletion 48 | // events. Here, a flow represents either (1) a TCP connection, or (2) an implicit "flow" created 49 | // by the first sent/received packet for non-TCP traffic, e.g., UDP. Old flows are deleted when 50 | // the corresponding connection is closed (for TCP), or based on an activity timeout (non-TCP). 51 | // Flow-related events can be captured, but not blocked nor injected. Process ID information is 52 | // also available at this layer. Due to technical limitations, the WINDIVERT_LAYER_FLOW layer 53 | // cannot capture flow events that occurred before the handle was opened. 54 | type Flow struct { 55 | EndpointID uint64 56 | ParentEndpointID uint64 57 | ProcessID uint32 58 | LocalAddress [16]uint8 59 | RemoteAddress [16]uint8 60 | LocalPort uint16 61 | RemotePort uint16 62 | Protocol uint8 63 | _ [3]uint8 64 | _ uint32 65 | } 66 | 67 | // Reflect is ... 68 | // Finally, the WINDIVERT_LAYER_REFLECT layer can capture events relating to WinDivert itself, 69 | // such as when another process opens a new WinDivert handle, or closes an old WinDivert handle. 70 | // WinDivert events can be captured but not injected nor blocked. Process ID information 71 | // (of the process responsible for opening the WinDivert handle) is available at this layer. 72 | // This layer also returns data in the form of an "object" representation of the filter string 73 | // used to open the handle. The object representation can be converted back into a human-readable 74 | // filter string using the WinDivertHelperFormatFilter() function. This layer can also capture 75 | // events that occurred before the handle was opened. This layer cannot capture events related 76 | // to other WINDIVERT_LAYER_REFLECT-layer handles. 77 | type Reflect struct { 78 | TimeStamp int64 79 | ProcessID uint32 80 | layer uint32 81 | Flags uint64 82 | Priority int16 83 | _ int16 84 | _ int32 85 | _ [4]uint64 86 | } 87 | 88 | // Layer is ... 89 | func (r *Reflect) Layer() Layer { 90 | return Layer(r.layer) 91 | } 92 | 93 | // Address is ... 94 | type Address struct { 95 | Timestamp int64 96 | layer uint8 97 | event uint8 98 | Flags uint8 99 | _ uint8 100 | length uint32 101 | union [64]uint8 102 | } 103 | 104 | // Layer is ... 105 | func (a *Address) Layer() Layer { 106 | return Layer(a.layer) 107 | } 108 | 109 | // SetLayer is ... 110 | func (a *Address) SetLayer(layer Layer) { 111 | a.layer = uint8(layer) 112 | } 113 | 114 | // Event is ... 115 | func (a *Address) Event() Event { 116 | return Event(a.event) 117 | } 118 | 119 | // SetEvent is ... 120 | func (a *Address) SetEvent(event Event) { 121 | a.event = uint8(event) 122 | } 123 | 124 | // Length is ... 125 | func (a *Address) Length() uint32 { 126 | return a.length >> 12 127 | } 128 | 129 | // SetLength is ... 130 | func (a *Address) SetLength(n uint32) { 131 | a.length = n << 12 132 | } 133 | 134 | // Ethernet is ... 135 | func (a *Address) Ethernet() *Ethernet { 136 | return (*Ethernet)(unsafe.Pointer(&a.union)) 137 | } 138 | 139 | // Network is ... 140 | func (a *Address) Network() *Network { 141 | return (*Network)(unsafe.Pointer(&a.union)) 142 | } 143 | 144 | // Socket is ... 145 | func (a *Address) Socket() *Socket { 146 | return (*Socket)(unsafe.Pointer(&a.union)) 147 | } 148 | 149 | // Flow is ... 150 | func (a *Address) Flow() *Flow { 151 | return (*Flow)(unsafe.Pointer(&a.union)) 152 | } 153 | 154 | // Reflect is ... 155 | func (a *Address) Reflect() *Reflect { 156 | return (*Reflect)(unsafe.Pointer(&a.union)) 157 | } 158 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | 9 | "golang.org/x/sys/windows" 10 | ) 11 | 12 | var ( 13 | errQueueLength = fmt.Errorf("Queue length is not correct, Max: %v, Min: %v", QueueLengthMax, QueueLengthMin) 14 | errQueueTime = fmt.Errorf("Queue time is not correct, Max: %v, Min: %v", QueueTimeMax, QueueTimeMin) 15 | errQueueSize = fmt.Errorf("Queue size is not correct, Max: %v, Min: %v", QueueSizeMax, QueueSizeMin) 16 | errQueueParam = errors.New("VersionMajor and VersionMinor only can be used in function GetParam") 17 | errPriority = fmt.Errorf("Priority is not Correct, Max: %v, Min: %v", PriorityHighest, PriorityLowest) 18 | ) 19 | 20 | const ( 21 | // The driver files WinDivert32.sys or WinDivert64.sys were not found 22 | ErrFileNotFound = Error(windows.ERROR_FILE_NOT_FOUND) 23 | 24 | // The calling application does not have Administrator privileges 25 | ErrAccessDenied = Error(windows.ERROR_ACCESS_DENIED) 26 | 27 | // This indicates an invalid packet filter string, layer, priority, or flags 28 | ErrInvalidParameter = Error(windows.ERROR_INVALID_PARAMETER) 29 | 30 | // The WinDivert32.sys or WinDivert64.sys driver does not have a valid digital signature (see the driver signing requirements above) 31 | ErrInvalidImageHash = Error(windows.ERROR_INVALID_IMAGE_HASH) 32 | 33 | // An incompatible version of the WinDivert driver is currently loaded 34 | ErrDriverFailedPriorUnload = Error(windows.ERROR_DRIVER_FAILED_PRIOR_UNLOAD) 35 | 36 | // The handle was opened with the WINDIVERT_FLAG_NO_INSTALL flag and the WinDivert driver is not already installed 37 | ErrServiceDoseNotExist = Error(windows.ERROR_SERVICE_DOES_NOT_EXIST) 38 | 39 | // This error occurs for various reasons, including: the WinDivert driver is blocked by security software; or you are using a virtualization environment that does not support drivers 40 | ErrDriverBlocked = Error(windows.ERROR_DRIVER_BLOCKED) 41 | 42 | // The captured packet is larger than the pPacket buffer 43 | ErrInsufficientBuffer = Error(windows.ERROR_INSUFFICIENT_BUFFER) 44 | 45 | // The handle has been shutdown using WinDivertShutdown() and the packet queue is empty 46 | ErrNoData = Error(windows.ERROR_NO_DATA) 47 | 48 | // The error code ERROR_IO_PENDING indicates that the overlapped operation has been successfully initiated and that completion will be indicated at a later time 49 | ErrIOPending = Error(windows.ERROR_IO_PENDING) 50 | 51 | // This error occurs when an impostor packet (with pAddr->Impostor set to 1) is injected and the ip.TTL or ipv6.HopLimit field goes to zero. This is a defense of "last resort" against infinite loops caused by impostor packets 52 | ErrHostUnreachable = Error(windows.ERROR_HOST_UNREACHABLE) 53 | 54 | // This error occurs when the Base Filtering Engine service has been disabled 55 | ErrNotRegistered = Error(windows.EPT_S_NOT_REGISTERED) 56 | 57 | // The I/O operation has been aborted because of either a thread exit or an application request 58 | ErrOperationAborted = Error(windows.ERROR_OPERATION_ABORTED) 59 | 60 | // The handle is invalid 61 | ErrInvalidHandle = Error(windows.ERROR_INVALID_HANDLE) 62 | ) 63 | 64 | // Error is ... 65 | type Error windows.Errno 66 | 67 | // Error is ... 68 | func (e Error) Error() string { 69 | switch windows.Errno(e) { 70 | case windows.ERROR_FILE_NOT_FOUND: 71 | return "The driver files WinDivert32.sys or WinDivert64.sys were not found" 72 | case windows.ERROR_ACCESS_DENIED: 73 | return "The calling application does not have Administrator privileges" 74 | case windows.ERROR_INVALID_PARAMETER: 75 | return "This indicates an invalid packet filter string, layer, priority, or flags" 76 | case windows.ERROR_INVALID_IMAGE_HASH: 77 | return "The WinDivert32.sys or WinDivert64.sys driver does not have a valid digital signature (see the driver signing requirements above)" 78 | case windows.ERROR_DRIVER_FAILED_PRIOR_UNLOAD: 79 | return "An incompatible version of the WinDivert driver is currently loaded" 80 | case windows.ERROR_SERVICE_DOES_NOT_EXIST: 81 | return "The handle was opened with the WINDIVERT_FLAG_NO_INSTALL flag and the WinDivert driver is not already installed" 82 | case windows.ERROR_DRIVER_BLOCKED: 83 | return "This error occurs for various reasons, including: the WinDivert driver is blocked by security software; or you are using a virtualization environment that does not support drivers" 84 | case windows.ERROR_INSUFFICIENT_BUFFER: 85 | return "The captured packet is larger than the pPacket buffer" 86 | case windows.ERROR_NO_DATA: 87 | return "The handle has been shutdown using WinDivertShutdown() and the packet queue is empty" 88 | case windows.ERROR_IO_PENDING: 89 | return "The error code ERROR_IO_PENDING indicates that the overlapped operation has been successfully initiated and that completion will be indicated at a later time" 90 | case windows.ERROR_HOST_UNREACHABLE: 91 | return "This error occurs when an impostor packet (with pAddr->Impostor set to 1) is injected and the ip.TTL or ipv6.HopLimit field goes to zero. This is a defense of \"last resort\" against infinite loops caused by impostor packets" 92 | case windows.EPT_S_NOT_REGISTERED: 93 | return "This error occurs when the Base Filtering Engine service has been disabled" 94 | case windows.ERROR_OPERATION_ABORTED: 95 | return "The I/O operation has been aborted because of either a thread exit or an application request" 96 | case windows.ERROR_INVALID_HANDLE: 97 | return "The handle is invalid" 98 | default: 99 | return windows.Errno(e).Error() 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /divert.go: -------------------------------------------------------------------------------- 1 | //go:build windows && (amd64 || 386 || arm64) 2 | 3 | package divert 4 | 5 | import ( 6 | "strconv" 7 | "strings" 8 | "sync" 9 | "unsafe" 10 | 11 | "golang.org/x/sys/windows" 12 | ) 13 | 14 | var once = sync.Once{} 15 | 16 | // GerVersionInfo is ... 17 | func GetVersionInfo() (ver string, err error) { 18 | h, err := Open("false", LayerNetwork, PriorityDefault, FlagDefault) 19 | if err != nil { 20 | return 21 | } 22 | defer func() { 23 | err = h.Close() 24 | }() 25 | 26 | major, err := h.GetParam(VersionMajor) 27 | if err != nil { 28 | return 29 | } 30 | 31 | minor, err := h.GetParam(VersionMinor) 32 | if err != nil { 33 | return 34 | } 35 | 36 | ver = strings.Join([]string{strconv.Itoa(int(major)), strconv.Itoa(int(minor))}, ".") 37 | return 38 | } 39 | 40 | func ioControlEx(h windows.Handle, code CtlCode, ioctl unsafe.Pointer, buf *byte, bufLen uint32, overlapped *windows.Overlapped) (iolen uint32, err error) { 41 | err = windows.DeviceIoControl(h, uint32(code), (*byte)(ioctl), uint32(unsafe.Sizeof(ioCtl{})), buf, bufLen, &iolen, overlapped) 42 | if err != windows.ERROR_IO_PENDING { 43 | return 44 | } 45 | 46 | err = windows.GetOverlappedResult(h, overlapped, &iolen, true) 47 | 48 | return 49 | } 50 | 51 | func ioControl(h windows.Handle, code CtlCode, ioctl unsafe.Pointer, buf *byte, bufLen uint32) (iolen uint32, err error) { 52 | event, _ := windows.CreateEvent(nil, 0, 0, nil) 53 | 54 | overlapped := windows.Overlapped{ 55 | HEvent: event, 56 | } 57 | 58 | iolen, err = ioControlEx(h, code, ioctl, buf, bufLen, &overlapped) 59 | 60 | windows.CloseHandle(event) 61 | return 62 | } 63 | 64 | // Handle is ... 65 | type Handle struct { 66 | sync.Mutex 67 | windows.Handle 68 | rOverlapped windows.Overlapped 69 | wOverlapped windows.Overlapped 70 | } 71 | 72 | // Recv is ... 73 | func (h *Handle) Recv(buffer []byte, address *Address) (uint, error) { 74 | addrLen := uint(unsafe.Sizeof(Address{})) 75 | recv := recv{ 76 | Addr: uint64(uintptr(unsafe.Pointer(address))), 77 | AddrLenPtr: uint64(uintptr(unsafe.Pointer(&addrLen))), 78 | } 79 | 80 | iolen, err := ioControlEx(h.Handle, ioCtlRecv, unsafe.Pointer(&recv), &buffer[0], uint32(len(buffer)), &h.rOverlapped) 81 | if err != nil { 82 | return uint(iolen), Error(err.(windows.Errno)) 83 | } 84 | 85 | return uint(iolen), nil 86 | } 87 | 88 | // RecvEx is ... 89 | func (h *Handle) RecvEx(buffer []byte, address []Address) (uint, uint, error) { 90 | addrLen := uint(len(address)) * uint(unsafe.Sizeof(Address{})) 91 | recv := recv{ 92 | Addr: uint64(uintptr(unsafe.Pointer(&address[0]))), 93 | AddrLenPtr: uint64(uintptr(unsafe.Pointer(&addrLen))), 94 | } 95 | 96 | iolen, err := ioControlEx(h.Handle, ioCtlRecv, unsafe.Pointer(&recv), &buffer[0], uint32(len(buffer)), &h.rOverlapped) 97 | if err != nil { 98 | return uint(iolen), addrLen / uint(unsafe.Sizeof(Address{})), Error(err.(windows.Errno)) 99 | } 100 | 101 | return uint(iolen), addrLen / uint(unsafe.Sizeof(Address{})), nil 102 | } 103 | 104 | // Send is ... 105 | func (h *Handle) Send(buffer []byte, address *Address) (uint, error) { 106 | send := send{ 107 | Addr: uint64(uintptr(unsafe.Pointer(address))), 108 | AddrLen: uint64(unsafe.Sizeof(Address{})), 109 | } 110 | 111 | iolen, err := ioControlEx(h.Handle, ioCtlSend, unsafe.Pointer(&send), &buffer[0], uint32(len(buffer)), &h.wOverlapped) 112 | if err != nil { 113 | return uint(iolen), Error(err.(windows.Errno)) 114 | } 115 | 116 | return uint(iolen), nil 117 | } 118 | 119 | // SendEx is ... 120 | func (h *Handle) SendEx(buffer []byte, address []Address) (uint, error) { 121 | send := send{ 122 | Addr: uint64(uintptr(unsafe.Pointer(&address[0]))), 123 | AddrLen: uint64(unsafe.Sizeof(Address{})) * uint64(len(address)), 124 | } 125 | 126 | iolen, err := ioControlEx(h.Handle, ioCtlSend, unsafe.Pointer(&send), &buffer[0], uint32(len(buffer)), &h.wOverlapped) 127 | if err != nil { 128 | return uint(iolen), Error(err.(windows.Errno)) 129 | } 130 | 131 | return uint(iolen), nil 132 | } 133 | 134 | // Shutdown is ... 135 | func (h *Handle) Shutdown(how Shutdown) error { 136 | shutdown := shutdown{ 137 | How: uint32(how), 138 | } 139 | 140 | _, err := ioControl(h.Handle, ioCtlShutdown, unsafe.Pointer(&shutdown), nil, 0) 141 | if err != nil { 142 | return Error(err.(windows.Errno)) 143 | } 144 | 145 | return nil 146 | } 147 | 148 | // Close is ... 149 | func (h *Handle) Close() error { 150 | windows.CloseHandle(h.rOverlapped.HEvent) 151 | windows.CloseHandle(h.wOverlapped.HEvent) 152 | 153 | err := windows.CloseHandle(h.Handle) 154 | if err != nil { 155 | return Error(err.(windows.Errno)) 156 | } 157 | 158 | return nil 159 | } 160 | 161 | // GetParam is ... 162 | func (h *Handle) GetParam(p Param) (uint64, error) { 163 | getParam := getParam{ 164 | Param: uint32(p), 165 | Value: 0, 166 | } 167 | 168 | _, err := ioControl(h.Handle, ioCtlGetParam, unsafe.Pointer(&getParam), (*byte)(unsafe.Pointer(&getParam.Value)), uint32(unsafe.Sizeof(getParam.Value))) 169 | if err != nil { 170 | return getParam.Value, Error(err.(windows.Errno)) 171 | } 172 | 173 | return getParam.Value, nil 174 | } 175 | 176 | // SetParam is ... 177 | func (h *Handle) SetParam(p Param, v uint64) error { 178 | switch p { 179 | case QueueLength: 180 | if v < QueueLengthMin || v > QueueLengthMax { 181 | return errQueueLength 182 | } 183 | case QueueTime: 184 | if v < QueueTimeMin || v > QueueTimeMax { 185 | return errQueueTime 186 | } 187 | case QueueSize: 188 | if v < QueueSizeMin || v > QueueSizeMax { 189 | return errQueueSize 190 | } 191 | default: 192 | return errQueueParam 193 | } 194 | 195 | setParam := setParam{ 196 | Value: v, 197 | Param: uint32(p), 198 | } 199 | 200 | _, err := ioControl(h.Handle, ioCtlSetParam, unsafe.Pointer(&setParam), nil, 0) 201 | if err != nil { 202 | return Error(err.(windows.Errno)) 203 | } 204 | 205 | return nil 206 | } 207 | -------------------------------------------------------------------------------- /divert/windivert_hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * windivert_hash.c 3 | * (C) 2019, all rights reserved, 4 | * 5 | * This file is part of WinDivert. 6 | * 7 | * WinDivert is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program. If not, see . 19 | * 20 | * WinDivert is free software; you can redistribute it and/or modify it under 21 | * the terms of the GNU General Public License as published by the Free 22 | * Software Foundation; either version 2 of the License, or (at your option) 23 | * any later version. 24 | * 25 | * This program is distributed in the hope that it will be useful, but 26 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 27 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 28 | * for more details. 29 | * 30 | * You should have received a copy of the GNU General Public License along 31 | * with this program; if not, write to the Free Software Foundation, Inc., 51 32 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 33 | * 34 | * xxHash - Fast Hash algorithm 35 | * Copyright (C) 2012-2016, Yann Collet 36 | * 37 | * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) 38 | * 39 | * Redistribution and use in source and binary forms, with or without 40 | * modification, are permitted provided that the following conditions are 41 | * met: 42 | * 43 | * * Redistributions of source code must retain the above copyright 44 | * notice, this list of conditions and the following disclaimer. 45 | * * Redistributions in binary form must reproduce the above 46 | * copyright notice, this list of conditions and the following disclaimer 47 | * in the documentation and/or other materials provided with the 48 | * distribution. 49 | * 50 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 54 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 55 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 56 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 57 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 58 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 59 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 60 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 | */ 62 | 63 | /* 64 | * This is a modified version of the 64bit xxHash algorithm: 65 | * - The algorithm is seeded with packet data rather than the single 64bit 66 | * "seed" value. 67 | * - The input sized is fixed to 32bytes (excluding the seed), so there is 68 | * only ever a single round. As such, the algorithm has been specialized. 69 | */ 70 | 71 | #define WINDIVERT_ROTL64(x, r) (((x) << (r)) | ((x) >> (64 - (r)))) 72 | 73 | static const UINT64 WINDIVERT_PRIME64_1 = 11400714785074694791ull; 74 | static const UINT64 WINDIVERT_PRIME64_2 = 14029467366897019727ull; 75 | static const UINT64 WINDIVERT_PRIME64_3 = 1609587929392839161ull; 76 | static const UINT64 WINDIVERT_PRIME64_4 = 9650029242287828579ull; 77 | 78 | static UINT64 WinDivertXXH64Round(UINT64 acc, UINT64 input) 79 | { 80 | acc += WINDIVERT_MUL64(input, WINDIVERT_PRIME64_2); 81 | acc = WINDIVERT_ROTL64(acc, 31); 82 | acc = WINDIVERT_MUL64(acc, WINDIVERT_PRIME64_1); 83 | return acc; 84 | } 85 | 86 | static UINT64 WinDivertXXH64MergeRound(UINT64 acc, UINT64 val) 87 | { 88 | val = WinDivertXXH64Round(0, val); 89 | acc ^= val; 90 | acc = WINDIVERT_MUL64(acc, WINDIVERT_PRIME64_1) + WINDIVERT_PRIME64_4; 91 | return acc; 92 | } 93 | 94 | static UINT64 WinDivertXXH64Avalanche(UINT64 h64) 95 | { 96 | h64 ^= h64 >> 33; 97 | h64 = WINDIVERT_MUL64(h64, WINDIVERT_PRIME64_2); 98 | h64 ^= h64 >> 29; 99 | h64 = WINDIVERT_MUL64(h64, WINDIVERT_PRIME64_3); 100 | h64 ^= h64 >> 32; 101 | return h64; 102 | } 103 | 104 | /* 105 | * WinDivert packet hash function. 106 | */ 107 | static UINT64 WinDivertHashPacket(UINT64 seed, 108 | const WINDIVERT_IPHDR *ip_header, const WINDIVERT_IPV6HDR *ipv6_header, 109 | const WINDIVERT_ICMPHDR *icmp_header, 110 | const WINDIVERT_ICMPV6HDR *icmpv6_header, 111 | const WINDIVERT_TCPHDR *tcp_header, const WINDIVERT_UDPHDR *udp_header) 112 | { 113 | UINT64 h64, v1, v2, v3, v4, v[4]; 114 | const UINT64 *data64; 115 | const UINT32 *data32; 116 | UINT i; 117 | static const UINT64 padding64[] = // SHA2 IV 118 | { 119 | 0x428A2F9871374491ull, 0xB5C0FBCFE9B5DBA5ull, 0x3956C25B59F111F1ull, 120 | 0x923F82A4AB1C5ED5ull, 0xD807AA9812835B01ull, 0x243185BE550C7DC3ull, 121 | 0x72BE5D7480DEB1FEull, 0x9BDC06A7C19BF174ull, 0xE49B69C1EFBE4786ull, 122 | }; 123 | 124 | // Set-up seed & data 125 | v1 = seed ^ padding64[0]; 126 | if (ip_header != NULL) 127 | { 128 | data64 = (const UINT64 *)ip_header; 129 | v2 = data64[0] ^ padding64[1]; 130 | v3 = data64[1] ^ padding64[2]; 131 | data32 = (const UINT32 *)ip_header; 132 | v4 = (UINT64)data32[4] ^ padding64[3]; 133 | i = 0; 134 | } 135 | else if (ipv6_header != NULL) 136 | { 137 | data64 = (const UINT64 *)ipv6_header; 138 | v2 = data64[0] ^ padding64[1]; 139 | v3 = data64[1] ^ padding64[2]; 140 | v4 = data64[2] ^ padding64[3]; 141 | v[0] = data64[3] ^ padding64[4]; 142 | v[1] = data64[4] ^ padding64[5]; 143 | i = 2; 144 | } 145 | else 146 | return 0; 147 | 148 | if (tcp_header != NULL) 149 | { 150 | data64 = (const UINT64 *)tcp_header; 151 | v[i] = data64[0] ^ padding64[i+4]; i++; 152 | v[i] = data64[1] ^ padding64[i+4]; i++; 153 | data32 = (const UINT32 *)tcp_header; 154 | if (i <= 3) 155 | { 156 | v[i] = (UINT64)data32[4] ^ padding64[i+4]; i++; 157 | } 158 | else 159 | { 160 | v2 ^= ((UINT64)data32[4] << 32); 161 | } 162 | } 163 | else 164 | { 165 | if (udp_header != NULL) 166 | { 167 | data64 = (const UINT64 *)udp_header; 168 | v[i] = data64[0] ^ padding64[i+4]; i++; 169 | } 170 | else if (icmp_header != NULL) 171 | { 172 | data64 = (const UINT64 *)icmp_header; 173 | v[i] = data64[0] ^ padding64[i+4]; i++; 174 | } 175 | else if (icmpv6_header != NULL) 176 | { 177 | data64 = (const UINT64 *)icmpv6_header; 178 | v[i] = data64[0] ^ padding64[i+4]; i++; 179 | } 180 | } 181 | 182 | while (i <= 3) 183 | { 184 | v[i] = seed ^ padding64[i+4]; i++; 185 | } 186 | 187 | // Hash 188 | v1 = WinDivertXXH64Round(v[0], v1); 189 | v2 = WinDivertXXH64Round(v[1], v2); 190 | v3 = WinDivertXXH64Round(v[2], v3); 191 | v4 = WinDivertXXH64Round(v[3], v4); 192 | h64 = WINDIVERT_ROTL64(v1, 1) + WINDIVERT_ROTL64(v2, 7) + 193 | WINDIVERT_ROTL64(v3, 12) + WINDIVERT_ROTL64(v4, 18); 194 | h64 = WinDivertXXH64MergeRound(h64, v1); 195 | h64 = WinDivertXXH64MergeRound(h64, v2); 196 | h64 = WinDivertXXH64MergeRound(h64, v3); 197 | h64 = WinDivertXXH64MergeRound(h64, v4); 198 | h64 += 32; // "length" 199 | h64 = WinDivertXXH64Avalanche(h64); 200 | 201 | return h64; 202 | } 203 | 204 | -------------------------------------------------------------------------------- /header/checksum.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The gVisor Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package header provides the implementation of the encoding and decoding of 16 | // network protocol headers. 17 | package header 18 | 19 | import ( 20 | "encoding/binary" 21 | "fmt" 22 | ) 23 | 24 | // ChecksumSize is the size of a checksum. 25 | // 26 | // The checksum is held in a uint16 which is 2 bytes. 27 | const ChecksumSize = 2 28 | 29 | // PutChecksum puts the checksum in the provided byte slice. 30 | func PutChecksum(b []byte, xsum uint16) { 31 | binary.BigEndian.PutUint16(b, xsum) 32 | } 33 | 34 | func calculateChecksum(buf []byte, odd bool, initial uint32) (uint16, bool) { 35 | v := initial 36 | 37 | if odd { 38 | v += uint32(buf[0]) 39 | buf = buf[1:] 40 | } 41 | 42 | l := len(buf) 43 | odd = l&1 != 0 44 | if odd { 45 | l-- 46 | v += uint32(buf[l]) << 8 47 | } 48 | 49 | for i := 0; i < l; i += 2 { 50 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 51 | } 52 | 53 | return ChecksumCombine(uint16(v), uint16(v>>16)), odd 54 | } 55 | 56 | func unrolledCalculateChecksum(buf []byte, odd bool, initial uint32) (uint16, bool) { 57 | v := initial 58 | 59 | if odd { 60 | v += uint32(buf[0]) 61 | buf = buf[1:] 62 | } 63 | 64 | l := len(buf) 65 | odd = l&1 != 0 66 | if odd { 67 | l-- 68 | v += uint32(buf[l]) << 8 69 | } 70 | for (l - 64) >= 0 { 71 | i := 0 72 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 73 | v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3]) 74 | v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5]) 75 | v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7]) 76 | v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9]) 77 | v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11]) 78 | v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13]) 79 | v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15]) 80 | i += 16 81 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 82 | v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3]) 83 | v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5]) 84 | v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7]) 85 | v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9]) 86 | v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11]) 87 | v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13]) 88 | v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15]) 89 | i += 16 90 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 91 | v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3]) 92 | v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5]) 93 | v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7]) 94 | v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9]) 95 | v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11]) 96 | v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13]) 97 | v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15]) 98 | i += 16 99 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 100 | v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3]) 101 | v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5]) 102 | v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7]) 103 | v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9]) 104 | v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11]) 105 | v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13]) 106 | v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15]) 107 | buf = buf[64:] 108 | l = l - 64 109 | } 110 | if (l - 32) >= 0 { 111 | i := 0 112 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 113 | v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3]) 114 | v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5]) 115 | v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7]) 116 | v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9]) 117 | v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11]) 118 | v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13]) 119 | v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15]) 120 | i += 16 121 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 122 | v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3]) 123 | v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5]) 124 | v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7]) 125 | v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9]) 126 | v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11]) 127 | v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13]) 128 | v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15]) 129 | buf = buf[32:] 130 | l = l - 32 131 | } 132 | if (l - 16) >= 0 { 133 | i := 0 134 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 135 | v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3]) 136 | v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5]) 137 | v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7]) 138 | v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9]) 139 | v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11]) 140 | v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13]) 141 | v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15]) 142 | buf = buf[16:] 143 | l = l - 16 144 | } 145 | if (l - 8) >= 0 { 146 | i := 0 147 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 148 | v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3]) 149 | v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5]) 150 | v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7]) 151 | buf = buf[8:] 152 | l = l - 8 153 | } 154 | if (l - 4) >= 0 { 155 | i := 0 156 | v += (uint32(buf[i]) << 8) + uint32(buf[i+1]) 157 | v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3]) 158 | buf = buf[4:] 159 | l = l - 4 160 | } 161 | 162 | // At this point since l was even before we started unrolling 163 | // there can be only two bytes left to add. 164 | if l != 0 { 165 | v += (uint32(buf[0]) << 8) + uint32(buf[1]) 166 | } 167 | 168 | return ChecksumCombine(uint16(v), uint16(v>>16)), odd 169 | } 170 | 171 | // ChecksumOld calculates the checksum (as defined in RFC 1071) of the bytes in 172 | // the given byte array. This function uses a non-optimized implementation. Its 173 | // only retained for reference and to use as a benchmark/test. Most code should 174 | // use the header.Checksum function. 175 | // 176 | // The initial checksum must have been computed on an even number of bytes. 177 | func ChecksumOld(buf []byte, initial uint16) uint16 { 178 | s, _ := calculateChecksum(buf, false, uint32(initial)) 179 | return s 180 | } 181 | 182 | // Checksum calculates the checksum (as defined in RFC 1071) of the bytes in the 183 | // given byte array. This function uses an optimized unrolled version of the 184 | // checksum algorithm. 185 | // 186 | // The initial checksum must have been computed on an even number of bytes. 187 | func Checksum(buf []byte, initial uint16) uint16 { 188 | s, _ := unrolledCalculateChecksum(buf, false, uint32(initial)) 189 | return s 190 | } 191 | 192 | // Checksumer calculates checksum defined in RFC 1071. 193 | type Checksumer struct { 194 | sum uint16 195 | odd bool 196 | } 197 | 198 | // Add adds b to checksum. 199 | func (c *Checksumer) Add(b []byte) { 200 | if len(b) > 0 { 201 | c.sum, c.odd = unrolledCalculateChecksum(b, c.odd, uint32(c.sum)) 202 | } 203 | } 204 | 205 | // Checksum returns the latest checksum value. 206 | func (c *Checksumer) Checksum() uint16 { 207 | return c.sum 208 | } 209 | 210 | // ChecksumCombine combines the two uint16 to form their checksum. This is done 211 | // by adding them and the carry. 212 | // 213 | // Note that checksum a must have been computed on an even number of bytes. 214 | func ChecksumCombine(a, b uint16) uint16 { 215 | v := uint32(a) + uint32(b) 216 | return uint16(v + v>>16) 217 | } 218 | 219 | // PseudoHeaderChecksum calculates the pseudo-header checksum for the given 220 | // destination protocol and network address. Pseudo-headers are needed by 221 | // transport layers when calculating their own checksum. 222 | func PseudoHeaderChecksum(protocol uint, srcAddr []byte, dstAddr []byte, totalLen uint16) uint16 { 223 | xsum := Checksum([]byte(srcAddr), 0) 224 | xsum = Checksum([]byte(dstAddr), xsum) 225 | 226 | // Add the length portion of the checksum to the pseudo-checksum. 227 | tmp := make([]byte, 2) 228 | binary.BigEndian.PutUint16(tmp, totalLen) 229 | xsum = Checksum(tmp, xsum) 230 | 231 | return Checksum([]byte{0, uint8(protocol)}, xsum) 232 | } 233 | 234 | // checksumUpdate2ByteAlignedUint16 updates a uint16 value in a calculated 235 | // checksum. 236 | // 237 | // The value MUST begin at a 2-byte boundary in the original buffer. 238 | func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 { 239 | // As per RFC 1071 page 4, 240 | // (4) Incremental Update 241 | // 242 | // ... 243 | // 244 | // To update the checksum, simply add the differences of the 245 | // sixteen bit integers that have been changed. To see why this 246 | // works, observe that every 16-bit integer has an additive inverse 247 | // and that addition is associative. From this it follows that 248 | // given the original value m, the new value m', and the old 249 | // checksum C, the new checksum C' is: 250 | // 251 | // C' = C + (-m) + m' = C + (m' - m) 252 | return ChecksumCombine(xsum, ChecksumCombine(new, ^old)) 253 | } 254 | 255 | // checksumUpdate2ByteAlignedAddress updates an address in a calculated 256 | // checksum. 257 | // 258 | // The addresses must have the same length and must contain an even number 259 | // of bytes. The address MUST begin at a 2-byte boundary in the original buffer. 260 | func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new []byte) uint16 { 261 | const uint16Bytes = 2 262 | 263 | if len(old) != len(new) { 264 | panic(fmt.Sprintf("buffer lengths are different; old = %d, new = %d", len(old), len(new))) 265 | } 266 | 267 | if len(old)%uint16Bytes != 0 { 268 | panic(fmt.Sprintf("buffer has an odd number of bytes; got = %d", len(old))) 269 | } 270 | 271 | // As per RFC 1071 page 4, 272 | // (4) Incremental Update 273 | // 274 | // ... 275 | // 276 | // To update the checksum, simply add the differences of the 277 | // sixteen bit integers that have been changed. To see why this 278 | // works, observe that every 16-bit integer has an additive inverse 279 | // and that addition is associative. From this it follows that 280 | // given the original value m, the new value m', and the old 281 | // checksum C, the new checksum C' is: 282 | // 283 | // C' = C + (-m) + m' = C + (m' - m) 284 | for len(old) != 0 { 285 | // Convert the 2 byte sequences to uint16 values then apply the increment 286 | // update. 287 | xsum = checksumUpdate2ByteAlignedUint16(xsum, (uint16(old[0])<<8)+uint16(old[1]), (uint16(new[0])<<8)+uint16(new[1])) 288 | old = old[uint16Bytes:] 289 | new = new[uint16Bytes:] 290 | } 291 | 292 | return xsum 293 | } 294 | -------------------------------------------------------------------------------- /divert/windivert_device.h: -------------------------------------------------------------------------------- 1 | /* 2 | * windivert_device.h 3 | * (C) 2019, all rights reserved, 4 | * 5 | * This file is part of WinDivert. 6 | * 7 | * WinDivert is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program. If not, see . 19 | * 20 | * WinDivert is free software; you can redistribute it and/or modify it under 21 | * the terms of the GNU General Public License as published by the Free 22 | * Software Foundation; either version 2 of the License, or (at your option) 23 | * any later version. 24 | * 25 | * This program is distributed in the hope that it will be useful, but 26 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 27 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 28 | * for more details. 29 | * 30 | * You should have received a copy of the GNU General Public License along 31 | * with this program; if not, write to the Free Software Foundation, Inc., 51 32 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 33 | */ 34 | 35 | #ifndef __WINDIVERT_DEVICE_H 36 | #define __WINDIVERT_DEVICE_H 37 | 38 | /* 39 | * NOTE: This is the low-level interface to the WinDivert device driver. 40 | * This interface should not be used directly, instead use the high-level 41 | * interface provided by the WinDivert API. 42 | */ 43 | 44 | #define WINDIVERT_KERNEL 45 | #include "windivert.h" 46 | 47 | #define WINDIVERT_VERSION_MAJOR 2 48 | #define WINDIVERT_VERSION_MINOR 2 49 | 50 | #define WINDIVERT_MAGIC_DLL 0x4C4C447669645724ull 51 | #define WINDIVERT_MAGIC_SYS 0x5359537669645723ull 52 | 53 | #define WINDIVERT_STR2(s) #s 54 | #define WINDIVERT_STR(s) WINDIVERT_STR2(s) 55 | #define WINDIVERT_LSTR2(s) L ## #s 56 | #define WINDIVERT_LSTR(s) WINDIVERT_LSTR2(s) 57 | 58 | #define WINDIVERT_VERSION_LSTR \ 59 | WINDIVERT_LSTR(WINDIVERT_VERSION_MAJOR) L"." \ 60 | WINDIVERT_LSTR(WINDIVERT_VERSION_MINOR) 61 | 62 | #define WINDIVERT_DEVICE_NAME \ 63 | L"WinDivert" 64 | #define WINDIVERT_LAYER_NAME \ 65 | WINDIVERT_DEVICE_NAME WINDIVERT_VERSION_LSTR 66 | 67 | #define WINDIVERT_FILTER_FIELD_ZERO 0 68 | #define WINDIVERT_FILTER_FIELD_INBOUND 1 69 | #define WINDIVERT_FILTER_FIELD_OUTBOUND 2 70 | #define WINDIVERT_FILTER_FIELD_IFIDX 3 71 | #define WINDIVERT_FILTER_FIELD_SUBIFIDX 4 72 | #define WINDIVERT_FILTER_FIELD_IP 5 73 | #define WINDIVERT_FILTER_FIELD_IPV6 6 74 | #define WINDIVERT_FILTER_FIELD_ICMP 7 75 | #define WINDIVERT_FILTER_FIELD_TCP 8 76 | #define WINDIVERT_FILTER_FIELD_UDP 9 77 | #define WINDIVERT_FILTER_FIELD_ICMPV6 10 78 | #define WINDIVERT_FILTER_FIELD_IP_HDRLENGTH 11 79 | #define WINDIVERT_FILTER_FIELD_IP_TOS 12 80 | #define WINDIVERT_FILTER_FIELD_IP_LENGTH 13 81 | #define WINDIVERT_FILTER_FIELD_IP_ID 14 82 | #define WINDIVERT_FILTER_FIELD_IP_DF 15 83 | #define WINDIVERT_FILTER_FIELD_IP_MF 16 84 | #define WINDIVERT_FILTER_FIELD_IP_FRAGOFF 17 85 | #define WINDIVERT_FILTER_FIELD_IP_TTL 18 86 | #define WINDIVERT_FILTER_FIELD_IP_PROTOCOL 19 87 | #define WINDIVERT_FILTER_FIELD_IP_CHECKSUM 20 88 | #define WINDIVERT_FILTER_FIELD_IP_SRCADDR 21 89 | #define WINDIVERT_FILTER_FIELD_IP_DSTADDR 22 90 | #define WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS 23 91 | #define WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL 24 92 | #define WINDIVERT_FILTER_FIELD_IPV6_LENGTH 25 93 | #define WINDIVERT_FILTER_FIELD_IPV6_NEXTHDR 26 94 | #define WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT 27 95 | #define WINDIVERT_FILTER_FIELD_IPV6_SRCADDR 28 96 | #define WINDIVERT_FILTER_FIELD_IPV6_DSTADDR 29 97 | #define WINDIVERT_FILTER_FIELD_ICMP_TYPE 30 98 | #define WINDIVERT_FILTER_FIELD_ICMP_CODE 31 99 | #define WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM 32 100 | #define WINDIVERT_FILTER_FIELD_ICMP_BODY 33 101 | #define WINDIVERT_FILTER_FIELD_ICMPV6_TYPE 34 102 | #define WINDIVERT_FILTER_FIELD_ICMPV6_CODE 35 103 | #define WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM 36 104 | #define WINDIVERT_FILTER_FIELD_ICMPV6_BODY 37 105 | #define WINDIVERT_FILTER_FIELD_TCP_SRCPORT 38 106 | #define WINDIVERT_FILTER_FIELD_TCP_DSTPORT 39 107 | #define WINDIVERT_FILTER_FIELD_TCP_SEQNUM 40 108 | #define WINDIVERT_FILTER_FIELD_TCP_ACKNUM 41 109 | #define WINDIVERT_FILTER_FIELD_TCP_HDRLENGTH 42 110 | #define WINDIVERT_FILTER_FIELD_TCP_URG 43 111 | #define WINDIVERT_FILTER_FIELD_TCP_ACK 44 112 | #define WINDIVERT_FILTER_FIELD_TCP_PSH 45 113 | #define WINDIVERT_FILTER_FIELD_TCP_RST 46 114 | #define WINDIVERT_FILTER_FIELD_TCP_SYN 47 115 | #define WINDIVERT_FILTER_FIELD_TCP_FIN 48 116 | #define WINDIVERT_FILTER_FIELD_TCP_WINDOW 49 117 | #define WINDIVERT_FILTER_FIELD_TCP_CHECKSUM 50 118 | #define WINDIVERT_FILTER_FIELD_TCP_URGPTR 51 119 | #define WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH 52 120 | #define WINDIVERT_FILTER_FIELD_UDP_SRCPORT 53 121 | #define WINDIVERT_FILTER_FIELD_UDP_DSTPORT 54 122 | #define WINDIVERT_FILTER_FIELD_UDP_LENGTH 55 123 | #define WINDIVERT_FILTER_FIELD_UDP_CHECKSUM 56 124 | #define WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH 57 125 | #define WINDIVERT_FILTER_FIELD_LOOPBACK 58 126 | #define WINDIVERT_FILTER_FIELD_IMPOSTOR 59 127 | #define WINDIVERT_FILTER_FIELD_PROCESSID 60 128 | #define WINDIVERT_FILTER_FIELD_LOCALADDR 61 129 | #define WINDIVERT_FILTER_FIELD_REMOTEADDR 62 130 | #define WINDIVERT_FILTER_FIELD_LOCALPORT 63 131 | #define WINDIVERT_FILTER_FIELD_REMOTEPORT 64 132 | #define WINDIVERT_FILTER_FIELD_PROTOCOL 65 133 | #define WINDIVERT_FILTER_FIELD_ENDPOINTID 66 134 | #define WINDIVERT_FILTER_FIELD_PARENTENDPOINTID 67 135 | #define WINDIVERT_FILTER_FIELD_LAYER 68 136 | #define WINDIVERT_FILTER_FIELD_PRIORITY 69 137 | #define WINDIVERT_FILTER_FIELD_EVENT 70 138 | #define WINDIVERT_FILTER_FIELD_PACKET 71 139 | #define WINDIVERT_FILTER_FIELD_PACKET16 72 140 | #define WINDIVERT_FILTER_FIELD_PACKET32 73 141 | #define WINDIVERT_FILTER_FIELD_TCP_PAYLOAD 74 142 | #define WINDIVERT_FILTER_FIELD_TCP_PAYLOAD16 75 143 | #define WINDIVERT_FILTER_FIELD_TCP_PAYLOAD32 76 144 | #define WINDIVERT_FILTER_FIELD_UDP_PAYLOAD 77 145 | #define WINDIVERT_FILTER_FIELD_UDP_PAYLOAD16 78 146 | #define WINDIVERT_FILTER_FIELD_UDP_PAYLOAD32 79 147 | #define WINDIVERT_FILTER_FIELD_LENGTH 80 148 | #define WINDIVERT_FILTER_FIELD_TIMESTAMP 81 149 | #define WINDIVERT_FILTER_FIELD_RANDOM8 82 150 | #define WINDIVERT_FILTER_FIELD_RANDOM16 83 151 | #define WINDIVERT_FILTER_FIELD_RANDOM32 84 152 | #define WINDIVERT_FILTER_FIELD_FRAGMENT 85 153 | #define WINDIVERT_FILTER_FIELD_MAX \ 154 | WINDIVERT_FILTER_FIELD_FRAGMENT 155 | 156 | #define WINDIVERT_FILTER_TEST_EQ 0 157 | #define WINDIVERT_FILTER_TEST_NEQ 1 158 | #define WINDIVERT_FILTER_TEST_LT 2 159 | #define WINDIVERT_FILTER_TEST_LEQ 3 160 | #define WINDIVERT_FILTER_TEST_GT 4 161 | #define WINDIVERT_FILTER_TEST_GEQ 5 162 | #define WINDIVERT_FILTER_TEST_MAX WINDIVERT_FILTER_TEST_GEQ 163 | 164 | #define WINDIVERT_FILTER_MAXLEN 256 165 | 166 | #define WINDIVERT_FILTER_RESULT_ACCEPT 0x7FFE 167 | #define WINDIVERT_FILTER_RESULT_REJECT 0x7FFF 168 | 169 | /* 170 | * WinDivert layers. 171 | */ 172 | #define WINDIVERT_LAYER_MAX WINDIVERT_LAYER_REFLECT 173 | 174 | /* 175 | * WinDivert events. 176 | */ 177 | #define WINDIVERT_EVENT_MAX \ 178 | WINDIVERT_EVENT_REFLECT_CLOSE 179 | 180 | /* 181 | * WinDivert flags. 182 | */ 183 | #define WINDIVERT_FLAGS_ALL \ 184 | (WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_DROP | WINDIVERT_FLAG_RECV_ONLY |\ 185 | WINDIVERT_FLAG_SEND_ONLY | WINDIVERT_FLAG_NO_INSTALL | \ 186 | WINDIVERT_FLAG_FRAGMENTS) 187 | #define WINDIVERT_FLAGS_EXCLUDE(flags, flag1, flag2) \ 188 | (((flags) & ((flag1) | (flag2))) != ((flag1) | (flag2))) 189 | #define WINDIVERT_FLAGS_VALID(flags) \ 190 | ((((flags) & ~WINDIVERT_FLAGS_ALL) == 0) && \ 191 | WINDIVERT_FLAGS_EXCLUDE(flags, WINDIVERT_FLAG_SNIFF, \ 192 | WINDIVERT_FLAG_DROP) && \ 193 | WINDIVERT_FLAGS_EXCLUDE(flags, WINDIVERT_FLAG_RECV_ONLY, \ 194 | WINDIVERT_FLAG_SEND_ONLY)) 195 | 196 | /* 197 | * WinDivert filter flags. 198 | */ 199 | #define WINDIVERT_FILTER_FLAG_INBOUND 0x0000000000000010ull 200 | #define WINDIVERT_FILTER_FLAG_OUTBOUND 0x0000000000000020ull 201 | #define WINDIVERT_FILTER_FLAG_IP 0x0000000000000040ull 202 | #define WINDIVERT_FILTER_FLAG_IPV6 0x0000000000000080ull 203 | #define WINDIVERT_FILTER_FLAG_EVENT_FLOW_DELETED 0x0000000000000100ull 204 | #define WINDIVERT_FILTER_FLAG_EVENT_SOCKET_BIND 0x0000000000000200ull 205 | #define WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CONNECT 0x0000000000000400ull 206 | #define WINDIVERT_FILTER_FLAG_EVENT_SOCKET_LISTEN 0x0000000000000800ull 207 | #define WINDIVERT_FILTER_FLAG_EVENT_SOCKET_ACCEPT 0x0000000000001000ull 208 | #define WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CLOSE 0x0000000000002000ull 209 | 210 | #define WINDIVERT_FILTER_FLAGS_ALL \ 211 | (WINDIVERT_FILTER_FLAG_INBOUND | \ 212 | WINDIVERT_FILTER_FLAG_OUTBOUND | \ 213 | WINDIVERT_FILTER_FLAG_IP | \ 214 | WINDIVERT_FILTER_FLAG_IPV6 | \ 215 | WINDIVERT_FILTER_FLAG_EVENT_FLOW_DELETED | \ 216 | WINDIVERT_FILTER_FLAG_EVENT_SOCKET_BIND | \ 217 | WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CONNECT | \ 218 | WINDIVERT_FILTER_FLAG_EVENT_SOCKET_LISTEN | \ 219 | WINDIVERT_FILTER_FLAG_EVENT_SOCKET_ACCEPT | \ 220 | WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CLOSE) 221 | 222 | /* 223 | * WinDivert priorities. 224 | */ 225 | #define WINDIVERT_PRIORITY_MAX WINDIVERT_PRIORITY_HIGHEST 226 | #define WINDIVERT_PRIORITY_MIN WINDIVERT_PRIORITY_LOWEST 227 | 228 | /* 229 | * WinDivert timestamps. 230 | */ 231 | #define WINDIVERT_TIMESTAMP_MAX 0x7FFFFFFFFFFFFFFFull 232 | 233 | /* 234 | * WinDivert message definitions. 235 | */ 236 | #pragma pack(push, 1) 237 | typedef union 238 | { 239 | struct 240 | { 241 | UINT64 addr; // WINDIVERT_ADDRESS pointer. 242 | UINT64 addr_len_ptr; // sizeof(addr) pointer. 243 | } recv; 244 | struct 245 | { 246 | UINT64 addr; // WINDIVERT_ADDRESS pointer. 247 | UINT64 addr_len; // sizeof(addr). 248 | } send; 249 | struct 250 | { 251 | UINT32 layer; // Handle layer. 252 | UINT32 priority; // Handle priority. 253 | UINT64 flags; // Handle flags. 254 | } initialize; 255 | struct 256 | { 257 | UINT64 flags; // Filter flags. 258 | } startup; 259 | struct 260 | { 261 | UINT32 how; // WINDIVERT_SHUTDOWN_* 262 | } shutdown; 263 | struct 264 | { 265 | UINT32 param; // WINDIVERT_PARAM_* 266 | } get_param; 267 | struct 268 | { 269 | UINT64 val; // Value pointer. 270 | UINT32 param; // WINDIVERT_PARAM_* 271 | } set_param; 272 | } WINDIVERT_IOCTL, *PWINDIVERT_IOCTL; 273 | 274 | /* 275 | * WinDivert initialization structure. 276 | */ 277 | typedef struct 278 | { 279 | UINT64 magic; // Magic number (in/out). 280 | UINT32 major; // Driver major version (in/out). 281 | UINT32 minor; // Driver minor version (in/out). 282 | UINT32 bits; // 32 or 64 (in/out). 283 | UINT32 reserved32[3]; 284 | UINT64 reserved64[4]; 285 | } WINDIVERT_VERSION, *PWINDIVERT_VERSION; 286 | 287 | /* 288 | * WinDivert filter structure. 289 | */ 290 | typedef struct 291 | { 292 | UINT32 field:11; // WINDIVERT_FILTER_FIELD_* 293 | UINT32 test:5; // WINDIVERT_FILTER_TEST_* 294 | UINT32 success:16; // Success continuation. 295 | UINT32 failure:16; // Fail continuation. 296 | UINT32 neg:1; // Argument negative? 297 | UINT32 reserved:15; 298 | UINT32 arg[4]; // Argument. 299 | } WINDIVERT_FILTER, *PWINDIVERT_FILTER; 300 | #pragma pack(pop) 301 | 302 | /* 303 | * IOCTL codes. 304 | */ 305 | #define IOCTL_WINDIVERT_INITIALIZE \ 306 | CTL_CODE(FILE_DEVICE_NETWORK, 0x921, METHOD_OUT_DIRECT, FILE_READ_DATA |\ 307 | FILE_WRITE_DATA) 308 | #define IOCTL_WINDIVERT_STARTUP \ 309 | CTL_CODE(FILE_DEVICE_NETWORK, 0x922, METHOD_IN_DIRECT, FILE_READ_DATA | \ 310 | FILE_WRITE_DATA) 311 | #define IOCTL_WINDIVERT_RECV \ 312 | CTL_CODE(FILE_DEVICE_NETWORK, 0x923, METHOD_OUT_DIRECT, FILE_READ_DATA) 313 | #define IOCTL_WINDIVERT_SEND \ 314 | CTL_CODE(FILE_DEVICE_NETWORK, 0x924, METHOD_IN_DIRECT, FILE_READ_DATA | \ 315 | FILE_WRITE_DATA) 316 | #define IOCTL_WINDIVERT_SET_PARAM \ 317 | CTL_CODE(FILE_DEVICE_NETWORK, 0x925, METHOD_IN_DIRECT, FILE_READ_DATA | \ 318 | FILE_WRITE_DATA) 319 | #define IOCTL_WINDIVERT_GET_PARAM \ 320 | CTL_CODE(FILE_DEVICE_NETWORK, 0x926, METHOD_OUT_DIRECT, FILE_READ_DATA) 321 | #define IOCTL_WINDIVERT_SHUTDOWN \ 322 | CTL_CODE(FILE_DEVICE_NETWORK, 0x927, METHOD_IN_DIRECT, FILE_READ_DATA | \ 323 | FILE_WRITE_DATA) 324 | 325 | #endif /* __WINDIVERT_DEVICE_H */ 326 | -------------------------------------------------------------------------------- /memmod/syscall_windows.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package memmod 7 | 8 | import "unsafe" 9 | 10 | const ( 11 | IMAGE_DOS_SIGNATURE = 0x5A4D // MZ 12 | IMAGE_OS2_SIGNATURE = 0x454E // NE 13 | IMAGE_OS2_SIGNATURE_LE = 0x454C // LE 14 | IMAGE_VXD_SIGNATURE = 0x454C // LE 15 | IMAGE_NT_SIGNATURE = 0x00004550 // PE00 16 | ) 17 | 18 | // DOS .EXE header 19 | type IMAGE_DOS_HEADER struct { 20 | E_magic uint16 // Magic number 21 | E_cblp uint16 // Bytes on last page of file 22 | E_cp uint16 // Pages in file 23 | E_crlc uint16 // Relocations 24 | E_cparhdr uint16 // Size of header in paragraphs 25 | E_minalloc uint16 // Minimum extra paragraphs needed 26 | E_maxalloc uint16 // Maximum extra paragraphs needed 27 | E_ss uint16 // Initial (relative) SS value 28 | E_sp uint16 // Initial SP value 29 | E_csum uint16 // Checksum 30 | E_ip uint16 // Initial IP value 31 | E_cs uint16 // Initial (relative) CS value 32 | E_lfarlc uint16 // File address of relocation table 33 | E_ovno uint16 // Overlay number 34 | E_res [4]uint16 // Reserved words 35 | E_oemid uint16 // OEM identifier (for e_oeminfo) 36 | E_oeminfo uint16 // OEM information; e_oemid specific 37 | E_res2 [10]uint16 // Reserved words 38 | E_lfanew int32 // File address of new exe header 39 | } 40 | 41 | // File header format 42 | type IMAGE_FILE_HEADER struct { 43 | Machine uint16 44 | NumberOfSections uint16 45 | TimeDateStamp uint32 46 | PointerToSymbolTable uint32 47 | NumberOfSymbols uint32 48 | SizeOfOptionalHeader uint16 49 | Characteristics uint16 50 | } 51 | 52 | const ( 53 | IMAGE_SIZEOF_FILE_HEADER = 20 54 | 55 | IMAGE_FILE_RELOCS_STRIPPED = 0x0001 // Relocation info stripped from file. 56 | IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 // File is executable (i.e. no unresolved external references). 57 | IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 // Line nunbers stripped from file. 58 | IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 // Local symbols stripped from file. 59 | IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010 // Aggressively trim working set 60 | IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 // App can handle >2gb addresses 61 | IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 // Bytes of machine word are reversed. 62 | IMAGE_FILE_32BIT_MACHINE = 0x0100 // 32 bit word machine. 63 | IMAGE_FILE_DEBUG_STRIPPED = 0x0200 // Debugging info stripped from file in .DBG file 64 | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 // If Image is on removable media, copy and run from the swap file. 65 | IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 // If Image is on Net, copy and run from the swap file. 66 | IMAGE_FILE_SYSTEM = 0x1000 // System File. 67 | IMAGE_FILE_DLL = 0x2000 // File is a DLL. 68 | IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 // File should only be run on a UP machine 69 | IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 // Bytes of machine word are reversed. 70 | 71 | IMAGE_FILE_MACHINE_UNKNOWN = 0 72 | IMAGE_FILE_MACHINE_TARGET_HOST = 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest. 73 | IMAGE_FILE_MACHINE_I386 = 0x014c // Intel 386. 74 | IMAGE_FILE_MACHINE_R3000 = 0x0162 // MIPS little-endian, 0x160 big-endian 75 | IMAGE_FILE_MACHINE_R4000 = 0x0166 // MIPS little-endian 76 | IMAGE_FILE_MACHINE_R10000 = 0x0168 // MIPS little-endian 77 | IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x0169 // MIPS little-endian WCE v2 78 | IMAGE_FILE_MACHINE_ALPHA = 0x0184 // Alpha_AXP 79 | IMAGE_FILE_MACHINE_SH3 = 0x01a2 // SH3 little-endian 80 | IMAGE_FILE_MACHINE_SH3DSP = 0x01a3 81 | IMAGE_FILE_MACHINE_SH3E = 0x01a4 // SH3E little-endian 82 | IMAGE_FILE_MACHINE_SH4 = 0x01a6 // SH4 little-endian 83 | IMAGE_FILE_MACHINE_SH5 = 0x01a8 // SH5 84 | IMAGE_FILE_MACHINE_ARM = 0x01c0 // ARM Little-Endian 85 | IMAGE_FILE_MACHINE_THUMB = 0x01c2 // ARM Thumb/Thumb-2 Little-Endian 86 | IMAGE_FILE_MACHINE_ARMNT = 0x01c4 // ARM Thumb-2 Little-Endian 87 | IMAGE_FILE_MACHINE_AM33 = 0x01d3 88 | IMAGE_FILE_MACHINE_POWERPC = 0x01F0 // IBM PowerPC Little-Endian 89 | IMAGE_FILE_MACHINE_POWERPCFP = 0x01f1 90 | IMAGE_FILE_MACHINE_IA64 = 0x0200 // Intel 64 91 | IMAGE_FILE_MACHINE_MIPS16 = 0x0266 // MIPS 92 | IMAGE_FILE_MACHINE_ALPHA64 = 0x0284 // ALPHA64 93 | IMAGE_FILE_MACHINE_MIPSFPU = 0x0366 // MIPS 94 | IMAGE_FILE_MACHINE_MIPSFPU16 = 0x0466 // MIPS 95 | IMAGE_FILE_MACHINE_AXP64 = IMAGE_FILE_MACHINE_ALPHA64 96 | IMAGE_FILE_MACHINE_TRICORE = 0x0520 // Infineon 97 | IMAGE_FILE_MACHINE_CEF = 0x0CEF 98 | IMAGE_FILE_MACHINE_EBC = 0x0EBC // EFI Byte Code 99 | IMAGE_FILE_MACHINE_AMD64 = 0x8664 // AMD64 (K8) 100 | IMAGE_FILE_MACHINE_M32R = 0x9041 // M32R little-endian 101 | IMAGE_FILE_MACHINE_ARM64 = 0xAA64 // ARM64 Little-Endian 102 | IMAGE_FILE_MACHINE_CEE = 0xC0EE 103 | ) 104 | 105 | // Directory format 106 | type IMAGE_DATA_DIRECTORY struct { 107 | VirtualAddress uint32 108 | Size uint32 109 | } 110 | 111 | const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16 112 | 113 | type IMAGE_NT_HEADERS struct { 114 | Signature uint32 115 | FileHeader IMAGE_FILE_HEADER 116 | OptionalHeader IMAGE_OPTIONAL_HEADER 117 | } 118 | 119 | func (ntheader *IMAGE_NT_HEADERS) Sections() []IMAGE_SECTION_HEADER { 120 | return (*[0xffff]IMAGE_SECTION_HEADER)(unsafe.Pointer( 121 | (uintptr)(unsafe.Pointer(ntheader)) + 122 | unsafe.Offsetof(ntheader.OptionalHeader) + 123 | uintptr(ntheader.FileHeader.SizeOfOptionalHeader)))[:ntheader.FileHeader.NumberOfSections] 124 | } 125 | 126 | const ( 127 | IMAGE_DIRECTORY_ENTRY_EXPORT = 0 // Export Directory 128 | IMAGE_DIRECTORY_ENTRY_IMPORT = 1 // Import Directory 129 | IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 // Resource Directory 130 | IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 // Exception Directory 131 | IMAGE_DIRECTORY_ENTRY_SECURITY = 4 // Security Directory 132 | IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 // Base Relocation Table 133 | IMAGE_DIRECTORY_ENTRY_DEBUG = 6 // Debug Directory 134 | IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 // (X86 usage) 135 | IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 // Architecture Specific Data 136 | IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 // RVA of GP 137 | IMAGE_DIRECTORY_ENTRY_TLS = 9 // TLS Directory 138 | IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 // Load Configuration Directory 139 | IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 // Bound Import Directory in headers 140 | IMAGE_DIRECTORY_ENTRY_IAT = 12 // Import Address Table 141 | IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 // Delay Load Import Descriptors 142 | IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 // COM Runtime descriptor 143 | ) 144 | 145 | const IMAGE_SIZEOF_SHORT_NAME = 8 146 | 147 | // Section header format 148 | type IMAGE_SECTION_HEADER struct { 149 | Name [IMAGE_SIZEOF_SHORT_NAME]byte 150 | physicalAddressOrVirtualSize uint32 151 | VirtualAddress uint32 152 | SizeOfRawData uint32 153 | PointerToRawData uint32 154 | PointerToRelocations uint32 155 | PointerToLinenumbers uint32 156 | NumberOfRelocations uint16 157 | NumberOfLinenumbers uint16 158 | Characteristics uint32 159 | } 160 | 161 | func (ishdr *IMAGE_SECTION_HEADER) PhysicalAddress() uint32 { 162 | return ishdr.physicalAddressOrVirtualSize 163 | } 164 | 165 | func (ishdr *IMAGE_SECTION_HEADER) SetPhysicalAddress(addr uint32) { 166 | ishdr.physicalAddressOrVirtualSize = addr 167 | } 168 | 169 | func (ishdr *IMAGE_SECTION_HEADER) VirtualSize() uint32 { 170 | return ishdr.physicalAddressOrVirtualSize 171 | } 172 | 173 | func (ishdr *IMAGE_SECTION_HEADER) SetVirtualSize(addr uint32) { 174 | ishdr.physicalAddressOrVirtualSize = addr 175 | } 176 | 177 | const ( 178 | // Dll characteristics. 179 | IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020 180 | IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040 181 | IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080 182 | IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100 183 | IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION = 0x0200 184 | IMAGE_DLL_CHARACTERISTICS_NO_SEH = 0x0400 185 | IMAGE_DLL_CHARACTERISTICS_NO_BIND = 0x0800 186 | IMAGE_DLL_CHARACTERISTICS_APPCONTAINER = 0x1000 187 | IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER = 0x2000 188 | IMAGE_DLL_CHARACTERISTICS_GUARD_CF = 0x4000 189 | IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000 190 | ) 191 | 192 | const ( 193 | // Section characteristics. 194 | IMAGE_SCN_TYPE_REG = 0x00000000 // Reserved. 195 | IMAGE_SCN_TYPE_DSECT = 0x00000001 // Reserved. 196 | IMAGE_SCN_TYPE_NOLOAD = 0x00000002 // Reserved. 197 | IMAGE_SCN_TYPE_GROUP = 0x00000004 // Reserved. 198 | IMAGE_SCN_TYPE_NO_PAD = 0x00000008 // Reserved. 199 | IMAGE_SCN_TYPE_COPY = 0x00000010 // Reserved. 200 | 201 | IMAGE_SCN_CNT_CODE = 0x00000020 // Section contains code. 202 | IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 // Section contains initialized data. 203 | IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 // Section contains uninitialized data. 204 | 205 | IMAGE_SCN_LNK_OTHER = 0x00000100 // Reserved. 206 | IMAGE_SCN_LNK_INFO = 0x00000200 // Section contains comments or some other type of information. 207 | IMAGE_SCN_TYPE_OVER = 0x00000400 // Reserved. 208 | IMAGE_SCN_LNK_REMOVE = 0x00000800 // Section contents will not become part of image. 209 | IMAGE_SCN_LNK_COMDAT = 0x00001000 // Section contents comdat. 210 | IMAGE_SCN_MEM_PROTECTED = 0x00004000 // Obsolete. 211 | IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section. 212 | IMAGE_SCN_GPREL = 0x00008000 // Section content can be accessed relative to GP 213 | IMAGE_SCN_MEM_FARDATA = 0x00008000 214 | IMAGE_SCN_MEM_SYSHEAP = 0x00010000 // Obsolete. 215 | IMAGE_SCN_MEM_PURGEABLE = 0x00020000 216 | IMAGE_SCN_MEM_16BIT = 0x00020000 217 | IMAGE_SCN_MEM_LOCKED = 0x00040000 218 | IMAGE_SCN_MEM_PRELOAD = 0x00080000 219 | 220 | IMAGE_SCN_ALIGN_1BYTES = 0x00100000 // 221 | IMAGE_SCN_ALIGN_2BYTES = 0x00200000 // 222 | IMAGE_SCN_ALIGN_4BYTES = 0x00300000 // 223 | IMAGE_SCN_ALIGN_8BYTES = 0x00400000 // 224 | IMAGE_SCN_ALIGN_16BYTES = 0x00500000 // Default alignment if no others are specified. 225 | IMAGE_SCN_ALIGN_32BYTES = 0x00600000 // 226 | IMAGE_SCN_ALIGN_64BYTES = 0x00700000 // 227 | IMAGE_SCN_ALIGN_128BYTES = 0x00800000 // 228 | IMAGE_SCN_ALIGN_256BYTES = 0x00900000 // 229 | IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 // 230 | IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 // 231 | IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 // 232 | IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 // 233 | IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 // 234 | IMAGE_SCN_ALIGN_MASK = 0x00F00000 235 | 236 | IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 // Section contains extended relocations. 237 | IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 // Section can be discarded. 238 | IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 // Section is not cachable. 239 | IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 // Section is not pageable. 240 | IMAGE_SCN_MEM_SHARED = 0x10000000 // Section is shareable. 241 | IMAGE_SCN_MEM_EXECUTE = 0x20000000 // Section is executable. 242 | IMAGE_SCN_MEM_READ = 0x40000000 // Section is readable. 243 | IMAGE_SCN_MEM_WRITE = 0x80000000 // Section is writeable. 244 | 245 | // TLS Characteristic Flags 246 | IMAGE_SCN_SCALE_INDEX = 0x00000001 // Tls index is scaled. 247 | ) 248 | 249 | // Based relocation format 250 | type IMAGE_BASE_RELOCATION struct { 251 | VirtualAddress uint32 252 | SizeOfBlock uint32 253 | } 254 | 255 | const ( 256 | IMAGE_REL_BASED_ABSOLUTE = 0 257 | IMAGE_REL_BASED_HIGH = 1 258 | IMAGE_REL_BASED_LOW = 2 259 | IMAGE_REL_BASED_HIGHLOW = 3 260 | IMAGE_REL_BASED_HIGHADJ = 4 261 | IMAGE_REL_BASED_MACHINE_SPECIFIC_5 = 5 262 | IMAGE_REL_BASED_RESERVED = 6 263 | IMAGE_REL_BASED_MACHINE_SPECIFIC_7 = 7 264 | IMAGE_REL_BASED_MACHINE_SPECIFIC_8 = 8 265 | IMAGE_REL_BASED_MACHINE_SPECIFIC_9 = 9 266 | IMAGE_REL_BASED_DIR64 = 10 267 | 268 | IMAGE_REL_BASED_IA64_IMM64 = 9 269 | 270 | IMAGE_REL_BASED_MIPS_JMPADDR = 5 271 | IMAGE_REL_BASED_MIPS_JMPADDR16 = 9 272 | 273 | IMAGE_REL_BASED_ARM_MOV32 = 5 274 | IMAGE_REL_BASED_THUMB_MOV32 = 7 275 | ) 276 | 277 | // Export Format 278 | type IMAGE_EXPORT_DIRECTORY struct { 279 | Characteristics uint32 280 | TimeDateStamp uint32 281 | MajorVersion uint16 282 | MinorVersion uint16 283 | Name uint32 284 | Base uint32 285 | NumberOfFunctions uint32 286 | NumberOfNames uint32 287 | AddressOfFunctions uint32 // RVA from base of image 288 | AddressOfNames uint32 // RVA from base of image 289 | AddressOfNameOrdinals uint32 // RVA from base of image 290 | } 291 | 292 | type IMAGE_IMPORT_BY_NAME struct { 293 | Hint uint16 294 | Name [1]byte 295 | } 296 | 297 | func IMAGE_ORDINAL(ordinal uintptr) uintptr { 298 | return ordinal & 0xffff 299 | } 300 | 301 | func IMAGE_SNAP_BY_ORDINAL(ordinal uintptr) bool { 302 | return (ordinal & IMAGE_ORDINAL_FLAG) != 0 303 | } 304 | 305 | // Thread Local Storage 306 | type IMAGE_TLS_DIRECTORY struct { 307 | StartAddressOfRawData uintptr 308 | EndAddressOfRawData uintptr 309 | AddressOfIndex uintptr // PDWORD 310 | AddressOfCallbacks uintptr // PIMAGE_TLS_CALLBACK *; 311 | SizeOfZeroFill uint32 312 | Characteristics uint32 313 | } 314 | 315 | type IMAGE_IMPORT_DESCRIPTOR struct { 316 | characteristicsOrOriginalFirstThunk uint32 // 0 for terminating null import descriptor 317 | // RVA to original unbound IAT (PIMAGE_THUNK_DATA) 318 | TimeDateStamp uint32 // 0 if not bound, 319 | // -1 if bound, and real date\time stamp 320 | // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) 321 | // O.W. date/time stamp of DLL bound to (Old BIND) 322 | ForwarderChain uint32 // -1 if no forwarders 323 | Name uint32 324 | FirstThunk uint32 // RVA to IAT (if bound this IAT has actual addresses) 325 | } 326 | 327 | func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) Characteristics() uint32 { 328 | return imgimpdesc.characteristicsOrOriginalFirstThunk 329 | } 330 | 331 | func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) OriginalFirstThunk() uint32 { 332 | return imgimpdesc.characteristicsOrOriginalFirstThunk 333 | } 334 | 335 | type IMAGE_DELAYLOAD_DESCRIPTOR struct { 336 | Attributes uint32 337 | DllNameRVA uint32 338 | ModuleHandleRVA uint32 339 | ImportAddressTableRVA uint32 340 | ImportNameTableRVA uint32 341 | BoundImportAddressTableRVA uint32 342 | UnloadInformationTableRVA uint32 343 | TimeDateStamp uint32 344 | } 345 | 346 | type IMAGE_LOAD_CONFIG_CODE_INTEGRITY struct { 347 | Flags uint16 348 | Catalog uint16 349 | CatalogOffset uint32 350 | Reserved uint32 351 | } 352 | 353 | const ( 354 | IMAGE_GUARD_CF_INSTRUMENTED = 0x00000100 355 | IMAGE_GUARD_CFW_INSTRUMENTED = 0x00000200 356 | IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT = 0x00000400 357 | IMAGE_GUARD_SECURITY_COOKIE_UNUSED = 0x00000800 358 | IMAGE_GUARD_PROTECT_DELAYLOAD_IAT = 0x00001000 359 | IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION = 0x00002000 360 | IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT = 0x00004000 361 | IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION = 0x00008000 362 | IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT = 0x00010000 363 | IMAGE_GUARD_RF_INSTRUMENTED = 0x00020000 364 | IMAGE_GUARD_RF_ENABLE = 0x00040000 365 | IMAGE_GUARD_RF_STRICT = 0x00080000 366 | IMAGE_GUARD_RETPOLINE_PRESENT = 0x00100000 367 | IMAGE_GUARD_EH_CONTINUATION_TABLE_PRESENT = 0x00400000 368 | IMAGE_GUARD_XFG_ENABLED = 0x00800000 369 | IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK = 0xF0000000 370 | IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT = 28 371 | ) 372 | 373 | const ( 374 | DLL_PROCESS_ATTACH = 1 375 | DLL_THREAD_ATTACH = 2 376 | DLL_THREAD_DETACH = 3 377 | DLL_PROCESS_DETACH = 0 378 | ) 379 | 380 | type SYSTEM_INFO struct { 381 | ProcessorArchitecture uint16 382 | Reserved uint16 383 | PageSize uint32 384 | MinimumApplicationAddress uintptr 385 | MaximumApplicationAddress uintptr 386 | ActiveProcessorMask uintptr 387 | NumberOfProcessors uint32 388 | ProcessorType uint32 389 | AllocationGranularity uint32 390 | ProcessorLevel uint16 391 | ProcessorRevision uint16 392 | } 393 | -------------------------------------------------------------------------------- /divert/windivert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * windivert.h 3 | * (C) 2019, all rights reserved, 4 | * 5 | * This file is part of WinDivert. 6 | * 7 | * WinDivert is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program. If not, see . 19 | * 20 | * WinDivert is free software; you can redistribute it and/or modify it under 21 | * the terms of the GNU General Public License as published by the Free 22 | * Software Foundation; either version 2 of the License, or (at your option) 23 | * any later version. 24 | * 25 | * This program is distributed in the hope that it will be useful, but 26 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 27 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 28 | * for more details. 29 | * 30 | * You should have received a copy of the GNU General Public License along 31 | * with this program; if not, write to the Free Software Foundation, Inc., 51 32 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 33 | */ 34 | 35 | #ifndef __WINDIVERT_H 36 | #define __WINDIVERT_H 37 | 38 | #ifndef WINDIVERT_KERNEL 39 | #include 40 | #endif /* WINDIVERT_KERNEL */ 41 | 42 | #ifndef WINDIVERTEXPORT 43 | #define WINDIVERTEXPORT extern __declspec(dllimport) 44 | #endif /* WINDIVERTEXPORT */ 45 | 46 | #ifdef __MINGW32__ 47 | #define __in 48 | #define __in_opt 49 | #define __out 50 | #define __out_opt 51 | #define __inout 52 | #define __inout_opt 53 | #include 54 | #define INT8 int8_t 55 | #define UINT8 uint8_t 56 | #define INT16 int16_t 57 | #define UINT16 uint16_t 58 | #define INT32 int32_t 59 | #define UINT32 uint32_t 60 | #define INT64 int64_t 61 | #define UINT64 uint64_t 62 | #endif /* __MINGW32__ */ 63 | 64 | #ifdef __cplusplus 65 | extern "C" { 66 | #endif 67 | 68 | /****************************************************************************/ 69 | /* WINDIVERT API */ 70 | /****************************************************************************/ 71 | 72 | /* 73 | * WinDivert layers. 74 | */ 75 | typedef enum 76 | { 77 | WINDIVERT_LAYER_NETWORK = 0, /* Network layer. */ 78 | WINDIVERT_LAYER_NETWORK_FORWARD = 1,/* Network layer (forwarded packets) */ 79 | WINDIVERT_LAYER_FLOW = 2, /* Flow layer. */ 80 | WINDIVERT_LAYER_SOCKET = 3, /* Socket layer. */ 81 | WINDIVERT_LAYER_REFLECT = 4, /* Reflect layer. */ 82 | } WINDIVERT_LAYER, *PWINDIVERT_LAYER; 83 | 84 | /* 85 | * WinDivert NETWORK and NETWORK_FORWARD layer data. 86 | */ 87 | typedef struct 88 | { 89 | UINT32 IfIdx; /* Packet's interface index. */ 90 | UINT32 SubIfIdx; /* Packet's sub-interface index. */ 91 | } WINDIVERT_DATA_NETWORK, *PWINDIVERT_DATA_NETWORK; 92 | 93 | /* 94 | * WinDivert FLOW layer data. 95 | */ 96 | typedef struct 97 | { 98 | UINT64 EndpointId; /* Endpoint ID. */ 99 | UINT64 ParentEndpointId; /* Parent endpoint ID. */ 100 | UINT32 ProcessId; /* Process ID. */ 101 | UINT32 LocalAddr[4]; /* Local address. */ 102 | UINT32 RemoteAddr[4]; /* Remote address. */ 103 | UINT16 LocalPort; /* Local port. */ 104 | UINT16 RemotePort; /* Remote port. */ 105 | UINT8 Protocol; /* Protocol. */ 106 | } WINDIVERT_DATA_FLOW, *PWINDIVERT_DATA_FLOW; 107 | 108 | /* 109 | * WinDivert SOCKET layer data. 110 | */ 111 | typedef struct 112 | { 113 | UINT64 EndpointId; /* Endpoint ID. */ 114 | UINT64 ParentEndpointId; /* Parent Endpoint ID. */ 115 | UINT32 ProcessId; /* Process ID. */ 116 | UINT32 LocalAddr[4]; /* Local address. */ 117 | UINT32 RemoteAddr[4]; /* Remote address. */ 118 | UINT16 LocalPort; /* Local port. */ 119 | UINT16 RemotePort; /* Remote port. */ 120 | UINT8 Protocol; /* Protocol. */ 121 | } WINDIVERT_DATA_SOCKET, *PWINDIVERT_DATA_SOCKET; 122 | 123 | /* 124 | * WinDivert REFLECTION layer data. 125 | */ 126 | typedef struct 127 | { 128 | INT64 Timestamp; /* Handle open time. */ 129 | UINT32 ProcessId; /* Handle process ID. */ 130 | WINDIVERT_LAYER Layer; /* Handle layer. */ 131 | UINT64 Flags; /* Handle flags. */ 132 | INT16 Priority; /* Handle priority. */ 133 | } WINDIVERT_DATA_REFLECT, *PWINDIVERT_DATA_REFLECT; 134 | 135 | /* 136 | * WinDivert address. 137 | */ 138 | #ifdef _MSC_VER 139 | #pragma warning(push) 140 | #pragma warning(disable: 4201) 141 | #endif 142 | typedef struct 143 | { 144 | INT64 Timestamp; /* Packet's timestamp. */ 145 | UINT32 Layer:8; /* Packet's layer. */ 146 | UINT32 Event:8; /* Packet event. */ 147 | UINT32 Sniffed:1; /* Packet was sniffed? */ 148 | UINT32 Outbound:1; /* Packet is outound? */ 149 | UINT32 Loopback:1; /* Packet is loopback? */ 150 | UINT32 Impostor:1; /* Packet is impostor? */ 151 | UINT32 IPv6:1; /* Packet is IPv6? */ 152 | UINT32 IPChecksum:1; /* Packet has valid IPv4 checksum? */ 153 | UINT32 TCPChecksum:1; /* Packet has valid TCP checksum? */ 154 | UINT32 UDPChecksum:1; /* Packet has valid UDP checksum? */ 155 | UINT32 Reserved1:8; 156 | UINT32 Reserved2; 157 | union 158 | { 159 | WINDIVERT_DATA_NETWORK Network; /* Network layer data. */ 160 | WINDIVERT_DATA_FLOW Flow; /* Flow layer data. */ 161 | WINDIVERT_DATA_SOCKET Socket; /* Socket layer data. */ 162 | WINDIVERT_DATA_REFLECT Reflect; /* Reflect layer data. */ 163 | UINT8 Reserved3[64]; 164 | }; 165 | } WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS; 166 | #ifdef _MSC_VER 167 | #pragma warning(pop) 168 | #endif 169 | 170 | /* 171 | * WinDivert events. 172 | */ 173 | typedef enum 174 | { 175 | WINDIVERT_EVENT_NETWORK_PACKET = 0, /* Network packet. */ 176 | WINDIVERT_EVENT_FLOW_ESTABLISHED = 1, 177 | /* Flow established. */ 178 | WINDIVERT_EVENT_FLOW_DELETED = 2, /* Flow deleted. */ 179 | WINDIVERT_EVENT_SOCKET_BIND = 3, /* Socket bind. */ 180 | WINDIVERT_EVENT_SOCKET_CONNECT = 4, /* Socket connect. */ 181 | WINDIVERT_EVENT_SOCKET_LISTEN = 5, /* Socket listen. */ 182 | WINDIVERT_EVENT_SOCKET_ACCEPT = 6, /* Socket accept. */ 183 | WINDIVERT_EVENT_SOCKET_CLOSE = 7, /* Socket close. */ 184 | WINDIVERT_EVENT_REFLECT_OPEN = 8, /* WinDivert handle opened. */ 185 | WINDIVERT_EVENT_REFLECT_CLOSE = 9, /* WinDivert handle closed. */ 186 | } WINDIVERT_EVENT, *PWINDIVERT_EVENT; 187 | 188 | /* 189 | * WinDivert flags. 190 | */ 191 | #define WINDIVERT_FLAG_SNIFF 0x0001 192 | #define WINDIVERT_FLAG_DROP 0x0002 193 | #define WINDIVERT_FLAG_RECV_ONLY 0x0004 194 | #define WINDIVERT_FLAG_READ_ONLY WINDIVERT_FLAG_RECV_ONLY 195 | #define WINDIVERT_FLAG_SEND_ONLY 0x0008 196 | #define WINDIVERT_FLAG_WRITE_ONLY WINDIVERT_FLAG_SEND_ONLY 197 | #define WINDIVERT_FLAG_NO_INSTALL 0x0010 198 | #define WINDIVERT_FLAG_FRAGMENTS 0x0020 199 | 200 | /* 201 | * WinDivert parameters. 202 | */ 203 | typedef enum 204 | { 205 | WINDIVERT_PARAM_QUEUE_LENGTH = 0, /* Packet queue length. */ 206 | WINDIVERT_PARAM_QUEUE_TIME = 1, /* Packet queue time. */ 207 | WINDIVERT_PARAM_QUEUE_SIZE = 2, /* Packet queue size. */ 208 | WINDIVERT_PARAM_VERSION_MAJOR = 3, /* Driver version (major). */ 209 | WINDIVERT_PARAM_VERSION_MINOR = 4, /* Driver version (minor). */ 210 | } WINDIVERT_PARAM, *PWINDIVERT_PARAM; 211 | #define WINDIVERT_PARAM_MAX WINDIVERT_PARAM_VERSION_MINOR 212 | 213 | /* 214 | * WinDivert shutdown parameter. 215 | */ 216 | typedef enum 217 | { 218 | WINDIVERT_SHUTDOWN_RECV = 0x1, /* Shutdown recv. */ 219 | WINDIVERT_SHUTDOWN_SEND = 0x2, /* Shutdown send. */ 220 | WINDIVERT_SHUTDOWN_BOTH = 0x3, /* Shutdown recv and send. */ 221 | } WINDIVERT_SHUTDOWN, *PWINDIVERT_SHUTDOWN; 222 | #define WINDIVERT_SHUTDOWN_MAX WINDIVERT_SHUTDOWN_BOTH 223 | 224 | #ifndef WINDIVERT_KERNEL 225 | 226 | /* 227 | * Open a WinDivert handle. 228 | */ 229 | WINDIVERTEXPORT HANDLE WinDivertOpen( 230 | __in const char *filter, 231 | __in WINDIVERT_LAYER layer, 232 | __in INT16 priority, 233 | __in UINT64 flags); 234 | 235 | /* 236 | * Receive (read) a packet from a WinDivert handle. 237 | */ 238 | WINDIVERTEXPORT BOOL WinDivertRecv( 239 | __in HANDLE handle, 240 | __out_opt VOID *pPacket, 241 | __in UINT packetLen, 242 | __out_opt UINT *pRecvLen, 243 | __out_opt WINDIVERT_ADDRESS *pAddr); 244 | 245 | /* 246 | * Receive (read) a packet from a WinDivert handle. 247 | */ 248 | WINDIVERTEXPORT BOOL WinDivertRecvEx( 249 | __in HANDLE handle, 250 | __out_opt VOID *pPacket, 251 | __in UINT packetLen, 252 | __out_opt UINT *pRecvLen, 253 | __in UINT64 flags, 254 | __out WINDIVERT_ADDRESS *pAddr, 255 | __inout_opt UINT *pAddrLen, 256 | __inout_opt LPOVERLAPPED lpOverlapped); 257 | 258 | /* 259 | * Send (write/inject) a packet to a WinDivert handle. 260 | */ 261 | WINDIVERTEXPORT BOOL WinDivertSend( 262 | __in HANDLE handle, 263 | __in const VOID *pPacket, 264 | __in UINT packetLen, 265 | __out_opt UINT *pSendLen, 266 | __in const WINDIVERT_ADDRESS *pAddr); 267 | 268 | /* 269 | * Send (write/inject) a packet to a WinDivert handle. 270 | */ 271 | WINDIVERTEXPORT BOOL WinDivertSendEx( 272 | __in HANDLE handle, 273 | __in const VOID *pPacket, 274 | __in UINT packetLen, 275 | __out_opt UINT *pSendLen, 276 | __in UINT64 flags, 277 | __in const WINDIVERT_ADDRESS *pAddr, 278 | __in UINT addrLen, 279 | __inout_opt LPOVERLAPPED lpOverlapped); 280 | 281 | /* 282 | * Shutdown a WinDivert handle. 283 | */ 284 | WINDIVERTEXPORT BOOL WinDivertShutdown( 285 | __in HANDLE handle, 286 | __in WINDIVERT_SHUTDOWN how); 287 | 288 | /* 289 | * Close a WinDivert handle. 290 | */ 291 | WINDIVERTEXPORT BOOL WinDivertClose( 292 | __in HANDLE handle); 293 | 294 | /* 295 | * Set a WinDivert handle parameter. 296 | */ 297 | WINDIVERTEXPORT BOOL WinDivertSetParam( 298 | __in HANDLE handle, 299 | __in WINDIVERT_PARAM param, 300 | __in UINT64 value); 301 | 302 | /* 303 | * Get a WinDivert handle parameter. 304 | */ 305 | WINDIVERTEXPORT BOOL WinDivertGetParam( 306 | __in HANDLE handle, 307 | __in WINDIVERT_PARAM param, 308 | __out UINT64 *pValue); 309 | 310 | #endif /* WINDIVERT_KERNEL */ 311 | 312 | /* 313 | * WinDivert constants. 314 | */ 315 | #define WINDIVERT_PRIORITY_HIGHEST 30000 316 | #define WINDIVERT_PRIORITY_LOWEST (-WINDIVERT_PRIORITY_HIGHEST) 317 | #define WINDIVERT_PARAM_QUEUE_LENGTH_DEFAULT 4096 318 | #define WINDIVERT_PARAM_QUEUE_LENGTH_MIN 32 319 | #define WINDIVERT_PARAM_QUEUE_LENGTH_MAX 16384 320 | #define WINDIVERT_PARAM_QUEUE_TIME_DEFAULT 2000 /* 2s */ 321 | #define WINDIVERT_PARAM_QUEUE_TIME_MIN 100 /* 100ms */ 322 | #define WINDIVERT_PARAM_QUEUE_TIME_MAX 16000 /* 16s */ 323 | #define WINDIVERT_PARAM_QUEUE_SIZE_DEFAULT 4194304 /* 4MB */ 324 | #define WINDIVERT_PARAM_QUEUE_SIZE_MIN 65535 /* 64KB */ 325 | #define WINDIVERT_PARAM_QUEUE_SIZE_MAX 33554432 /* 32MB */ 326 | #define WINDIVERT_BATCH_MAX 0xFF /* 255 */ 327 | #define WINDIVERT_MTU_MAX (40 + 0xFFFF) 328 | 329 | /****************************************************************************/ 330 | /* WINDIVERT HELPER API */ 331 | /****************************************************************************/ 332 | 333 | #ifdef _MSC_VER 334 | #pragma warning(push) 335 | #pragma warning(disable: 4214) 336 | #endif 337 | 338 | /* 339 | * IPv4/IPv6/ICMP/ICMPv6/TCP/UDP header definitions. 340 | */ 341 | typedef struct 342 | { 343 | UINT8 HdrLength:4; 344 | UINT8 Version:4; 345 | UINT8 TOS; 346 | UINT16 Length; 347 | UINT16 Id; 348 | UINT16 FragOff0; 349 | UINT8 TTL; 350 | UINT8 Protocol; 351 | UINT16 Checksum; 352 | UINT32 SrcAddr; 353 | UINT32 DstAddr; 354 | } WINDIVERT_IPHDR, *PWINDIVERT_IPHDR; 355 | 356 | #define WINDIVERT_IPHDR_GET_FRAGOFF(hdr) \ 357 | (((hdr)->FragOff0) & 0xFF1F) 358 | #define WINDIVERT_IPHDR_GET_MF(hdr) \ 359 | ((((hdr)->FragOff0) & 0x0020) != 0) 360 | #define WINDIVERT_IPHDR_GET_DF(hdr) \ 361 | ((((hdr)->FragOff0) & 0x0040) != 0) 362 | #define WINDIVERT_IPHDR_GET_RESERVED(hdr) \ 363 | ((((hdr)->FragOff0) & 0x0080) != 0) 364 | 365 | #define WINDIVERT_IPHDR_SET_FRAGOFF(hdr, val) \ 366 | do \ 367 | { \ 368 | (hdr)->FragOff0 = (((hdr)->FragOff0) & 0x00E0) | \ 369 | ((val) & 0xFF1F); \ 370 | } \ 371 | while (FALSE) 372 | #define WINDIVERT_IPHDR_SET_MF(hdr, val) \ 373 | do \ 374 | { \ 375 | (hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFFDF) | \ 376 | (((val) & 0x0001) << 5); \ 377 | } \ 378 | while (FALSE) 379 | #define WINDIVERT_IPHDR_SET_DF(hdr, val) \ 380 | do \ 381 | { \ 382 | (hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFFBF) | \ 383 | (((val) & 0x0001) << 6); \ 384 | } \ 385 | while (FALSE) 386 | #define WINDIVERT_IPHDR_SET_RESERVED(hdr, val) \ 387 | do \ 388 | { \ 389 | (hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFF7F) | \ 390 | (((val) & 0x0001) << 7); \ 391 | } \ 392 | while (FALSE) 393 | 394 | typedef struct 395 | { 396 | UINT8 TrafficClass0:4; 397 | UINT8 Version:4; 398 | UINT8 FlowLabel0:4; 399 | UINT8 TrafficClass1:4; 400 | UINT16 FlowLabel1; 401 | UINT16 Length; 402 | UINT8 NextHdr; 403 | UINT8 HopLimit; 404 | UINT32 SrcAddr[4]; 405 | UINT32 DstAddr[4]; 406 | } WINDIVERT_IPV6HDR, *PWINDIVERT_IPV6HDR; 407 | 408 | #define WINDIVERT_IPV6HDR_GET_TRAFFICCLASS(hdr) \ 409 | ((((hdr)->TrafficClass0) << 4) | ((hdr)->TrafficClass1)) 410 | #define WINDIVERT_IPV6HDR_GET_FLOWLABEL(hdr) \ 411 | ((((UINT32)(hdr)->FlowLabel0) << 16) | ((UINT32)(hdr)->FlowLabel1)) 412 | 413 | #define WINDIVERT_IPV6HDR_SET_TRAFFICCLASS(hdr, val) \ 414 | do \ 415 | { \ 416 | (hdr)->TrafficClass0 = ((UINT8)(val) >> 4); \ 417 | (hdr)->TrafficClass1 = (UINT8)(val); \ 418 | } \ 419 | while (FALSE) 420 | #define WINDIVERT_IPV6HDR_SET_FLOWLABEL(hdr, val) \ 421 | do \ 422 | { \ 423 | (hdr)->FlowLabel0 = (UINT8)((val) >> 16); \ 424 | (hdr)->FlowLabel1 = (UINT16)(val); \ 425 | } \ 426 | while (FALSE) 427 | 428 | typedef struct 429 | { 430 | UINT8 Type; 431 | UINT8 Code; 432 | UINT16 Checksum; 433 | UINT32 Body; 434 | } WINDIVERT_ICMPHDR, *PWINDIVERT_ICMPHDR; 435 | 436 | typedef struct 437 | { 438 | UINT8 Type; 439 | UINT8 Code; 440 | UINT16 Checksum; 441 | UINT32 Body; 442 | } WINDIVERT_ICMPV6HDR, *PWINDIVERT_ICMPV6HDR; 443 | 444 | typedef struct 445 | { 446 | UINT16 SrcPort; 447 | UINT16 DstPort; 448 | UINT32 SeqNum; 449 | UINT32 AckNum; 450 | UINT16 Reserved1:4; 451 | UINT16 HdrLength:4; 452 | UINT16 Fin:1; 453 | UINT16 Syn:1; 454 | UINT16 Rst:1; 455 | UINT16 Psh:1; 456 | UINT16 Ack:1; 457 | UINT16 Urg:1; 458 | UINT16 Reserved2:2; 459 | UINT16 Window; 460 | UINT16 Checksum; 461 | UINT16 UrgPtr; 462 | } WINDIVERT_TCPHDR, *PWINDIVERT_TCPHDR; 463 | 464 | typedef struct 465 | { 466 | UINT16 SrcPort; 467 | UINT16 DstPort; 468 | UINT16 Length; 469 | UINT16 Checksum; 470 | } WINDIVERT_UDPHDR, *PWINDIVERT_UDPHDR; 471 | 472 | #ifdef _MSC_VER 473 | #pragma warning(pop) 474 | #endif 475 | 476 | /* 477 | * Flags for WinDivertHelperCalcChecksums() 478 | */ 479 | #define WINDIVERT_HELPER_NO_IP_CHECKSUM 1 480 | #define WINDIVERT_HELPER_NO_ICMP_CHECKSUM 2 481 | #define WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM 4 482 | #define WINDIVERT_HELPER_NO_TCP_CHECKSUM 8 483 | #define WINDIVERT_HELPER_NO_UDP_CHECKSUM 16 484 | 485 | #ifndef WINDIVERT_KERNEL 486 | 487 | /* 488 | * Hash a packet. 489 | */ 490 | WINDIVERTEXPORT UINT64 WinDivertHelperHashPacket( 491 | __in const VOID *pPacket, 492 | __in UINT packetLen, 493 | __in UINT64 seed 494 | #ifdef __cplusplus 495 | = 0 496 | #endif 497 | ); 498 | 499 | /* 500 | * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. 501 | */ 502 | WINDIVERTEXPORT BOOL WinDivertHelperParsePacket( 503 | __in const VOID *pPacket, 504 | __in UINT packetLen, 505 | __out_opt PWINDIVERT_IPHDR *ppIpHdr, 506 | __out_opt PWINDIVERT_IPV6HDR *ppIpv6Hdr, 507 | __out_opt UINT8 *pProtocol, 508 | __out_opt PWINDIVERT_ICMPHDR *ppIcmpHdr, 509 | __out_opt PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr, 510 | __out_opt PWINDIVERT_TCPHDR *ppTcpHdr, 511 | __out_opt PWINDIVERT_UDPHDR *ppUdpHdr, 512 | __out_opt PVOID *ppData, 513 | __out_opt UINT *pDataLen, 514 | __out_opt PVOID *ppNext, 515 | __out_opt UINT *pNextLen); 516 | 517 | /* 518 | * Parse an IPv4 address. 519 | */ 520 | WINDIVERTEXPORT BOOL WinDivertHelperParseIPv4Address( 521 | __in const char *addrStr, 522 | __out_opt UINT32 *pAddr); 523 | 524 | /* 525 | * Parse an IPv6 address. 526 | */ 527 | WINDIVERTEXPORT BOOL WinDivertHelperParseIPv6Address( 528 | __in const char *addrStr, 529 | __out_opt UINT32 *pAddr); 530 | 531 | /* 532 | * Format an IPv4 address. 533 | */ 534 | WINDIVERTEXPORT BOOL WinDivertHelperFormatIPv4Address( 535 | __in UINT32 addr, 536 | __out char *buffer, 537 | __in UINT bufLen); 538 | 539 | /* 540 | * Format an IPv6 address. 541 | */ 542 | WINDIVERTEXPORT BOOL WinDivertHelperFormatIPv6Address( 543 | __in const UINT32 *pAddr, 544 | __out char *buffer, 545 | __in UINT bufLen); 546 | 547 | /* 548 | * Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums. 549 | */ 550 | WINDIVERTEXPORT BOOL WinDivertHelperCalcChecksums( 551 | __inout VOID *pPacket, 552 | __in UINT packetLen, 553 | __out_opt WINDIVERT_ADDRESS *pAddr, 554 | __in UINT64 flags); 555 | 556 | /* 557 | * Decrement the TTL/HopLimit. 558 | */ 559 | WINDIVERTEXPORT BOOL WinDivertHelperDecrementTTL( 560 | __inout VOID *pPacket, 561 | __in UINT packetLen); 562 | 563 | /* 564 | * Compile the given filter string. 565 | */ 566 | WINDIVERTEXPORT BOOL WinDivertHelperCompileFilter( 567 | __in const char *filter, 568 | __in WINDIVERT_LAYER layer, 569 | __out_opt char *object, 570 | __in UINT objLen, 571 | __out_opt const char **errorStr, 572 | __out_opt UINT *errorPos); 573 | 574 | /* 575 | * Evaluate the given filter string. 576 | */ 577 | WINDIVERTEXPORT BOOL WinDivertHelperEvalFilter( 578 | __in const char *filter, 579 | __in const VOID *pPacket, 580 | __in UINT packetLen, 581 | __in const WINDIVERT_ADDRESS *pAddr); 582 | 583 | /* 584 | * Format the given filter string. 585 | */ 586 | WINDIVERTEXPORT BOOL WinDivertHelperFormatFilter( 587 | __in const char *filter, 588 | __in WINDIVERT_LAYER layer, 589 | __out char *buffer, 590 | __in UINT bufLen); 591 | 592 | /* 593 | * Byte ordering. 594 | */ 595 | WINDIVERTEXPORT UINT16 WinDivertHelperNtohs( 596 | __in UINT16 x); 597 | WINDIVERTEXPORT UINT16 WinDivertHelperHtons( 598 | __in UINT16 x); 599 | WINDIVERTEXPORT UINT32 WinDivertHelperNtohl( 600 | __in UINT32 x); 601 | WINDIVERTEXPORT UINT32 WinDivertHelperHtonl( 602 | __in UINT32 x); 603 | WINDIVERTEXPORT UINT64 WinDivertHelperNtohll( 604 | __in UINT64 x); 605 | WINDIVERTEXPORT UINT64 WinDivertHelperHtonll( 606 | __in UINT64 x); 607 | WINDIVERTEXPORT void WinDivertHelperNtohIPv6Address( 608 | __in const UINT *inAddr, 609 | __out UINT *outAddr); 610 | WINDIVERTEXPORT void WinDivertHelperHtonIPv6Address( 611 | __in const UINT *inAddr, 612 | __out UINT *outAddr); 613 | 614 | /* 615 | * Old names to be removed in the next version. 616 | */ 617 | WINDIVERTEXPORT void WinDivertHelperNtohIpv6Address( 618 | __in const UINT *inAddr, 619 | __out UINT *outAddr); 620 | WINDIVERTEXPORT void WinDivertHelperHtonIpv6Address( 621 | __in const UINT *inAddr, 622 | __out UINT *outAddr); 623 | 624 | #endif /* WINDIVERT_KERNEL */ 625 | 626 | #ifdef __cplusplus 627 | } 628 | #endif 629 | 630 | #endif /* __WINDIVERT_H */ 631 | -------------------------------------------------------------------------------- /memmod/memmod_windows.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package memmod 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | "strings" 12 | "sync" 13 | "syscall" 14 | "unsafe" 15 | 16 | "golang.org/x/sys/windows" 17 | ) 18 | 19 | type addressList struct { 20 | next *addressList 21 | address uintptr 22 | } 23 | 24 | func (head *addressList) free() { 25 | for node := head; node != nil; node = node.next { 26 | windows.VirtualFree(node.address, 0, windows.MEM_RELEASE) 27 | } 28 | } 29 | 30 | type Module struct { 31 | headers *IMAGE_NT_HEADERS 32 | codeBase uintptr 33 | modules []windows.Handle 34 | initialized bool 35 | isDLL bool 36 | isRelocated bool 37 | nameExports map[string]uint16 38 | entry uintptr 39 | blockedMemory *addressList 40 | } 41 | 42 | func (module *Module) BaseAddr() uintptr { 43 | return module.codeBase 44 | } 45 | 46 | func (module *Module) headerDirectory(idx int) *IMAGE_DATA_DIRECTORY { 47 | return &module.headers.OptionalHeader.DataDirectory[idx] 48 | } 49 | 50 | func (module *Module) copySections(address, size uintptr, oldHeaders *IMAGE_NT_HEADERS) error { 51 | sections := module.headers.Sections() 52 | for i := range sections { 53 | if sections[i].SizeOfRawData == 0 { 54 | // Section doesn't contain data in the dll itself, but may define uninitialized data. 55 | sectionSize := oldHeaders.OptionalHeader.SectionAlignment 56 | if sectionSize == 0 { 57 | continue 58 | } 59 | dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress), 60 | uintptr(sectionSize), 61 | windows.MEM_COMMIT, 62 | windows.PAGE_READWRITE) 63 | if err != nil { 64 | return fmt.Errorf("Error allocating section: %w", err) 65 | } 66 | 67 | // Always use position from file to support alignments smaller than page size (allocation above will align to page size). 68 | dest = module.codeBase + uintptr(sections[i].VirtualAddress) 69 | // NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used. 70 | sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff)) 71 | dst := unsafe.Slice((*byte)(a2p(dest)), sectionSize) 72 | for j := range dst { 73 | dst[j] = 0 74 | } 75 | continue 76 | } 77 | 78 | if size < uintptr(sections[i].PointerToRawData+sections[i].SizeOfRawData) { 79 | return errors.New("Incomplete section") 80 | } 81 | 82 | // Commit memory block and copy data from dll. 83 | dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress), 84 | uintptr(sections[i].SizeOfRawData), 85 | windows.MEM_COMMIT, 86 | windows.PAGE_READWRITE) 87 | if err != nil { 88 | return fmt.Errorf("Error allocating memory block: %w", err) 89 | } 90 | 91 | // Always use position from file to support alignments smaller than page size (allocation above will align to page size). 92 | memcpy( 93 | module.codeBase+uintptr(sections[i].VirtualAddress), 94 | address+uintptr(sections[i].PointerToRawData), 95 | uintptr(sections[i].SizeOfRawData)) 96 | // NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used. 97 | sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff)) 98 | } 99 | 100 | return nil 101 | } 102 | 103 | func (module *Module) realSectionSize(section *IMAGE_SECTION_HEADER) uintptr { 104 | size := section.SizeOfRawData 105 | if size != 0 { 106 | return uintptr(size) 107 | } 108 | if (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) != 0 { 109 | return uintptr(module.headers.OptionalHeader.SizeOfInitializedData) 110 | } 111 | if (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0 { 112 | return uintptr(module.headers.OptionalHeader.SizeOfUninitializedData) 113 | } 114 | return 0 115 | } 116 | 117 | type sectionFinalizeData struct { 118 | address uintptr 119 | alignedAddress uintptr 120 | size uintptr 121 | characteristics uint32 122 | last bool 123 | } 124 | 125 | func (module *Module) finalizeSection(sectionData *sectionFinalizeData) error { 126 | if sectionData.size == 0 { 127 | return nil 128 | } 129 | 130 | if (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0 { 131 | // Section is not needed any more and can safely be freed. 132 | if sectionData.address == sectionData.alignedAddress && 133 | (sectionData.last || 134 | (sectionData.size%uintptr(module.headers.OptionalHeader.SectionAlignment)) == 0) { 135 | // Only allowed to decommit whole pages. 136 | windows.VirtualFree(sectionData.address, sectionData.size, windows.MEM_DECOMMIT) 137 | } 138 | return nil 139 | } 140 | 141 | // determine protection flags based on characteristics 142 | ProtectionFlags := [8]uint32{ 143 | windows.PAGE_NOACCESS, // not writeable, not readable, not executable 144 | windows.PAGE_EXECUTE, // not writeable, not readable, executable 145 | windows.PAGE_READONLY, // not writeable, readable, not executable 146 | windows.PAGE_EXECUTE_READ, // not writeable, readable, executable 147 | windows.PAGE_WRITECOPY, // writeable, not readable, not executable 148 | windows.PAGE_EXECUTE_WRITECOPY, // writeable, not readable, executable 149 | windows.PAGE_READWRITE, // writeable, readable, not executable 150 | windows.PAGE_EXECUTE_READWRITE, // writeable, readable, executable 151 | } 152 | protect := ProtectionFlags[sectionData.characteristics>>29] 153 | if (sectionData.characteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0 { 154 | protect |= windows.PAGE_NOCACHE 155 | } 156 | 157 | // Change memory access flags. 158 | var oldProtect uint32 159 | err := windows.VirtualProtect(sectionData.address, sectionData.size, protect, &oldProtect) 160 | if err != nil { 161 | return fmt.Errorf("Error protecting memory page: %w", err) 162 | } 163 | 164 | return nil 165 | } 166 | 167 | func (module *Module) registerExceptionHandlers() { 168 | directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXCEPTION) 169 | if directory.Size == 0 || directory.VirtualAddress == 0 { 170 | return 171 | } 172 | runtimeFuncs := (*windows.RUNTIME_FUNCTION)(unsafe.Pointer(module.codeBase + uintptr(directory.VirtualAddress))) 173 | windows.RtlAddFunctionTable(runtimeFuncs, uint32(uintptr(directory.Size)/unsafe.Sizeof(*runtimeFuncs)), module.codeBase) 174 | } 175 | 176 | func (module *Module) finalizeSections() error { 177 | sections := module.headers.Sections() 178 | imageOffset := module.headers.OptionalHeader.imageOffset() 179 | sectionData := sectionFinalizeData{} 180 | sectionData.address = uintptr(sections[0].PhysicalAddress()) | imageOffset 181 | sectionData.alignedAddress = alignDown(sectionData.address, uintptr(module.headers.OptionalHeader.SectionAlignment)) 182 | sectionData.size = module.realSectionSize(§ions[0]) 183 | sections[0].SetVirtualSize(uint32(sectionData.size)) 184 | sectionData.characteristics = sections[0].Characteristics 185 | 186 | // Loop through all sections and change access flags. 187 | for i := uint16(1); i < module.headers.FileHeader.NumberOfSections; i++ { 188 | sectionAddress := uintptr(sections[i].PhysicalAddress()) | imageOffset 189 | alignedAddress := alignDown(sectionAddress, uintptr(module.headers.OptionalHeader.SectionAlignment)) 190 | sectionSize := module.realSectionSize(§ions[i]) 191 | sections[i].SetVirtualSize(uint32(sectionSize)) 192 | // Combine access flags of all sections that share a page. 193 | // TODO: We currently share flags of a trailing large section with the page of a first small section. This should be optimized. 194 | if sectionData.alignedAddress == alignedAddress || sectionData.address+sectionData.size > alignedAddress { 195 | // Section shares page with previous. 196 | if (sections[i].Characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 { 197 | sectionData.characteristics = (sectionData.characteristics | sections[i].Characteristics) &^ IMAGE_SCN_MEM_DISCARDABLE 198 | } else { 199 | sectionData.characteristics |= sections[i].Characteristics 200 | } 201 | sectionData.size = sectionAddress + sectionSize - sectionData.address 202 | continue 203 | } 204 | 205 | err := module.finalizeSection(§ionData) 206 | if err != nil { 207 | return fmt.Errorf("Error finalizing section: %w", err) 208 | } 209 | sectionData.address = sectionAddress 210 | sectionData.alignedAddress = alignedAddress 211 | sectionData.size = sectionSize 212 | sectionData.characteristics = sections[i].Characteristics 213 | } 214 | sectionData.last = true 215 | err := module.finalizeSection(§ionData) 216 | if err != nil { 217 | return fmt.Errorf("Error finalizing section: %w", err) 218 | } 219 | return nil 220 | } 221 | 222 | func (module *Module) executeTLS() { 223 | directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_TLS) 224 | if directory.VirtualAddress == 0 { 225 | return 226 | } 227 | 228 | tls := (*IMAGE_TLS_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) 229 | callback := tls.AddressOfCallbacks 230 | if callback != 0 { 231 | for { 232 | f := *(*uintptr)(a2p(callback)) 233 | if f == 0 { 234 | break 235 | } 236 | syscall.SyscallN(f, module.codeBase, DLL_PROCESS_ATTACH, 0) 237 | callback += unsafe.Sizeof(f) 238 | } 239 | } 240 | } 241 | 242 | func (module *Module) performBaseRelocation(delta uintptr) (relocated bool, err error) { 243 | directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC) 244 | if directory.Size == 0 { 245 | return delta == 0, nil 246 | } 247 | 248 | relocationHdr := (*IMAGE_BASE_RELOCATION)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) 249 | for relocationHdr.VirtualAddress > 0 { 250 | dest := module.codeBase + uintptr(relocationHdr.VirtualAddress) 251 | 252 | relInfos := unsafe.Slice( 253 | (*uint16)(a2p(uintptr(unsafe.Pointer(relocationHdr))+unsafe.Sizeof(*relocationHdr))), 254 | (uintptr(relocationHdr.SizeOfBlock)-unsafe.Sizeof(*relocationHdr))/unsafe.Sizeof(uint16(0))) 255 | for _, relInfo := range relInfos { 256 | // The upper 4 bits define the type of relocation. 257 | relType := relInfo >> 12 258 | // The lower 12 bits define the offset. 259 | relOffset := uintptr(relInfo & 0xfff) 260 | 261 | switch relType { 262 | case IMAGE_REL_BASED_ABSOLUTE: 263 | // Skip relocation. 264 | 265 | case IMAGE_REL_BASED_LOW: 266 | *(*uint16)(a2p(dest + relOffset)) += uint16(delta & 0xffff) 267 | break 268 | 269 | case IMAGE_REL_BASED_HIGH: 270 | *(*uint16)(a2p(dest + relOffset)) += uint16(uint32(delta) >> 16) 271 | break 272 | 273 | case IMAGE_REL_BASED_HIGHLOW: 274 | *(*uint32)(a2p(dest + relOffset)) += uint32(delta) 275 | 276 | case IMAGE_REL_BASED_DIR64: 277 | *(*uint64)(a2p(dest + relOffset)) += uint64(delta) 278 | 279 | case IMAGE_REL_BASED_THUMB_MOV32: 280 | inst := *(*uint32)(a2p(dest + relOffset)) 281 | imm16 := ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) + 282 | ((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff) 283 | if (inst & 0x8000fbf0) != 0x0000f240 { 284 | return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVW", inst) 285 | } 286 | imm16 += uint32(delta) & 0xffff 287 | hiDelta := (uint32(delta&0xffff0000) >> 16) + ((imm16 & 0xffff0000) >> 16) 288 | *(*uint32)(a2p(dest + relOffset)) = (inst & 0x8f00fbf0) + ((imm16 >> 1) & 0x0400) + 289 | ((imm16 >> 12) & 0x000f) + 290 | ((imm16 << 20) & 0x70000000) + 291 | ((imm16 << 16) & 0xff0000) 292 | if hiDelta != 0 { 293 | inst = *(*uint32)(a2p(dest + relOffset + 4)) 294 | imm16 = ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) + 295 | ((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff) 296 | if (inst & 0x8000fbf0) != 0x0000f2c0 { 297 | return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVT", inst) 298 | } 299 | imm16 += hiDelta 300 | if imm16 > 0xffff { 301 | return false, fmt.Errorf("Resulting immediate value won't fit: %08x", imm16) 302 | } 303 | *(*uint32)(a2p(dest + relOffset + 4)) = (inst & 0x8f00fbf0) + 304 | ((imm16 >> 1) & 0x0400) + 305 | ((imm16 >> 12) & 0x000f) + 306 | ((imm16 << 20) & 0x70000000) + 307 | ((imm16 << 16) & 0xff0000) 308 | } 309 | 310 | default: 311 | return false, fmt.Errorf("Unsupported relocation: %v", relType) 312 | } 313 | } 314 | 315 | // Advance to next relocation block. 316 | relocationHdr = (*IMAGE_BASE_RELOCATION)(a2p(uintptr(unsafe.Pointer(relocationHdr)) + uintptr(relocationHdr.SizeOfBlock))) 317 | } 318 | return true, nil 319 | } 320 | 321 | func (module *Module) buildImportTable() error { 322 | directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT) 323 | if directory.Size == 0 { 324 | return nil 325 | } 326 | 327 | module.modules = make([]windows.Handle, 0, 16) 328 | importDesc := (*IMAGE_IMPORT_DESCRIPTOR)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) 329 | for importDesc.Name != 0 { 330 | handle, err := windows.LoadLibraryEx(windows.BytePtrToString((*byte)(a2p(module.codeBase+uintptr(importDesc.Name)))), 0, windows.LOAD_LIBRARY_SEARCH_SYSTEM32) 331 | if err != nil { 332 | return fmt.Errorf("Error loading module: %w", err) 333 | } 334 | var thunkRef, funcRef *uintptr 335 | if importDesc.OriginalFirstThunk() != 0 { 336 | thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.OriginalFirstThunk()))) 337 | funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk))) 338 | } else { 339 | // No hint table. 340 | thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk))) 341 | funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk))) 342 | } 343 | for *thunkRef != 0 { 344 | if IMAGE_SNAP_BY_ORDINAL(*thunkRef) { 345 | *funcRef, err = windows.GetProcAddressByOrdinal(handle, IMAGE_ORDINAL(*thunkRef)) 346 | } else { 347 | thunkData := (*IMAGE_IMPORT_BY_NAME)(a2p(module.codeBase + *thunkRef)) 348 | *funcRef, err = windows.GetProcAddress(handle, windows.BytePtrToString(&thunkData.Name[0])) 349 | } 350 | if err != nil { 351 | windows.FreeLibrary(handle) 352 | return fmt.Errorf("Error getting function address: %w", err) 353 | } 354 | thunkRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(thunkRef)) + unsafe.Sizeof(*thunkRef))) 355 | funcRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(funcRef)) + unsafe.Sizeof(*funcRef))) 356 | } 357 | module.modules = append(module.modules, handle) 358 | importDesc = (*IMAGE_IMPORT_DESCRIPTOR)(a2p(uintptr(unsafe.Pointer(importDesc)) + unsafe.Sizeof(*importDesc))) 359 | } 360 | return nil 361 | } 362 | 363 | func (module *Module) buildNameExports() error { 364 | directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT) 365 | if directory.Size == 0 { 366 | return errors.New("No export table found") 367 | } 368 | exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) 369 | if exports.NumberOfNames == 0 || exports.NumberOfFunctions == 0 { 370 | return errors.New("No functions exported") 371 | } 372 | if exports.NumberOfNames == 0 { 373 | return errors.New("No functions exported by name") 374 | } 375 | nameRefs := unsafe.Slice((*uint32)(a2p(module.codeBase+uintptr(exports.AddressOfNames))), exports.NumberOfNames) 376 | ordinals := unsafe.Slice((*uint16)(a2p(module.codeBase+uintptr(exports.AddressOfNameOrdinals))), exports.NumberOfNames) 377 | module.nameExports = make(map[string]uint16) 378 | for i := range nameRefs { 379 | nameArray := windows.BytePtrToString((*byte)(a2p(module.codeBase + uintptr(nameRefs[i])))) 380 | module.nameExports[nameArray] = ordinals[i] 381 | } 382 | return nil 383 | } 384 | 385 | type addressRange struct { 386 | start uintptr 387 | end uintptr 388 | } 389 | 390 | var ( 391 | loadedAddressRanges []addressRange 392 | loadedAddressRangesMu sync.RWMutex 393 | haveHookedRtlPcToFileHeader sync.Once 394 | hookRtlPcToFileHeaderResult error 395 | ) 396 | 397 | func hookRtlPcToFileHeader() error { 398 | var kernelBase windows.Handle 399 | err := windows.GetModuleHandleEx(windows.GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, windows.StringToUTF16Ptr("kernelbase.dll"), &kernelBase) 400 | if err != nil { 401 | return err 402 | } 403 | imageBase := unsafe.Pointer(kernelBase) 404 | dosHeader := (*IMAGE_DOS_HEADER)(imageBase) 405 | ntHeaders := (*IMAGE_NT_HEADERS)(unsafe.Add(imageBase, dosHeader.E_lfanew)) 406 | importsDirectory := ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] 407 | importDescriptor := (*IMAGE_IMPORT_DESCRIPTOR)(unsafe.Add(imageBase, importsDirectory.VirtualAddress)) 408 | for ; importDescriptor.Name != 0; importDescriptor = (*IMAGE_IMPORT_DESCRIPTOR)(unsafe.Add(unsafe.Pointer(importDescriptor), unsafe.Sizeof(*importDescriptor))) { 409 | libraryName := windows.BytePtrToString((*byte)(unsafe.Add(imageBase, importDescriptor.Name))) 410 | if strings.EqualFold(libraryName, "ntdll.dll") { 411 | break 412 | } 413 | } 414 | if importDescriptor.Name == 0 { 415 | return errors.New("ntdll.dll not found") 416 | } 417 | originalThunk := (*uintptr)(unsafe.Add(imageBase, importDescriptor.OriginalFirstThunk())) 418 | thunk := (*uintptr)(unsafe.Add(imageBase, importDescriptor.FirstThunk)) 419 | for ; *originalThunk != 0; originalThunk = (*uintptr)(unsafe.Add(unsafe.Pointer(originalThunk), unsafe.Sizeof(*originalThunk))) { 420 | if *originalThunk&IMAGE_ORDINAL_FLAG == 0 { 421 | function := (*IMAGE_IMPORT_BY_NAME)(unsafe.Add(imageBase, *originalThunk)) 422 | name := windows.BytePtrToString(&function.Name[0]) 423 | if name == "RtlPcToFileHeader" { 424 | break 425 | } 426 | } 427 | thunk = (*uintptr)(unsafe.Add(unsafe.Pointer(thunk), unsafe.Sizeof(*thunk))) 428 | } 429 | if *originalThunk == 0 { 430 | return errors.New("RtlPcToFileHeader not found") 431 | } 432 | var oldProtect uint32 433 | err = windows.VirtualProtect(uintptr(unsafe.Pointer(thunk)), unsafe.Sizeof(*thunk), windows.PAGE_READWRITE, &oldProtect) 434 | if err != nil { 435 | return err 436 | } 437 | originalRtlPcToFileHeader := *thunk 438 | *thunk = windows.NewCallback(func(pcValue uintptr, baseOfImage *uintptr) uintptr { 439 | loadedAddressRangesMu.RLock() 440 | for i := range loadedAddressRanges { 441 | if pcValue >= loadedAddressRanges[i].start && pcValue < loadedAddressRanges[i].end { 442 | pcValue = *thunk 443 | break 444 | } 445 | } 446 | loadedAddressRangesMu.RUnlock() 447 | ret, _, _ := syscall.SyscallN(originalRtlPcToFileHeader, pcValue, uintptr(unsafe.Pointer(baseOfImage))) 448 | return ret 449 | }) 450 | err = windows.VirtualProtect(uintptr(unsafe.Pointer(thunk)), unsafe.Sizeof(*thunk), oldProtect, &oldProtect) 451 | if err != nil { 452 | return err 453 | } 454 | return nil 455 | } 456 | 457 | // LoadLibrary loads module image to memory. 458 | func LoadLibrary(data []byte) (module *Module, err error) { 459 | addr := uintptr(unsafe.Pointer(&data[0])) 460 | size := uintptr(len(data)) 461 | if size < unsafe.Sizeof(IMAGE_DOS_HEADER{}) { 462 | return nil, errors.New("Incomplete IMAGE_DOS_HEADER") 463 | } 464 | dosHeader := (*IMAGE_DOS_HEADER)(a2p(addr)) 465 | if dosHeader.E_magic != IMAGE_DOS_SIGNATURE { 466 | return nil, fmt.Errorf("Not an MS-DOS binary (provided: %x, expected: %x)", dosHeader.E_magic, IMAGE_DOS_SIGNATURE) 467 | } 468 | if (size < uintptr(dosHeader.E_lfanew)+unsafe.Sizeof(IMAGE_NT_HEADERS{})) { 469 | return nil, errors.New("Incomplete IMAGE_NT_HEADERS") 470 | } 471 | oldHeader := (*IMAGE_NT_HEADERS)(a2p(addr + uintptr(dosHeader.E_lfanew))) 472 | if oldHeader.Signature != IMAGE_NT_SIGNATURE { 473 | return nil, fmt.Errorf("Not an NT binary (provided: %x, expected: %x)", oldHeader.Signature, IMAGE_NT_SIGNATURE) 474 | } 475 | if oldHeader.FileHeader.Machine != imageFileProcess { 476 | return nil, fmt.Errorf("Foreign platform (provided: %x, expected: %x)", oldHeader.FileHeader.Machine, imageFileProcess) 477 | } 478 | if (oldHeader.OptionalHeader.SectionAlignment & 1) != 0 { 479 | return nil, errors.New("Unaligned section") 480 | } 481 | lastSectionEnd := uintptr(0) 482 | sections := oldHeader.Sections() 483 | optionalSectionSize := oldHeader.OptionalHeader.SectionAlignment 484 | for i := range sections { 485 | var endOfSection uintptr 486 | if sections[i].SizeOfRawData == 0 { 487 | // Section without data in the DLL 488 | endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(optionalSectionSize) 489 | } else { 490 | endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(sections[i].SizeOfRawData) 491 | } 492 | if endOfSection > lastSectionEnd { 493 | lastSectionEnd = endOfSection 494 | } 495 | } 496 | alignedImageSize := alignUp(uintptr(oldHeader.OptionalHeader.SizeOfImage), uintptr(oldHeader.OptionalHeader.SectionAlignment)) 497 | if alignedImageSize != alignUp(lastSectionEnd, uintptr(oldHeader.OptionalHeader.SectionAlignment)) { 498 | return nil, errors.New("Section is not page-aligned") 499 | } 500 | 501 | module = &Module{isDLL: (oldHeader.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0} 502 | defer func() { 503 | if err != nil { 504 | module.Free() 505 | module = nil 506 | } 507 | }() 508 | 509 | // Reserve memory for image of library. 510 | // TODO: Is it correct to commit the complete memory region at once? Calling DllEntry raises an exception if we don't. 511 | module.codeBase, err = windows.VirtualAlloc(oldHeader.OptionalHeader.ImageBase, 512 | alignedImageSize, 513 | windows.MEM_RESERVE|windows.MEM_COMMIT, 514 | windows.PAGE_READWRITE) 515 | if err != nil { 516 | // Try to allocate memory at arbitrary position. 517 | module.codeBase, err = windows.VirtualAlloc(0, 518 | alignedImageSize, 519 | windows.MEM_RESERVE|windows.MEM_COMMIT, 520 | windows.PAGE_READWRITE) 521 | if err != nil { 522 | err = fmt.Errorf("Error allocating code: %w", err) 523 | return 524 | } 525 | } 526 | err = module.check4GBBoundaries(alignedImageSize) 527 | if err != nil { 528 | err = fmt.Errorf("Error reallocating code: %w", err) 529 | return 530 | } 531 | 532 | if size < uintptr(oldHeader.OptionalHeader.SizeOfHeaders) { 533 | err = errors.New("Incomplete headers") 534 | return 535 | } 536 | // Commit memory for headers. 537 | headers, err := windows.VirtualAlloc(module.codeBase, 538 | uintptr(oldHeader.OptionalHeader.SizeOfHeaders), 539 | windows.MEM_COMMIT, 540 | windows.PAGE_READWRITE) 541 | if err != nil { 542 | err = fmt.Errorf("Error allocating headers: %w", err) 543 | return 544 | } 545 | // Copy PE header to code. 546 | memcpy(headers, addr, uintptr(oldHeader.OptionalHeader.SizeOfHeaders)) 547 | module.headers = (*IMAGE_NT_HEADERS)(a2p(headers + uintptr(dosHeader.E_lfanew))) 548 | 549 | // Update position. 550 | module.headers.OptionalHeader.ImageBase = module.codeBase 551 | 552 | // Copy sections from DLL file block to new memory location. 553 | err = module.copySections(addr, size, oldHeader) 554 | if err != nil { 555 | err = fmt.Errorf("Error copying sections: %w", err) 556 | return 557 | } 558 | 559 | // Adjust base address of imported data. 560 | locationDelta := module.headers.OptionalHeader.ImageBase - oldHeader.OptionalHeader.ImageBase 561 | if locationDelta != 0 { 562 | module.isRelocated, err = module.performBaseRelocation(locationDelta) 563 | if err != nil { 564 | err = fmt.Errorf("Error relocating module: %w", err) 565 | return 566 | } 567 | } else { 568 | module.isRelocated = true 569 | } 570 | 571 | // Load required dlls and adjust function table of imports. 572 | err = module.buildImportTable() 573 | if err != nil { 574 | err = fmt.Errorf("Error building import table: %w", err) 575 | return 576 | } 577 | 578 | // Mark memory pages depending on section headers and release sections that are marked as "discardable". 579 | err = module.finalizeSections() 580 | if err != nil { 581 | err = fmt.Errorf("Error finalizing sections: %w", err) 582 | return 583 | } 584 | 585 | // Register exception tables, if they exist. 586 | module.registerExceptionHandlers() 587 | 588 | // Register function PCs. 589 | loadedAddressRangesMu.Lock() 590 | loadedAddressRanges = append(loadedAddressRanges, addressRange{module.codeBase, module.codeBase + alignedImageSize}) 591 | loadedAddressRangesMu.Unlock() 592 | haveHookedRtlPcToFileHeader.Do(func() { 593 | hookRtlPcToFileHeaderResult = hookRtlPcToFileHeader() 594 | }) 595 | err = hookRtlPcToFileHeaderResult 596 | if err != nil { 597 | return 598 | } 599 | 600 | // TLS callbacks are executed BEFORE the main loading. 601 | module.executeTLS() 602 | 603 | // Get entry point of loaded module. 604 | if module.headers.OptionalHeader.AddressOfEntryPoint != 0 { 605 | module.entry = module.codeBase + uintptr(module.headers.OptionalHeader.AddressOfEntryPoint) 606 | if module.isDLL { 607 | // Notify library about attaching to process. 608 | r0, _, _ := syscall.SyscallN(module.entry, module.codeBase, DLL_PROCESS_ATTACH, 0) 609 | successful := r0 != 0 610 | if !successful { 611 | err = windows.ERROR_DLL_INIT_FAILED 612 | return 613 | } 614 | module.initialized = true 615 | } 616 | } 617 | 618 | module.buildNameExports() 619 | return 620 | } 621 | 622 | // Free releases module resources and unloads it. 623 | func (module *Module) Free() { 624 | if module.initialized { 625 | // Notify library about detaching from process. 626 | syscall.SyscallN(module.entry, module.codeBase, DLL_PROCESS_DETACH, 0) 627 | module.initialized = false 628 | } 629 | if module.modules != nil { 630 | // Free previously opened libraries. 631 | for _, handle := range module.modules { 632 | windows.FreeLibrary(handle) 633 | } 634 | module.modules = nil 635 | } 636 | if module.codeBase != 0 { 637 | windows.VirtualFree(module.codeBase, 0, windows.MEM_RELEASE) 638 | module.codeBase = 0 639 | } 640 | if module.blockedMemory != nil { 641 | module.blockedMemory.free() 642 | module.blockedMemory = nil 643 | } 644 | } 645 | 646 | // ProcAddressByName returns function address by exported name. 647 | func (module *Module) ProcAddressByName(name string) (uintptr, error) { 648 | directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT) 649 | if directory.Size == 0 { 650 | return 0, errors.New("No export table found") 651 | } 652 | exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) 653 | if module.nameExports == nil { 654 | return 0, errors.New("No functions exported by name") 655 | } 656 | if idx, ok := module.nameExports[name]; ok { 657 | if uint32(idx) > exports.NumberOfFunctions { 658 | return 0, errors.New("Ordinal number too high") 659 | } 660 | // AddressOfFunctions contains the RVAs to the "real" functions. 661 | return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil 662 | } 663 | return 0, errors.New("Function not found by name") 664 | } 665 | 666 | // ProcAddressByOrdinal returns function address by exported ordinal. 667 | func (module *Module) ProcAddressByOrdinal(ordinal uint16) (uintptr, error) { 668 | directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT) 669 | if directory.Size == 0 { 670 | return 0, errors.New("No export table found") 671 | } 672 | exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) 673 | if uint32(ordinal) < exports.Base { 674 | return 0, errors.New("Ordinal number too low") 675 | } 676 | idx := ordinal - uint16(exports.Base) 677 | if uint32(idx) > exports.NumberOfFunctions { 678 | return 0, errors.New("Ordinal number too high") 679 | } 680 | // AddressOfFunctions contains the RVAs to the "real" functions. 681 | return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil 682 | } 683 | 684 | func alignDown(value, alignment uintptr) uintptr { 685 | return value & ^(alignment - 1) 686 | } 687 | 688 | func alignUp(value, alignment uintptr) uintptr { 689 | return (value + alignment - 1) & ^(alignment - 1) 690 | } 691 | 692 | func a2p(addr uintptr) unsafe.Pointer { 693 | return unsafe.Pointer(addr) 694 | } 695 | 696 | func memcpy(dst, src, size uintptr) { 697 | copy(unsafe.Slice((*byte)(a2p(dst)), size), unsafe.Slice((*byte)(a2p(src)), size)) 698 | } 699 | -------------------------------------------------------------------------------- /divert/windivert.c: -------------------------------------------------------------------------------- 1 | /* 2 | * windivert.c 3 | * (C) 2019, all rights reserved, 4 | * 5 | * This file is part of WinDivert. 6 | * 7 | * WinDivert is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this program. If not, see . 19 | * 20 | * WinDivert is free software; you can redistribute it and/or modify it under 21 | * the terms of the GNU General Public License as published by the Free 22 | * Software Foundation; either version 2 of the License, or (at your option) 23 | * any later version. 24 | * 25 | * This program is distributed in the hope that it will be useful, but 26 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 27 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 28 | * for more details. 29 | * 30 | * You should have received a copy of the GNU General Public License along 31 | * with this program; if not, write to the Free Software Foundation, Inc., 51 32 | * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 33 | */ 34 | 35 | #ifndef UNICODE 36 | #define UNICODE 37 | #endif 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | #include 45 | 46 | #ifndef WINDIVERTEXPORT 47 | #define WINDIVERTEXPORT extern 48 | #endif 49 | #include "windivert.h" 50 | #include "windivert_device.h" 51 | 52 | #define WINDIVERT_DRIVER_NAME L"WinDivert" 53 | #define WINDIVERT_DRIVER32_SYS L"\\" WINDIVERT_DRIVER_NAME L"32.sys" 54 | #define WINDIVERT_DRIVER64_SYS L"\\" WINDIVERT_DRIVER_NAME L"64.sys" 55 | #define WINDIVERT_VERSION_MAJOR_MIN 2 56 | 57 | #ifndef ERROR_DRIVER_FAILED_PRIOR_UNLOAD 58 | #define ERROR_DRIVER_FAILED_PRIOR_UNLOAD ((DWORD)654) 59 | #endif 60 | 61 | static BOOLEAN WinDivertIsDigit(char c); 62 | static BOOLEAN WinDivertIsXDigit(char c); 63 | static BOOLEAN WinDivertIsSpace(char c); 64 | static BOOLEAN WinDivertIsAlNum(char c); 65 | static char WinDivertToLower(char c); 66 | static BOOLEAN WinDivertStrLen(const wchar_t *s, size_t maxlen, 67 | size_t *lenptr); 68 | static BOOLEAN WinDivertStrCpy(wchar_t *dst, size_t dstlen, 69 | const wchar_t *src); 70 | static int WinDivertStrCmp(const char *s, const char *t); 71 | static BOOLEAN WinDivertAToI(const char *str, char **endptr, UINT32 *intptr, 72 | UINT size); 73 | static BOOLEAN WinDivertAToX(const char *str, char **endptr, UINT32 *intptr, 74 | UINT size, BOOL prefix); 75 | static UINT32 WinDivertDivTen128(UINT32 *a); 76 | 77 | /* 78 | * Misc. 79 | */ 80 | #ifndef UINT8_MAX 81 | #define UINT8_MAX 0xFF 82 | #endif 83 | #ifndef UINT16_MAX 84 | #define UINT16_MAX 0xFFFF 85 | #endif 86 | #ifndef UINT32_MAX 87 | #define UINT32_MAX 0xFFFFFFFF 88 | #endif 89 | 90 | #define IPPROTO_MH 135 91 | 92 | #ifdef _MSC_VER 93 | 94 | #pragma intrinsic(memcpy) 95 | #pragma function(memcpy) 96 | void *memcpy(void *dst, const void *src, size_t n) 97 | { 98 | size_t i; 99 | for (i = 0; i < n; i++) 100 | ((UINT8 *)dst)[i] = ((const UINT8 *)src)[i]; 101 | return dst; 102 | } 103 | 104 | #pragma intrinsic(memset) 105 | #pragma function(memset) 106 | void *memset(void *dst, int c, size_t n) 107 | { 108 | size_t i; 109 | for (i = 0; i < n; i++) 110 | ((UINT8 *)dst)[i] = (UINT8)c; 111 | return dst; 112 | } 113 | 114 | #define WINDIVERT_INLINE __forceinline 115 | 116 | #else /* _MSC_VER */ 117 | 118 | #define WINDIVERT_INLINE __attribute__((__always_inline__)) inline 119 | 120 | #endif /* _MSC_VER */ 121 | 122 | /* 123 | * Filter interpreter config. 124 | */ 125 | static BOOL WinDivertGetData(const VOID *packet, UINT packet_len, INT min, 126 | INT max, INT idx, PVOID data, UINT size); 127 | #define WINDIVERT_GET_DATA(packet, packet_len, min, max, index, data, size) \ 128 | WinDivertGetData((packet), (packet_len), (min), (max), (index), (data), \ 129 | (size)) 130 | 131 | /* 132 | * Prototypes. 133 | */ 134 | static BOOLEAN WinDivertUse32Bit(void); 135 | static BOOLEAN WinDivertGetDriverFileName(LPWSTR sys_str); 136 | static BOOLEAN WinDivertDriverInstall(VOID); 137 | 138 | /* 139 | * Include the helper API implementation. 140 | */ 141 | #include "windivert_shared.c" 142 | #include "windivert_helper.c" 143 | 144 | /* 145 | * Thread local. 146 | */ 147 | static DWORD windivert_tls_idx; 148 | 149 | /* 150 | * Current DLL hmodule. 151 | */ 152 | static HMODULE module = NULL; 153 | 154 | /* 155 | * Dll Entry 156 | */ 157 | BOOL APIENTRY WinDivertDllEntry(HANDLE module0, DWORD reason, LPVOID reserved) 158 | { 159 | HANDLE event; 160 | switch (reason) 161 | { 162 | case DLL_PROCESS_ATTACH: 163 | module = module0; 164 | if ((windivert_tls_idx = TlsAlloc()) == TLS_OUT_OF_INDEXES) 165 | { 166 | return FALSE; 167 | } 168 | // Fallthrough 169 | case DLL_THREAD_ATTACH: 170 | event = CreateEvent(NULL, FALSE, FALSE, NULL); 171 | if (event == NULL) 172 | { 173 | return FALSE; 174 | } 175 | TlsSetValue(windivert_tls_idx, (LPVOID)event); 176 | break; 177 | 178 | case DLL_PROCESS_DETACH: 179 | event = (HANDLE)TlsGetValue(windivert_tls_idx); 180 | if (event != (HANDLE)NULL) 181 | { 182 | CloseHandle(event); 183 | } 184 | TlsFree(windivert_tls_idx); 185 | break; 186 | 187 | case DLL_THREAD_DETACH: 188 | event = (HANDLE)TlsGetValue(windivert_tls_idx); 189 | if (event != (HANDLE)NULL) 190 | { 191 | CloseHandle(event); 192 | } 193 | break; 194 | } 195 | return TRUE; 196 | } 197 | 198 | /* 199 | * Test if we should use the 32-bit or 64-bit driver. 200 | */ 201 | static BOOLEAN WinDivertUse32Bit(void) 202 | { 203 | BOOL is_wow64; 204 | 205 | if (sizeof(void *) == sizeof(UINT64)) 206 | { 207 | return FALSE; 208 | } 209 | if (!IsWow64Process(GetCurrentProcess(), &is_wow64)) 210 | { 211 | // Just guess: 212 | return FALSE; 213 | } 214 | return (is_wow64? FALSE: TRUE); 215 | } 216 | 217 | /* 218 | * Locate the WinDivert driver files. 219 | */ 220 | static BOOLEAN WinDivertGetDriverFileName(LPWSTR sys_str) 221 | { 222 | size_t dir_len, sys_len; 223 | BOOLEAN is_32bit; 224 | 225 | is_32bit = WinDivertUse32Bit(); 226 | 227 | if (is_32bit) 228 | { 229 | if (!WinDivertStrLen(WINDIVERT_DRIVER32_SYS, MAX_PATH, &sys_len)) 230 | { 231 | SetLastError(ERROR_BAD_PATHNAME); 232 | return FALSE; 233 | } 234 | } 235 | else 236 | { 237 | if (!WinDivertStrLen(WINDIVERT_DRIVER64_SYS, MAX_PATH, &sys_len)) 238 | { 239 | SetLastError(ERROR_BAD_PATHNAME); 240 | return FALSE; 241 | } 242 | } 243 | 244 | dir_len = (size_t)GetModuleFileName(module, sys_str, MAX_PATH); 245 | if (dir_len == 0) 246 | { 247 | return FALSE; 248 | } 249 | for (; dir_len > 0 && sys_str[dir_len] != L'\\'; dir_len--) 250 | ; 251 | if (sys_str[dir_len] != L'\\' || dir_len + sys_len + 1 >= MAX_PATH) 252 | { 253 | SetLastError(ERROR_BAD_PATHNAME); 254 | return FALSE; 255 | } 256 | if (!WinDivertStrCpy(sys_str + dir_len, MAX_PATH-dir_len-1, 257 | (is_32bit? WINDIVERT_DRIVER32_SYS: WINDIVERT_DRIVER64_SYS))) 258 | { 259 | SetLastError(ERROR_BAD_PATHNAME); 260 | return FALSE; 261 | } 262 | 263 | return TRUE; 264 | } 265 | 266 | /* 267 | * Register event log. It is not an error if this function fails. 268 | */ 269 | static void WinDivertRegisterEventSource(const wchar_t *windivert_sys) 270 | { 271 | HKEY key; 272 | size_t len; 273 | DWORD types = 7; 274 | 275 | if (!WinDivertStrLen(windivert_sys, MAX_PATH, &len)) 276 | { 277 | return; 278 | } 279 | if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, 280 | "System\\CurrentControlSet\\Services\\EventLog\\System\\WinDivert", 281 | 0, NULL, REG_OPTION_VOLATILE, KEY_SET_VALUE, NULL, &key, NULL) 282 | != ERROR_SUCCESS) 283 | { 284 | return; 285 | } 286 | RegSetValueExW(key, L"EventMessageFile", 0, REG_SZ, (LPBYTE)windivert_sys, 287 | (len + 1) * sizeof(wchar_t)); 288 | RegSetValueExA(key, "TypesSupported", 0, REG_DWORD, (LPBYTE)&types, 289 | sizeof(types)); 290 | RegCloseKey(key); 291 | } 292 | 293 | /* 294 | * Install the WinDivert driver. 295 | */ 296 | static BOOLEAN WinDivertDriverInstall(VOID) 297 | { 298 | DWORD err; 299 | SC_HANDLE manager = NULL, service = NULL; 300 | wchar_t windivert_sys[MAX_PATH+1]; 301 | HANDLE mutex = NULL; 302 | BOOL success = TRUE; 303 | 304 | // Create & lock a named mutex. This is to stop two processes trying 305 | // to start the driver at the same time. 306 | mutex = CreateMutex(NULL, FALSE, L"WinDivertDriverInstallMutex"); 307 | if (mutex == NULL) 308 | { 309 | return FALSE; 310 | } 311 | switch (WaitForSingleObject(mutex, INFINITE)) 312 | { 313 | case WAIT_OBJECT_0: case WAIT_ABANDONED: 314 | break; 315 | default: 316 | return FALSE; 317 | } 318 | 319 | // Open the service manager: 320 | manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 321 | if (manager == NULL) 322 | { 323 | goto WinDivertDriverInstallExit; 324 | } 325 | 326 | // Check if the WinDivert service already exists; if so, start it. 327 | service = OpenService(manager, WINDIVERT_DEVICE_NAME, SERVICE_ALL_ACCESS); 328 | if (service != NULL) 329 | { 330 | goto WinDivertDriverInstallExit; 331 | } 332 | 333 | // Get driver file: 334 | if (!WinDivertGetDriverFileName(windivert_sys)) 335 | { 336 | goto WinDivertDriverInstallExit; 337 | } 338 | 339 | // Create the service: 340 | service = CreateService(manager, WINDIVERT_DEVICE_NAME, 341 | WINDIVERT_DEVICE_NAME, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, 342 | SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, windivert_sys, NULL, NULL, 343 | NULL, NULL, NULL); 344 | if (service == NULL) 345 | { 346 | if (GetLastError() == ERROR_SERVICE_EXISTS) 347 | { 348 | service = OpenService(manager, WINDIVERT_DEVICE_NAME, 349 | SERVICE_ALL_ACCESS); 350 | } 351 | goto WinDivertDriverInstallExit; 352 | } 353 | 354 | // Register event logging: 355 | WinDivertRegisterEventSource(windivert_sys); 356 | 357 | WinDivertDriverInstallExit: 358 | 359 | success = (service != NULL); 360 | if (service != NULL) 361 | { 362 | // Start the service: 363 | success = StartService(service, 0, NULL); 364 | if (!success) 365 | { 366 | success = (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING); 367 | } 368 | else 369 | { 370 | // Mark the service for deletion. This will cause the driver to 371 | // unload if (1) there are no more open handles, and (2) the 372 | // service is STOPPED or on system reboot. 373 | (VOID)DeleteService(service); 374 | } 375 | } 376 | 377 | err = GetLastError(); 378 | if (manager != NULL) 379 | { 380 | CloseServiceHandle(manager); 381 | } 382 | if (service != NULL) 383 | { 384 | CloseServiceHandle(service); 385 | } 386 | ReleaseMutex(mutex); 387 | CloseHandle(mutex); 388 | SetLastError(err); 389 | 390 | return success; 391 | } 392 | 393 | /* 394 | * Perform an (overlapped) DeviceIoControl. 395 | */ 396 | static BOOL WinDivertIoControlEx(HANDLE handle, DWORD code, 397 | PWINDIVERT_IOCTL ioctl, PVOID buf, UINT len, UINT *iolen, 398 | LPOVERLAPPED overlapped) 399 | { 400 | BOOL result; 401 | DWORD iolen0; 402 | 403 | result = DeviceIoControl(handle, code, ioctl, sizeof(WINDIVERT_IOCTL), buf, 404 | (DWORD)len, &iolen0, overlapped); 405 | if (result && iolen != NULL) 406 | { 407 | *iolen = (UINT)iolen0; 408 | } 409 | return result; 410 | } 411 | 412 | /* 413 | * Perform a DeviceIoControl. 414 | */ 415 | static BOOL WinDivertIoControl(HANDLE handle, DWORD code, 416 | PWINDIVERT_IOCTL ioctl, PVOID buf, UINT len, UINT *iolen) 417 | { 418 | OVERLAPPED overlapped; 419 | DWORD iolen0; 420 | HANDLE event; 421 | 422 | event = (HANDLE)TlsGetValue(windivert_tls_idx); 423 | if (event == (HANDLE)NULL) 424 | { 425 | event = CreateEvent(NULL, FALSE, FALSE, NULL); 426 | if (event == NULL) 427 | { 428 | return FALSE; 429 | } 430 | TlsSetValue(windivert_tls_idx, (LPVOID)event); 431 | } 432 | 433 | memset(&overlapped, 0, sizeof(overlapped)); 434 | overlapped.hEvent = event; 435 | if (!WinDivertIoControlEx(handle, code, ioctl, buf, len, iolen, 436 | &overlapped)) 437 | { 438 | if (GetLastError() != ERROR_IO_PENDING || 439 | !GetOverlappedResult(handle, &overlapped, &iolen0, TRUE)) 440 | { 441 | return FALSE; 442 | } 443 | if (iolen != NULL) 444 | { 445 | *iolen = (UINT)iolen0; 446 | } 447 | } 448 | return TRUE; 449 | } 450 | 451 | /* 452 | * Open a WinDivert handle. 453 | */ 454 | HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, INT16 priority, 455 | UINT64 flags) 456 | { 457 | WINDIVERT_FILTER *object; 458 | UINT obj_len; 459 | ERROR comp_err; 460 | DWORD err; 461 | HANDLE handle, pool; 462 | UINT64 filter_flags; 463 | WINDIVERT_IOCTL ioctl; 464 | WINDIVERT_VERSION version; 465 | 466 | // Static checks (should be compiled away if TRUE): 467 | if (sizeof(WINDIVERT_ADDRESS) != 80 || 468 | sizeof(WINDIVERT_DATA_NETWORK) != 8 || 469 | offsetof(WINDIVERT_DATA_FLOW, Protocol) != 56 || 470 | offsetof(WINDIVERT_DATA_SOCKET, Protocol) != 56 || 471 | offsetof(WINDIVERT_DATA_REFLECT, Priority) != 24 || 472 | sizeof(WINDIVERT_FILTER) != 24 || 473 | offsetof(WINDIVERT_ADDRESS, Reserved3) != 16) 474 | { 475 | SetLastError(ERROR_INVALID_PARAMETER); 476 | return INVALID_HANDLE_VALUE; 477 | } 478 | 479 | // Parameter checking: 480 | switch (layer) 481 | { 482 | case WINDIVERT_LAYER_NETWORK: 483 | case WINDIVERT_LAYER_NETWORK_FORWARD: 484 | case WINDIVERT_LAYER_FLOW: 485 | case WINDIVERT_LAYER_SOCKET: 486 | case WINDIVERT_LAYER_REFLECT: 487 | break; 488 | default: 489 | SetLastError(ERROR_INVALID_PARAMETER); 490 | return INVALID_HANDLE_VALUE; 491 | } 492 | if (!WINDIVERT_FLAGS_VALID(flags)) 493 | { 494 | SetLastError(ERROR_INVALID_PARAMETER); 495 | return INVALID_HANDLE_VALUE; 496 | } 497 | 498 | if (priority < WINDIVERT_PRIORITY_MIN || 499 | priority > WINDIVERT_PRIORITY_MAX) 500 | { 501 | SetLastError(ERROR_INVALID_PARAMETER); 502 | return INVALID_HANDLE_VALUE; 503 | } 504 | 505 | // Compile & analyze the filter: 506 | pool = HeapCreate(HEAP_NO_SERIALIZE, WINDIVERT_MIN_POOL_SIZE, 507 | WINDIVERT_MAX_POOL_SIZE); 508 | if (pool == NULL) 509 | { 510 | return FALSE; 511 | } 512 | object = HeapAlloc(pool, 0, 513 | WINDIVERT_FILTER_MAXLEN * sizeof(WINDIVERT_FILTER)); 514 | if (object == NULL) 515 | { 516 | err = GetLastError(); 517 | HeapDestroy(pool); 518 | SetLastError(err); 519 | return FALSE; 520 | } 521 | comp_err = WinDivertCompileFilter(filter, pool, layer, object, &obj_len); 522 | if (IS_ERROR(comp_err)) 523 | { 524 | HeapDestroy(pool); 525 | SetLastError(ERROR_INVALID_PARAMETER); 526 | return INVALID_HANDLE_VALUE; 527 | } 528 | filter_flags = WinDivertAnalyzeFilter(layer, object, obj_len); 529 | 530 | // Attempt to open the WinDivert device: 531 | handle = CreateFile(L"\\\\.\\" WINDIVERT_DEVICE_NAME, 532 | GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 533 | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, INVALID_HANDLE_VALUE); 534 | if (handle == INVALID_HANDLE_VALUE) 535 | { 536 | err = GetLastError(); 537 | if (err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND) 538 | { 539 | HeapDestroy(pool); 540 | SetLastError(err); 541 | return INVALID_HANDLE_VALUE; 542 | } 543 | 544 | // Open failed because the device isn't installed; install it now. 545 | if ((flags & WINDIVERT_FLAG_NO_INSTALL) != 0) 546 | { 547 | HeapDestroy(pool); 548 | SetLastError(ERROR_SERVICE_DOES_NOT_EXIST); 549 | return INVALID_HANDLE_VALUE; 550 | } 551 | SetLastError(0); 552 | if (!WinDivertDriverInstall()) 553 | { 554 | err = GetLastError(); 555 | err = (err == 0? ERROR_OPEN_FAILED: err); 556 | HeapDestroy(pool); 557 | SetLastError(err); 558 | return INVALID_HANDLE_VALUE; 559 | } 560 | handle = CreateFile(L"\\\\.\\" WINDIVERT_DEVICE_NAME, 561 | GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 562 | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 563 | INVALID_HANDLE_VALUE); 564 | if (handle == INVALID_HANDLE_VALUE) 565 | { 566 | err = GetLastError(); 567 | HeapDestroy(pool); 568 | SetLastError(err); 569 | return INVALID_HANDLE_VALUE; 570 | } 571 | } 572 | 573 | // Initialize the handle: 574 | memset(&ioctl, 0, sizeof(ioctl)); 575 | ioctl.initialize.layer = layer; 576 | ioctl.initialize.priority = (INT32)priority + WINDIVERT_PRIORITY_MAX; 577 | ioctl.initialize.flags = flags; 578 | memset(&version, 0, sizeof(version)); 579 | version.magic = WINDIVERT_MAGIC_DLL; 580 | version.major = WINDIVERT_VERSION_MAJOR; 581 | version.minor = WINDIVERT_VERSION_MINOR; 582 | version.bits = 8 * sizeof(void *); 583 | if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_INITIALIZE, &ioctl, 584 | &version, sizeof(version), NULL)) 585 | { 586 | err = GetLastError(); 587 | CloseHandle(handle); 588 | HeapDestroy(pool); 589 | SetLastError(err); 590 | return INVALID_HANDLE_VALUE; 591 | } 592 | if (version.magic != WINDIVERT_MAGIC_SYS || 593 | version.major < WINDIVERT_VERSION_MAJOR_MIN) 594 | { 595 | CloseHandle(handle); 596 | HeapDestroy(pool); 597 | SetLastError(ERROR_DRIVER_FAILED_PRIOR_UNLOAD); 598 | return INVALID_HANDLE_VALUE; 599 | } 600 | 601 | // Start the filter: 602 | memset(&ioctl, 0, sizeof(ioctl)); 603 | ioctl.startup.flags = filter_flags; 604 | if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_STARTUP, &ioctl, 605 | object, obj_len * sizeof(WINDIVERT_FILTER), NULL)) 606 | { 607 | err = GetLastError(); 608 | CloseHandle(handle); 609 | HeapDestroy(pool); 610 | SetLastError(err); 611 | return INVALID_HANDLE_VALUE; 612 | } 613 | HeapDestroy(pool); 614 | 615 | // Success! 616 | return handle; 617 | } 618 | 619 | /* 620 | * Receive a WinDivert packet. 621 | */ 622 | BOOL WinDivertRecv(HANDLE handle, PVOID pPacket, UINT packetLen, UINT *readLen, 623 | PWINDIVERT_ADDRESS addr) 624 | { 625 | WINDIVERT_IOCTL ioctl; 626 | memset(&ioctl, 0, sizeof(ioctl)); 627 | ioctl.recv.addr = (UINT64)(ULONG_PTR)addr; 628 | ioctl.recv.addr_len_ptr = (UINT64)(ULONG_PTR)NULL; 629 | return WinDivertIoControl(handle, IOCTL_WINDIVERT_RECV, &ioctl, 630 | pPacket, packetLen, readLen); 631 | } 632 | 633 | /* 634 | * Receive a WinDivert packet. 635 | */ 636 | BOOL WinDivertRecvEx(HANDLE handle, PVOID pPacket, UINT packetLen, 637 | UINT *readLen, UINT64 flags, PWINDIVERT_ADDRESS addr, UINT *pAddrLen, 638 | LPOVERLAPPED overlapped) 639 | { 640 | WINDIVERT_IOCTL ioctl; 641 | memset(&ioctl, 0, sizeof(ioctl)); 642 | ioctl.recv.addr = (UINT64)(ULONG_PTR)addr; 643 | ioctl.recv.addr_len_ptr = (UINT64)(ULONG_PTR)pAddrLen; 644 | if (flags != 0) 645 | { 646 | SetLastError(ERROR_INVALID_PARAMETER); 647 | return FALSE; 648 | } 649 | if (overlapped == NULL) 650 | { 651 | return WinDivertIoControl(handle, IOCTL_WINDIVERT_RECV, &ioctl, 652 | pPacket, packetLen, readLen); 653 | } 654 | else 655 | { 656 | return WinDivertIoControlEx(handle, IOCTL_WINDIVERT_RECV, &ioctl, 657 | pPacket, packetLen, readLen, overlapped); 658 | } 659 | } 660 | 661 | /* 662 | * Send a WinDivert packet. 663 | */ 664 | BOOL WinDivertSend(HANDLE handle, const VOID *pPacket, UINT packetLen, 665 | UINT *writeLen, const WINDIVERT_ADDRESS *addr) 666 | { 667 | WINDIVERT_IOCTL ioctl; 668 | memset(&ioctl, 0, sizeof(ioctl)); 669 | ioctl.send.addr = (UINT64)(ULONG_PTR)addr; 670 | ioctl.send.addr_len = sizeof(WINDIVERT_ADDRESS); 671 | return WinDivertIoControl(handle, IOCTL_WINDIVERT_SEND, &ioctl, 672 | (PVOID)pPacket, packetLen, writeLen); 673 | } 674 | 675 | /* 676 | * Send a WinDivert packet. 677 | */ 678 | BOOL WinDivertSendEx(HANDLE handle, const VOID *pPacket, UINT packetLen, 679 | UINT *writeLen, UINT64 flags, const WINDIVERT_ADDRESS *addr, UINT addrLen, 680 | LPOVERLAPPED overlapped) 681 | { 682 | WINDIVERT_IOCTL ioctl; 683 | memset(&ioctl, 0, sizeof(ioctl)); 684 | ioctl.send.addr = (UINT64)(ULONG_PTR)addr; 685 | ioctl.send.addr_len = addrLen; 686 | if (flags != 0) 687 | { 688 | SetLastError(ERROR_INVALID_PARAMETER); 689 | return FALSE; 690 | } 691 | if (overlapped == NULL) 692 | { 693 | return WinDivertIoControl(handle, IOCTL_WINDIVERT_SEND, &ioctl, 694 | (PVOID)pPacket, packetLen, writeLen); 695 | } 696 | else 697 | { 698 | return WinDivertIoControlEx(handle, IOCTL_WINDIVERT_SEND, &ioctl, 699 | (PVOID)pPacket, packetLen, writeLen, overlapped); 700 | } 701 | } 702 | 703 | /* 704 | * Shutdown a WinDivert handle. 705 | */ 706 | BOOL WinDivertShutdown(HANDLE handle, WINDIVERT_SHUTDOWN how) 707 | { 708 | WINDIVERT_IOCTL ioctl; 709 | memset(&ioctl, 0, sizeof(ioctl)); 710 | ioctl.shutdown.how = (UINT32)how; 711 | return WinDivertIoControl(handle, IOCTL_WINDIVERT_SHUTDOWN, &ioctl, NULL, 712 | 0, NULL); 713 | } 714 | 715 | /* 716 | * Close a WinDivert handle. 717 | */ 718 | BOOL WinDivertClose(HANDLE handle) 719 | { 720 | return CloseHandle(handle); 721 | } 722 | 723 | /* 724 | * Set a WinDivert parameter. 725 | */ 726 | BOOL WinDivertSetParam(HANDLE handle, WINDIVERT_PARAM param, UINT64 value) 727 | { 728 | WINDIVERT_IOCTL ioctl; 729 | memset(&ioctl, 0, sizeof(ioctl)); 730 | ioctl.set_param.param = (UINT32)param; 731 | ioctl.set_param.val = value; 732 | return WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_PARAM, &ioctl, NULL, 733 | 0, NULL); 734 | } 735 | 736 | /* 737 | * Get a WinDivert parameter. 738 | */ 739 | BOOL WinDivertGetParam(HANDLE handle, WINDIVERT_PARAM param, UINT64 *pValue) 740 | { 741 | WINDIVERT_IOCTL ioctl; 742 | memset(&ioctl, 0, sizeof(ioctl)); 743 | ioctl.get_param.param = (UINT32)param; 744 | return WinDivertIoControl(handle, IOCTL_WINDIVERT_GET_PARAM, &ioctl, 745 | pValue, sizeof(UINT64), NULL); 746 | } 747 | 748 | /*****************************************************************************/ 749 | /* REPLACEMENTS */ 750 | /*****************************************************************************/ 751 | 752 | static BOOLEAN WinDivertIsDigit(char c) 753 | { 754 | return (c >= '0' && c <= '9'); 755 | } 756 | 757 | static BOOLEAN WinDivertIsXDigit(char c) 758 | { 759 | return (c >= '0' && c <= '9') || 760 | (c >= 'a' && c <= 'f') || 761 | (c >= 'A' && c <= 'F'); 762 | } 763 | 764 | static BOOLEAN WinDivertIsSpace(char c) 765 | { 766 | return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || 767 | c == '\v'); 768 | } 769 | 770 | static BOOLEAN WinDivertIsAlNum(char c) 771 | { 772 | return (c >= 'a' && c <= 'z') || 773 | (c >= 'A' && c <= 'Z') || 774 | (c >= '0' && c <= '9'); 775 | } 776 | 777 | static char WinDivertToLower(char c) 778 | { 779 | if (c >= 'A' && c <= 'Z') 780 | return 'a' + (c - 'A'); 781 | return c; 782 | } 783 | 784 | static BOOLEAN WinDivertStrLen(const wchar_t *s, size_t maxlen, 785 | size_t *lenptr) 786 | { 787 | size_t i; 788 | for (i = 0; s[i]; i++) 789 | { 790 | if (i > maxlen) 791 | { 792 | return FALSE; 793 | } 794 | } 795 | *lenptr = i; 796 | return TRUE; 797 | } 798 | 799 | static BOOLEAN WinDivertStrCpy(wchar_t *dst, size_t dstlen, const wchar_t *src) 800 | { 801 | size_t i; 802 | for (i = 0; src[i]; i++) 803 | { 804 | if (i > dstlen) 805 | { 806 | return FALSE; 807 | } 808 | dst[i] = src[i]; 809 | } 810 | if (i > dstlen) 811 | { 812 | return FALSE; 813 | } 814 | dst[i] = src[i]; 815 | return TRUE; 816 | } 817 | 818 | static int WinDivertStrCmp(const char *s, const char *t) 819 | { 820 | int cmp; 821 | size_t i; 822 | for (i = 0; ; i++) 823 | { 824 | cmp = s[i] - t[i]; 825 | if (cmp != 0) 826 | { 827 | return cmp; 828 | } 829 | if (s[i] == '\0') 830 | { 831 | return 0; 832 | } 833 | } 834 | } 835 | 836 | static BOOLEAN WinDivertMul128(UINT32 *n, UINT32 m) 837 | { 838 | UINT64 n64 = (UINT64)n[0] * (UINT64)m; 839 | n[0] = (UINT32)n64; 840 | n64 = (UINT64)n[1] * (UINT64)m + (n64 >> 32); 841 | n[1] = (UINT32)n64; 842 | n64 = (UINT64)n[2] * (UINT64)m + (n64 >> 32); 843 | n[2] = (UINT32)n64; 844 | n64 = (UINT64)n[3] * (UINT64)m + (n64 >> 32); 845 | n[3] = (UINT32)n64; 846 | return ((n64 >> 32) == 0); 847 | } 848 | 849 | static BOOLEAN WinDivertAdd128(UINT32 *n, UINT32 a) 850 | { 851 | UINT64 n64 = (UINT64)n[0] + (UINT64)a; 852 | n[0] = (UINT32)n64; 853 | n64 = (UINT64)n[1] + (n64 >> 32); 854 | n[1] = (UINT32)n64; 855 | n64 = (UINT64)n[2] + (n64 >> 32); 856 | n[2] = (UINT32)n64; 857 | n64 = (UINT64)n[3] + (n64 >> 32); 858 | n[3] = (UINT32)n64; 859 | return ((n64 >> 32) == 0); 860 | } 861 | 862 | static BOOLEAN WinDivertAToI(const char *str, char **endptr, UINT32 *intptr, 863 | UINT size) 864 | { 865 | size_t i = 0; 866 | UINT32 n[4] = {0}; 867 | BOOLEAN result = TRUE; 868 | for (; str[i] && WinDivertIsDigit(str[i]); i++) 869 | { 870 | if (!WinDivertMul128(n, 10) || !WinDivertAdd128(n, str[i] - '0')) 871 | { 872 | return FALSE; 873 | } 874 | } 875 | if (i == 0) 876 | { 877 | return FALSE; 878 | } 879 | if (endptr != NULL) 880 | { 881 | *endptr = (char *)str + i; 882 | } 883 | for (i = 0; i < size; i++) 884 | { 885 | intptr[i] = n[i]; 886 | } 887 | for (; result && i < size && i < 4; i++) 888 | { 889 | result = result && (n[i] == 0); 890 | } 891 | return result; 892 | } 893 | 894 | static BOOLEAN WinDivertAToX(const char *str, char **endptr, UINT32 *intptr, 895 | UINT size, BOOL prefix) 896 | { 897 | size_t i = 0; 898 | UINT32 n[4] = {0}, dig; 899 | BOOLEAN result = TRUE; 900 | if (prefix) 901 | { 902 | if (str[i] == '0' && str[i+1] == 'x') 903 | { 904 | i += 2; 905 | } 906 | else 907 | { 908 | return FALSE; 909 | } 910 | } 911 | for (; str[i] && WinDivertIsXDigit(str[i]); i++) 912 | { 913 | if (WinDivertIsDigit(str[i])) 914 | { 915 | dig = (UINT32)(str[i] - '0'); 916 | } 917 | else 918 | { 919 | dig = (UINT32)(WinDivertToLower(str[i]) - 'a') + 0x0A; 920 | } 921 | if (!WinDivertMul128(n, 16) || !WinDivertAdd128(n, dig)) 922 | { 923 | return FALSE; 924 | } 925 | } 926 | if (i == 0) 927 | { 928 | return FALSE; 929 | } 930 | if (endptr != NULL) 931 | { 932 | *endptr = (char *)str + i; 933 | } 934 | for (i = 0; i < size; i++) 935 | { 936 | intptr[i] = n[i]; 937 | } 938 | for (; result && i < size && i < 4; i++) 939 | { 940 | result = result && (n[i] == 0); 941 | } 942 | return result; 943 | } 944 | 945 | /* 946 | * Divide by 10 and return the remainder. 947 | */ 948 | #define WINDIVERT_BIG_MUL_ROUND(a, c, r, i) \ 949 | do { \ 950 | UINT64 t = WINDIVERT_MUL64((UINT64)(a), (UINT64)(c)); \ 951 | UINT k; \ 952 | for (k = (i); k < 9 && t != 0; k++) \ 953 | { \ 954 | UINT64 s = (UINT64)(r)[k] + (t & 0xFFFFFFFF); \ 955 | (r)[k] = (UINT32)s; \ 956 | t = (t >> 32) + (s >> 32); \ 957 | } \ 958 | } while (FALSE) 959 | static UINT32 WinDivertDivTen128(UINT32 *a) 960 | { 961 | const UINT32 c[5] = 962 | { 963 | 0x9999999A, 0x99999999, 0x99999999, 0x99999999, 0x19999999 964 | }; 965 | UINT32 r[9] = {0}, m[6] = {0}; 966 | UINT i, j; 967 | 968 | for (i = 0; i < 4; i++) 969 | { 970 | for (j = 0; j < 5; j++) 971 | { 972 | WINDIVERT_BIG_MUL_ROUND(a[i], c[j], r, i+j); 973 | } 974 | } 975 | 976 | a[0] = r[5]; 977 | a[1] = r[6]; 978 | a[2] = r[7]; 979 | a[3] = r[8]; 980 | 981 | for (i = 0; i < 5; i++) 982 | { 983 | WINDIVERT_BIG_MUL_ROUND(r[i], 10, m, i); 984 | } 985 | 986 | return m[5]; 987 | } 988 | 989 | --------------------------------------------------------------------------------