diff options
Diffstat (limited to 'driver')
-rw-r--r-- | driver/configuration_windows.go | 183 | ||||
-rw-r--r-- | driver/dll_fromfile_windows.go | 56 | ||||
-rw-r--r-- | driver/dll_fromrsrc_windows.go | 62 | ||||
-rw-r--r-- | driver/dll_windows.go | 91 | ||||
-rw-r--r-- | driver/driver_windows.go | 170 | ||||
-rw-r--r-- | driver/memmod/memmod_windows.go | 698 | ||||
-rw-r--r-- | driver/memmod/memmod_windows_32.go | 16 | ||||
-rw-r--r-- | driver/memmod/memmod_windows_386.go | 8 | ||||
-rw-r--r-- | driver/memmod/memmod_windows_64.go | 36 | ||||
-rw-r--r-- | driver/memmod/memmod_windows_amd64.go | 8 | ||||
-rw-r--r-- | driver/memmod/memmod_windows_arm.go | 8 | ||||
-rw-r--r-- | driver/memmod/memmod_windows_arm64.go | 8 | ||||
-rw-r--r-- | driver/memmod/syscall_windows.go | 392 | ||||
-rw-r--r-- | driver/memmod/syscall_windows_32.go | 96 | ||||
-rw-r--r-- | driver/memmod/syscall_windows_64.go | 95 | ||||
-rw-r--r-- | driver/wintunremoval_windows.go | 50 |
16 files changed, 1977 insertions, 0 deletions
diff --git a/driver/configuration_windows.go b/driver/configuration_windows.go new file mode 100644 index 00000000..d838dcf2 --- /dev/null +++ b/driver/configuration_windows.go @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" +) + +type AdapterState uint32 + +const ( + AdapterStateDown AdapterState = 0 + AdapterStateUp AdapterState = 1 +) + +type AllowedIP struct { + Address [16]byte + AddressFamily winipcfg.AddressFamily + Cidr uint8 + _ [4]byte +} + +type PeerFlag uint32 + +const ( + PeerHasPublicKey PeerFlag = 1 << 0 + PeerHasPresharedKey PeerFlag = 1 << 1 + PeerHasPersistentKeepalive PeerFlag = 1 << 2 + PeerHasEndpoint PeerFlag = 1 << 3 + PeerReplaceAllowedIPs PeerFlag = 1 << 5 + PeerRemove PeerFlag = 1 << 6 + PeerUpdateOnly PeerFlag = 1 << 7 +) + +type Peer struct { + Flags PeerFlag + _ uint32 + PublicKey [32]byte + PresharedKey [32]byte + PersistentKeepalive uint16 + _ uint16 + Endpoint winipcfg.RawSockaddrInet + TxBytes uint64 + RxBytes uint64 + LastHandshake uint64 + AllowedIPsCount uint32 + _ [4]byte +} + +type InterfaceFlag uint32 + +const ( + InterfaceHasPublicKey InterfaceFlag = 1 << 0 + InterfaceHasPrivateKey InterfaceFlag = 1 << 1 + InterfaceHasListenPort InterfaceFlag = 1 << 2 + InterfaceReplacePeers InterfaceFlag = 1 << 3 +) + +type Interface struct { + Flags InterfaceFlag + ListenPort uint16 + PrivateKey [32]byte + PublicKey [32]byte + PeerCount uint32 + _ [4]byte +} + +var ( + procWireGuardSetAdapterState = modwireguard.NewProc("WireGuardSetAdapterState") + procWireGuardGetAdapterState = modwireguard.NewProc("WireGuardGetAdapterState") + procWireGuardSetConfiguration = modwireguard.NewProc("WireGuardSetConfiguration") + procWireGuardGetConfiguration = modwireguard.NewProc("WireGuardGetConfiguration") +) + +// SetAdapterState sets the adapter either Up or Down. +func (wireguard *Adapter) SetAdapterState(adapterState AdapterState) (err error) { + r0, _, e1 := syscall.SyscallN(procWireGuardSetAdapterState.Addr(), wireguard.handle, uintptr(adapterState)) + if r0 == 0 { + err = e1 + } + return +} + +// AdapterState returns the current state of the adapter. +func (wireguard *Adapter) AdapterState() (adapterState AdapterState, err error) { + r0, _, e1 := syscall.SyscallN(procWireGuardGetAdapterState.Addr(), wireguard.handle, uintptr(unsafe.Pointer(&adapterState))) + if r0 == 0 { + err = e1 + } + return +} + +// SetConfiguration sets the adapter configuration. +func (wireguard *Adapter) SetConfiguration(interfaze *Interface, size uint32) (err error) { + r0, _, e1 := syscall.SyscallN(procWireGuardSetConfiguration.Addr(), wireguard.handle, uintptr(unsafe.Pointer(interfaze)), uintptr(size)) + if r0 == 0 { + err = e1 + } + return +} + +// Configuration gets the adapter configuration. +func (wireguard *Adapter) Configuration() (interfaze *Interface, err error) { + size := wireguard.lastGetGuessSize + if size == 0 { + size = 512 + } + for { + buf := make([]byte, size) + r0, _, e1 := syscall.SyscallN(procWireGuardGetConfiguration.Addr(), wireguard.handle, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size))) + if r0 != 0 { + wireguard.lastGetGuessSize = size + return (*Interface)(unsafe.Pointer(&buf[0])), nil + } + if e1 != windows.ERROR_MORE_DATA { + return nil, e1 + } + } +} + +// FirstPeer returns the first peer attached to the interface. +func (interfaze *Interface) FirstPeer() *Peer { + return (*Peer)(unsafe.Add(unsafe.Pointer(interfaze), unsafe.Sizeof(*interfaze))) +} + +// NextPeer returns the subsequent peer of the current one. +func (peer *Peer) NextPeer() *Peer { + return (*Peer)(unsafe.Pointer(uintptr(unsafe.Pointer(peer)) + unsafe.Sizeof(*peer) + uintptr(peer.AllowedIPsCount)*unsafe.Sizeof(AllowedIP{}))) +} + +// FirstAllowedIP returns the first allowed IP attached to the peer. +func (peer *Peer) FirstAllowedIP() *AllowedIP { + return (*AllowedIP)(unsafe.Add(unsafe.Pointer(peer), unsafe.Sizeof(*peer))) +} + +// NextAllowedIP returns the subsequent allowed IP of the current one. +func (allowedIP *AllowedIP) NextAllowedIP() *AllowedIP { + return (*AllowedIP)(unsafe.Add(unsafe.Pointer(allowedIP), unsafe.Sizeof(*allowedIP))) +} + +type ConfigBuilder struct { + buffer []byte +} + +// Preallocate reserves memory in the config builder to reduce allocations of append operations. +func (builder *ConfigBuilder) Preallocate(size uint32) { + if builder.buffer == nil { + builder.buffer = make([]byte, 0, size) + } +} + +// AppendInterface appends an interface to the building configuration. This should be called first. +func (builder *ConfigBuilder) AppendInterface(interfaze *Interface) { + newBytes := unsafe.Slice((*byte)(unsafe.Pointer(interfaze)), unsafe.Sizeof(*interfaze)) + builder.buffer = append(builder.buffer, newBytes...) +} + +// AppendPeer appends a peer to the building configuration. This should be called after an interface has been added. +func (builder *ConfigBuilder) AppendPeer(peer *Peer) { + newBytes := unsafe.Slice((*byte)(unsafe.Pointer(peer)), unsafe.Sizeof(*peer)) + builder.buffer = append(builder.buffer, newBytes...) +} + +// AppendAllowedIP appends an allowed IP to the building configuration. This should be called after a peer has been added. +func (builder *ConfigBuilder) AppendAllowedIP(allowedIP *AllowedIP) { + newBytes := unsafe.Slice((*byte)(unsafe.Pointer(allowedIP)), unsafe.Sizeof(*allowedIP)) + builder.buffer = append(builder.buffer, newBytes...) +} + +// Interface assembles the configuration and returns the interface and length to be passed to SetConfiguration. +func (builder *ConfigBuilder) Interface() (*Interface, uint32) { + if builder.buffer == nil { + return nil, 0 + } + return (*Interface)(unsafe.Pointer(&builder.buffer[0])), uint32(len(builder.buffer)) +} diff --git a/driver/dll_fromfile_windows.go b/driver/dll_fromfile_windows.go new file mode 100644 index 00000000..b815b4c0 --- /dev/null +++ b/driver/dll_fromfile_windows.go @@ -0,0 +1,56 @@ +//go:build !load_wgnt_from_rsrc + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + "fmt" + "sync" + "sync/atomic" + "unsafe" + + "golang.org/x/sys/windows" +) + +type lazyDLL struct { + Name string + Base windows.Handle + mu sync.Mutex + module windows.Handle + onLoad func(d *lazyDLL) +} + +func (d *lazyDLL) Load() error { + if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil { + return nil + } + d.mu.Lock() + defer d.mu.Unlock() + if d.module != 0 { + return nil + } + + const ( + LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200 + LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 + ) + module, err := windows.LoadLibraryEx(d.Name, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32) + if err != nil { + return fmt.Errorf("Unable to load library: %w", err) + } + d.Base = module + + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module)) + if d.onLoad != nil { + d.onLoad(d) + } + return nil +} + +func (p *lazyProc) nameToAddr() (uintptr, error) { + return windows.GetProcAddress(p.dll.module, p.Name) +} diff --git a/driver/dll_fromrsrc_windows.go b/driver/dll_fromrsrc_windows.go new file mode 100644 index 00000000..65b1cfce --- /dev/null +++ b/driver/dll_fromrsrc_windows.go @@ -0,0 +1,62 @@ +//go:build load_wgnt_from_rsrc + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + "fmt" + "sync" + "sync/atomic" + "unsafe" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/wireguard/windows/driver/memmod" +) + +type lazyDLL struct { + Name string + Base windows.Handle + mu sync.Mutex + module *memmod.Module + onLoad func(d *lazyDLL) +} + +func (d *lazyDLL) Load() error { + if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil { + return nil + } + d.mu.Lock() + defer d.mu.Unlock() + if d.module != nil { + return nil + } + + const ourModule windows.Handle = 0 + resInfo, err := windows.FindResource(ourModule, d.Name, windows.RT_RCDATA) + if err != nil { + return fmt.Errorf("Unable to find \"%v\" RCDATA resource: %w", d.Name, err) + } + data, err := windows.LoadResourceData(ourModule, resInfo) + if err != nil { + return fmt.Errorf("Unable to load resource: %w", err) + } + module, err := memmod.LoadLibrary(data) + if err != nil { + return fmt.Errorf("Unable to load library: %w", err) + } + d.Base = windows.Handle(module.BaseAddr()) + + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module)) + if d.onLoad != nil { + d.onLoad(d) + } + return nil +} + +func (p *lazyProc) nameToAddr() (uintptr, error) { + return p.dll.module.ProcAddressByName(p.Name) +} diff --git a/driver/dll_windows.go b/driver/dll_windows.go new file mode 100644 index 00000000..5dcb849e --- /dev/null +++ b/driver/dll_windows.go @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + "fmt" + "sync" + "sync/atomic" + "unsafe" + + "golang.org/x/sys/windows" +) + +func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL { + return &lazyDLL{Name: name, onLoad: onLoad} +} + +func (d *lazyDLL) NewProc(name string) *lazyProc { + return &lazyProc{dll: d, Name: name} +} + +type lazyProc struct { + Name string + mu sync.Mutex + dll *lazyDLL + addr uintptr +} + +func (p *lazyProc) Find() error { + if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil { + return nil + } + p.mu.Lock() + defer p.mu.Unlock() + if p.addr != 0 { + return nil + } + + err := p.dll.Load() + if err != nil { + return fmt.Errorf("Error loading %v DLL: %w", p.dll.Name, err) + } + addr, err := p.nameToAddr() + if err != nil { + return fmt.Errorf("Error getting %v address: %w", p.Name, err) + } + + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr)) + return nil +} + +func (p *lazyProc) Addr() uintptr { + err := p.Find() + if err != nil { + panic(err) + } + return p.addr +} + +// Version returns the version of the driver DLL. +func Version() string { + if modwireguard.Load() != nil { + return "unknown" + } + resInfo, err := windows.FindResource(modwireguard.Base, windows.ResourceID(1), windows.RT_VERSION) + if err != nil { + return "unknown" + } + data, err := windows.LoadResourceData(modwireguard.Base, resInfo) + if err != nil { + return "unknown" + } + + var fixedInfo *windows.VS_FIXEDFILEINFO + fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo)) + err = windows.VerQueryValue(unsafe.Pointer(&data[0]), `\`, unsafe.Pointer(&fixedInfo), &fixedInfoLen) + if err != nil { + return "unknown" + } + version := fmt.Sprintf("%d.%d", (fixedInfo.FileVersionMS>>16)&0xff, (fixedInfo.FileVersionMS>>0)&0xff) + if nextNibble := (fixedInfo.FileVersionLS >> 16) & 0xff; nextNibble != 0 { + version += fmt.Sprintf(".%d", nextNibble) + } + if nextNibble := (fixedInfo.FileVersionLS >> 0) & 0xff; nextNibble != 0 { + version += fmt.Sprintf(".%d", nextNibble) + } + return version +} diff --git a/driver/driver_windows.go b/driver/driver_windows.go new file mode 100644 index 00000000..462c3a30 --- /dev/null +++ b/driver/driver_windows.go @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + "log" + "runtime" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" +) + +type loggerLevel int + +const ( + logInfo loggerLevel = iota + logWarn + logErr +) + +const AdapterNameMax = 128 + +type Adapter struct { + handle uintptr + lastGetGuessSize uint32 +} + +var ( + modwireguard = newLazyDLL("wireguard.dll", setupLogger) + procWireGuardCreateAdapter = modwireguard.NewProc("WireGuardCreateAdapter") + procWireGuardOpenAdapter = modwireguard.NewProc("WireGuardOpenAdapter") + procWireGuardCloseAdapter = modwireguard.NewProc("WireGuardCloseAdapter") + procWireGuardDeleteDriver = modwireguard.NewProc("WireGuardDeleteDriver") + procWireGuardGetAdapterLUID = modwireguard.NewProc("WireGuardGetAdapterLUID") + procWireGuardGetRunningDriverVersion = modwireguard.NewProc("WireGuardGetRunningDriverVersion") + procWireGuardSetAdapterLogging = modwireguard.NewProc("WireGuardSetAdapterLogging") +) + +type TimestampedWriter interface { + WriteWithTimestamp(p []byte, ts int64) (n int, err error) +} + +func logMessage(level loggerLevel, timestamp uint64, msg *uint16) int { + if tw, ok := log.Default().Writer().(TimestampedWriter); ok { + tw.WriteWithTimestamp([]byte(log.Default().Prefix()+windows.UTF16PtrToString(msg)), (int64(timestamp)-116444736000000000)*100) + } else { + log.Println(windows.UTF16PtrToString(msg)) + } + return 0 +} + +func setupLogger(dll *lazyDLL) { + var callback uintptr + if runtime.GOARCH == "386" { + callback = windows.NewCallback(func(level loggerLevel, timestampLow, timestampHigh uint32, msg *uint16) int { + return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg) + }) + } else if runtime.GOARCH == "arm" { + callback = windows.NewCallback(func(level loggerLevel, _, timestampLow, timestampHigh uint32, msg *uint16) int { + return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg) + }) + } else if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" { + callback = windows.NewCallback(logMessage) + } + syscall.SyscallN(dll.NewProc("WireGuardSetLogger").Addr(), callback) +} + +func closeAdapter(wireguard *Adapter) { + syscall.SyscallN(procWireGuardCloseAdapter.Addr(), wireguard.handle) +} + +// CreateAdapter creates a WireGuard adapter. name is the cosmetic name of the adapter. +// tunnelType represents the type of adapter and should be "WireGuard". requestedGUID is +// the GUID of the created network adapter, which then influences NLA generation +// deterministically. If it is set to nil, the GUID is chosen by the system at random, +// and hence a new NLA entry is created for each new adapter. +func CreateAdapter(name, tunnelType string, requestedGUID *windows.GUID) (wireguard *Adapter, err error) { + var name16 *uint16 + name16, err = windows.UTF16PtrFromString(name) + if err != nil { + return + } + var tunnelType16 *uint16 + tunnelType16, err = windows.UTF16PtrFromString(tunnelType) + if err != nil { + return + } + r0, _, e1 := syscall.SyscallN(procWireGuardCreateAdapter.Addr(), uintptr(unsafe.Pointer(name16)), uintptr(unsafe.Pointer(tunnelType16)), uintptr(unsafe.Pointer(requestedGUID))) + if r0 == 0 { + err = e1 + return + } + wireguard = &Adapter{handle: r0} + runtime.SetFinalizer(wireguard, closeAdapter) + return +} + +// OpenAdapter opens an existing WireGuard adapter by name. +func OpenAdapter(name string) (wireguard *Adapter, err error) { + var name16 *uint16 + name16, err = windows.UTF16PtrFromString(name) + if err != nil { + return + } + r0, _, e1 := syscall.SyscallN(procWireGuardOpenAdapter.Addr(), uintptr(unsafe.Pointer(name16))) + if r0 == 0 { + err = e1 + return + } + wireguard = &Adapter{handle: r0} + runtime.SetFinalizer(wireguard, closeAdapter) + return +} + +// Close closes a WireGuard adapter. +func (wireguard *Adapter) Close() (err error) { + runtime.SetFinalizer(wireguard, nil) + r1, _, e1 := syscall.SyscallN(procWireGuardCloseAdapter.Addr(), wireguard.handle) + if r1 == 0 { + err = e1 + } + return +} + +// Uninstall removes the driver from the system if no drivers are currently in use. +func Uninstall() (err error) { + r1, _, e1 := syscall.SyscallN(procWireGuardDeleteDriver.Addr()) + if r1 == 0 { + err = e1 + } + return +} + +type AdapterLogState uint32 + +const ( + AdapterLogOff AdapterLogState = 0 + AdapterLogOn AdapterLogState = 1 + AdapterLogOnWithPrefix AdapterLogState = 2 +) + +// SetLogging enables or disables logging on the WireGuard adapter. +func (wireguard *Adapter) SetLogging(logState AdapterLogState) (err error) { + r1, _, e1 := syscall.SyscallN(procWireGuardSetAdapterLogging.Addr(), wireguard.handle, uintptr(logState)) + if r1 == 0 { + err = e1 + } + return +} + +// RunningVersion returns the version of the loaded driver. +func RunningVersion() (version uint32, err error) { + r0, _, e1 := syscall.SyscallN(procWireGuardGetRunningDriverVersion.Addr()) + version = uint32(r0) + if version == 0 { + err = e1 + } + return +} + +// LUID returns the LUID of the adapter. +func (wireguard *Adapter) LUID() (luid winipcfg.LUID) { + syscall.SyscallN(procWireGuardGetAdapterLUID.Addr(), wireguard.handle, uintptr(unsafe.Pointer(&luid))) + return +} diff --git a/driver/memmod/memmod_windows.go b/driver/memmod/memmod_windows.go new file mode 100644 index 00000000..7b54282a --- /dev/null +++ b/driver/memmod/memmod_windows.go @@ -0,0 +1,698 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +import ( + "errors" + "fmt" + "strings" + "sync" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +type addressList struct { + next *addressList + address uintptr +} + +func (head *addressList) free() { + for node := head; node != nil; node = node.next { + windows.VirtualFree(node.address, 0, windows.MEM_RELEASE) + } +} + +type Module struct { + headers *IMAGE_NT_HEADERS + codeBase uintptr + modules []windows.Handle + initialized bool + isDLL bool + isRelocated bool + nameExports map[string]uint16 + entry uintptr + blockedMemory *addressList +} + +func (module *Module) BaseAddr() uintptr { + return module.codeBase +} + +func (module *Module) headerDirectory(idx int) *IMAGE_DATA_DIRECTORY { + return &module.headers.OptionalHeader.DataDirectory[idx] +} + +func (module *Module) copySections(address, size uintptr, oldHeaders *IMAGE_NT_HEADERS) error { + sections := module.headers.Sections() + for i := range sections { + if sections[i].SizeOfRawData == 0 { + // Section doesn't contain data in the dll itself, but may define uninitialized data. + sectionSize := oldHeaders.OptionalHeader.SectionAlignment + if sectionSize == 0 { + continue + } + dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress), + uintptr(sectionSize), + windows.MEM_COMMIT, + windows.PAGE_READWRITE) + if err != nil { + return fmt.Errorf("Error allocating section: %w", err) + } + + // Always use position from file to support alignments smaller than page size (allocation above will align to page size). + dest = module.codeBase + uintptr(sections[i].VirtualAddress) + // NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used. + sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff)) + dst := unsafe.Slice((*byte)(a2p(dest)), sectionSize) + for j := range dst { + dst[j] = 0 + } + continue + } + + if size < uintptr(sections[i].PointerToRawData+sections[i].SizeOfRawData) { + return errors.New("Incomplete section") + } + + // Commit memory block and copy data from dll. + dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress), + uintptr(sections[i].SizeOfRawData), + windows.MEM_COMMIT, + windows.PAGE_READWRITE) + if err != nil { + return fmt.Errorf("Error allocating memory block: %w", err) + } + + // Always use position from file to support alignments smaller than page size (allocation above will align to page size). + memcpy( + module.codeBase+uintptr(sections[i].VirtualAddress), + address+uintptr(sections[i].PointerToRawData), + uintptr(sections[i].SizeOfRawData)) + // NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used. + sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff)) + } + + return nil +} + +func (module *Module) realSectionSize(section *IMAGE_SECTION_HEADER) uintptr { + size := section.SizeOfRawData + if size != 0 { + return uintptr(size) + } + if (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) != 0 { + return uintptr(module.headers.OptionalHeader.SizeOfInitializedData) + } + if (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0 { + return uintptr(module.headers.OptionalHeader.SizeOfUninitializedData) + } + return 0 +} + +type sectionFinalizeData struct { + address uintptr + alignedAddress uintptr + size uintptr + characteristics uint32 + last bool +} + +func (module *Module) finalizeSection(sectionData *sectionFinalizeData) error { + if sectionData.size == 0 { + return nil + } + + if (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0 { + // Section is not needed any more and can safely be freed. + if sectionData.address == sectionData.alignedAddress && + (sectionData.last || + (sectionData.size%uintptr(module.headers.OptionalHeader.SectionAlignment)) == 0) { + // Only allowed to decommit whole pages. + windows.VirtualFree(sectionData.address, sectionData.size, windows.MEM_DECOMMIT) + } + return nil + } + + // determine protection flags based on characteristics + ProtectionFlags := [8]uint32{ + windows.PAGE_NOACCESS, // not writeable, not readable, not executable + windows.PAGE_EXECUTE, // not writeable, not readable, executable + windows.PAGE_READONLY, // not writeable, readable, not executable + windows.PAGE_EXECUTE_READ, // not writeable, readable, executable + windows.PAGE_WRITECOPY, // writeable, not readable, not executable + windows.PAGE_EXECUTE_WRITECOPY, // writeable, not readable, executable + windows.PAGE_READWRITE, // writeable, readable, not executable + windows.PAGE_EXECUTE_READWRITE, // writeable, readable, executable + } + protect := ProtectionFlags[sectionData.characteristics>>29] + if (sectionData.characteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0 { + protect |= windows.PAGE_NOCACHE + } + + // Change memory access flags. + var oldProtect uint32 + err := windows.VirtualProtect(sectionData.address, sectionData.size, protect, &oldProtect) + if err != nil { + return fmt.Errorf("Error protecting memory page: %w", err) + } + + return nil +} + +func (module *Module) registerExceptionHandlers() { + directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXCEPTION) + if directory.Size == 0 || directory.VirtualAddress == 0 { + return + } + runtimeFuncs := (*windows.RUNTIME_FUNCTION)(unsafe.Pointer(module.codeBase + uintptr(directory.VirtualAddress))) + windows.RtlAddFunctionTable(runtimeFuncs, uint32(uintptr(directory.Size)/unsafe.Sizeof(*runtimeFuncs)), module.codeBase) +} + +func (module *Module) finalizeSections() error { + sections := module.headers.Sections() + imageOffset := module.headers.OptionalHeader.imageOffset() + sectionData := sectionFinalizeData{} + sectionData.address = uintptr(sections[0].PhysicalAddress()) | imageOffset + sectionData.alignedAddress = alignDown(sectionData.address, uintptr(module.headers.OptionalHeader.SectionAlignment)) + sectionData.size = module.realSectionSize(§ions[0]) + sections[0].SetVirtualSize(uint32(sectionData.size)) + sectionData.characteristics = sections[0].Characteristics + + // Loop through all sections and change access flags. + for i := uint16(1); i < module.headers.FileHeader.NumberOfSections; i++ { + sectionAddress := uintptr(sections[i].PhysicalAddress()) | imageOffset + alignedAddress := alignDown(sectionAddress, uintptr(module.headers.OptionalHeader.SectionAlignment)) + sectionSize := module.realSectionSize(§ions[i]) + sections[i].SetVirtualSize(uint32(sectionSize)) + // Combine access flags of all sections that share a page. + // TODO: We currently share flags of a trailing large section with the page of a first small section. This should be optimized. + if sectionData.alignedAddress == alignedAddress || sectionData.address+sectionData.size > alignedAddress { + // Section shares page with previous. + if (sections[i].Characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 { + sectionData.characteristics = (sectionData.characteristics | sections[i].Characteristics) &^ IMAGE_SCN_MEM_DISCARDABLE + } else { + sectionData.characteristics |= sections[i].Characteristics + } + sectionData.size = sectionAddress + sectionSize - sectionData.address + continue + } + + err := module.finalizeSection(§ionData) + if err != nil { + return fmt.Errorf("Error finalizing section: %w", err) + } + sectionData.address = sectionAddress + sectionData.alignedAddress = alignedAddress + sectionData.size = sectionSize + sectionData.characteristics = sections[i].Characteristics + } + sectionData.last = true + err := module.finalizeSection(§ionData) + if err != nil { + return fmt.Errorf("Error finalizing section: %w", err) + } + return nil +} + +func (module *Module) executeTLS() { + directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_TLS) + if directory.VirtualAddress == 0 { + return + } + + tls := (*IMAGE_TLS_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) + callback := tls.AddressOfCallbacks + if callback != 0 { + for { + f := *(*uintptr)(a2p(callback)) + if f == 0 { + break + } + syscall.SyscallN(f, module.codeBase, DLL_PROCESS_ATTACH, 0) + callback += unsafe.Sizeof(f) + } + } +} + +func (module *Module) performBaseRelocation(delta uintptr) (relocated bool, err error) { + directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC) + if directory.Size == 0 { + return delta == 0, nil + } + + relocationHdr := (*IMAGE_BASE_RELOCATION)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) + for relocationHdr.VirtualAddress > 0 { + dest := module.codeBase + uintptr(relocationHdr.VirtualAddress) + + relInfos := unsafe.Slice( + (*uint16)(a2p(uintptr(unsafe.Pointer(relocationHdr))+unsafe.Sizeof(*relocationHdr))), + (uintptr(relocationHdr.SizeOfBlock)-unsafe.Sizeof(*relocationHdr))/unsafe.Sizeof(uint16(0))) + for _, relInfo := range relInfos { + // The upper 4 bits define the type of relocation. + relType := relInfo >> 12 + // The lower 12 bits define the offset. + relOffset := uintptr(relInfo & 0xfff) + + switch relType { + case IMAGE_REL_BASED_ABSOLUTE: + // Skip relocation. + + case IMAGE_REL_BASED_LOW: + *(*uint16)(a2p(dest + relOffset)) += uint16(delta & 0xffff) + break + + case IMAGE_REL_BASED_HIGH: + *(*uint16)(a2p(dest + relOffset)) += uint16(uint32(delta) >> 16) + break + + case IMAGE_REL_BASED_HIGHLOW: + *(*uint32)(a2p(dest + relOffset)) += uint32(delta) + + case IMAGE_REL_BASED_DIR64: + *(*uint64)(a2p(dest + relOffset)) += uint64(delta) + + case IMAGE_REL_BASED_THUMB_MOV32: + inst := *(*uint32)(a2p(dest + relOffset)) + imm16 := ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) + + ((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff) + if (inst & 0x8000fbf0) != 0x0000f240 { + return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVW", inst) + } + imm16 += uint32(delta) & 0xffff + hiDelta := (uint32(delta&0xffff0000) >> 16) + ((imm16 & 0xffff0000) >> 16) + *(*uint32)(a2p(dest + relOffset)) = (inst & 0x8f00fbf0) + ((imm16 >> 1) & 0x0400) + + ((imm16 >> 12) & 0x000f) + + ((imm16 << 20) & 0x70000000) + + ((imm16 << 16) & 0xff0000) + if hiDelta != 0 { + inst = *(*uint32)(a2p(dest + relOffset + 4)) + imm16 = ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) + + ((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff) + if (inst & 0x8000fbf0) != 0x0000f2c0 { + return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVT", inst) + } + imm16 += hiDelta + if imm16 > 0xffff { + return false, fmt.Errorf("Resulting immediate value won't fit: %08x", imm16) + } + *(*uint32)(a2p(dest + relOffset + 4)) = (inst & 0x8f00fbf0) + + ((imm16 >> 1) & 0x0400) + + ((imm16 >> 12) & 0x000f) + + ((imm16 << 20) & 0x70000000) + + ((imm16 << 16) & 0xff0000) + } + + default: + return false, fmt.Errorf("Unsupported relocation: %v", relType) + } + } + + // Advance to next relocation block. + relocationHdr = (*IMAGE_BASE_RELOCATION)(a2p(uintptr(unsafe.Pointer(relocationHdr)) + uintptr(relocationHdr.SizeOfBlock))) + } + return true, nil +} + +func (module *Module) buildImportTable() error { + directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT) + if directory.Size == 0 { + return nil + } + + module.modules = make([]windows.Handle, 0, 16) + importDesc := (*IMAGE_IMPORT_DESCRIPTOR)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) + for importDesc.Name != 0 { + handle, err := windows.LoadLibraryEx(windows.BytePtrToString((*byte)(a2p(module.codeBase+uintptr(importDesc.Name)))), 0, windows.LOAD_LIBRARY_SEARCH_SYSTEM32) + if err != nil { + return fmt.Errorf("Error loading module: %w", err) + } + var thunkRef, funcRef *uintptr + if importDesc.OriginalFirstThunk() != 0 { + thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.OriginalFirstThunk()))) + funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk))) + } else { + // No hint table. + thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk))) + funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk))) + } + for *thunkRef != 0 { + if IMAGE_SNAP_BY_ORDINAL(*thunkRef) { + *funcRef, err = windows.GetProcAddressByOrdinal(handle, IMAGE_ORDINAL(*thunkRef)) + } else { + thunkData := (*IMAGE_IMPORT_BY_NAME)(a2p(module.codeBase + *thunkRef)) + *funcRef, err = windows.GetProcAddress(handle, windows.BytePtrToString(&thunkData.Name[0])) + } + if err != nil { + windows.FreeLibrary(handle) + return fmt.Errorf("Error getting function address: %w", err) + } + thunkRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(thunkRef)) + unsafe.Sizeof(*thunkRef))) + funcRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(funcRef)) + unsafe.Sizeof(*funcRef))) + } + module.modules = append(module.modules, handle) + importDesc = (*IMAGE_IMPORT_DESCRIPTOR)(a2p(uintptr(unsafe.Pointer(importDesc)) + unsafe.Sizeof(*importDesc))) + } + return nil +} + +func (module *Module) buildNameExports() error { + directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT) + if directory.Size == 0 { + return errors.New("No export table found") + } + exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) + if exports.NumberOfNames == 0 || exports.NumberOfFunctions == 0 { + return errors.New("No functions exported") + } + if exports.NumberOfNames == 0 { + return errors.New("No functions exported by name") + } + nameRefs := unsafe.Slice((*uint32)(a2p(module.codeBase+uintptr(exports.AddressOfNames))), exports.NumberOfNames) + ordinals := unsafe.Slice((*uint16)(a2p(module.codeBase+uintptr(exports.AddressOfNameOrdinals))), exports.NumberOfNames) + module.nameExports = make(map[string]uint16) + for i := range nameRefs { + nameArray := windows.BytePtrToString((*byte)(a2p(module.codeBase + uintptr(nameRefs[i])))) + module.nameExports[nameArray] = ordinals[i] + } + return nil +} + +type addressRange struct { + start uintptr + end uintptr +} + +var ( + loadedAddressRanges []addressRange + loadedAddressRangesMu sync.RWMutex + haveHookedRtlPcToFileHeader sync.Once + hookRtlPcToFileHeaderResult error +) + +func hookRtlPcToFileHeader() error { + var kernelBase windows.Handle + err := windows.GetModuleHandleEx(windows.GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, windows.StringToUTF16Ptr("kernelbase.dll"), &kernelBase) + if err != nil { + return err + } + imageBase := unsafe.Pointer(kernelBase) + dosHeader := (*IMAGE_DOS_HEADER)(imageBase) + ntHeaders := (*IMAGE_NT_HEADERS)(unsafe.Add(imageBase, dosHeader.E_lfanew)) + importsDirectory := ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] + importDescriptor := (*IMAGE_IMPORT_DESCRIPTOR)(unsafe.Add(imageBase, importsDirectory.VirtualAddress)) + for ; importDescriptor.Name != 0; importDescriptor = (*IMAGE_IMPORT_DESCRIPTOR)(unsafe.Add(unsafe.Pointer(importDescriptor), unsafe.Sizeof(*importDescriptor))) { + libraryName := windows.BytePtrToString((*byte)(unsafe.Add(imageBase, importDescriptor.Name))) + if strings.EqualFold(libraryName, "ntdll.dll") { + break + } + } + if importDescriptor.Name == 0 { + return errors.New("ntdll.dll not found") + } + originalThunk := (*uintptr)(unsafe.Add(imageBase, importDescriptor.OriginalFirstThunk())) + thunk := (*uintptr)(unsafe.Add(imageBase, importDescriptor.FirstThunk)) + for ; *originalThunk != 0; originalThunk = (*uintptr)(unsafe.Add(unsafe.Pointer(originalThunk), unsafe.Sizeof(*originalThunk))) { + if *originalThunk&IMAGE_ORDINAL_FLAG == 0 { + function := (*IMAGE_IMPORT_BY_NAME)(unsafe.Add(imageBase, *originalThunk)) + name := windows.BytePtrToString(&function.Name[0]) + if name == "RtlPcToFileHeader" { + break + } + } + thunk = (*uintptr)(unsafe.Add(unsafe.Pointer(thunk), unsafe.Sizeof(*thunk))) + } + if *originalThunk == 0 { + return errors.New("RtlPcToFileHeader not found") + } + var oldProtect uint32 + err = windows.VirtualProtect(uintptr(unsafe.Pointer(thunk)), unsafe.Sizeof(*thunk), windows.PAGE_READWRITE, &oldProtect) + if err != nil { + return err + } + originalRtlPcToFileHeader := *thunk + *thunk = windows.NewCallback(func(pcValue uintptr, baseOfImage *uintptr) uintptr { + loadedAddressRangesMu.RLock() + for i := range loadedAddressRanges { + if pcValue >= loadedAddressRanges[i].start && pcValue < loadedAddressRanges[i].end { + pcValue = *thunk + break + } + } + loadedAddressRangesMu.RUnlock() + ret, _, _ := syscall.SyscallN(originalRtlPcToFileHeader, pcValue, uintptr(unsafe.Pointer(baseOfImage))) + return ret + }) + err = windows.VirtualProtect(uintptr(unsafe.Pointer(thunk)), unsafe.Sizeof(*thunk), oldProtect, &oldProtect) + if err != nil { + return err + } + return nil +} + +// LoadLibrary loads module image to memory. +func LoadLibrary(data []byte) (module *Module, err error) { + addr := uintptr(unsafe.Pointer(&data[0])) + size := uintptr(len(data)) + if size < unsafe.Sizeof(IMAGE_DOS_HEADER{}) { + return nil, errors.New("Incomplete IMAGE_DOS_HEADER") + } + dosHeader := (*IMAGE_DOS_HEADER)(a2p(addr)) + if dosHeader.E_magic != IMAGE_DOS_SIGNATURE { + return nil, fmt.Errorf("Not an MS-DOS binary (provided: %x, expected: %x)", dosHeader.E_magic, IMAGE_DOS_SIGNATURE) + } + if (size < uintptr(dosHeader.E_lfanew)+unsafe.Sizeof(IMAGE_NT_HEADERS{})) { + return nil, errors.New("Incomplete IMAGE_NT_HEADERS") + } + oldHeader := (*IMAGE_NT_HEADERS)(a2p(addr + uintptr(dosHeader.E_lfanew))) + if oldHeader.Signature != IMAGE_NT_SIGNATURE { + return nil, fmt.Errorf("Not an NT binary (provided: %x, expected: %x)", oldHeader.Signature, IMAGE_NT_SIGNATURE) + } + if oldHeader.FileHeader.Machine != imageFileProcess { + return nil, fmt.Errorf("Foreign platform (provided: %x, expected: %x)", oldHeader.FileHeader.Machine, imageFileProcess) + } + if (oldHeader.OptionalHeader.SectionAlignment & 1) != 0 { + return nil, errors.New("Unaligned section") + } + lastSectionEnd := uintptr(0) + sections := oldHeader.Sections() + optionalSectionSize := oldHeader.OptionalHeader.SectionAlignment + for i := range sections { + var endOfSection uintptr + if sections[i].SizeOfRawData == 0 { + // Section without data in the DLL + endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(optionalSectionSize) + } else { + endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(sections[i].SizeOfRawData) + } + if endOfSection > lastSectionEnd { + lastSectionEnd = endOfSection + } + } + alignedImageSize := alignUp(uintptr(oldHeader.OptionalHeader.SizeOfImage), uintptr(oldHeader.OptionalHeader.SectionAlignment)) + if alignedImageSize != alignUp(lastSectionEnd, uintptr(oldHeader.OptionalHeader.SectionAlignment)) { + return nil, errors.New("Section is not page-aligned") + } + + module = &Module{isDLL: (oldHeader.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0} + defer func() { + if err != nil { + module.Free() + module = nil + } + }() + + // Reserve memory for image of library. + // TODO: Is it correct to commit the complete memory region at once? Calling DllEntry raises an exception if we don't. + module.codeBase, err = windows.VirtualAlloc(oldHeader.OptionalHeader.ImageBase, + alignedImageSize, + windows.MEM_RESERVE|windows.MEM_COMMIT, + windows.PAGE_READWRITE) + if err != nil { + // Try to allocate memory at arbitrary position. + module.codeBase, err = windows.VirtualAlloc(0, + alignedImageSize, + windows.MEM_RESERVE|windows.MEM_COMMIT, + windows.PAGE_READWRITE) + if err != nil { + err = fmt.Errorf("Error allocating code: %w", err) + return + } + } + err = module.check4GBBoundaries(alignedImageSize) + if err != nil { + err = fmt.Errorf("Error reallocating code: %w", err) + return + } + + if size < uintptr(oldHeader.OptionalHeader.SizeOfHeaders) { + err = errors.New("Incomplete headers") + return + } + // Commit memory for headers. + headers, err := windows.VirtualAlloc(module.codeBase, + uintptr(oldHeader.OptionalHeader.SizeOfHeaders), + windows.MEM_COMMIT, + windows.PAGE_READWRITE) + if err != nil { + err = fmt.Errorf("Error allocating headers: %w", err) + return + } + // Copy PE header to code. + memcpy(headers, addr, uintptr(oldHeader.OptionalHeader.SizeOfHeaders)) + module.headers = (*IMAGE_NT_HEADERS)(a2p(headers + uintptr(dosHeader.E_lfanew))) + + // Update position. + module.headers.OptionalHeader.ImageBase = module.codeBase + + // Copy sections from DLL file block to new memory location. + err = module.copySections(addr, size, oldHeader) + if err != nil { + err = fmt.Errorf("Error copying sections: %w", err) + return + } + + // Adjust base address of imported data. + locationDelta := module.headers.OptionalHeader.ImageBase - oldHeader.OptionalHeader.ImageBase + if locationDelta != 0 { + module.isRelocated, err = module.performBaseRelocation(locationDelta) + if err != nil { + err = fmt.Errorf("Error relocating module: %w", err) + return + } + } else { + module.isRelocated = true + } + + // Load required dlls and adjust function table of imports. + err = module.buildImportTable() + if err != nil { + err = fmt.Errorf("Error building import table: %w", err) + return + } + + // Mark memory pages depending on section headers and release sections that are marked as "discardable". + err = module.finalizeSections() + if err != nil { + err = fmt.Errorf("Error finalizing sections: %w", err) + return + } + + // Register exception tables, if they exist. + module.registerExceptionHandlers() + + // Register function PCs. + loadedAddressRangesMu.Lock() + loadedAddressRanges = append(loadedAddressRanges, addressRange{module.codeBase, module.codeBase + alignedImageSize}) + loadedAddressRangesMu.Unlock() + haveHookedRtlPcToFileHeader.Do(func() { + hookRtlPcToFileHeaderResult = hookRtlPcToFileHeader() + }) + err = hookRtlPcToFileHeaderResult + if err != nil { + return + } + + // TLS callbacks are executed BEFORE the main loading. + module.executeTLS() + + // Get entry point of loaded module. + if module.headers.OptionalHeader.AddressOfEntryPoint != 0 { + module.entry = module.codeBase + uintptr(module.headers.OptionalHeader.AddressOfEntryPoint) + if module.isDLL { + // Notify library about attaching to process. + r0, _, _ := syscall.SyscallN(module.entry, module.codeBase, DLL_PROCESS_ATTACH, 0) + successful := r0 != 0 + if !successful { + err = windows.ERROR_DLL_INIT_FAILED + return + } + module.initialized = true + } + } + + module.buildNameExports() + return +} + +// Free releases module resources and unloads it. +func (module *Module) Free() { + if module.initialized { + // Notify library about detaching from process. + syscall.SyscallN(module.entry, module.codeBase, DLL_PROCESS_DETACH, 0) + module.initialized = false + } + if module.modules != nil { + // Free previously opened libraries. + for _, handle := range module.modules { + windows.FreeLibrary(handle) + } + module.modules = nil + } + if module.codeBase != 0 { + windows.VirtualFree(module.codeBase, 0, windows.MEM_RELEASE) + module.codeBase = 0 + } + if module.blockedMemory != nil { + module.blockedMemory.free() + module.blockedMemory = nil + } +} + +// ProcAddressByName returns function address by exported name. +func (module *Module) ProcAddressByName(name string) (uintptr, error) { + directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT) + if directory.Size == 0 { + return 0, errors.New("No export table found") + } + exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) + if module.nameExports == nil { + return 0, errors.New("No functions exported by name") + } + if idx, ok := module.nameExports[name]; ok { + if uint32(idx) > exports.NumberOfFunctions { + return 0, errors.New("Ordinal number too high") + } + // AddressOfFunctions contains the RVAs to the "real" functions. + return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil + } + return 0, errors.New("Function not found by name") +} + +// ProcAddressByOrdinal returns function address by exported ordinal. +func (module *Module) ProcAddressByOrdinal(ordinal uint16) (uintptr, error) { + directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT) + if directory.Size == 0 { + return 0, errors.New("No export table found") + } + exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress))) + if uint32(ordinal) < exports.Base { + return 0, errors.New("Ordinal number too low") + } + idx := ordinal - uint16(exports.Base) + if uint32(idx) > exports.NumberOfFunctions { + return 0, errors.New("Ordinal number too high") + } + // AddressOfFunctions contains the RVAs to the "real" functions. + return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil +} + +func alignDown(value, alignment uintptr) uintptr { + return value & ^(alignment - 1) +} + +func alignUp(value, alignment uintptr) uintptr { + return (value + alignment - 1) & ^(alignment - 1) +} + +func a2p(addr uintptr) unsafe.Pointer { + return unsafe.Pointer(addr) +} + +func memcpy(dst, src, size uintptr) { + copy(unsafe.Slice((*byte)(a2p(dst)), size), unsafe.Slice((*byte)(a2p(src)), size)) +} diff --git a/driver/memmod/memmod_windows_32.go b/driver/memmod/memmod_windows_32.go new file mode 100644 index 00000000..611fbe53 --- /dev/null +++ b/driver/memmod/memmod_windows_32.go @@ -0,0 +1,16 @@ +//go:build (windows && 386) || (windows && arm) + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr { + return 0 +} + +func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) { + return +} diff --git a/driver/memmod/memmod_windows_386.go b/driver/memmod/memmod_windows_386.go new file mode 100644 index 00000000..71d6c93e --- /dev/null +++ b/driver/memmod/memmod_windows_386.go @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +const imageFileProcess = IMAGE_FILE_MACHINE_I386 diff --git a/driver/memmod/memmod_windows_64.go b/driver/memmod/memmod_windows_64.go new file mode 100644 index 00000000..28ada96e --- /dev/null +++ b/driver/memmod/memmod_windows_64.go @@ -0,0 +1,36 @@ +//go:build (windows && amd64) || (windows && arm64) + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +import ( + "fmt" + + "golang.org/x/sys/windows" +) + +func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr { + return uintptr(opthdr.ImageBase & 0xffffffff00000000) +} + +func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) { + for (module.codeBase >> 32) < ((module.codeBase + alignedImageSize) >> 32) { + node := &addressList{ + next: module.blockedMemory, + address: module.codeBase, + } + module.blockedMemory = node + module.codeBase, err = windows.VirtualAlloc(0, + alignedImageSize, + windows.MEM_RESERVE|windows.MEM_COMMIT, + windows.PAGE_READWRITE) + if err != nil { + return fmt.Errorf("Error allocating memory block: %w", err) + } + } + return +} diff --git a/driver/memmod/memmod_windows_amd64.go b/driver/memmod/memmod_windows_amd64.go new file mode 100644 index 00000000..2459b2d2 --- /dev/null +++ b/driver/memmod/memmod_windows_amd64.go @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +const imageFileProcess = IMAGE_FILE_MACHINE_AMD64 diff --git a/driver/memmod/memmod_windows_arm.go b/driver/memmod/memmod_windows_arm.go new file mode 100644 index 00000000..a644cee6 --- /dev/null +++ b/driver/memmod/memmod_windows_arm.go @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +const imageFileProcess = IMAGE_FILE_MACHINE_ARMNT diff --git a/driver/memmod/memmod_windows_arm64.go b/driver/memmod/memmod_windows_arm64.go new file mode 100644 index 00000000..09e24639 --- /dev/null +++ b/driver/memmod/memmod_windows_arm64.go @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +const imageFileProcess = IMAGE_FILE_MACHINE_ARM64 diff --git a/driver/memmod/syscall_windows.go b/driver/memmod/syscall_windows.go new file mode 100644 index 00000000..cdc6ef67 --- /dev/null +++ b/driver/memmod/syscall_windows.go @@ -0,0 +1,392 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +import "unsafe" + +const ( + IMAGE_DOS_SIGNATURE = 0x5A4D // MZ + IMAGE_OS2_SIGNATURE = 0x454E // NE + IMAGE_OS2_SIGNATURE_LE = 0x454C // LE + IMAGE_VXD_SIGNATURE = 0x454C // LE + IMAGE_NT_SIGNATURE = 0x00004550 // PE00 +) + +// DOS .EXE header +type IMAGE_DOS_HEADER struct { + E_magic uint16 // Magic number + E_cblp uint16 // Bytes on last page of file + E_cp uint16 // Pages in file + E_crlc uint16 // Relocations + E_cparhdr uint16 // Size of header in paragraphs + E_minalloc uint16 // Minimum extra paragraphs needed + E_maxalloc uint16 // Maximum extra paragraphs needed + E_ss uint16 // Initial (relative) SS value + E_sp uint16 // Initial SP value + E_csum uint16 // Checksum + E_ip uint16 // Initial IP value + E_cs uint16 // Initial (relative) CS value + E_lfarlc uint16 // File address of relocation table + E_ovno uint16 // Overlay number + E_res [4]uint16 // Reserved words + E_oemid uint16 // OEM identifier (for e_oeminfo) + E_oeminfo uint16 // OEM information; e_oemid specific + E_res2 [10]uint16 // Reserved words + E_lfanew int32 // File address of new exe header +} + +// File header format +type IMAGE_FILE_HEADER struct { + Machine uint16 + NumberOfSections uint16 + TimeDateStamp uint32 + PointerToSymbolTable uint32 + NumberOfSymbols uint32 + SizeOfOptionalHeader uint16 + Characteristics uint16 +} + +const ( + IMAGE_SIZEOF_FILE_HEADER = 20 + + IMAGE_FILE_RELOCS_STRIPPED = 0x0001 // Relocation info stripped from file. + IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 // File is executable (i.e. no unresolved external references). + IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 // Line nunbers stripped from file. + IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 // Local symbols stripped from file. + IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010 // Aggressively trim working set + IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 // App can handle >2gb addresses + IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 // Bytes of machine word are reversed. + IMAGE_FILE_32BIT_MACHINE = 0x0100 // 32 bit word machine. + IMAGE_FILE_DEBUG_STRIPPED = 0x0200 // Debugging info stripped from file in .DBG file + IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 // If Image is on removable media, copy and run from the swap file. + IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 // If Image is on Net, copy and run from the swap file. + IMAGE_FILE_SYSTEM = 0x1000 // System File. + IMAGE_FILE_DLL = 0x2000 // File is a DLL. + IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 // File should only be run on a UP machine + IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 // Bytes of machine word are reversed. + + IMAGE_FILE_MACHINE_UNKNOWN = 0 + IMAGE_FILE_MACHINE_TARGET_HOST = 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest. + IMAGE_FILE_MACHINE_I386 = 0x014c // Intel 386. + IMAGE_FILE_MACHINE_R3000 = 0x0162 // MIPS little-endian, 0x160 big-endian + IMAGE_FILE_MACHINE_R4000 = 0x0166 // MIPS little-endian + IMAGE_FILE_MACHINE_R10000 = 0x0168 // MIPS little-endian + IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x0169 // MIPS little-endian WCE v2 + IMAGE_FILE_MACHINE_ALPHA = 0x0184 // Alpha_AXP + IMAGE_FILE_MACHINE_SH3 = 0x01a2 // SH3 little-endian + IMAGE_FILE_MACHINE_SH3DSP = 0x01a3 + IMAGE_FILE_MACHINE_SH3E = 0x01a4 // SH3E little-endian + IMAGE_FILE_MACHINE_SH4 = 0x01a6 // SH4 little-endian + IMAGE_FILE_MACHINE_SH5 = 0x01a8 // SH5 + IMAGE_FILE_MACHINE_ARM = 0x01c0 // ARM Little-Endian + IMAGE_FILE_MACHINE_THUMB = 0x01c2 // ARM Thumb/Thumb-2 Little-Endian + IMAGE_FILE_MACHINE_ARMNT = 0x01c4 // ARM Thumb-2 Little-Endian + IMAGE_FILE_MACHINE_AM33 = 0x01d3 + IMAGE_FILE_MACHINE_POWERPC = 0x01F0 // IBM PowerPC Little-Endian + IMAGE_FILE_MACHINE_POWERPCFP = 0x01f1 + IMAGE_FILE_MACHINE_IA64 = 0x0200 // Intel 64 + IMAGE_FILE_MACHINE_MIPS16 = 0x0266 // MIPS + IMAGE_FILE_MACHINE_ALPHA64 = 0x0284 // ALPHA64 + IMAGE_FILE_MACHINE_MIPSFPU = 0x0366 // MIPS + IMAGE_FILE_MACHINE_MIPSFPU16 = 0x0466 // MIPS + IMAGE_FILE_MACHINE_AXP64 = IMAGE_FILE_MACHINE_ALPHA64 + IMAGE_FILE_MACHINE_TRICORE = 0x0520 // Infineon + IMAGE_FILE_MACHINE_CEF = 0x0CEF + IMAGE_FILE_MACHINE_EBC = 0x0EBC // EFI Byte Code + IMAGE_FILE_MACHINE_AMD64 = 0x8664 // AMD64 (K8) + IMAGE_FILE_MACHINE_M32R = 0x9041 // M32R little-endian + IMAGE_FILE_MACHINE_ARM64 = 0xAA64 // ARM64 Little-Endian + IMAGE_FILE_MACHINE_CEE = 0xC0EE +) + +// Directory format +type IMAGE_DATA_DIRECTORY struct { + VirtualAddress uint32 + Size uint32 +} + +const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16 + +type IMAGE_NT_HEADERS struct { + Signature uint32 + FileHeader IMAGE_FILE_HEADER + OptionalHeader IMAGE_OPTIONAL_HEADER +} + +func (ntheader *IMAGE_NT_HEADERS) Sections() []IMAGE_SECTION_HEADER { + return (*[0xffff]IMAGE_SECTION_HEADER)(unsafe.Pointer( + (uintptr)(unsafe.Pointer(ntheader)) + + unsafe.Offsetof(ntheader.OptionalHeader) + + uintptr(ntheader.FileHeader.SizeOfOptionalHeader)))[:ntheader.FileHeader.NumberOfSections] +} + +const ( + IMAGE_DIRECTORY_ENTRY_EXPORT = 0 // Export Directory + IMAGE_DIRECTORY_ENTRY_IMPORT = 1 // Import Directory + IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 // Resource Directory + IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 // Exception Directory + IMAGE_DIRECTORY_ENTRY_SECURITY = 4 // Security Directory + IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 // Base Relocation Table + IMAGE_DIRECTORY_ENTRY_DEBUG = 6 // Debug Directory + IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 // (X86 usage) + IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 // Architecture Specific Data + IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 // RVA of GP + IMAGE_DIRECTORY_ENTRY_TLS = 9 // TLS Directory + IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 // Load Configuration Directory + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 // Bound Import Directory in headers + IMAGE_DIRECTORY_ENTRY_IAT = 12 // Import Address Table + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 // Delay Load Import Descriptors + IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 // COM Runtime descriptor +) + +const IMAGE_SIZEOF_SHORT_NAME = 8 + +// Section header format +type IMAGE_SECTION_HEADER struct { + Name [IMAGE_SIZEOF_SHORT_NAME]byte + physicalAddressOrVirtualSize uint32 + VirtualAddress uint32 + SizeOfRawData uint32 + PointerToRawData uint32 + PointerToRelocations uint32 + PointerToLinenumbers uint32 + NumberOfRelocations uint16 + NumberOfLinenumbers uint16 + Characteristics uint32 +} + +func (ishdr *IMAGE_SECTION_HEADER) PhysicalAddress() uint32 { + return ishdr.physicalAddressOrVirtualSize +} + +func (ishdr *IMAGE_SECTION_HEADER) SetPhysicalAddress(addr uint32) { + ishdr.physicalAddressOrVirtualSize = addr +} + +func (ishdr *IMAGE_SECTION_HEADER) VirtualSize() uint32 { + return ishdr.physicalAddressOrVirtualSize +} + +func (ishdr *IMAGE_SECTION_HEADER) SetVirtualSize(addr uint32) { + ishdr.physicalAddressOrVirtualSize = addr +} + +const ( + // Dll characteristics. + IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020 + IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040 + IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080 + IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100 + IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION = 0x0200 + IMAGE_DLL_CHARACTERISTICS_NO_SEH = 0x0400 + IMAGE_DLL_CHARACTERISTICS_NO_BIND = 0x0800 + IMAGE_DLL_CHARACTERISTICS_APPCONTAINER = 0x1000 + IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER = 0x2000 + IMAGE_DLL_CHARACTERISTICS_GUARD_CF = 0x4000 + IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000 +) + +const ( + // Section characteristics. + IMAGE_SCN_TYPE_REG = 0x00000000 // Reserved. + IMAGE_SCN_TYPE_DSECT = 0x00000001 // Reserved. + IMAGE_SCN_TYPE_NOLOAD = 0x00000002 // Reserved. + IMAGE_SCN_TYPE_GROUP = 0x00000004 // Reserved. + IMAGE_SCN_TYPE_NO_PAD = 0x00000008 // Reserved. + IMAGE_SCN_TYPE_COPY = 0x00000010 // Reserved. + + IMAGE_SCN_CNT_CODE = 0x00000020 // Section contains code. + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 // Section contains initialized data. + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 // Section contains uninitialized data. + + IMAGE_SCN_LNK_OTHER = 0x00000100 // Reserved. + IMAGE_SCN_LNK_INFO = 0x00000200 // Section contains comments or some other type of information. + IMAGE_SCN_TYPE_OVER = 0x00000400 // Reserved. + IMAGE_SCN_LNK_REMOVE = 0x00000800 // Section contents will not become part of image. + IMAGE_SCN_LNK_COMDAT = 0x00001000 // Section contents comdat. + IMAGE_SCN_MEM_PROTECTED = 0x00004000 // Obsolete. + IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section. + IMAGE_SCN_GPREL = 0x00008000 // Section content can be accessed relative to GP + IMAGE_SCN_MEM_FARDATA = 0x00008000 + IMAGE_SCN_MEM_SYSHEAP = 0x00010000 // Obsolete. + IMAGE_SCN_MEM_PURGEABLE = 0x00020000 + IMAGE_SCN_MEM_16BIT = 0x00020000 + IMAGE_SCN_MEM_LOCKED = 0x00040000 + IMAGE_SCN_MEM_PRELOAD = 0x00080000 + + IMAGE_SCN_ALIGN_1BYTES = 0x00100000 // + IMAGE_SCN_ALIGN_2BYTES = 0x00200000 // + IMAGE_SCN_ALIGN_4BYTES = 0x00300000 // + IMAGE_SCN_ALIGN_8BYTES = 0x00400000 // + IMAGE_SCN_ALIGN_16BYTES = 0x00500000 // Default alignment if no others are specified. + IMAGE_SCN_ALIGN_32BYTES = 0x00600000 // + IMAGE_SCN_ALIGN_64BYTES = 0x00700000 // + IMAGE_SCN_ALIGN_128BYTES = 0x00800000 // + IMAGE_SCN_ALIGN_256BYTES = 0x00900000 // + IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 // + IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 // + IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 // + IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 // + IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 // + IMAGE_SCN_ALIGN_MASK = 0x00F00000 + + IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 // Section contains extended relocations. + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 // Section can be discarded. + IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 // Section is not cachable. + IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 // Section is not pageable. + IMAGE_SCN_MEM_SHARED = 0x10000000 // Section is shareable. + IMAGE_SCN_MEM_EXECUTE = 0x20000000 // Section is executable. + IMAGE_SCN_MEM_READ = 0x40000000 // Section is readable. + IMAGE_SCN_MEM_WRITE = 0x80000000 // Section is writeable. + + // TLS Characteristic Flags + IMAGE_SCN_SCALE_INDEX = 0x00000001 // Tls index is scaled. +) + +// Based relocation format +type IMAGE_BASE_RELOCATION struct { + VirtualAddress uint32 + SizeOfBlock uint32 +} + +const ( + IMAGE_REL_BASED_ABSOLUTE = 0 + IMAGE_REL_BASED_HIGH = 1 + IMAGE_REL_BASED_LOW = 2 + IMAGE_REL_BASED_HIGHLOW = 3 + IMAGE_REL_BASED_HIGHADJ = 4 + IMAGE_REL_BASED_MACHINE_SPECIFIC_5 = 5 + IMAGE_REL_BASED_RESERVED = 6 + IMAGE_REL_BASED_MACHINE_SPECIFIC_7 = 7 + IMAGE_REL_BASED_MACHINE_SPECIFIC_8 = 8 + IMAGE_REL_BASED_MACHINE_SPECIFIC_9 = 9 + IMAGE_REL_BASED_DIR64 = 10 + + IMAGE_REL_BASED_IA64_IMM64 = 9 + + IMAGE_REL_BASED_MIPS_JMPADDR = 5 + IMAGE_REL_BASED_MIPS_JMPADDR16 = 9 + + IMAGE_REL_BASED_ARM_MOV32 = 5 + IMAGE_REL_BASED_THUMB_MOV32 = 7 +) + +// Export Format +type IMAGE_EXPORT_DIRECTORY struct { + Characteristics uint32 + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + Name uint32 + Base uint32 + NumberOfFunctions uint32 + NumberOfNames uint32 + AddressOfFunctions uint32 // RVA from base of image + AddressOfNames uint32 // RVA from base of image + AddressOfNameOrdinals uint32 // RVA from base of image +} + +type IMAGE_IMPORT_BY_NAME struct { + Hint uint16 + Name [1]byte +} + +func IMAGE_ORDINAL(ordinal uintptr) uintptr { + return ordinal & 0xffff +} + +func IMAGE_SNAP_BY_ORDINAL(ordinal uintptr) bool { + return (ordinal & IMAGE_ORDINAL_FLAG) != 0 +} + +// Thread Local Storage +type IMAGE_TLS_DIRECTORY struct { + StartAddressOfRawData uintptr + EndAddressOfRawData uintptr + AddressOfIndex uintptr // PDWORD + AddressOfCallbacks uintptr // PIMAGE_TLS_CALLBACK *; + SizeOfZeroFill uint32 + Characteristics uint32 +} + +type IMAGE_IMPORT_DESCRIPTOR struct { + characteristicsOrOriginalFirstThunk uint32 // 0 for terminating null import descriptor + // RVA to original unbound IAT (PIMAGE_THUNK_DATA) + TimeDateStamp uint32 // 0 if not bound, + // -1 if bound, and real date\time stamp + // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) + // O.W. date/time stamp of DLL bound to (Old BIND) + ForwarderChain uint32 // -1 if no forwarders + Name uint32 + FirstThunk uint32 // RVA to IAT (if bound this IAT has actual addresses) +} + +func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) Characteristics() uint32 { + return imgimpdesc.characteristicsOrOriginalFirstThunk +} + +func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) OriginalFirstThunk() uint32 { + return imgimpdesc.characteristicsOrOriginalFirstThunk +} + +type IMAGE_DELAYLOAD_DESCRIPTOR struct { + Attributes uint32 + DllNameRVA uint32 + ModuleHandleRVA uint32 + ImportAddressTableRVA uint32 + ImportNameTableRVA uint32 + BoundImportAddressTableRVA uint32 + UnloadInformationTableRVA uint32 + TimeDateStamp uint32 +} + +type IMAGE_LOAD_CONFIG_CODE_INTEGRITY struct { + Flags uint16 + Catalog uint16 + CatalogOffset uint32 + Reserved uint32 +} + +const ( + IMAGE_GUARD_CF_INSTRUMENTED = 0x00000100 + IMAGE_GUARD_CFW_INSTRUMENTED = 0x00000200 + IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT = 0x00000400 + IMAGE_GUARD_SECURITY_COOKIE_UNUSED = 0x00000800 + IMAGE_GUARD_PROTECT_DELAYLOAD_IAT = 0x00001000 + IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION = 0x00002000 + IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT = 0x00004000 + IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION = 0x00008000 + IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT = 0x00010000 + IMAGE_GUARD_RF_INSTRUMENTED = 0x00020000 + IMAGE_GUARD_RF_ENABLE = 0x00040000 + IMAGE_GUARD_RF_STRICT = 0x00080000 + IMAGE_GUARD_RETPOLINE_PRESENT = 0x00100000 + IMAGE_GUARD_EH_CONTINUATION_TABLE_PRESENT = 0x00400000 + IMAGE_GUARD_XFG_ENABLED = 0x00800000 + IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK = 0xF0000000 + IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT = 28 +) + +const ( + DLL_PROCESS_ATTACH = 1 + DLL_THREAD_ATTACH = 2 + DLL_THREAD_DETACH = 3 + DLL_PROCESS_DETACH = 0 +) + +type SYSTEM_INFO struct { + ProcessorArchitecture uint16 + Reserved uint16 + PageSize uint32 + MinimumApplicationAddress uintptr + MaximumApplicationAddress uintptr + ActiveProcessorMask uintptr + NumberOfProcessors uint32 + ProcessorType uint32 + AllocationGranularity uint32 + ProcessorLevel uint16 + ProcessorRevision uint16 +} diff --git a/driver/memmod/syscall_windows_32.go b/driver/memmod/syscall_windows_32.go new file mode 100644 index 00000000..dde8b360 --- /dev/null +++ b/driver/memmod/syscall_windows_32.go @@ -0,0 +1,96 @@ +//go:build (windows && 386) || (windows && arm) + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +// Optional header format +type IMAGE_OPTIONAL_HEADER struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + BaseOfData uint32 + ImageBase uintptr + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uintptr + SizeOfStackCommit uintptr + SizeOfHeapReserve uintptr + SizeOfHeapCommit uintptr + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY +} + +const IMAGE_ORDINAL_FLAG uintptr = 0x80000000 + +type IMAGE_LOAD_CONFIG_DIRECTORY struct { + Size uint32 + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + GlobalFlagsClear uint32 + GlobalFlagsSet uint32 + CriticalSectionDefaultTimeout uint32 + DeCommitFreeBlockThreshold uint32 + DeCommitTotalFreeThreshold uint32 + LockPrefixTable uint32 + MaximumAllocationSize uint32 + VirtualMemoryThreshold uint32 + ProcessHeapFlags uint32 + ProcessAffinityMask uint32 + CSDVersion uint16 + DependentLoadFlags uint16 + EditList uint32 + SecurityCookie uint32 + SEHandlerTable uint32 + SEHandlerCount uint32 + GuardCFCheckFunctionPointer uint32 + GuardCFDispatchFunctionPointer uint32 + GuardCFFunctionTable uint32 + GuardCFFunctionCount uint32 + GuardFlags uint32 + CodeIntegrity IMAGE_LOAD_CONFIG_CODE_INTEGRITY + GuardAddressTakenIatEntryTable uint32 + GuardAddressTakenIatEntryCount uint32 + GuardLongJumpTargetTable uint32 + GuardLongJumpTargetCount uint32 + DynamicValueRelocTable uint32 + CHPEMetadataPointer uint32 + GuardRFFailureRoutine uint32 + GuardRFFailureRoutineFunctionPointer uint32 + DynamicValueRelocTableOffset uint32 + DynamicValueRelocTableSection uint16 + Reserved2 uint16 + GuardRFVerifyStackPointerFunctionPointer uint32 + HotPatchTableOffset uint32 + Reserved3 uint32 + EnclaveConfigurationPointer uint32 + VolatileMetadataPointer uint32 + GuardEHContinuationTable uint32 + GuardEHContinuationCount uint32 + GuardXFGCheckFunctionPointer uint32 + GuardXFGDispatchFunctionPointer uint32 + GuardXFGTableDispatchFunctionPointer uint32 + CastGuardOsDeterminedFailureMode uint32 +} diff --git a/driver/memmod/syscall_windows_64.go b/driver/memmod/syscall_windows_64.go new file mode 100644 index 00000000..f23524fb --- /dev/null +++ b/driver/memmod/syscall_windows_64.go @@ -0,0 +1,95 @@ +//go:build (windows && amd64) || (windows && arm64) + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package memmod + +// Optional header format +type IMAGE_OPTIONAL_HEADER struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + ImageBase uintptr + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uintptr + SizeOfStackCommit uintptr + SizeOfHeapReserve uintptr + SizeOfHeapCommit uintptr + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY +} + +const IMAGE_ORDINAL_FLAG uintptr = 0x8000000000000000 + +type IMAGE_LOAD_CONFIG_DIRECTORY struct { + Size uint32 + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + GlobalFlagsClear uint32 + GlobalFlagsSet uint32 + CriticalSectionDefaultTimeout uint32 + DeCommitFreeBlockThreshold uint64 + DeCommitTotalFreeThreshold uint64 + LockPrefixTable uint64 + MaximumAllocationSize uint64 + VirtualMemoryThreshold uint64 + ProcessAffinityMask uint64 + ProcessHeapFlags uint32 + CSDVersion uint16 + DependentLoadFlags uint16 + EditList uint64 + SecurityCookie uint64 + SEHandlerTable uint64 + SEHandlerCount uint64 + GuardCFCheckFunctionPointer uint64 + GuardCFDispatchFunctionPointer uint64 + GuardCFFunctionTable uint64 + GuardCFFunctionCount uint64 + GuardFlags uint32 + CodeIntegrity IMAGE_LOAD_CONFIG_CODE_INTEGRITY + GuardAddressTakenIatEntryTable uint64 + GuardAddressTakenIatEntryCount uint64 + GuardLongJumpTargetTable uint64 + GuardLongJumpTargetCount uint64 + DynamicValueRelocTable uint64 + CHPEMetadataPointer uint64 + GuardRFFailureRoutine uint64 + GuardRFFailureRoutineFunctionPointer uint64 + DynamicValueRelocTableOffset uint32 + DynamicValueRelocTableSection uint16 + Reserved2 uint16 + GuardRFVerifyStackPointerFunctionPointer uint64 + HotPatchTableOffset uint32 + Reserved3 uint32 + EnclaveConfigurationPointer uint64 + VolatileMetadataPointer uint64 + GuardEHContinuationTable uint64 + GuardEHContinuationCount uint64 + GuardXFGCheckFunctionPointer uint64 + GuardXFGDispatchFunctionPointer uint64 + GuardXFGTableDispatchFunctionPointer uint64 + CastGuardOsDeterminedFailureMode uint64 +} diff --git a/driver/wintunremoval_windows.go b/driver/wintunremoval_windows.go new file mode 100644 index 00000000..ab51d989 --- /dev/null +++ b/driver/wintunremoval_windows.go @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. + */ + +package driver + +import ( + "path/filepath" + + "golang.org/x/sys/windows" +) + +func UninstallLegacyWintun() error { + deviceClassNetGUID := &windows.GUID{0x4d36e972, 0xe325, 0x11ce, [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}} + devInfo, err := windows.SetupDiCreateDeviceInfoListEx(deviceClassNetGUID, 0, "") + if err != nil { + return err + } + defer devInfo.Close() + devInfoData, err := devInfo.CreateDeviceInfo("Wintun", deviceClassNetGUID, "", 0, windows.DICD_GENERATE_ID) + if err != nil { + return err + } + err = devInfo.SetDeviceRegistryProperty(devInfoData, windows.SPDRP_HARDWAREID, []byte("W\x00i\x00n\x00t\x00u\x00n\x00\x00\x00\x00\x00")) + if err != nil { + return err + } + err = devInfo.BuildDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER) + if err != nil { + return err + } + defer devInfo.DestroyDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER) + var lastError error + for i := 0; ; i++ { + drvInfoData, err := devInfo.EnumDriverInfo(devInfoData, windows.SPDIT_COMPATDRIVER, i) + if err != nil { + if err == windows.ERROR_NO_MORE_ITEMS { + break + } + continue + } + drvInfoDetailData, err := devInfo.DriverInfoDetail(devInfoData, drvInfoData) + if err != nil { + continue + } + lastError = windows.SetupUninstallOEMInf(filepath.Base(drvInfoDetailData.InfFileName()), 0) + } + return lastError +} |