├── 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 |
--------------------------------------------------------------------------------