Update dependencies

This commit is contained in:
bluepython508
2024-11-01 17:33:34 +00:00
parent 033ac0b400
commit 5cdfab398d
3596 changed files with 1033483 additions and 259 deletions

19
vendor/github.com/dblohm7/wingoes/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,19 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Vim
.*.swo
.*.swp
# Dependency directories (remove the comment below to include it)
# vendor/

29
vendor/github.com/dblohm7/wingoes/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2022, Tailscale Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

2
vendor/github.com/dblohm7/wingoes/README.md generated vendored Normal file
View File

@@ -0,0 +1,2 @@
# wingoes, an opinionated library for writing Win32 programs in Go

140
vendor/github.com/dblohm7/wingoes/com/api.go generated vendored Normal file
View File

@@ -0,0 +1,140 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package com
import (
"unsafe"
"github.com/dblohm7/wingoes"
)
// MustGetAppID parses s, a string containing an app ID and returns a pointer to the
// parsed AppID. s must be specified in the format "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
// If there is an error parsing s, MustGetAppID panics.
func MustGetAppID(s string) *AppID {
return (*AppID)(unsafe.Pointer(wingoes.MustGetGUID(s)))
}
// MustGetCLSID parses s, a string containing a CLSID and returns a pointer to the
// parsed CLSID. s must be specified in the format "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
// If there is an error parsing s, MustGetCLSID panics.
func MustGetCLSID(s string) *CLSID {
return (*CLSID)(unsafe.Pointer(wingoes.MustGetGUID(s)))
}
// MustGetIID parses s, a string containing an IID and returns a pointer to the
// parsed IID. s must be specified in the format "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
// If there is an error parsing s, MustGetIID panics.
func MustGetIID(s string) *IID {
return (*IID)(unsafe.Pointer(wingoes.MustGetGUID(s)))
}
// MustGetServiceID parses s, a string containing a service ID and returns a pointer to the
// parsed ServiceID. s must be specified in the format "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
// If there is an error parsing s, MustGetServiceID panics.
func MustGetServiceID(s string) *ServiceID {
return (*ServiceID)(unsafe.Pointer(wingoes.MustGetGUID(s)))
}
func getCurrentApartmentInfo() (aptInfo, error) {
var info aptInfo
hr := coGetApartmentType(&info.apt, &info.qualifier)
if err := wingoes.ErrorFromHRESULT(hr); err.Failed() {
return info, err
}
return info, nil
}
// aptChecker is a function that applies an arbitrary predicate to an OS thread's
// apartment information, returning true if the input satisifes that predicate.
type aptChecker func(*aptInfo) bool
// checkCurrentApartment obtains information about the COM apartment that the
// current OS thread resides in, and then passes that information to chk,
// which evaluates that information and determines the return value.
func checkCurrentApartment(chk aptChecker) bool {
info, err := getCurrentApartmentInfo()
if err != nil {
return false
}
return chk(&info)
}
// AssertCurrentOSThreadSTA checks if the current OS thread resides in a
// single-threaded apartment, and if not, panics.
func AssertCurrentOSThreadSTA() {
if IsCurrentOSThreadSTA() {
return
}
panic("current OS thread does not reside in a single-threaded apartment")
}
// IsCurrentOSThreadSTA checks if the current OS thread resides in a
// single-threaded apartment and returns true if so.
func IsCurrentOSThreadSTA() bool {
chk := func(i *aptInfo) bool {
return i.apt == coAPTTYPE_STA || i.apt == coAPTTYPE_MAINSTA
}
return checkCurrentApartment(chk)
}
// AssertCurrentOSThreadMTA checks if the current OS thread resides in the
// multi-threaded apartment, and if not, panics.
func AssertCurrentOSThreadMTA() {
if IsCurrentOSThreadMTA() {
return
}
panic("current OS thread does not reside in the multi-threaded apartment")
}
// IsCurrentOSThreadMTA checks if the current OS thread resides in the
// multi-threaded apartment and returns true if so.
func IsCurrentOSThreadMTA() bool {
chk := func(i *aptInfo) bool {
return i.apt == coAPTTYPE_MTA
}
return checkCurrentApartment(chk)
}
// createInstanceWithCLSCTX creates a new garbage-collected COM object of type T
// using class clsid. clsctx determines the acceptable location for hosting the
// COM object (in-process, local but out-of-process, or remote).
func createInstanceWithCLSCTX[T Object](clsid *CLSID, clsctx coCLSCTX) (T, error) {
var t T
iid := t.IID()
ppunk := NewABIReceiver()
hr := coCreateInstance(
clsid,
nil,
clsctx,
iid,
ppunk,
)
if err := wingoes.ErrorFromHRESULT(hr); err.Failed() {
return t, err
}
return t.Make(ppunk).(T), nil
}
// CreateInstance instantiates a new in-process COM object of type T
// using class clsid.
func CreateInstance[T Object](clsid *CLSID) (T, error) {
return createInstanceWithCLSCTX[T](clsid, coCLSCTX_INPROC_SERVER)
}
// CreateInstance instantiates a new local, out-of-process COM object of type T
// using class clsid.
func CreateOutOfProcessInstance[T Object](clsid *CLSID) (T, error) {
return createInstanceWithCLSCTX[T](clsid, coCLSCTX_LOCAL_SERVER)
}

View File

@@ -0,0 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
// Package automation provides essential types for interacting with COM Automation (IDispatch).
package automation

View File

@@ -0,0 +1,14 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
package automation
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go mksyscall.go
//go:generate go run golang.org/x/tools/cmd/goimports -w zsyscall_windows.go
//sys sysAllocString(str *uint16) (ret BSTR) = oleaut32.SysAllocString
//sys sysAllocStringLen(str *uint16, strLen uint32) (ret BSTR) = oleaut32.SysAllocStringLen
//sys sysFreeString(bstr BSTR) = oleaut32.SysFreeString
//sys sysStringLen(bstr BSTR) (ret uint32) = oleaut32.SysStringLen

View File

@@ -0,0 +1,93 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
package automation
import (
"unsafe"
"golang.org/x/sys/windows"
)
// BSTR is the string format used by COM Automation. They are not garbage
// collected and must be explicitly closed when no longer needed.
type BSTR uintptr
// NewBSTR creates a new BSTR from string s.
func NewBSTR(s string) BSTR {
buf, err := windows.UTF16FromString(s)
if err != nil {
return 0
}
return NewBSTRFromUTF16(buf)
}
// NewBSTR creates a new BSTR from slice us, which contains UTF-16 code units.
func NewBSTRFromUTF16(us []uint16) BSTR {
if len(us) == 0 {
return 0
}
return sysAllocStringLen(unsafe.SliceData(us), uint32(len(us)))
}
// NewBSTR creates a new BSTR from up, a C-style string pointer to UTF-16 code units.
func NewBSTRFromUTF16Ptr(up *uint16) BSTR {
if up == nil {
return 0
}
return sysAllocString(up)
}
// Len returns the length of bs in code units.
func (bs *BSTR) Len() uint32 {
return sysStringLen(*bs)
}
// String returns the contents of bs as a Go string.
func (bs *BSTR) String() string {
return windows.UTF16ToString(bs.toUTF16())
}
// toUTF16 is unsafe for general use because it returns a pointer that is
// not managed by the Go GC.
func (bs *BSTR) toUTF16() []uint16 {
return unsafe.Slice(bs.toUTF16Ptr(), bs.Len())
}
// ToUTF16 returns the contents of bs as a slice of UTF-16 code units.
func (bs *BSTR) ToUTF16() []uint16 {
return append([]uint16{}, bs.toUTF16()...)
}
// toUTF16Ptr is unsafe for general use because it returns a pointer that is
// not managed by the Go GC.
func (bs *BSTR) toUTF16Ptr() *uint16 {
return (*uint16)(unsafe.Pointer(*bs))
}
// ToUTF16 returns the contents of bs as C-style string pointer to UTF-16 code units.
func (bs *BSTR) ToUTF16Ptr() *uint16 {
return unsafe.SliceData(bs.ToUTF16())
}
// Clone creates a clone of bs whose lifetime becomes independent of the original.
// It must be explicitly closed when no longer needed.
func (bs *BSTR) Clone() BSTR {
return sysAllocStringLen(bs.toUTF16Ptr(), bs.Len())
}
// IsNil returns true if bs holds a nil value.
func (bs *BSTR) IsNil() bool {
return *bs == 0
}
// Close frees bs.
func (bs *BSTR) Close() error {
if *bs != 0 {
sysFreeString(*bs)
*bs = 0
}
return nil
}

View File

@@ -0,0 +1,70 @@
// Code generated by 'go generate'; DO NOT EDIT.
package automation
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modoleaut32 = windows.NewLazySystemDLL("oleaut32.dll")
procSysAllocString = modoleaut32.NewProc("SysAllocString")
procSysAllocStringLen = modoleaut32.NewProc("SysAllocStringLen")
procSysFreeString = modoleaut32.NewProc("SysFreeString")
procSysStringLen = modoleaut32.NewProc("SysStringLen")
)
func sysAllocString(str *uint16) (ret BSTR) {
r0, _, _ := syscall.Syscall(procSysAllocString.Addr(), 1, uintptr(unsafe.Pointer(str)), 0, 0)
ret = BSTR(r0)
return
}
func sysAllocStringLen(str *uint16, strLen uint32) (ret BSTR) {
r0, _, _ := syscall.Syscall(procSysAllocStringLen.Addr(), 2, uintptr(unsafe.Pointer(str)), uintptr(strLen), 0)
ret = BSTR(r0)
return
}
func sysFreeString(bstr BSTR) {
syscall.Syscall(procSysFreeString.Addr(), 1, uintptr(bstr), 0, 0)
return
}
func sysStringLen(bstr BSTR) (ret uint32) {
r0, _, _ := syscall.Syscall(procSysStringLen.Addr(), 1, uintptr(bstr), 0, 0)
ret = uint32(r0)
return
}

9
vendor/github.com/dblohm7/wingoes/com/com.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
// Package com provides an idiomatic foundation for instantiating and invoking
// COM objects.
package com

120
vendor/github.com/dblohm7/wingoes/com/globalopts.go generated vendored Normal file
View File

@@ -0,0 +1,120 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package com
import (
"runtime"
"syscall"
"unsafe"
"github.com/dblohm7/wingoes"
)
var (
CLSID_GlobalOptions = &CLSID{0x0000034B, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}
)
var (
IID_IGlobalOptions = &IID{0x0000015B, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}
)
type GLOBALOPT_PROPERTIES int32
const (
COMGLB_EXCEPTION_HANDLING = GLOBALOPT_PROPERTIES(1)
COMGLB_APPID = GLOBALOPT_PROPERTIES(2)
COMGLB_RPC_THREADPOOL_SETTING = GLOBALOPT_PROPERTIES(3)
COMGLB_RO_SETTINGS = GLOBALOPT_PROPERTIES(4)
COMGLB_UNMARSHALING_POLICY = GLOBALOPT_PROPERTIES(5)
)
const (
COMGLB_EXCEPTION_HANDLE = 0
COMGLB_EXCEPTION_DONOT_HANDLE_FATAL = 1
COMGLB_EXCEPTION_DONOT_HANDLE = 1
COMGLB_EXCEPTION_DONOT_HANDLE_ANY = 2
)
// IGlobalOptionsABI represents the COM ABI for the IGlobalOptions interface.
type IGlobalOptionsABI struct {
IUnknownABI
}
// GlobalOptions is the COM object used for setting global configuration settings
// on the COM runtime. It must be called after COM runtime security has been
// initialized, but before anything else "significant" is done using COM.
type GlobalOptions struct {
GenericObject[IGlobalOptionsABI]
}
func (abi *IGlobalOptionsABI) Set(prop GLOBALOPT_PROPERTIES, value uintptr) error {
method := unsafe.Slice(abi.Vtbl, 5)[3]
rc, _, _ := syscall.Syscall(
method,
3,
uintptr(unsafe.Pointer(abi)),
uintptr(prop),
value,
)
if e := wingoes.ErrorFromHRESULT(wingoes.HRESULT(rc)); e.Failed() {
return e
}
return nil
}
func (abi *IGlobalOptionsABI) Query(prop GLOBALOPT_PROPERTIES) (uintptr, error) {
var result uintptr
method := unsafe.Slice(abi.Vtbl, 5)[4]
rc, _, _ := syscall.Syscall(
method,
3,
uintptr(unsafe.Pointer(abi)),
uintptr(prop),
uintptr(unsafe.Pointer(&result)),
)
if e := wingoes.ErrorFromHRESULT(wingoes.HRESULT(rc)); e.Failed() {
return 0, e
}
return result, nil
}
func (o GlobalOptions) IID() *IID {
return IID_IGlobalOptions
}
func (o GlobalOptions) Make(r ABIReceiver) any {
if r == nil {
return GlobalOptions{}
}
runtime.SetFinalizer(r, ReleaseABI)
pp := (**IGlobalOptionsABI)(unsafe.Pointer(r))
return GlobalOptions{GenericObject[IGlobalOptionsABI]{Pp: pp}}
}
// UnsafeUnwrap returns the underlying IGlobalOptionsABI of the object. As the
// name implies, this is unsafe -- you had better know what you are doing!
func (o GlobalOptions) UnsafeUnwrap() *IGlobalOptionsABI {
return *(o.Pp)
}
// Set sets the global property prop to value.
func (o GlobalOptions) Set(prop GLOBALOPT_PROPERTIES, value uintptr) error {
p := *(o.Pp)
return p.Set(prop, value)
}
// Query returns the value of global property prop.
func (o GlobalOptions) Query(prop GLOBALOPT_PROPERTIES) (uintptr, error) {
p := *(o.Pp)
return p.Query(prop)
}

22
vendor/github.com/dblohm7/wingoes/com/guid.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package com
import (
"github.com/dblohm7/wingoes"
)
// We intentionally export these types across all GOOSes
// IID is a GUID that represents an interface ID.
type IID wingoes.GUID
// CLSID is a GUID that represents a class ID.
type CLSID wingoes.GUID
// AppID is a GUID that represents an application ID.
type AppID wingoes.GUID
// ServiceID is a GUID that represents a service ID.
type ServiceID wingoes.GUID

112
vendor/github.com/dblohm7/wingoes/com/interface.go generated vendored Normal file
View File

@@ -0,0 +1,112 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package com
import (
"syscall"
"unsafe"
"github.com/dblohm7/wingoes"
)
// IUnknown is the base COM interface.
type IUnknown interface {
QueryInterface(iid *IID) (IUnknown, error)
AddRef() int32
Release() int32
}
// This is a sentinel that indicates that a struct implements the COM ABI.
// Only IUnknownABI should implement this.
type hasVTable interface {
vtable() *uintptr
}
// IUnknownABI describes the ABI of the IUnknown interface (ie, a vtable).
type IUnknownABI struct {
Vtbl *uintptr
}
func (abi IUnknownABI) vtable() *uintptr {
return abi.Vtbl
}
// ABI is a type constraint allowing the COM ABI, or any struct that embeds it.
type ABI interface {
hasVTable
}
// PUnknown is a type constraint for types that both implement IUnknown and
// are also pointers to a COM ABI.
type PUnknown[A ABI] interface {
IUnknown
*A
}
// ABIReceiver is the type that receives COM interface pointers from COM
// method outparams.
type ABIReceiver **IUnknownABI
// NewABIReceiver instantiates a new ABIReceiver.
func NewABIReceiver() ABIReceiver {
return ABIReceiver(new(*IUnknownABI))
}
// ReleaseABI releases a COM object. Finalizers must always invoke this function
// when destroying COM interfaces.
func ReleaseABI(p **IUnknownABI) {
(*p).Release()
}
// QueryInterface implements the QueryInterface call for a COM interface pointer.
// iid is the desired interface ID.
func (abi *IUnknownABI) QueryInterface(iid *IID) (IUnknown, error) {
var punk *IUnknownABI
r, _, _ := syscall.Syscall(
*(abi.Vtbl),
3,
uintptr(unsafe.Pointer(abi)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&punk)),
)
if e := wingoes.ErrorFromHRESULT(wingoes.HRESULT(r)); e.Failed() {
return nil, e
}
return punk, nil
}
// AddRef implements the AddRef call for a COM interface pointer.
func (abi *IUnknownABI) AddRef() int32 {
method := unsafe.Slice(abi.Vtbl, 3)[1]
r, _, _ := syscall.Syscall(
method,
1,
uintptr(unsafe.Pointer(abi)),
0,
0,
)
return int32(r)
}
// Release implements the Release call for a COM interface pointer.
func (abi *IUnknownABI) Release() int32 {
method := unsafe.Slice(abi.Vtbl, 3)[2]
r, _, _ := syscall.Syscall(
method,
1,
uintptr(unsafe.Pointer(abi)),
0,
0,
)
return int32(r)
}

25
vendor/github.com/dblohm7/wingoes/com/mksyscall.go generated vendored Normal file
View File

@@ -0,0 +1,25 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package com
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go mksyscall.go
//go:generate go run golang.org/x/tools/cmd/goimports -w zsyscall_windows.go
//sys coCreateInstance(clsid *CLSID, unkOuter *IUnknownABI, clsctx coCLSCTX, iid *IID, ppv **IUnknownABI) (hr wingoes.HRESULT) = ole32.CoCreateInstance
//sys coGetApartmentType(aptType *coAPTTYPE, qual *coAPTTYPEQUALIFIER) (hr wingoes.HRESULT) = ole32.CoGetApartmentType
//sys coInitializeEx(reserved uintptr, flags uint32) (hr wingoes.HRESULT) = ole32.CoInitializeEx
//sys coInitializeSecurity(sd *windows.SECURITY_DESCRIPTOR, authSvcLen int32, authSvc *soleAuthenticationService, reserved1 uintptr, authnLevel rpcAuthnLevel, impLevel rpcImpersonationLevel, authList *soleAuthenticationList, capabilities authCapabilities, reserved2 uintptr) (hr wingoes.HRESULT) = ole32.CoInitializeSecurity
// We don't use '?' on coIncrementMTAUsage because that doesn't play nicely with HRESULTs. We manually check for its presence in process.go
//sys coIncrementMTAUsage(cookie *coMTAUsageCookie) (hr wingoes.HRESULT) = ole32.CoIncrementMTAUsage
// Technically this proc is __cdecl, but since it has 0 args this doesn't matter
//sys setOaNoCache() = oleaut32.SetOaNoCache
// For the following two functions we use IUnknownABI instead of IStreamABI because it makes the callsites cleaner.
//sys shCreateMemStream(pInit *byte, cbInit uint32) (stream *IUnknownABI) = shlwapi.SHCreateMemStream
//sys createStreamOnHGlobal(hglobal internal.HGLOBAL, deleteOnRelease bool, stream **IUnknownABI) (hr wingoes.HRESULT) = ole32.CreateStreamOnHGlobal

89
vendor/github.com/dblohm7/wingoes/com/object.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package com
import (
"fmt"
"unsafe"
)
// GenericObject is a struct that wraps any interface that implements the COM ABI.
type GenericObject[A ABI] struct {
Pp **A
}
func (o GenericObject[A]) pp() **A {
return o.Pp
}
// Object is the interface that all garbage-collected instances of COM interfaces
// must implement.
type Object interface {
// IID returns the interface ID for the object. This method may be called
// on Objects containing the zero value, so its return value must not depend
// on the value of the method's receiver.
IID() *IID
// Make converts r to an instance of a garbage-collected COM object. The type
// of its return value must always match the type of the method's receiver.
Make(r ABIReceiver) any
}
// EmbedsGenericObject is a type constraint matching any struct that embeds
// a GenericObject[A].
type EmbedsGenericObject[A ABI] interface {
Object
~struct{ GenericObject[A] }
pp() **A
}
// As casts obj to an object of type O, or panics if obj cannot be converted to O.
func As[O Object, A ABI, PU PUnknown[A], E EmbedsGenericObject[A]](obj E) O {
o, err := TryAs[O, A, PU](obj)
if err != nil {
panic(fmt.Sprintf("wingoes.com.As error: %v", err))
}
return o
}
// TryAs casts obj to an object of type O, or returns an error if obj cannot be
// converted to O.
func TryAs[O Object, A ABI, PU PUnknown[A], E EmbedsGenericObject[A]](obj E) (O, error) {
var o O
iid := o.IID()
p := (PU)(unsafe.Pointer(*(obj.pp())))
i, err := p.QueryInterface(iid)
if err != nil {
return o, err
}
r := NewABIReceiver()
*r = i.(*IUnknownABI)
return o.Make(r).(O), nil
}
// IsSameObject returns true when both l and r refer to the same underlying object.
func IsSameObject[AL, AR ABI, PL PUnknown[AL], PR PUnknown[AR], EL EmbedsGenericObject[AL], ER EmbedsGenericObject[AR]](l EL, r ER) bool {
pl := (PL)(unsafe.Pointer(*(l.pp())))
ul, err := pl.QueryInterface(IID_IUnknown)
if err != nil {
return false
}
defer ul.Release()
pr := (PR)(unsafe.Pointer(*(r.pp())))
ur, err := pr.QueryInterface(IID_IUnknown)
if err != nil {
return false
}
defer ur.Release()
return ul.(*IUnknownABI) == ur.(*IUnknownABI)
}

268
vendor/github.com/dblohm7/wingoes/com/process.go generated vendored Normal file
View File

@@ -0,0 +1,268 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package com
import (
"os"
"runtime"
"github.com/dblohm7/wingoes"
"golang.org/x/sys/windows"
)
// ProcessType is an enumeration that specifies the type of the current process
// when calling StartRuntime.
type ProcessType uint
const (
// ConsoleApp is a text-mode Windows program.
ConsoleApp = ProcessType(iota)
// Service is a Windows service.
Service
// GUIApp is a GUI-mode Windows program.
GUIApp
// Note: Even though this implementation is not yet internally distinguishing
// between console apps and services, this distinction may be useful in the
// future. For example, a service could receive more restrictive default
// security settings than a console app.
// Having this as part of the API now avoids future breakage.
)
// StartRuntime permanently initializes COM for the remaining lifetime of the
// current process. To avoid errors, it should be called as early as possible
// during program initialization. When processType == GUIApp, the current
// OS thread becomes permanently locked to the current goroutine; any subsequent
// GUI *must* be created on the same OS thread.
// An excellent location to call StartRuntime is in the init function of the
// main package.
func StartRuntime(processType ProcessType) error {
return StartRuntimeWithDACL(processType, nil)
}
// StartRuntimeWithDACL permanently initializes COM for the remaining lifetime
// of the current process. To avoid errors, it should be called as early as
// possible during program initialization. When processType == GUIApp, the
// current OS thread becomes permanently locked to the current goroutine; any
// subsequent GUI *must* be created on the same OS thread. dacl is an ACL that
// controls access of other processes connecting to the current process over COM.
// For further information about COM access control, look up the COM_RIGHTS_*
// access flags in the Windows developer documentation.
// An excellent location to call StartRuntimeWithDACL is in the init function of
// the main package.
func StartRuntimeWithDACL(processType ProcessType, dacl *windows.ACL) error {
runtime.LockOSThread()
defer func() {
// When initializing for non-GUI processes, the OS thread may be unlocked
// upon return from this function.
if processType != GUIApp {
runtime.UnlockOSThread()
}
}()
switch processType {
case ConsoleApp, Service:
// Just start the MTA implicitly.
if err := startMTAImplicitly(); err != nil {
return err
}
case GUIApp:
// For GUIApp, we want the current OS thread to enter a single-threaded
// apartment (STA). However, we want all other OS threads to reside inside
// a multi-threaded apartment (MTA). The way to so this is to first start
// the MTA implicitly, affecting all OS threads who have not yet explicitly
// entered a COM apartment...
if err := startMTAImplicitly(); err != nil {
runtime.UnlockOSThread()
return err
}
// ...and then subsequently explicitly enter a STA on this OS thread, which
// automatically removes this OS thread from the MTA.
if err := enterSTA(); err != nil {
runtime.UnlockOSThread()
return err
}
// From this point forward, we must never unlock the OS thread.
default:
return os.ErrInvalid
}
// Order is extremely important here: initSecurity must be called immediately
// after apartments are set up, but before doing anything else.
if err := initSecurity(dacl); err != nil {
return err
}
// By default, for compatibility reasons, COM internally sets a catch-all
// exception handler at its API boundary. This is dangerous, so we override it.
// This work must happen after security settings are initialized, but before
// anything "significant" is done with COM.
globalOpts, err := CreateInstance[GlobalOptions](CLSID_GlobalOptions)
if err != nil {
return err
}
err = globalOpts.Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE_ANY)
// The BSTR cache never invalidates itself, so we disable it unconditionally.
// We do this here to ensure that the BSTR cache is off before anything
// can possibly start using oleaut32.dll.
setOaNoCache()
return err
}
// startMTAImplicitly creates an implicit multi-threaded apartment (MTA) for
// all threads in a process that do not otherwise explicitly enter a COM apartment.
func startMTAImplicitly() error {
// CoIncrementMTAUsage is the modern API to use for creating the MTA implicitly,
// however we may fall back to a legacy mechanism when the former API is unavailable.
if err := procCoIncrementMTAUsage.Find(); err != nil {
return startMTAImplicitlyLegacy()
}
// We do not retain cookie beyond this function, as we have no intention of
// tearing any of this back down.
var cookie coMTAUsageCookie
hr := coIncrementMTAUsage(&cookie)
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return e
}
return nil
}
// startMTAImplicitlyLegacy works by having a background OS thread explicitly enter
// the multi-threaded apartment. All other OS threads that have not explicitly
// entered an apartment will become implicit members of that MTA. This function is
// written assuming that the current OS thread has already been locked.
func startMTAImplicitlyLegacy() error {
// We need to start the MTA on a background OS thread, HOWEVER we also want this
// to happen synchronously, so we wait on c for MTA initialization to complete.
c := make(chan error)
go bgMTASustainer(c)
return <-c
}
// bgMTASustainer locks the current goroutine to the current OS thread, enters
// the COM multi-threaded apartment, and then blocks for the remainder of the
// process's lifetime. It sends its result to c so that startMTAImplicitlyLegacy
// can wait for the MTA to be ready before proceeding.
func bgMTASustainer(c chan error) {
runtime.LockOSThread()
err := enterMTA()
c <- err
if err != nil {
// We didn't enter the MTA, so just unlock and bail.
runtime.UnlockOSThread()
return
}
select {}
}
// enterMTA causes the current OS thread to explicitly declare itself to be a
// member of COM's multi-threaded apartment. Note that this function affects
// thread-local state, so use carefully!
func enterMTA() error {
return coInit(windows.COINIT_MULTITHREADED)
}
// enterSTA causes the current OS thread to create and enter a single-threaded
// apartment. The current OS thread must be locked and remain locked for the
// duration of the thread's time in the apartment. For our purposes, the calling
// OS thread never leaves the STA, so it must effectively remain locked for
// the remaining lifetime of the process. A single-threaded apartment should be
// used if and only if an OS thread is going to be creating windows and pumping
// messages; STAs are NOT generic containers for single-threaded COM code,
// contrary to popular belief. Note that this function affects thread-local
// state, so use carefully!
func enterSTA() error {
return coInit(windows.COINIT_APARTMENTTHREADED)
}
// coInit is a wrapper for CoInitializeEx that properly handles the S_FALSE
// error code (x/sys/windows.CoInitializeEx does not).
func coInit(apartment uint32) error {
hr := coInitializeEx(0, apartment)
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return e
}
return nil
}
const (
authSvcCOMChooses = -1
)
// initSecurity initializes COM security using the ACL specified by dacl.
// A nil dacl implies that a default ACL should be used instead.
func initSecurity(dacl *windows.ACL) error {
sd, err := buildSecurityDescriptor(dacl)
if err != nil {
return err
}
caps := authCapNone
if sd == nil {
// For COM to fall back to system-wide defaults, we need to set this bit.
caps |= authCapAppID
}
hr := coInitializeSecurity(
sd,
authSvcCOMChooses,
nil, // authSvc (not used because previous arg is authSvcCOMChooses)
0, // Reserved, must be 0
rpcAuthnLevelDefault,
rpcImpLevelIdentify,
nil, // authlist: use defaults
caps,
0, // Reserved, must be 0
)
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return e
}
return nil
}
// buildSecurityDescriptor inserts dacl into a valid security descriptor for use
// with CoInitializeSecurity. A nil dacl results in a nil security descriptor,
// which we consider to be a valid "use defaults" sentinel.
func buildSecurityDescriptor(dacl *windows.ACL) (*windows.SECURITY_DESCRIPTOR, error) {
if dacl == nil {
// Not an error, just use defaults.
return nil, nil
}
sd, err := windows.NewSecurityDescriptor()
if err != nil {
return nil, err
}
if err := sd.SetDACL(dacl, true, false); err != nil {
return nil, err
}
// CoInitializeSecurity will fail unless the SD's owner and group are both set.
userSIDs, err := wingoes.CurrentProcessUserSIDs()
if err != nil {
return nil, err
}
if err := sd.SetOwner(userSIDs.User, false); err != nil {
return nil, err
}
if err := sd.SetGroup(userSIDs.PrimaryGroup, false); err != nil {
return nil, err
}
return sd, nil
}

556
vendor/github.com/dblohm7/wingoes/com/stream.go generated vendored Normal file
View File

@@ -0,0 +1,556 @@
// Copyright (c) 2023 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package com
import (
"io"
"runtime"
"syscall"
"unsafe"
"github.com/dblohm7/wingoes"
"github.com/dblohm7/wingoes/internal"
"golang.org/x/sys/windows"
)
var (
IID_ISequentialStream = &IID{0x0C733A30, 0x2A1C, 0x11CE, [8]byte{0xAD, 0xE5, 0x00, 0xAA, 0x00, 0x44, 0x77, 0x3D}}
IID_IStream = &IID{0x0000000C, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}
)
type STGC uint32
const (
STGC_DEFAULT = STGC(0)
STGC_OVERWRITE = STGC(1)
STGC_ONLYIFCURRENT = STGC(2)
STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = STGC(4)
STGC_CONSOLIDATE = STGC(8)
)
type LOCKTYPE uint32
const (
LOCK_WRITE = LOCKTYPE(1)
LOCK_EXCLUSIVE = LOCKTYPE(2)
LOCK_ONLYONCE = LOCKTYPE(4)
)
type STGTY uint32
const (
STGTY_STORAGE = STGTY(1)
STGTY_STREAM = STGTY(2)
STGTY_LOCKBYTES = STGTY(3)
STGTY_PROPERTY = STGTY(4)
)
type STATFLAG uint32
const (
STATFLAG_DEFAULT = STATFLAG(0)
STATFLAG_NONAME = STATFLAG(1)
STATFLAG_NOOPEN = STATFLAG(2)
)
type STATSTG struct {
Name COMAllocatedString
Type STGTY
Size uint64
MTime windows.Filetime
CTime windows.Filetime
ATime windows.Filetime
Mode uint32
LocksSupported LOCKTYPE
ClsID CLSID
_ uint32 // StateBits
_ uint32 // reserved
}
func (st *STATSTG) Close() error {
return st.Name.Close()
}
type ISequentialStreamABI struct {
IUnknownABI
}
type IStreamABI struct {
ISequentialStreamABI
}
type SequentialStream struct {
GenericObject[ISequentialStreamABI]
}
type Stream struct {
GenericObject[IStreamABI]
}
func (abi *ISequentialStreamABI) Read(p []byte) (int, error) {
if len(p) > maxStreamRWLen {
p = p[:maxStreamRWLen]
}
var cbRead uint32
method := unsafe.Slice(abi.Vtbl, 5)[3]
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(unsafe.Pointer(unsafe.SliceData(p))),
uintptr(uint32(len(p))),
uintptr(unsafe.Pointer(&cbRead)),
)
n := int(cbRead)
e := wingoes.ErrorFromHRESULT(wingoes.HRESULT(rc))
if e.Failed() {
return n, e
}
// Various implementations of IStream handle EOF differently. We need to
// deal with both.
if e.AsHRESULT() == wingoes.S_FALSE || (n == 0 && len(p) > 0) {
return n, io.EOF
}
return n, nil
}
func (abi *ISequentialStreamABI) Write(p []byte) (int, error) {
w := p
if len(w) > maxStreamRWLen {
w = w[:maxStreamRWLen]
}
var cbWritten uint32
method := unsafe.Slice(abi.Vtbl, 5)[4]
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(unsafe.Pointer(unsafe.SliceData(w))),
uintptr(uint32(len(w))),
uintptr(unsafe.Pointer(&cbWritten)),
)
n := int(cbWritten)
if e := wingoes.ErrorFromHRESULT(wingoes.HRESULT(rc)); e.Failed() {
return n, e
}
// Need this to satisfy Writer.
if n < len(p) {
return n, io.ErrShortWrite
}
return n, nil
}
func (o SequentialStream) IID() *IID {
return IID_ISequentialStream
}
func (o SequentialStream) Make(r ABIReceiver) any {
if r == nil {
return SequentialStream{}
}
runtime.SetFinalizer(r, ReleaseABI)
pp := (**ISequentialStreamABI)(unsafe.Pointer(r))
return SequentialStream{GenericObject[ISequentialStreamABI]{Pp: pp}}
}
func (o SequentialStream) UnsafeUnwrap() *ISequentialStreamABI {
return *(o.Pp)
}
func (o SequentialStream) Read(b []byte) (n int, err error) {
p := *(o.Pp)
return p.Read(b)
}
func (o SequentialStream) Write(b []byte) (int, error) {
p := *(o.Pp)
return p.Write(b)
}
func (abi *IStreamABI) Seek(offset int64, whence int) (n int64, _ error) {
var hr wingoes.HRESULT
method := unsafe.Slice(abi.Vtbl, 14)[5]
if runtime.GOARCH == "386" {
words := (*[2]uintptr)(unsafe.Pointer(&offset))
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
words[0],
words[1],
uintptr(uint32(whence)),
uintptr(unsafe.Pointer(&n)),
)
hr = wingoes.HRESULT(rc)
} else {
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(offset),
uintptr(uint32(whence)),
uintptr(unsafe.Pointer(&n)),
)
hr = wingoes.HRESULT(rc)
}
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return 0, e
}
return n, nil
}
func (abi *IStreamABI) SetSize(newSize uint64) error {
var hr wingoes.HRESULT
method := unsafe.Slice(abi.Vtbl, 14)[6]
if runtime.GOARCH == "386" {
words := (*[2]uintptr)(unsafe.Pointer(&newSize))
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
words[0],
words[1],
)
hr = wingoes.HRESULT(rc)
} else {
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(newSize),
)
hr = wingoes.HRESULT(rc)
}
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return e
}
return nil
}
func (abi *IStreamABI) CopyTo(dest *IStreamABI, numBytesToCopy uint64) (bytesRead, bytesWritten uint64, _ error) {
var hr wingoes.HRESULT
method := unsafe.Slice(abi.Vtbl, 14)[7]
if runtime.GOARCH == "386" {
words := (*[2]uintptr)(unsafe.Pointer(&numBytesToCopy))
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(unsafe.Pointer(dest)),
words[0],
words[1],
uintptr(unsafe.Pointer(&bytesRead)),
uintptr(unsafe.Pointer(&bytesWritten)),
)
hr = wingoes.HRESULT(rc)
} else {
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(unsafe.Pointer(dest)),
uintptr(numBytesToCopy),
uintptr(unsafe.Pointer(&bytesRead)),
uintptr(unsafe.Pointer(&bytesWritten)),
)
hr = wingoes.HRESULT(rc)
}
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return bytesRead, bytesWritten, e
}
return bytesRead, bytesWritten, nil
}
func (abi *IStreamABI) Commit(flags STGC) error {
method := unsafe.Slice(abi.Vtbl, 14)[8]
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(flags),
)
if e := wingoes.ErrorFromHRESULT(wingoes.HRESULT(rc)); e.Failed() {
return e
}
return nil
}
func (abi *IStreamABI) Revert() error {
method := unsafe.Slice(abi.Vtbl, 14)[9]
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
)
if e := wingoes.ErrorFromHRESULT(wingoes.HRESULT(rc)); e.Failed() {
return e
}
return nil
}
func (abi *IStreamABI) LockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
var hr wingoes.HRESULT
method := unsafe.Slice(abi.Vtbl, 14)[10]
if runtime.GOARCH == "386" {
oWords := (*[2]uintptr)(unsafe.Pointer(&offset))
nWords := (*[2]uintptr)(unsafe.Pointer(&numBytes))
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
oWords[0],
oWords[1],
nWords[0],
nWords[1],
uintptr(lockType),
)
hr = wingoes.HRESULT(rc)
} else {
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(offset),
uintptr(numBytes),
uintptr(lockType),
)
hr = wingoes.HRESULT(rc)
}
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return e
}
return nil
}
func (abi *IStreamABI) UnlockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
var hr wingoes.HRESULT
method := unsafe.Slice(abi.Vtbl, 14)[11]
if runtime.GOARCH == "386" {
oWords := (*[2]uintptr)(unsafe.Pointer(&offset))
nWords := (*[2]uintptr)(unsafe.Pointer(&numBytes))
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
oWords[0],
oWords[1],
nWords[0],
nWords[1],
uintptr(lockType),
)
hr = wingoes.HRESULT(rc)
} else {
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(offset),
uintptr(numBytes),
uintptr(lockType),
)
hr = wingoes.HRESULT(rc)
}
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return e
}
return nil
}
func (abi *IStreamABI) Stat(flags STATFLAG) (*STATSTG, error) {
result := new(STATSTG)
method := unsafe.Slice(abi.Vtbl, 14)[12]
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(unsafe.Pointer(result)),
uintptr(flags),
)
if e := wingoes.ErrorFromHRESULT(wingoes.HRESULT(rc)); e.Failed() {
return nil, e
}
return result, nil
}
func (abi *IStreamABI) Clone() (result *IUnknownABI, _ error) {
method := unsafe.Slice(abi.Vtbl, 14)[13]
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
uintptr(unsafe.Pointer(&result)),
)
if e := wingoes.ErrorFromHRESULT(wingoes.HRESULT(rc)); e.Failed() {
return nil, e
}
return result, nil
}
func (o Stream) IID() *IID {
return IID_IStream
}
func (o Stream) Make(r ABIReceiver) any {
if r == nil {
return Stream{}
}
runtime.SetFinalizer(r, ReleaseABI)
pp := (**IStreamABI)(unsafe.Pointer(r))
return Stream{GenericObject[IStreamABI]{Pp: pp}}
}
func (o Stream) UnsafeUnwrap() *IStreamABI {
return *(o.Pp)
}
func (o Stream) Read(buf []byte) (int, error) {
p := *(o.Pp)
return p.Read(buf)
}
func (o Stream) Write(buf []byte) (int, error) {
p := *(o.Pp)
return p.Write(buf)
}
func (o Stream) Seek(offset int64, whence int) (n int64, _ error) {
p := *(o.Pp)
return p.Seek(offset, whence)
}
func (o Stream) SetSize(newSize uint64) error {
p := *(o.Pp)
return p.SetSize(newSize)
}
func (o Stream) CopyTo(dest Stream, numBytesToCopy uint64) (bytesRead, bytesWritten uint64, _ error) {
p := *(o.Pp)
return p.CopyTo(dest.UnsafeUnwrap(), numBytesToCopy)
}
func (o Stream) Commit(flags STGC) error {
p := *(o.Pp)
return p.Commit(flags)
}
func (o Stream) Revert() error {
p := *(o.Pp)
return p.Revert()
}
func (o Stream) LockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
p := *(o.Pp)
return p.LockRegion(offset, numBytes, lockType)
}
func (o Stream) UnlockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
p := *(o.Pp)
return p.UnlockRegion(offset, numBytes, lockType)
}
func (o Stream) Stat(flags STATFLAG) (*STATSTG, error) {
p := *(o.Pp)
return p.Stat(flags)
}
func (o Stream) Clone() (result Stream, _ error) {
p := *(o.Pp)
punk, err := p.Clone()
if err != nil {
return result, err
}
return result.Make(&punk).(Stream), nil
}
const hrE_OUTOFMEMORY = wingoes.HRESULT(-((0x8007000E ^ 0xFFFFFFFF) + 1))
// NewMemoryStream creates a new in-memory Stream object initially containing a
// copy of initialBytes. Its seek pointer is guaranteed to reference the
// beginning of the stream.
func NewMemoryStream(initialBytes []byte) (result Stream, _ error) {
return newMemoryStreamInternal(initialBytes, false)
}
func newMemoryStreamInternal(initialBytes []byte, forceLegacy bool) (result Stream, _ error) {
if len(initialBytes) > maxStreamRWLen {
return result, wingoes.ErrorFromHRESULT(hrE_OUTOFMEMORY)
}
// SHCreateMemStream exists on Win7 but is not safe for us to use until Win8.
if forceLegacy || !wingoes.IsWin8OrGreater() {
return newMemoryStreamLegacy(initialBytes)
}
var base *byte
var length uint32
if l := uint32(len(initialBytes)); l > 0 {
base = unsafe.SliceData(initialBytes)
length = l
}
punk := shCreateMemStream(base, length)
if punk == nil {
return result, wingoes.ErrorFromHRESULT(hrE_OUTOFMEMORY)
}
obj := result.Make(&punk).(Stream)
if _, err := obj.Seek(0, io.SeekStart); err != nil {
return result, err
}
return obj, nil
}
func newMemoryStreamLegacy(initialBytes []byte) (result Stream, _ error) {
ppstream := NewABIReceiver()
hr := createStreamOnHGlobal(internal.HGLOBAL(0), true, ppstream)
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return result, e
}
obj := result.Make(ppstream).(Stream)
if err := obj.SetSize(uint64(len(initialBytes))); err != nil {
return result, err
}
if len(initialBytes) == 0 {
return obj, nil
}
_, err := obj.Write(initialBytes)
if err != nil {
return result, err
}
if _, err := obj.Seek(0, io.SeekStart); err != nil {
return result, err
}
return obj, nil
}

13
vendor/github.com/dblohm7/wingoes/com/stream_not386.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
// Copyright (c) 2023 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows && !386
package com
import (
"math"
)
const maxStreamRWLen = math.MaxUint32

View File

@@ -0,0 +1,11 @@
// Copyright (c) 2023 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package com
import (
"math"
)
const maxStreamRWLen = math.MaxInt32

150
vendor/github.com/dblohm7/wingoes/com/types.go generated vendored Normal file
View File

@@ -0,0 +1,150 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package com
import (
"unsafe"
"github.com/dblohm7/wingoes"
"golang.org/x/sys/windows"
)
type coMTAUsageCookie windows.Handle
type coCLSCTX uint32
const (
// We intentionally do not define combinations of these values, as in my experience
// people don't realize what they're doing when they use those.
coCLSCTX_INPROC_SERVER = coCLSCTX(0x1)
coCLSCTX_LOCAL_SERVER = coCLSCTX(0x4)
coCLSCTX_REMOTE_SERVER = coCLSCTX(0x10)
)
type coAPTTYPE int32
const (
coAPTTYPE_CURRENT = coAPTTYPE(-1)
coAPTTYPE_STA = coAPTTYPE(0)
coAPTTYPE_MTA = coAPTTYPE(1)
coAPTTYPE_NA = coAPTTYPE(2)
coAPTTYPE_MAINSTA = coAPTTYPE(3)
)
type coAPTTYPEQUALIFIER int32
const (
coAPTTYPEQUALIFIER_NONE = coAPTTYPEQUALIFIER(0)
coAPTTYPEQUALIFIER_IMPLICIT_MTA = coAPTTYPEQUALIFIER(1)
coAPTTYPEQUALIFIER_NA_ON_MTA = coAPTTYPEQUALIFIER(2)
coAPTTYPEQUALIFIER_NA_ON_STA = coAPTTYPEQUALIFIER(3)
coAPTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA = coAPTTYPEQUALIFIER(4)
coAPTTYPEQUALIFIER_NA_ON_MAINSTA = coAPTTYPEQUALIFIER(5)
coAPTTYPEQUALIFIER_APPLICATION_STA = coAPTTYPEQUALIFIER(6)
)
type aptInfo struct {
apt coAPTTYPE
qualifier coAPTTYPEQUALIFIER
}
type soleAuthenticationInfo struct {
authnSvc uint32
authzSvc uint32
authInfo uintptr
}
type soleAuthenticationList struct {
count uint32
authInfo *soleAuthenticationInfo
}
type soleAuthenticationService struct {
authnSvc uint32
authzSvc uint32
principalName *uint16
hr wingoes.HRESULT
}
type authCapabilities uint32
const (
authCapNone = authCapabilities(0)
authCapMutualAuth = authCapabilities(1)
authCapSecureRefs = authCapabilities(2)
authCapAccessControl = authCapabilities(4)
authCapAppID = authCapabilities(8)
authCapDynamic = authCapabilities(0x10)
authCapStaticCloaking = authCapabilities(0x20)
authCapDynamicCloaking = authCapabilities(0x40)
authCapAnyAuthority = authCapabilities(0x80)
authCapMakeFullsic = authCapabilities(0x100)
authCapRequireFullsic = authCapabilities(0x200)
authCapAutoImpersonate = authCapabilities(0x400)
authCapDefault = authCapabilities(0x800)
authCapDisableAAA = authCapabilities(0x1000)
authCapNoCustomMarshal = authCapabilities(0x2000)
)
type rpcAuthnLevel uint32
const (
rpcAuthnLevelDefault = rpcAuthnLevel(0)
rpcAuthnLevelNone = rpcAuthnLevel(1)
rpcAuthnLevelConnect = rpcAuthnLevel(2)
rpcAuthnLevelCall = rpcAuthnLevel(3)
rpcAuthnLevelPkt = rpcAuthnLevel(4)
rpcAuthnLevelPktIntegrity = rpcAuthnLevel(5)
rpcAuthnLevelPkgPrivacy = rpcAuthnLevel(6)
)
type rpcImpersonationLevel uint32
const (
rpcImpLevelDefault = rpcImpersonationLevel(0)
rpcImpLevelAnonymous = rpcImpersonationLevel(1)
rpcImpLevelIdentify = rpcImpersonationLevel(2)
rpcImpLevelImpersonate = rpcImpersonationLevel(3)
rpcImpLevelDelegate = rpcImpersonationLevel(4)
)
// COMAllocatedString encapsulates a UTF-16 string that was allocated by COM
// using its internal heap.
type COMAllocatedString uintptr
// Close frees the memory held by the string.
func (s *COMAllocatedString) Close() error {
windows.CoTaskMemFree(unsafe.Pointer(*s))
*s = 0
return nil
}
func (s *COMAllocatedString) String() string {
return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(*s)))
}
// UTF16 returns a slice containing a copy of the UTF-16 string, including a
// NUL terminator.
func (s *COMAllocatedString) UTF16() []uint16 {
p := (*uint16)(unsafe.Pointer(*s))
if p == nil {
return nil
}
n := 0
for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ {
ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p))
}
// Make a copy, including the NUL terminator.
return append([]uint16{}, unsafe.Slice(p, n+1)...)
}
// UTF16Ptr returns a pointer to a NUL-terminated copy of the UTF-16 string.
func (s *COMAllocatedString) UTF16Ptr() *uint16 {
return unsafe.SliceData(s.UTF16())
}

44
vendor/github.com/dblohm7/wingoes/com/unknown.go generated vendored Normal file
View File

@@ -0,0 +1,44 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package com
import (
"runtime"
)
var (
IID_IUnknown = &IID{0x00000000, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}
)
// ObjectBase is a garbage-collected instance of any COM object's base interface.
type ObjectBase struct {
GenericObject[IUnknownABI]
}
// IID always returns IID_IUnknown.
func (o ObjectBase) IID() *IID {
return IID_IUnknown
}
// Make produces a new instance of ObjectBase that wraps r. Its return type is
// always ObjectBase.
func (o ObjectBase) Make(r ABIReceiver) any {
if r == nil {
return ObjectBase{}
}
runtime.SetFinalizer(r, ReleaseABI)
pp := (**IUnknownABI)(r)
return ObjectBase{GenericObject[IUnknownABI]{Pp: pp}}
}
// UnsafeUnwrap returns the underlying IUnknownABI of the object. As the name
// implies, this is unsafe -- you had better know what you are doing!
func (o ObjectBase) UnsafeUnwrap() *IUnknownABI {
return *(o.Pp)
}

View File

@@ -0,0 +1,106 @@
// Code generated by 'go generate'; DO NOT EDIT.
package com
import (
"syscall"
"unsafe"
"github.com/dblohm7/wingoes"
"github.com/dblohm7/wingoes/internal"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modole32 = windows.NewLazySystemDLL("ole32.dll")
modoleaut32 = windows.NewLazySystemDLL("oleaut32.dll")
modshlwapi = windows.NewLazySystemDLL("shlwapi.dll")
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
procCoGetApartmentType = modole32.NewProc("CoGetApartmentType")
procCoIncrementMTAUsage = modole32.NewProc("CoIncrementMTAUsage")
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
procCoInitializeSecurity = modole32.NewProc("CoInitializeSecurity")
procCreateStreamOnHGlobal = modole32.NewProc("CreateStreamOnHGlobal")
procSetOaNoCache = modoleaut32.NewProc("SetOaNoCache")
procSHCreateMemStream = modshlwapi.NewProc("SHCreateMemStream")
)
func coCreateInstance(clsid *CLSID, unkOuter *IUnknownABI, clsctx coCLSCTX, iid *IID, ppv **IUnknownABI) (hr wingoes.HRESULT) {
r0, _, _ := syscall.Syscall6(procCoCreateInstance.Addr(), 5, uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(unkOuter)), uintptr(clsctx), uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(ppv)), 0)
hr = wingoes.HRESULT(r0)
return
}
func coGetApartmentType(aptType *coAPTTYPE, qual *coAPTTYPEQUALIFIER) (hr wingoes.HRESULT) {
r0, _, _ := syscall.Syscall(procCoGetApartmentType.Addr(), 2, uintptr(unsafe.Pointer(aptType)), uintptr(unsafe.Pointer(qual)), 0)
hr = wingoes.HRESULT(r0)
return
}
func coIncrementMTAUsage(cookie *coMTAUsageCookie) (hr wingoes.HRESULT) {
r0, _, _ := syscall.Syscall(procCoIncrementMTAUsage.Addr(), 1, uintptr(unsafe.Pointer(cookie)), 0, 0)
hr = wingoes.HRESULT(r0)
return
}
func coInitializeEx(reserved uintptr, flags uint32) (hr wingoes.HRESULT) {
r0, _, _ := syscall.Syscall(procCoInitializeEx.Addr(), 2, uintptr(reserved), uintptr(flags), 0)
hr = wingoes.HRESULT(r0)
return
}
func coInitializeSecurity(sd *windows.SECURITY_DESCRIPTOR, authSvcLen int32, authSvc *soleAuthenticationService, reserved1 uintptr, authnLevel rpcAuthnLevel, impLevel rpcImpersonationLevel, authList *soleAuthenticationList, capabilities authCapabilities, reserved2 uintptr) (hr wingoes.HRESULT) {
r0, _, _ := syscall.Syscall9(procCoInitializeSecurity.Addr(), 9, uintptr(unsafe.Pointer(sd)), uintptr(authSvcLen), uintptr(unsafe.Pointer(authSvc)), uintptr(reserved1), uintptr(authnLevel), uintptr(impLevel), uintptr(unsafe.Pointer(authList)), uintptr(capabilities), uintptr(reserved2))
hr = wingoes.HRESULT(r0)
return
}
func createStreamOnHGlobal(hglobal internal.HGLOBAL, deleteOnRelease bool, stream **IUnknownABI) (hr wingoes.HRESULT) {
var _p0 uint32
if deleteOnRelease {
_p0 = 1
}
r0, _, _ := syscall.Syscall(procCreateStreamOnHGlobal.Addr(), 3, uintptr(hglobal), uintptr(_p0), uintptr(unsafe.Pointer(stream)))
hr = wingoes.HRESULT(r0)
return
}
func setOaNoCache() {
syscall.Syscall(procSetOaNoCache.Addr(), 0, 0, 0, 0)
return
}
func shCreateMemStream(pInit *byte, cbInit uint32) (stream *IUnknownABI) {
r0, _, _ := syscall.Syscall(procSHCreateMemStream.Addr(), 2, uintptr(unsafe.Pointer(pInit)), uintptr(cbInit), 0)
stream = (*IUnknownABI)(unsafe.Pointer(r0))
return
}

329
vendor/github.com/dblohm7/wingoes/error.go generated vendored Normal file
View File

@@ -0,0 +1,329 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package wingoes
import (
"fmt"
"golang.org/x/sys/windows"
)
// Error represents various error codes that may be encountered when coding
// against Windows APIs, including HRESULTs, windows.NTStatus, and windows.Errno.
type Error HRESULT
// Errors are HRESULTs under the hood because the HRESULT encoding allows for
// all the other common types of Windows errors to be encoded within them.
const (
hrS_OK = HRESULT(0)
hrE_ABORT = HRESULT(-((0x80004004 ^ 0xFFFFFFFF) + 1))
hrE_FAIL = HRESULT(-((0x80004005 ^ 0xFFFFFFFF) + 1))
hrE_NOINTERFACE = HRESULT(-((0x80004002 ^ 0xFFFFFFFF) + 1))
hrE_NOTIMPL = HRESULT(-((0x80004001 ^ 0xFFFFFFFF) + 1))
hrE_POINTER = HRESULT(-((0x80004003 ^ 0xFFFFFFFF) + 1))
hrE_UNEXPECTED = HRESULT(-((0x8000FFFF ^ 0xFFFFFFFF) + 1))
hrTYPE_E_WRONGTYPEKIND = HRESULT(-((0x8002802A ^ 0xFFFFFFFF) + 1))
)
// S_FALSE is a peculiar HRESULT value which means that the call executed
// successfully, but returned false as its result.
const S_FALSE = HRESULT(1)
var (
// genericError encodes an Error whose message string is very generic.
genericError = Error(hresultFromFacilityAndCode(hrFail, facilityWin32, hrCode(windows.ERROR_UNIDENTIFIED_ERROR)))
)
// Common HRESULT codes that don't use Win32 facilities, but have meanings that
// we can manually translate to Win32 error codes.
var commonHRESULTToErrno = map[HRESULT]windows.Errno{
hrE_ABORT: windows.ERROR_REQUEST_ABORTED,
hrE_FAIL: windows.ERROR_UNIDENTIFIED_ERROR,
hrE_NOINTERFACE: windows.ERROR_NOINTERFACE,
hrE_NOTIMPL: windows.ERROR_CALL_NOT_IMPLEMENTED,
hrE_UNEXPECTED: windows.ERROR_INTERNAL_ERROR,
}
type hrCode uint16
type hrFacility uint16
type failBit bool
const (
hrFlagBitsMask = 0xF8000000
hrFacilityMax = 0x00001FFF
hrFacilityMask = hrFacilityMax << 16
hrCodeMax = 0x0000FFFF
hrCodeMask = hrCodeMax
hrFailBit = 0x80000000
hrCustomerBit = 0x20000000 // Also defined as syscall.APPLICATION_ERROR
hrFacilityNTBit = 0x10000000
)
const (
facilityWin32 = hrFacility(7)
)
// Succeeded returns true when hr is successful, but its actual error code
// may include additional status information.
func (hr HRESULT) Succeeded() bool {
return hr >= 0
}
// Failed returns true when hr contains a failure code.
func (hr HRESULT) Failed() bool {
return hr < 0
}
func (hr HRESULT) String() string {
return fmt.Sprintf("0x%08X", uint32(hr))
}
func (hr HRESULT) isNT() bool {
return (hr & (hrCustomerBit | hrFacilityNTBit)) == hrFacilityNTBit
}
func (hr HRESULT) isCustomer() bool {
return (hr & hrCustomerBit) != 0
}
// isNormal returns true when the customer and NT bits are cleared, ie hr's
// encoding contains valid facility and code fields.
func (hr HRESULT) isNormal() bool {
return (hr & (hrCustomerBit | hrFacilityNTBit)) == 0
}
// facility returns the facility bits of hr. Only valid when isNormal is true.
func (hr HRESULT) facility() hrFacility {
return hrFacility((uint32(hr) >> 16) & hrFacilityMax)
}
// facility returns the code bits of hr. Only valid when isNormal is true.
func (hr HRESULT) code() hrCode {
return hrCode(uint32(hr) & hrCodeMask)
}
const (
hrFail = failBit(true)
hrSuccess = failBit(false)
)
func hresultFromFacilityAndCode(isFail failBit, f hrFacility, c hrCode) HRESULT {
var r uint32
if isFail {
r |= hrFailBit
}
r |= (uint32(f) << 16) & hrFacilityMask
r |= uint32(c) & hrCodeMask
return HRESULT(r)
}
// ErrorFromErrno creates an Error from e.
func ErrorFromErrno(e windows.Errno) Error {
if e == windows.ERROR_SUCCESS {
return Error(hrS_OK)
}
if ue := uint32(e); (ue & hrFlagBitsMask) == hrCustomerBit {
// syscall.APPLICATION_ERROR == hrCustomerBit, so the only other thing
// we need to do to transform this into an HRESULT is add the fail flag
return Error(HRESULT(ue | hrFailBit))
}
if uint32(e) > hrCodeMax {
// Can't be encoded in HRESULT, return generic error instead
return genericError
}
return Error(hresultFromFacilityAndCode(hrFail, facilityWin32, hrCode(e)))
}
// ErrorFromNTStatus creates an Error from s.
func ErrorFromNTStatus(s windows.NTStatus) Error {
if s == windows.STATUS_SUCCESS {
return Error(hrS_OK)
}
return Error(HRESULT(s) | hrFacilityNTBit)
}
// ErrorFromHRESULT creates an Error from hr.
func ErrorFromHRESULT(hr HRESULT) Error {
return Error(hr)
}
// NewError converts e into an Error if e's type is supported. It returns
// both the Error and a bool indicating whether the conversion was successful.
func NewError(e any) (Error, bool) {
switch v := e.(type) {
case Error:
return v, true
case windows.NTStatus:
return ErrorFromNTStatus(v), true
case windows.Errno:
return ErrorFromErrno(v), true
case HRESULT:
return ErrorFromHRESULT(v), true
default:
return ErrorFromHRESULT(hrTYPE_E_WRONGTYPEKIND), false
}
}
// IsOK returns true when the Error is unconditionally successful.
func (e Error) IsOK() bool {
return HRESULT(e) == hrS_OK
}
// Succeeded returns true when the Error is successful, but its error code
// may include additional status information.
func (e Error) Succeeded() bool {
return HRESULT(e).Succeeded()
}
// Failed returns true when the Error contains a failure code.
func (e Error) Failed() bool {
return HRESULT(e).Failed()
}
// AsHRESULT converts the Error to a HRESULT.
func (e Error) AsHRESULT() HRESULT {
return HRESULT(e)
}
type errnoFailHandler func(hr HRESULT) windows.Errno
func (e Error) toErrno(f errnoFailHandler) windows.Errno {
hr := HRESULT(e)
if hr == hrS_OK {
return windows.ERROR_SUCCESS
}
if hr.isCustomer() {
return windows.Errno(uint32(e) ^ hrFailBit)
}
if hr.isNT() {
return e.AsNTStatus().Errno()
}
if hr.facility() == facilityWin32 {
return windows.Errno(hr.code())
}
if errno, ok := commonHRESULTToErrno[hr]; ok {
return errno
}
return f(hr)
}
// AsError converts the Error to a windows.Errno, but panics if not possible.
func (e Error) AsErrno() windows.Errno {
handler := func(hr HRESULT) windows.Errno {
panic(fmt.Sprintf("wingoes.Error: Called AsErrno on a non-convertable HRESULT 0x%08X", uint32(hr)))
return windows.ERROR_UNIDENTIFIED_ERROR
}
return e.toErrno(handler)
}
type ntStatusFailHandler func(hr HRESULT) windows.NTStatus
func (e Error) toNTStatus(f ntStatusFailHandler) windows.NTStatus {
hr := HRESULT(e)
if hr == hrS_OK {
return windows.STATUS_SUCCESS
}
if hr.isNT() {
return windows.NTStatus(hr ^ hrFacilityNTBit)
}
return f(hr)
}
// AsNTStatus converts the Error to a windows.NTStatus, but panics if not possible.
func (e Error) AsNTStatus() windows.NTStatus {
handler := func(hr HRESULT) windows.NTStatus {
panic(fmt.Sprintf("windows.Error: Called AsNTStatus on a non-NTSTATUS HRESULT 0x%08X", uint32(hr)))
return windows.STATUS_UNSUCCESSFUL
}
return e.toNTStatus(handler)
}
// TryAsErrno converts the Error to a windows.Errno, or returns defval if
// such a conversion is not possible.
func (e Error) TryAsErrno(defval windows.Errno) windows.Errno {
handler := func(hr HRESULT) windows.Errno {
return defval
}
return e.toErrno(handler)
}
// TryAsNTStatus converts the Error to a windows.NTStatus, or returns defval if
// such a conversion is not possible.
func (e Error) TryAsNTStatus(defval windows.NTStatus) windows.NTStatus {
handler := func(hr HRESULT) windows.NTStatus {
return defval
}
return e.toNTStatus(handler)
}
// IsAvailableAsHRESULT returns true if e may be converted to an HRESULT.
func (e Error) IsAvailableAsHRESULT() bool {
return true
}
// IsAvailableAsErrno returns true if e may be converted to a windows.Errno.
func (e Error) IsAvailableAsErrno() bool {
hr := HRESULT(e)
if hr.isCustomer() || e.IsAvailableAsNTStatus() || (hr.facility() == facilityWin32) {
return true
}
_, convertable := commonHRESULTToErrno[hr]
return convertable
}
// IsAvailableAsNTStatus returns true if e may be converted to a windows.NTStatus.
func (e Error) IsAvailableAsNTStatus() bool {
return HRESULT(e) == hrS_OK || HRESULT(e).isNT()
}
// Error produces a human-readable message describing Error e.
func (e Error) Error() string {
if HRESULT(e).isCustomer() {
return windows.Errno(uint32(e) ^ hrFailBit).Error()
}
buf := make([]uint16, 300)
const flags = windows.FORMAT_MESSAGE_FROM_SYSTEM | windows.FORMAT_MESSAGE_IGNORE_INSERTS
lenExclNul, err := windows.FormatMessage(flags, 0, uint32(e), 0, buf, nil)
if err != nil {
return fmt.Sprintf("wingoes.Error 0x%08X", uint32(e))
}
for ; lenExclNul > 0 && (buf[lenExclNul-1] == '\n' || buf[lenExclNul-1] == '\r'); lenExclNul-- {
}
return windows.UTF16ToString(buf[:lenExclNul])
}
// Unwrap permits extraction of underlying windows.NTStatus or windows.Errno
// errors that are encoded within e.
func (e Error) Unwrap() error {
// Order is important! We need earlier checks to exclude certain things that
// would otherwise be (in this case) false positives in later checks!
switch {
case e.IsOK():
return nil
case e.IsAvailableAsNTStatus():
return e.AsNTStatus()
case e.IsAvailableAsErrno():
return e.AsErrno()
default:
return nil
}
}

15
vendor/github.com/dblohm7/wingoes/guid.go generated vendored Normal file
View File

@@ -0,0 +1,15 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package wingoes
import (
"fmt"
)
func guidToString(guid GUID) string {
return fmt.Sprintf("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1],
guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7])
}

17
vendor/github.com/dblohm7/wingoes/guid_notwindows.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !windows
package wingoes
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
func (guid GUID) String() string {
return guidToString(guid)
}

24
vendor/github.com/dblohm7/wingoes/guid_windows.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wingoes
import (
"fmt"
"golang.org/x/sys/windows"
)
type GUID = windows.GUID
// MustGetGUID parses s, a string containing a GUID and returns a pointer to the
// parsed GUID. s must be specified in the format "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
// If there is an error parsing s, MustGetGUID panics.
func MustGetGUID(s string) *windows.GUID {
guid, err := windows.GUIDFromString(s)
if err != nil {
panic(fmt.Sprintf("wingoes.MustGetGUID(%q) error %v", s, err))
}
return &guid
}

12
vendor/github.com/dblohm7/wingoes/hresult.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Note this file is explicitly available on non-Windows platforms, in order to
// aid `go generate` tooling on those platforms. It should not take a dependency
// on x/sys/windows.
package wingoes
// HRESULT is equivalent to the HRESULT type in the Win32 SDK for C/C++.
type HRESULT int32

13
vendor/github.com/dblohm7/wingoes/internal/types.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
// Copyright (c) 2023 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package internal
import (
"golang.org/x/sys/windows"
)
type HGLOBAL windows.Handle

199
vendor/github.com/dblohm7/wingoes/osversion.go generated vendored Normal file
View File

@@ -0,0 +1,199 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package wingoes
import (
"fmt"
"sync"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
var (
verOnce sync.Once
verInfo osVersionInfo // must access via getVersionInfo()
)
// osVersionInfo is more compact than windows.OsVersionInfoEx, which contains
// extraneous information.
type osVersionInfo struct {
major uint32
minor uint32
build uint32
servicePack uint16
str string
isDC bool
isServer bool
}
const (
_VER_NT_WORKSTATION = 1
_VER_NT_DOMAIN_CONTROLLER = 2
_VER_NT_SERVER = 3
)
func getVersionInfo() *osVersionInfo {
verOnce.Do(func() {
osv := windows.RtlGetVersion()
verInfo = osVersionInfo{
major: osv.MajorVersion,
minor: osv.MinorVersion,
build: osv.BuildNumber,
servicePack: osv.ServicePackMajor,
str: fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.BuildNumber),
isDC: osv.ProductType == _VER_NT_DOMAIN_CONTROLLER,
// Domain Controllers are also implicitly servers.
isServer: osv.ProductType == _VER_NT_DOMAIN_CONTROLLER || osv.ProductType == _VER_NT_SERVER,
}
// UBR is only available on Windows 10 and 11 (MajorVersion == 10).
if osv.MajorVersion == 10 {
if ubr, err := getUBR(); err == nil {
verInfo.str = fmt.Sprintf("%s.%d", verInfo.str, ubr)
}
}
})
return &verInfo
}
// getUBR returns the "update build revision," ie. the fourth component of the
// version string found on Windows 10 and Windows 11 systems.
func getUBR() (uint32, error) {
key, err := registry.OpenKey(registry.LOCAL_MACHINE,
`SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE|registry.WOW64_64KEY)
if err != nil {
return 0, err
}
defer key.Close()
val, valType, err := key.GetIntegerValue("UBR")
if err != nil {
return 0, err
}
if valType != registry.DWORD {
return 0, registry.ErrUnexpectedType
}
return uint32(val), nil
}
// GetOSVersionString returns the Windows version of the current machine in
// dotted-decimal form. The version string contains 3 components on Windows 7
// and 8.x, and 4 components on Windows 10 and 11.
func GetOSVersionString() string {
return getVersionInfo().String()
}
// IsWinServer returns true if and only if this computer's version of Windows is
// a server edition.
func IsWinServer() bool {
return getVersionInfo().isServer
}
// IsWinDomainController returs true if this computer's version of Windows is
// configured to act as a domain controller.
func IsWinDomainController() bool {
return getVersionInfo().isDC
}
// IsWin7SP1OrGreater returns true when running on Windows 7 SP1 or newer.
func IsWin7SP1OrGreater() bool {
if IsWin8OrGreater() {
return true
}
vi := getVersionInfo()
return vi.major == 6 && vi.minor == 1 && vi.servicePack > 0
}
// IsWin8OrGreater returns true when running on Windows 8.0 or newer.
func IsWin8OrGreater() bool {
return getVersionInfo().isVersionOrGreater(6, 2, 0)
}
// IsWin8Point1OrGreater returns true when running on Windows 8.1 or newer.
func IsWin8Point1OrGreater() bool {
return getVersionInfo().isVersionOrGreater(6, 3, 0)
}
// IsWin10OrGreater returns true when running on any build of Windows 10 or newer.
func IsWin10OrGreater() bool {
return getVersionInfo().major >= 10
}
// Win10BuildConstant encodes build numbers for the various editions of Windows 10,
// for use with IsWin10BuildOrGreater.
type Win10BuildConstant uint32
const (
Win10BuildRTM = Win10BuildConstant(10240)
Win10Build1511 = Win10BuildConstant(10586)
Win10Build1607 = Win10BuildConstant(14393)
Win10BuildAnniversary = Win10Build1607
Win10Build1703 = Win10BuildConstant(15063)
Win10BuildCreators = Win10Build1703
Win10Build1709 = Win10BuildConstant(16299)
Win10BuildFallCreators = Win10Build1709
Win10Build1803 = Win10BuildConstant(17134)
Win10Build1809 = Win10BuildConstant(17763)
Win10Build1903 = Win10BuildConstant(18362)
Win10Build1909 = Win10BuildConstant(18363)
Win10Build2004 = Win10BuildConstant(19041)
Win10Build20H2 = Win10BuildConstant(19042)
Win10Build21H1 = Win10BuildConstant(19043)
Win10Build21H2 = Win10BuildConstant(19044)
Win10Build22H2 = Win10BuildConstant(19045)
)
// IsWin10BuildOrGreater returns true when running on the specified Windows 10
// build, or newer.
func IsWin10BuildOrGreater(build Win10BuildConstant) bool {
return getVersionInfo().isWin10BuildOrGreater(uint32(build))
}
// Win11BuildConstant encodes build numbers for the various editions of Windows 11,
// for use with IsWin11BuildOrGreater.
type Win11BuildConstant uint32
const (
Win11BuildRTM = Win11BuildConstant(22000)
Win11Build22H2 = Win11BuildConstant(22621)
Win11Build23H2 = Win11BuildConstant(22631)
)
// IsWin11OrGreater returns true when running on any release of Windows 11,
// or newer.
func IsWin11OrGreater() bool {
return IsWin11BuildOrGreater(Win11BuildRTM)
}
// IsWin11BuildOrGreater returns true when running on the specified Windows 11
// build, or newer.
func IsWin11BuildOrGreater(build Win11BuildConstant) bool {
// Under the hood, Windows 11 is just Windows 10 with a sufficiently advanced
// build number.
return getVersionInfo().isWin10BuildOrGreater(uint32(build))
}
func (osv *osVersionInfo) String() string {
return osv.str
}
func (osv *osVersionInfo) isWin10BuildOrGreater(build uint32) bool {
return osv.isVersionOrGreater(10, 0, build)
}
func (osv *osVersionInfo) isVersionOrGreater(major, minor, build uint32) bool {
return isVerGE(osv.major, major, osv.minor, minor, osv.build, build)
}
func isVerGE(lmajor, rmajor, lminor, rminor, lbuild, rbuild uint32) bool {
return lmajor > rmajor ||
lmajor == rmajor &&
(lminor > rminor ||
lminor == rminor && lbuild >= rbuild)
}

262
vendor/github.com/dblohm7/wingoes/pe/oh.go generated vendored Normal file
View File

@@ -0,0 +1,262 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package pe
import (
dpe "debug/pe"
"unsafe"
)
// OptionalHeader provides the fields of a PE/COFF optional header. Since the
// underlying format differs depending on whether the PE binary is 32-bit or
// 64-bit, this type provides a unified interface.
type OptionalHeader interface {
GetMagic() uint16
GetLinkerVersion() (major, minor uint8)
GetSizeOfCode() uint32
GetSizeOfInitializedData() uint32
GetSizeOfUninitializedData() uint32
GetAddressOfEntryPoint() uint32
GetBaseOfCode() uint32
GetImageBase() uint64
GetSectionAlignment() uint32
GetFileAlignment() uint32
GetOperatingSystemVersion() (major, minor uint16)
GetImageVersion() (major, minor uint16)
GetSubsystemVersion() (major, minor uint16)
GetWin32Version() uint32
GetSizeOfImage() uint32
GetSizeOfHeaders() uint32
GetCheckSum() uint32
GetSubsystem() uint16
GetDllCharacteristics() uint16
GetSizeOfStackReserve() uint64
GetSizeOfStackCommit() uint64
GetSizeOfHeapReserve() uint64
GetSizeOfHeapCommit() uint64
GetLoaderFlags() uint32
GetDataDirectory() []DataDirectoryEntry
SizeOf() uint16 // Size of the underlying struct, in bytes
}
type optionalHeader32 dpe.OptionalHeader32
func (oh *optionalHeader32) GetMagic() uint16 {
return oh.Magic
}
func (oh *optionalHeader32) GetLinkerVersion() (major, minor uint8) {
return oh.MajorLinkerVersion, oh.MinorLinkerVersion
}
func (oh *optionalHeader32) GetSizeOfCode() uint32 {
return oh.SizeOfCode
}
func (oh *optionalHeader32) GetSizeOfInitializedData() uint32 {
return oh.SizeOfInitializedData
}
func (oh *optionalHeader32) GetSizeOfUninitializedData() uint32 {
return oh.SizeOfUninitializedData
}
func (oh *optionalHeader32) GetAddressOfEntryPoint() uint32 {
return oh.AddressOfEntryPoint
}
func (oh *optionalHeader32) GetBaseOfCode() uint32 {
return oh.BaseOfCode
}
func (oh *optionalHeader32) GetImageBase() uint64 {
return uint64(oh.ImageBase)
}
func (oh *optionalHeader32) GetSectionAlignment() uint32 {
return oh.SectionAlignment
}
func (oh *optionalHeader32) GetFileAlignment() uint32 {
return oh.FileAlignment
}
func (oh *optionalHeader32) GetOperatingSystemVersion() (major, minor uint16) {
return oh.MajorOperatingSystemVersion, oh.MinorOperatingSystemVersion
}
func (oh *optionalHeader32) GetImageVersion() (major, minor uint16) {
return oh.MajorImageVersion, oh.MinorImageVersion
}
func (oh *optionalHeader32) GetSubsystemVersion() (major, minor uint16) {
return oh.MajorSubsystemVersion, oh.MinorSubsystemVersion
}
func (oh *optionalHeader32) GetWin32Version() uint32 {
return oh.Win32VersionValue
}
func (oh *optionalHeader32) GetSizeOfImage() uint32 {
return oh.SizeOfImage
}
func (oh *optionalHeader32) GetSizeOfHeaders() uint32 {
return oh.SizeOfHeaders
}
func (oh *optionalHeader32) GetCheckSum() uint32 {
return oh.CheckSum
}
func (oh *optionalHeader32) GetSubsystem() uint16 {
return oh.Subsystem
}
func (oh *optionalHeader32) GetDllCharacteristics() uint16 {
return oh.DllCharacteristics
}
func (oh *optionalHeader32) GetSizeOfStackReserve() uint64 {
return uint64(oh.SizeOfStackReserve)
}
func (oh *optionalHeader32) GetSizeOfStackCommit() uint64 {
return uint64(oh.SizeOfStackCommit)
}
func (oh *optionalHeader32) GetSizeOfHeapReserve() uint64 {
return uint64(oh.SizeOfHeapReserve)
}
func (oh *optionalHeader32) GetSizeOfHeapCommit() uint64 {
return uint64(oh.SizeOfHeapCommit)
}
func (oh *optionalHeader32) GetLoaderFlags() uint32 {
return oh.LoaderFlags
}
func (oh *optionalHeader32) GetDataDirectory() []DataDirectoryEntry {
cnt := oh.NumberOfRvaAndSizes
if maxCnt := uint32(len(oh.DataDirectory)); cnt > maxCnt {
cnt = maxCnt
}
return oh.DataDirectory[:cnt]
}
func (oh *optionalHeader32) SizeOf() uint16 {
return uint16(unsafe.Sizeof(*oh))
}
type optionalHeader64 dpe.OptionalHeader64
func (oh *optionalHeader64) GetMagic() uint16 {
return oh.Magic
}
func (oh *optionalHeader64) GetLinkerVersion() (major, minor uint8) {
return oh.MajorLinkerVersion, oh.MinorLinkerVersion
}
func (oh *optionalHeader64) GetSizeOfCode() uint32 {
return oh.SizeOfCode
}
func (oh *optionalHeader64) GetSizeOfInitializedData() uint32 {
return oh.SizeOfInitializedData
}
func (oh *optionalHeader64) GetSizeOfUninitializedData() uint32 {
return oh.SizeOfUninitializedData
}
func (oh *optionalHeader64) GetAddressOfEntryPoint() uint32 {
return oh.AddressOfEntryPoint
}
func (oh *optionalHeader64) GetBaseOfCode() uint32 {
return oh.BaseOfCode
}
func (oh *optionalHeader64) GetImageBase() uint64 {
return oh.ImageBase
}
func (oh *optionalHeader64) GetSectionAlignment() uint32 {
return oh.SectionAlignment
}
func (oh *optionalHeader64) GetFileAlignment() uint32 {
return oh.FileAlignment
}
func (oh *optionalHeader64) GetOperatingSystemVersion() (major, minor uint16) {
return oh.MajorOperatingSystemVersion, oh.MinorOperatingSystemVersion
}
func (oh *optionalHeader64) GetImageVersion() (major, minor uint16) {
return oh.MajorImageVersion, oh.MinorImageVersion
}
func (oh *optionalHeader64) GetSubsystemVersion() (major, minor uint16) {
return oh.MajorSubsystemVersion, oh.MinorSubsystemVersion
}
func (oh *optionalHeader64) GetWin32Version() uint32 {
return oh.Win32VersionValue
}
func (oh *optionalHeader64) GetSizeOfImage() uint32 {
return oh.SizeOfImage
}
func (oh *optionalHeader64) GetSizeOfHeaders() uint32 {
return oh.SizeOfHeaders
}
func (oh *optionalHeader64) GetCheckSum() uint32 {
return oh.CheckSum
}
func (oh *optionalHeader64) GetSubsystem() uint16 {
return oh.Subsystem
}
func (oh *optionalHeader64) GetDllCharacteristics() uint16 {
return oh.DllCharacteristics
}
func (oh *optionalHeader64) GetSizeOfStackReserve() uint64 {
return oh.SizeOfStackReserve
}
func (oh *optionalHeader64) GetSizeOfStackCommit() uint64 {
return oh.SizeOfStackCommit
}
func (oh *optionalHeader64) GetSizeOfHeapReserve() uint64 {
return oh.SizeOfHeapReserve
}
func (oh *optionalHeader64) GetSizeOfHeapCommit() uint64 {
return oh.SizeOfHeapCommit
}
func (oh *optionalHeader64) GetLoaderFlags() uint32 {
return oh.LoaderFlags
}
func (oh *optionalHeader64) GetDataDirectory() []DataDirectoryEntry {
cnt := oh.NumberOfRvaAndSizes
if maxCnt := uint32(len(oh.DataDirectory)); cnt > maxCnt {
cnt = maxCnt
}
return oh.DataDirectory[:cnt]
}
func (oh *optionalHeader64) SizeOf() uint16 {
return uint16(unsafe.Sizeof(*oh))
}

746
vendor/github.com/dblohm7/wingoes/pe/pe.go generated vendored Normal file
View File

@@ -0,0 +1,746 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package pe provides facilities for extracting information from PE binaries.
// It only supports the same CPU architectures as the rest of wingoes.
package pe
import (
"bufio"
"bytes"
dpe "debug/pe"
"encoding/binary"
"errors"
"fmt"
"io"
"math/bits"
"os"
"reflect"
"strings"
"unsafe"
"github.com/dblohm7/wingoes"
"golang.org/x/exp/constraints"
)
// The following constants are from the PE spec
const (
maxNumSections = 96
mzSignature = uint16(0x5A4D) // little-endian
offsetIMAGE_DOS_HEADERe_lfanew = 0x3C
peSignature = uint32(0x00004550) // little-endian
)
var (
// ErrBadLength is returned when the actual length of a data field in the
// binary is shorter than the expected length of that field.
ErrBadLength = errors.New("effective length did not match expected length")
// ErrBadCodeView is returned by (*PEHeaders).ExtractCodeViewInfo if the data
// at the requested address does not appear to contain valid CodeView information.
ErrBadCodeView = errors.New("invalid CodeView debug info")
// ErrIndexOutOfRange is returned by (*PEHeaders).DataDirectoryEntry if the
// specified index is greater than the maximum allowable index.
ErrIndexOutOfRange = errors.New("index out of range")
// ErrInvalidBinary is returned whenever the headers do not parse as expected,
// or reference locations outside the bounds of the PE file or module.
// The headers might be corrupt, malicious, or have been tampered with.
ErrInvalidBinary = errors.New("invalid PE binary")
// ErrBadCodeView is returned by (*PEHeaders).ExtractCodeViewInfo if the data
// at the requested address contains a non-CodeView debug info format.
ErrNotCodeView = errors.New("debug info is not CodeView")
// ErrIndexOutOfRange is returned by (*PEHeaders).DataDirectoryEntry if the
// corresponding entry is not populated in the PE image.
ErrNotPresent = errors.New("not present in this PE image")
// ErrResolvingFileRVA is returned when the result of arithmetic on a relative
// virtual address did not resolve to a valid RVA.
ErrResolvingFileRVA = errors.New("could not resolve file RVA")
// ErrUnavailableInModule is returned when requesting data from the binary
// that is not mapped into memory when loaded. The information must be
// loaded from a file-based PEHeaders.
ErrUnavailableInModule = errors.New("this information is unavailable from loaded modules; the PE file itself must be examined")
// ErrUnsupportedMachine is returned if the binary's CPU architecture is
// unsupported. This package currently implements support for x86, amd64,
// and arm64.
ErrUnsupportedMachine = errors.New("unsupported machine")
)
// FileHeader is the PE/COFF IMAGE_FILE_HEADER structure.
type FileHeader dpe.FileHeader
// SectionHeader is the PE/COFF IMAGE_SECTION_HEADER structure.
type SectionHeader dpe.SectionHeader32
// NameString returns the name of s as a Go string.
func (s *SectionHeader) NameString() string {
// s.Name is UTF-8. When the string's length is < len(s.Name), the remaining
// bytes are padded with zeros.
for i, c := range s.Name {
if c == 0 {
return string(s.Name[:i])
}
}
return string(s.Name[:])
}
type peReader interface {
io.Closer
io.ReaderAt
io.ReadSeeker
Base() uintptr
Limit() uintptr
}
// PEHeaders represents the partially-parsed headers from a PE binary.
type PEHeaders struct {
r peReader
fileHeader *FileHeader
optionalHeader OptionalHeader
sections []SectionHeader
}
// FileHeader returns the FileHeader that was parsed from peh.
func (peh *PEHeaders) FileHeader() *FileHeader {
return peh.fileHeader
}
// FileHeader returns the OptionalHeader that was parsed from peh.
func (peh *PEHeaders) OptionalHeader() OptionalHeader {
return peh.optionalHeader
}
// Sections returns a slice containing all section headers parsed from peh.
func (peh *PEHeaders) Sections() []SectionHeader {
return peh.sections
}
// DataDirectoryEntry is a PE/COFF IMAGE_DATA_DIRECTORY structure.
type DataDirectoryEntry = dpe.DataDirectory
func resolveOptionalHeader(machine uint16, r peReader, offset uint32) (OptionalHeader, error) {
switch machine {
case dpe.IMAGE_FILE_MACHINE_I386:
return readStruct[optionalHeader32](r, offset)
case dpe.IMAGE_FILE_MACHINE_AMD64, dpe.IMAGE_FILE_MACHINE_ARM64:
return readStruct[optionalHeader64](r, offset)
default:
return nil, ErrUnsupportedMachine
}
}
func checkMagic(oh OptionalHeader, machine uint16) bool {
var expectedMagic uint16
switch machine {
case dpe.IMAGE_FILE_MACHINE_I386:
expectedMagic = 0x010B
case dpe.IMAGE_FILE_MACHINE_AMD64, dpe.IMAGE_FILE_MACHINE_ARM64:
expectedMagic = 0x020B
default:
panic("unsupported machine")
}
return oh.GetMagic() == expectedMagic
}
type peBounds struct {
base uintptr
limit uintptr
}
type peFile struct {
*os.File
peBounds
}
func (pef *peFile) Base() uintptr {
return pef.base
}
func (pef *peFile) Limit() uintptr {
if pef.limit == 0 {
if fi, err := pef.Stat(); err == nil {
pef.limit = uintptr(fi.Size())
}
}
return pef.limit
}
type peModule struct {
*bytes.Reader
peBounds
modLock uintptr
}
func (pei *peModule) Base() uintptr {
return pei.base
}
func (pei *peModule) Limit() uintptr {
return pei.limit
}
// NewPEFromFileName opens a PE binary located at filename and parses its PE
// headers. Upon success it returns a non-nil *PEHeaders, otherwise it returns a
// nil *PEHeaders and a non-nil error.
// Call Close() on the returned *PEHeaders when it is no longer needed.
func NewPEFromFileName(filename string) (*PEHeaders, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
return newPEFromFile(f)
}
func newPEFromFile(f *os.File) (*PEHeaders, error) {
// peBounds base is 0, limit is loaded lazily
pef := &peFile{File: f}
peh, err := loadHeaders(pef)
if err != nil {
pef.Close()
return nil, err
}
return peh, nil
}
// Close frees any resources that were opened when peh was created.
func (peh *PEHeaders) Close() error {
return peh.r.Close()
}
type rvaType interface {
~int8 | ~int16 | ~int32 | ~uint8 | ~uint16 | ~uint32
}
// addOffset ensures that, if off is negative, it does not underflow base.
func addOffset[O rvaType](base uintptr, off O) uintptr {
if off >= 0 {
return base + uintptr(off)
}
negation := uintptr(-off)
if negation >= base {
return 0
}
return base - negation
}
func binaryRead(r io.Reader, data any) (err error) {
// Supported Windows archs are now always LittleEndian
err = binary.Read(r, binary.LittleEndian, data)
if err == io.ErrUnexpectedEOF {
err = ErrBadLength
}
return err
}
// readStruct reads a T from offset rva. If r is a *peModule, the returned *T
// points to the data in-place.
// Note that currently this function will fail if rva references memory beyond
// the bounds of the binary; in the case of modules, this may need to be relaxed
// in some cases due to tampering by third-party crapware.
func readStruct[T any, R rvaType](r peReader, rva R) (*T, error) {
switch v := r.(type) {
case *peFile:
if _, err := r.Seek(int64(rva), io.SeekStart); err != nil {
return nil, err
}
result := new(T)
if err := binaryRead(r, result); err != nil {
return nil, err
}
return result, nil
case *peModule:
addr := addOffset(r.Base(), rva)
szT := unsafe.Sizeof(*((*T)(nil)))
if addr+szT >= v.Limit() {
return nil, ErrInvalidBinary
}
return (*T)(unsafe.Pointer(addr)), nil
default:
return nil, os.ErrInvalid
}
}
// readStructArray reads a []T with length count from offset rva. If r is a
// *peModule, the returned []T references the data in-place.
// Note that currently this function will fail if rva references memory beyond
// the bounds of the binary; in the case of modules, this may need to be relaxed
// in some cases due to tampering by third-party crapware.
func readStructArray[T any, R rvaType](r peReader, rva R, count int) ([]T, error) {
switch v := r.(type) {
case *peFile:
if _, err := r.Seek(int64(rva), io.SeekStart); err != nil {
return nil, err
}
result := make([]T, count)
if err := binaryRead(r, result); err != nil {
return nil, err
}
return result, nil
case *peModule:
addr := addOffset(r.Base(), rva)
szT := reflect.ArrayOf(count, reflect.TypeOf((*T)(nil)).Elem()).Size()
if addr+szT >= v.Limit() {
return nil, ErrInvalidBinary
}
return unsafe.Slice((*T)(unsafe.Pointer(addr)), count), nil
default:
return nil, os.ErrInvalid
}
}
func loadHeaders(r peReader) (*PEHeaders, error) {
// Check the signature of the DOS stub header
if _, err := r.Seek(0, io.SeekStart); err != nil {
return nil, err
}
var mz uint16
if err := binaryRead(r, &mz); err != nil {
if err == ErrBadLength {
err = ErrInvalidBinary
}
return nil, err
}
if mz != mzSignature {
return nil, ErrInvalidBinary
}
// Seek to the offset of the value that points to the beginning of the PE headers
if _, err := r.Seek(offsetIMAGE_DOS_HEADERe_lfanew, io.SeekStart); err != nil {
return nil, ErrInvalidBinary
}
// Load the offset to the beginning of the PE headers
var e_lfanew int32
if err := binaryRead(r, &e_lfanew); err != nil {
if err == ErrBadLength {
err = ErrInvalidBinary
}
return nil, err
}
if e_lfanew <= 0 {
return nil, ErrInvalidBinary
}
if addOffset(r.Base(), e_lfanew) >= r.Limit() {
return nil, ErrInvalidBinary
}
// Check the PE signature
if _, err := r.Seek(int64(e_lfanew), io.SeekStart); err != nil {
return nil, ErrInvalidBinary
}
var pe uint32
if err := binaryRead(r, &pe); err != nil {
if err == ErrBadLength {
err = ErrInvalidBinary
}
return nil, err
}
if pe != peSignature {
return nil, ErrInvalidBinary
}
// Read the file header
fileHeaderOffset := uint32(e_lfanew) + uint32(unsafe.Sizeof(pe))
if r.Base()+uintptr(fileHeaderOffset) >= r.Limit() {
return nil, ErrInvalidBinary
}
fileHeader, err := readStruct[FileHeader](r, fileHeaderOffset)
if err != nil {
return nil, err
}
machine := fileHeader.Machine
if !checkMachine(r, machine) {
return nil, ErrUnsupportedMachine
}
// Read the optional header
optionalHeaderOffset := uint32(fileHeaderOffset) + uint32(unsafe.Sizeof(*fileHeader))
if r.Base()+uintptr(optionalHeaderOffset) >= r.Limit() {
return nil, ErrInvalidBinary
}
optionalHeader, err := resolveOptionalHeader(machine, r, optionalHeaderOffset)
if err != nil {
return nil, err
}
if !checkMagic(optionalHeader, machine) {
return nil, ErrInvalidBinary
}
if fileHeader.SizeOfOptionalHeader < optionalHeader.SizeOf() {
return nil, ErrInvalidBinary
}
// Coarse-grained check that header sizes make sense
totalEssentialHeaderLen := uint32(offsetIMAGE_DOS_HEADERe_lfanew) +
uint32(unsafe.Sizeof(e_lfanew)) +
uint32(unsafe.Sizeof(*fileHeader)) +
uint32(fileHeader.SizeOfOptionalHeader)
if optionalHeader.GetSizeOfImage() < totalEssentialHeaderLen {
return nil, ErrInvalidBinary
}
numSections := fileHeader.NumberOfSections
if numSections > maxNumSections {
// More than 96 sections?! Really?!
return nil, ErrInvalidBinary
}
// Read in the section table
sectionTableOffset := optionalHeaderOffset + uint32(fileHeader.SizeOfOptionalHeader)
if r.Base()+uintptr(sectionTableOffset) >= r.Limit() {
return nil, ErrInvalidBinary
}
sections, err := readStructArray[SectionHeader](r, sectionTableOffset, int(numSections))
if err != nil {
return nil, err
}
return &PEHeaders{r: r, fileHeader: fileHeader, optionalHeader: optionalHeader, sections: sections}, nil
}
type rva32 interface {
~int32 | ~uint32
}
// resolveRVA resolves rva, or returns 0 if unavailable.
func resolveRVA[R rva32](nfo *PEHeaders, rva R) R {
if _, ok := nfo.r.(*peFile); !ok {
// Just the identity function in this case.
return rva
}
if rva <= 0 {
return 0
}
// We walk the section table, locating the section that would contain rva if
// we were mapped into memory. We then calculate the offset of rva from the
// starting virtual address of the section, and then add that offset to the
// section's starting file pointer.
urva := uint32(rva)
for _, s := range nfo.sections {
if urva < s.VirtualAddress {
continue
}
if urva >= (s.VirtualAddress + s.VirtualSize) {
continue
}
voff := urva - s.VirtualAddress
foff := s.PointerToRawData + voff
if foff >= s.PointerToRawData+s.SizeOfRawData {
return 0
}
return R(foff)
}
return 0
}
// DataDirectoryIndex is an enumeration specifying a particular entry in the
// data directory.
type DataDirectoryIndex int
const (
IMAGE_DIRECTORY_ENTRY_EXPORT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_EXPORT)
IMAGE_DIRECTORY_ENTRY_IMPORT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_IMPORT)
IMAGE_DIRECTORY_ENTRY_RESOURCE = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_RESOURCE)
IMAGE_DIRECTORY_ENTRY_EXCEPTION = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_EXCEPTION)
IMAGE_DIRECTORY_ENTRY_SECURITY = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_SECURITY)
IMAGE_DIRECTORY_ENTRY_BASERELOC = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_BASERELOC)
IMAGE_DIRECTORY_ENTRY_DEBUG = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_DEBUG)
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_ARCHITECTURE)
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_GLOBALPTR)
IMAGE_DIRECTORY_ENTRY_TLS = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_TLS)
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG)
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT)
IMAGE_DIRECTORY_ENTRY_IAT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_IAT)
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT)
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
)
// DataDirectoryEntry returns information from nfo's data directory at index idx.
// The type of the return value depends on the value of idx. Most values for idx
// currently return the DataDirectoryEntry itself, however it will return more
// sophisticated information for the following values of idx:
//
// * IMAGE_DIRECTORY_ENTRY_SECURITY returns []AuthenticodeCert
// * IMAGE_DIRECTORY_ENTRY_DEBUG returns []IMAGE_DEBUG_DIRECTORY
//
// Note that other idx values _will_ be modified in the future to support more
// sophisticated return values, so be careful to structure your type assertions
// accordingly.
func (nfo *PEHeaders) DataDirectoryEntry(idx DataDirectoryIndex) (any, error) {
dd := nfo.optionalHeader.GetDataDirectory()
if int(idx) >= len(dd) {
return nil, ErrIndexOutOfRange
}
dde := dd[idx]
if dde.VirtualAddress == 0 || dde.Size == 0 {
return nil, ErrNotPresent
}
switch idx {
/* TODO(aaron): (don't forget to sync tests!)
case IMAGE_DIRECTORY_ENTRY_EXPORT:
case IMAGE_DIRECTORY_ENTRY_IMPORT:
case IMAGE_DIRECTORY_ENTRY_RESOURCE:
*/
case IMAGE_DIRECTORY_ENTRY_SECURITY:
return nfo.extractAuthenticode(dde)
case IMAGE_DIRECTORY_ENTRY_DEBUG:
return nfo.extractDebugInfo(dde)
// case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:
default:
return dde, nil
}
}
// WIN_CERT_REVISION is an enumeration from the Windows SDK.
type WIN_CERT_REVISION uint16
const (
WIN_CERT_REVISION_1_0 WIN_CERT_REVISION = 0x0100
WIN_CERT_REVISION_2_0 WIN_CERT_REVISION = 0x0200
)
// WIN_CERT_TYPE is an enumeration from the Windows SDK.
type WIN_CERT_TYPE uint16
const (
WIN_CERT_TYPE_X509 WIN_CERT_TYPE = 0x0001
WIN_CERT_TYPE_PKCS_SIGNED_DATA WIN_CERT_TYPE = 0x0002
WIN_CERT_TYPE_TS_STACK_SIGNED WIN_CERT_TYPE = 0x0004
)
type _WIN_CERTIFICATE_HEADER struct {
Length uint32
Revision WIN_CERT_REVISION
CertificateType WIN_CERT_TYPE
}
// AuthenticodeCert represents an authenticode signature that has been extracted
// from a signed PE binary but not fully parsed.
type AuthenticodeCert struct {
header _WIN_CERTIFICATE_HEADER
data []byte
}
// Revision returns the revision of ac.
func (ac *AuthenticodeCert) Revision() WIN_CERT_REVISION {
return ac.header.Revision
}
// Type returns the type of ac.
func (ac *AuthenticodeCert) Type() WIN_CERT_TYPE {
return ac.header.CertificateType
}
// Data returns the raw bytes of ac's cert.
func (ac *AuthenticodeCert) Data() []byte {
return ac.data
}
func alignUp[V constraints.Integer](v V, powerOfTwo uint8) V {
if bits.OnesCount8(powerOfTwo) != 1 {
panic("invalid powerOfTwo argument to alignUp")
}
return v + ((-v) & (V(powerOfTwo) - 1))
}
// IMAGE_DEBUG_TYPE is an enumeration for indicating the type of debug
// information referenced by a particular [IMAGE_DEBUG_DIRECTORY].
type IMAGE_DEBUG_TYPE uint32
const (
IMAGE_DEBUG_TYPE_UNKNOWN IMAGE_DEBUG_TYPE = 0
IMAGE_DEBUG_TYPE_COFF IMAGE_DEBUG_TYPE = 1
IMAGE_DEBUG_TYPE_CODEVIEW IMAGE_DEBUG_TYPE = 2
IMAGE_DEBUG_TYPE_FPO IMAGE_DEBUG_TYPE = 3
IMAGE_DEBUG_TYPE_MISC IMAGE_DEBUG_TYPE = 4
IMAGE_DEBUG_TYPE_EXCEPTION IMAGE_DEBUG_TYPE = 5
IMAGE_DEBUG_TYPE_FIXUP IMAGE_DEBUG_TYPE = 6
IMAGE_DEBUG_TYPE_OMAP_TO_SRC IMAGE_DEBUG_TYPE = 7
IMAGE_DEBUG_TYPE_OMAP_FROM_SRC IMAGE_DEBUG_TYPE = 8
IMAGE_DEBUG_TYPE_BORLAND IMAGE_DEBUG_TYPE = 9
IMAGE_DEBUG_TYPE_RESERVED10 IMAGE_DEBUG_TYPE = 10
IMAGE_DEBUG_TYPE_BBT IMAGE_DEBUG_TYPE = IMAGE_DEBUG_TYPE_RESERVED10
IMAGE_DEBUG_TYPE_CLSID IMAGE_DEBUG_TYPE = 11
IMAGE_DEBUG_TYPE_VC_FEATURE IMAGE_DEBUG_TYPE = 12
IMAGE_DEBUG_TYPE_POGO IMAGE_DEBUG_TYPE = 13
IMAGE_DEBUG_TYPE_ILTCG IMAGE_DEBUG_TYPE = 14
IMAGE_DEBUG_TYPE_MPX IMAGE_DEBUG_TYPE = 15
IMAGE_DEBUG_TYPE_REPRO IMAGE_DEBUG_TYPE = 16
IMAGE_DEBUG_TYPE_SPGO IMAGE_DEBUG_TYPE = 18
IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS IMAGE_DEBUG_TYPE = 20
)
// IMAGE_DEBUG_DIRECTORY describes debug information embedded in the binary.
type IMAGE_DEBUG_DIRECTORY struct {
Characteristics uint32
TimeDateStamp uint32
MajorVersion uint16
MinorVersion uint16
Type IMAGE_DEBUG_TYPE
SizeOfData uint32
AddressOfRawData uint32
PointerToRawData uint32
}
func (nfo *PEHeaders) extractDebugInfo(dde DataDirectoryEntry) (any, error) {
rva := resolveRVA(nfo, dde.VirtualAddress)
if rva == 0 {
return nil, ErrResolvingFileRVA
}
count := dde.Size / uint32(unsafe.Sizeof(IMAGE_DEBUG_DIRECTORY{}))
return readStructArray[IMAGE_DEBUG_DIRECTORY](nfo.r, rva, int(count))
}
// IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED contains CodeView debug information
// embedded in the PE file. Note that this structure's ABI does not match its C
// counterpart because the former uses a Go string and the latter is packed and
// also includes a signature field.
type IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED struct {
GUID wingoes.GUID
Age uint32
PDBPath string
}
// String returns the data from u formatted in the same way that Microsoft
// debugging tools and symbol servers use to identify PDB files corresponding
// to a specific binary.
func (u *IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED) String() string {
var b strings.Builder
fmt.Fprintf(&b, "%08X%04X%04X", u.GUID.Data1, u.GUID.Data2, u.GUID.Data3)
for _, v := range u.GUID.Data4 {
fmt.Fprintf(&b, "%02X", v)
}
fmt.Fprintf(&b, "%X", u.Age)
return b.String()
}
const codeViewSignature = 0x53445352
func (u *IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED) unpack(r *bufio.Reader) error {
var signature uint32
if err := binaryRead(r, &signature); err != nil {
return err
}
if signature != codeViewSignature {
return ErrBadCodeView
}
if err := binaryRead(r, &u.GUID); err != nil {
return err
}
if err := binaryRead(r, &u.Age); err != nil {
return err
}
var storage [16]byte
pdbBytes := storage[:0]
for b, err := r.ReadByte(); err == nil && b != 0; b, err = r.ReadByte() {
pdbBytes = append(pdbBytes, b)
}
u.PDBPath = string(pdbBytes)
return nil
}
// ExtractCodeViewInfo obtains CodeView debug information from de, assuming that
// de represents CodeView debug info.
func (nfo *PEHeaders) ExtractCodeViewInfo(de IMAGE_DEBUG_DIRECTORY) (*IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED, error) {
if de.Type != IMAGE_DEBUG_TYPE_CODEVIEW {
return nil, ErrNotCodeView
}
var sr *io.SectionReader
switch v := nfo.r.(type) {
case *peFile:
sr = io.NewSectionReader(v, int64(de.PointerToRawData), int64(de.SizeOfData))
case *peModule:
sr = io.NewSectionReader(v, int64(de.AddressOfRawData), int64(de.SizeOfData))
default:
return nil, ErrInvalidBinary
}
cv := new(IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED)
if err := cv.unpack(bufio.NewReader(sr)); err != nil {
return nil, err
}
return cv, nil
}
func readFull(r io.Reader, buf []byte) (n int, err error) {
n, err = io.ReadFull(r, buf)
if err == io.ErrUnexpectedEOF {
err = ErrBadLength
}
return n, err
}
func (nfo *PEHeaders) extractAuthenticode(dde DataDirectoryEntry) (any, error) {
if _, ok := nfo.r.(*peFile); !ok {
// Authenticode; only available in file, not loaded at runtime.
return nil, ErrUnavailableInModule
}
var result []AuthenticodeCert
// The VirtualAddress is a file offset.
sr := io.NewSectionReader(nfo.r, int64(dde.VirtualAddress), int64(dde.Size))
var curOffset int64
szEntry := unsafe.Sizeof(_WIN_CERTIFICATE_HEADER{})
for {
var entry AuthenticodeCert
if err := binaryRead(sr, &entry.header); err != nil {
if err == io.EOF {
break
}
return nil, err
}
curOffset += int64(szEntry)
if uintptr(entry.header.Length) < szEntry {
return nil, ErrInvalidBinary
}
entry.data = make([]byte, uintptr(entry.header.Length)-szEntry)
n, err := readFull(sr, entry.data)
if err != nil {
return nil, err
}
curOffset += int64(n)
result = append(result, entry)
curOffset = alignUp(curOffset, 8)
if _, err := sr.Seek(curOffset, io.SeekStart); err != nil {
if err == io.EOF {
break
}
return nil, err
}
}
return result, nil
}

16
vendor/github.com/dblohm7/wingoes/pe/pe_386.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
package pe
import (
dpe "debug/pe"
)
type optionalHeaderForGOARCH = optionalHeader32
const (
expectedMachineForGOARCH = dpe.IMAGE_FILE_MACHINE_I386
)

16
vendor/github.com/dblohm7/wingoes/pe/pe_amd64.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
package pe
import (
dpe "debug/pe"
)
type optionalHeaderForGOARCH = optionalHeader64
const (
expectedMachineForGOARCH = dpe.IMAGE_FILE_MACHINE_AMD64
)

16
vendor/github.com/dblohm7/wingoes/pe/pe_arm64.go generated vendored Normal file
View File

@@ -0,0 +1,16 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
package pe
import (
dpe "debug/pe"
)
type optionalHeaderForGOARCH = optionalHeader64
const (
expectedMachineForGOARCH = dpe.IMAGE_FILE_MACHINE_ARM64
)

14
vendor/github.com/dblohm7/wingoes/pe/pe_notwindows.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !windows
package pe
func (pei *peModule) Close() error {
return nil
}
func checkMachine(pe peReader, machine uint16) bool {
return true
}

144
vendor/github.com/dblohm7/wingoes/pe/pe_windows.go generated vendored Normal file
View File

@@ -0,0 +1,144 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package pe
import (
"bytes"
"fmt"
"os"
"unsafe"
"golang.org/x/sys/windows"
)
func (pei *peModule) Close() error {
return windows.FreeLibrary(windows.Handle(pei.modLock))
}
// NewPEFromBaseAddressAndSize parses the headers in a PE binary loaded
// into the current process's address space at address baseAddr with known
// size. If you do not have the size, use NewPEFromBaseAddress instead.
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
// *PEHeaders and a non-nil error.
func NewPEFromBaseAddressAndSize(baseAddr uintptr, size uint32) (*PEHeaders, error) {
// Grab a strong reference to the module until we're done with it.
var modLock windows.Handle
if err := windows.GetModuleHandleEx(
windows.GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(*uint16)(unsafe.Pointer(baseAddr)),
&modLock,
); err != nil {
return nil, err
}
slc := unsafe.Slice((*byte)(unsafe.Pointer(baseAddr)), size)
r := bytes.NewReader(slc)
peMod := &peModule{
Reader: r,
peBounds: peBounds{
base: baseAddr,
limit: baseAddr + uintptr(size),
},
modLock: uintptr(modLock),
}
peh, err := loadHeaders(peMod)
if err != nil {
peMod.Close()
return nil, err
}
return peh, nil
}
// NewPEFromBaseAddress parses the headers in a PE binary loaded into the
// current process's address space at address baseAddr.
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
// *PEHeaders and a non-nil error.
func NewPEFromBaseAddress(baseAddr uintptr) (*PEHeaders, error) {
var modInfo windows.ModuleInfo
if err := windows.GetModuleInformation(
windows.CurrentProcess(),
windows.Handle(baseAddr),
&modInfo,
uint32(unsafe.Sizeof(modInfo)),
); err != nil {
return nil, fmt.Errorf("querying module handle: %w", err)
}
return NewPEFromBaseAddressAndSize(baseAddr, modInfo.SizeOfImage)
}
// NewPEFromHMODULE parses the headers in a PE binary identified by hmodule that
// is currently loaded into the current process's address space.
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
// *PEHeaders and a non-nil error.
func NewPEFromHMODULE(hmodule windows.Handle) (*PEHeaders, error) {
// HMODULEs are just a loaded module's base address with the lowest two
// bits used for flags (see docs for LoadLibraryExW).
return NewPEFromBaseAddress(uintptr(hmodule) & ^uintptr(3))
}
// NewPEFromDLL parses the headers in a PE binary identified by dll that
// is currently loaded into the current process's address space.
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
// *PEHeaders and a non-nil error.
func NewPEFromDLL(dll *windows.DLL) (*PEHeaders, error) {
if dll == nil || dll.Handle == 0 {
return nil, os.ErrInvalid
}
return NewPEFromHMODULE(dll.Handle)
}
// NewPEFromLazyDLL parses the headers in a PE binary identified by ldll that
// is currently loaded into the current process's address space.
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
// *PEHeaders and a non-nil error.
func NewPEFromLazyDLL(ldll *windows.LazyDLL) (*PEHeaders, error) {
if ldll == nil {
return nil, os.ErrInvalid
}
if err := ldll.Load(); err != nil {
return nil, err
}
return NewPEFromHMODULE(windows.Handle(ldll.Handle()))
}
// NewPEFromFileHandle parses the PE headers from hfile, an open Win32 file handle.
// It does *not* consume hfile.
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a
// nil *PEHeaders and a non-nil error.
// Call Close() on the returned *PEHeaders when it is no longer needed.
func NewPEFromFileHandle(hfile windows.Handle) (*PEHeaders, error) {
if hfile == 0 || hfile == windows.InvalidHandle {
return nil, os.ErrInvalid
}
// Duplicate hfile so that we don't consume it.
var hfileDup windows.Handle
cp := windows.CurrentProcess()
if err := windows.DuplicateHandle(
cp,
hfile,
cp,
&hfileDup,
0,
false,
windows.DUPLICATE_SAME_ACCESS,
); err != nil {
return nil, err
}
return newPEFromFile(os.NewFile(uintptr(hfileDup), "PEFromFileHandle"))
}
func checkMachine(r peReader, machine uint16) bool {
// In-memory modules should always have a machine type that matches our own.
// (okay, so that's kinda sorta untrue with respect to WOW64, but that's
// a _very_ obscure use case).
_, isModule := r.(*peModule)
return !isModule || machine == expectedMachineForGOARCH
}

168
vendor/github.com/dblohm7/wingoes/pe/version.go generated vendored Normal file
View File

@@ -0,0 +1,168 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
package pe
import (
"errors"
"fmt"
"unsafe"
"golang.org/x/sys/windows"
)
var (
errFixedFileInfoBadSig = errors.New("bad VS_FIXEDFILEINFO signature")
errFixedFileInfoTooShort = errors.New("buffer smaller than VS_FIXEDFILEINFO")
)
// VersionNumber encapsulates a four-component version number that is stored
// in Windows VERSIONINFO resources.
type VersionNumber struct {
Major uint16
Minor uint16
Patch uint16
Build uint16
}
func (vn VersionNumber) String() string {
return fmt.Sprintf("%d.%d.%d.%d", vn.Major, vn.Minor, vn.Patch, vn.Build)
}
type langAndCodePage struct {
language uint16
codePage uint16
}
// VersionInfo encapsulates a buffer containing the VERSIONINFO resources that
// have been successfully extracted from a PE binary.
type VersionInfo struct {
buf []byte
fixed *windows.VS_FIXEDFILEINFO
translationIDs []langAndCodePage
}
const (
langEnUS = 0x0409
codePageUTF16LE = 0x04B0
langNeutral = 0
codePageNeutral = 0
)
// NewVersionInfo extracts any VERSIONINFO resource from filepath, parses its
// fixed-size information, and returns a *VersionInfo for further querying.
// It returns ErrNotPresent if no VERSIONINFO resources are found.
func NewVersionInfo(filepath string) (*VersionInfo, error) {
bufSize, err := windows.GetFileVersionInfoSize(filepath, nil)
if err != nil {
if errors.Is(err, windows.ERROR_RESOURCE_TYPE_NOT_FOUND) {
err = ErrNotPresent
}
return nil, err
}
buf := make([]byte, bufSize)
if err := windows.GetFileVersionInfo(filepath, 0, bufSize, unsafe.Pointer(unsafe.SliceData(buf))); err != nil {
return nil, err
}
var fixed *windows.VS_FIXEDFILEINFO
var fixedLen uint32
if err := windows.VerQueryValue(unsafe.Pointer(unsafe.SliceData(buf)), `\`, unsafe.Pointer(&fixed), &fixedLen); err != nil {
return nil, err
}
if fixedLen < uint32(unsafe.Sizeof(windows.VS_FIXEDFILEINFO{})) {
return nil, errFixedFileInfoTooShort
}
if fixed.Signature != 0xFEEF04BD {
return nil, errFixedFileInfoBadSig
}
return &VersionInfo{
buf: buf,
fixed: fixed,
}, nil
}
func (vi *VersionInfo) VersionNumber() VersionNumber {
f := vi.fixed
return VersionNumber{
Major: uint16(f.FileVersionMS >> 16),
Minor: uint16(f.FileVersionMS & 0xFFFF),
Patch: uint16(f.FileVersionLS >> 16),
Build: uint16(f.FileVersionLS & 0xFFFF),
}
}
func (vi *VersionInfo) maybeLoadTranslationIDs() {
if vi.translationIDs != nil {
// Already loaded
return
}
// Preferred translations, in order of preference.
preferredTranslationIDs := []langAndCodePage{
langAndCodePage{
language: langEnUS,
codePage: codePageUTF16LE,
},
langAndCodePage{
language: langNeutral,
codePage: codePageNeutral,
},
}
var ids *langAndCodePage
var idsNumBytes uint32
if err := windows.VerQueryValue(
unsafe.Pointer(unsafe.SliceData(vi.buf)),
`\VarFileInfo\Translation`,
unsafe.Pointer(&ids),
&idsNumBytes,
); err != nil {
// If nothing is listed, then just try to use our preferred translation IDs.
vi.translationIDs = preferredTranslationIDs
return
}
idsSlice := unsafe.Slice(ids, idsNumBytes/uint32(unsafe.Sizeof(*ids)))
vi.translationIDs = append(preferredTranslationIDs, idsSlice...)
}
func (vi *VersionInfo) queryWithLangAndCodePage(key string, lcp langAndCodePage) (string, error) {
fq := fmt.Sprintf("\\StringFileInfo\\%04x%04x\\%s", lcp.language, lcp.codePage, key)
var value *uint16
var valueLen uint32
if err := windows.VerQueryValue(unsafe.Pointer(unsafe.SliceData(vi.buf)), fq, unsafe.Pointer(&value), &valueLen); err != nil {
return "", err
}
return windows.UTF16ToString(unsafe.Slice(value, valueLen)), nil
}
// Field queries the version information for a field named key and either
// returns the field's value, or an error. It attempts to resolve strings using
// the following order of language preference: en-US, language-neutral, followed
// by the first entry in version info's list of supported languages that
// successfully resolves the key.
// If the key cannot be resolved, it returns ErrNotPresent.
func (vi *VersionInfo) Field(key string) (string, error) {
vi.maybeLoadTranslationIDs()
for _, lcp := range vi.translationIDs {
value, err := vi.queryWithLangAndCodePage(key, lcp)
if err == nil {
return value, nil
}
if !errors.Is(err, windows.ERROR_RESOURCE_TYPE_NOT_FOUND) {
return "", err
}
// Otherwise we continue looping and try the next language
}
return "", ErrNotPresent
}

29
vendor/github.com/dblohm7/wingoes/time.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package wingoes
import (
"errors"
"time"
"golang.org/x/sys/windows"
)
var (
// ErrDurationOutOfRange means that a time.Duration is too large to be able
// to be specified as a valid Win32 timeout value.
ErrDurationOutOfRange = errors.New("duration is out of timeout range")
)
// DurationToTimeoutMilliseconds converts d into a timeout usable by Win32 APIs.
func DurationToTimeoutMilliseconds(d time.Duration) (uint32, error) {
millis := d.Milliseconds()
if millis >= windows.INFINITE {
return 0, ErrDurationOutOfRange
}
return uint32(millis), nil
}

80
vendor/github.com/dblohm7/wingoes/util.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
package wingoes
import (
"unsafe"
"golang.org/x/sys/windows"
)
// UserSIDs contains pointers to the SIDs for a user and their primary group.
type UserSIDs struct {
User *windows.SID
PrimaryGroup *windows.SID
}
// CurrentProcessUserSIDs returns a UserSIDs containing the SIDs of the user
// and primary group who own the current process.
func CurrentProcessUserSIDs() (*UserSIDs, error) {
token, err := windows.OpenCurrentProcessToken()
if err != nil {
return nil, err
}
defer token.Close()
userInfo, err := token.GetTokenUser()
if err != nil {
return nil, err
}
primaryGroup, err := token.GetTokenPrimaryGroup()
if err != nil {
return nil, err
}
// We just want the SIDs, not the rest of the structs that were output.
userSid, err := userInfo.User.Sid.Copy()
if err != nil {
return nil, err
}
primaryGroupSid, err := primaryGroup.PrimaryGroup.Copy()
if err != nil {
return nil, err
}
return &UserSIDs{User: userSid, PrimaryGroup: primaryGroupSid}, nil
}
// getTokenInfoVariableLen obtains variable-length token information. Use
// this function for information classes that output variable-length data.
func getTokenInfoVariableLen[T any](token windows.Token, infoClass uint32) (*T, error) {
var buf []byte
var desiredLen uint32
err := windows.GetTokenInformation(token, infoClass, nil, 0, &desiredLen)
for err == windows.ERROR_INSUFFICIENT_BUFFER {
buf = make([]byte, desiredLen)
err = windows.GetTokenInformation(token, infoClass, unsafe.SliceData(buf), desiredLen, &desiredLen)
}
if err != nil {
return nil, err
}
return (*T)(unsafe.Pointer(unsafe.SliceData(buf))), nil
}
// getTokenInfoFixedLen obtains known fixed-length token information. Use this
// function for information classes that output enumerations, BOOLs, integers etc.
func getTokenInfoFixedLen[T any](token windows.Token, infoClass uint32) (result T, _ error) {
var actualLen uint32
err := windows.GetTokenInformation(token, infoClass, (*byte)(unsafe.Pointer(&result)), uint32(unsafe.Sizeof(result)), &actualLen)
return result, err
}