Update dependencies
This commit is contained in:
19
vendor/github.com/dblohm7/wingoes/.gitignore
generated
vendored
Normal file
19
vendor/github.com/dblohm7/wingoes/.gitignore
generated
vendored
Normal 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
29
vendor/github.com/dblohm7/wingoes/LICENSE
generated
vendored
Normal 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
2
vendor/github.com/dblohm7/wingoes/README.md
generated
vendored
Normal 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
140
vendor/github.com/dblohm7/wingoes/com/api.go
generated
vendored
Normal 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)
|
||||
}
|
||||
7
vendor/github.com/dblohm7/wingoes/com/automation/automation.go
generated
vendored
Normal file
7
vendor/github.com/dblohm7/wingoes/com/automation/automation.go
generated
vendored
Normal 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
|
||||
14
vendor/github.com/dblohm7/wingoes/com/automation/mksyscall.go
generated
vendored
Normal file
14
vendor/github.com/dblohm7/wingoes/com/automation/mksyscall.go
generated
vendored
Normal 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
|
||||
93
vendor/github.com/dblohm7/wingoes/com/automation/types.go
generated
vendored
Normal file
93
vendor/github.com/dblohm7/wingoes/com/automation/types.go
generated
vendored
Normal 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
|
||||
}
|
||||
70
vendor/github.com/dblohm7/wingoes/com/automation/zsyscall_windows.go
generated
vendored
Normal file
70
vendor/github.com/dblohm7/wingoes/com/automation/zsyscall_windows.go
generated
vendored
Normal 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
9
vendor/github.com/dblohm7/wingoes/com/com.go
generated
vendored
Normal 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
120
vendor/github.com/dblohm7/wingoes/com/globalopts.go
generated
vendored
Normal 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
22
vendor/github.com/dblohm7/wingoes/com/guid.go
generated
vendored
Normal 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
112
vendor/github.com/dblohm7/wingoes/com/interface.go
generated
vendored
Normal 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
25
vendor/github.com/dblohm7/wingoes/com/mksyscall.go
generated
vendored
Normal 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
89
vendor/github.com/dblohm7/wingoes/com/object.go
generated
vendored
Normal 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
268
vendor/github.com/dblohm7/wingoes/com/process.go
generated
vendored
Normal 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
556
vendor/github.com/dblohm7/wingoes/com/stream.go
generated
vendored
Normal 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
13
vendor/github.com/dblohm7/wingoes/com/stream_not386.go
generated
vendored
Normal 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
|
||||
11
vendor/github.com/dblohm7/wingoes/com/stream_windows_386.go
generated
vendored
Normal file
11
vendor/github.com/dblohm7/wingoes/com/stream_windows_386.go
generated
vendored
Normal 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
150
vendor/github.com/dblohm7/wingoes/com/types.go
generated
vendored
Normal 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
44
vendor/github.com/dblohm7/wingoes/com/unknown.go
generated
vendored
Normal 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)
|
||||
}
|
||||
106
vendor/github.com/dblohm7/wingoes/com/zsyscall_windows.go
generated
vendored
Normal file
106
vendor/github.com/dblohm7/wingoes/com/zsyscall_windows.go
generated
vendored
Normal 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
329
vendor/github.com/dblohm7/wingoes/error.go
generated
vendored
Normal 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
15
vendor/github.com/dblohm7/wingoes/guid.go
generated
vendored
Normal 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
17
vendor/github.com/dblohm7/wingoes/guid_notwindows.go
generated
vendored
Normal 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
24
vendor/github.com/dblohm7/wingoes/guid_windows.go
generated
vendored
Normal 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
12
vendor/github.com/dblohm7/wingoes/hresult.go
generated
vendored
Normal 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
13
vendor/github.com/dblohm7/wingoes/internal/types.go
generated
vendored
Normal 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
199
vendor/github.com/dblohm7/wingoes/osversion.go
generated
vendored
Normal 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
262
vendor/github.com/dblohm7/wingoes/pe/oh.go
generated
vendored
Normal 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
746
vendor/github.com/dblohm7/wingoes/pe/pe.go
generated
vendored
Normal 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
16
vendor/github.com/dblohm7/wingoes/pe/pe_386.go
generated
vendored
Normal 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
16
vendor/github.com/dblohm7/wingoes/pe/pe_amd64.go
generated
vendored
Normal 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
16
vendor/github.com/dblohm7/wingoes/pe/pe_arm64.go
generated
vendored
Normal 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
14
vendor/github.com/dblohm7/wingoes/pe/pe_notwindows.go
generated
vendored
Normal 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
144
vendor/github.com/dblohm7/wingoes/pe/pe_windows.go
generated
vendored
Normal 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
168
vendor/github.com/dblohm7/wingoes/pe/version.go
generated
vendored
Normal 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
29
vendor/github.com/dblohm7/wingoes/time.go
generated
vendored
Normal 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
80
vendor/github.com/dblohm7/wingoes/util.go
generated
vendored
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user