This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+3 -3
View File
@@ -50,8 +50,8 @@ 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.
// toUTF16 is unsafe for general use because it returns a slice referencing a
// pointer that is not managed by the Go GC.
func (bs *BSTR) toUTF16() []uint16 {
return unsafe.Slice(bs.toUTF16Ptr(), bs.Len())
}
@@ -67,7 +67,7 @@ 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.
// ToUTF16Ptr 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())
}
+4 -4
View File
@@ -47,24 +47,24 @@ var (
)
func sysAllocString(str *uint16) (ret BSTR) {
r0, _, _ := syscall.Syscall(procSysAllocString.Addr(), 1, uintptr(unsafe.Pointer(str)), 0, 0)
r0, _, _ := syscall.SyscallN(procSysAllocString.Addr(), uintptr(unsafe.Pointer(str)))
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)
r0, _, _ := syscall.SyscallN(procSysAllocStringLen.Addr(), uintptr(unsafe.Pointer(str)), uintptr(strLen))
ret = BSTR(r0)
return
}
func sysFreeString(bstr BSTR) {
syscall.Syscall(procSysFreeString.Addr(), 1, uintptr(bstr), 0, 0)
syscall.SyscallN(procSysFreeString.Addr(), uintptr(bstr))
return
}
func sysStringLen(bstr BSTR) (ret uint32) {
r0, _, _ := syscall.Syscall(procSysStringLen.Addr(), 1, uintptr(bstr), 0, 0)
r0, _, _ := syscall.SyscallN(procSysStringLen.Addr(), uintptr(bstr))
ret = uint32(r0)
return
}
+1 -2
View File
@@ -20,6 +20,5 @@ package com
// 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.
// The following function uses IUnknownABI instead of IStreamABI because it makes the callsite 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
-206
View File
@@ -13,7 +13,6 @@ import (
"unsafe"
"github.com/dblohm7/wingoes"
"github.com/dblohm7/wingoes/internal"
"golang.org/x/sys/windows"
)
@@ -179,103 +178,6 @@ func (o SequentialStream) Write(b []byte) (int, error) {
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]
@@ -306,76 +208,6 @@ func (abi *IStreamABI) Revert() error {
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]
@@ -493,19 +325,10 @@ const hrE_OUTOFMEMORY = wingoes.HRESULT(-((0x8007000E ^ 0xFFFFFFFF) + 1))
// 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 {
@@ -525,32 +348,3 @@ func newMemoryStreamInternal(initialBytes []byte, forceLegacy bool) (result Stre
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
}
+130
View File
@@ -0,0 +1,130 @@
// 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 (
"math"
"syscall"
"unsafe"
"github.com/dblohm7/wingoes"
)
const maxStreamRWLen = math.MaxInt32
func (abi *IStreamABI) Seek(offset int64, whence int) (n int64, _ error) {
var hr wingoes.HRESULT
method := unsafe.Slice(abi.Vtbl, 14)[5]
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)
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]
words := (*[2]uintptr)(unsafe.Pointer(&newSize))
rc, _, _ := syscall.SyscallN(
method,
uintptr(unsafe.Pointer(abi)),
words[0],
words[1],
)
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]
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)
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return bytesRead, bytesWritten, e
}
return bytesRead, bytesWritten, nil
}
func (abi *IStreamABI) LockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
var hr wingoes.HRESULT
method := unsafe.Slice(abi.Vtbl, 14)[10]
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)
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]
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)
if e := wingoes.ErrorFromHRESULT(hr); e.Failed() {
return e
}
return nil
}
+103
View File
@@ -8,6 +8,109 @@ package com
import (
"math"
"syscall"
"unsafe"
"github.com/dblohm7/wingoes"
)
const maxStreamRWLen = math.MaxUint32
func (abi *IStreamABI) Seek(offset int64, whence int) (n int64, _ error) {
var hr wingoes.HRESULT
method := unsafe.Slice(abi.Vtbl, 14)[5]
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]
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]
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) LockRegion(offset, numBytes uint64, lockType LOCKTYPE) error {
var hr wingoes.HRESULT
method := unsafe.Slice(abi.Vtbl, 14)[10]
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]
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
}
+11
View File
@@ -0,0 +1,11 @@
// Copyright (c) 2024 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
func getTooBigSlice() []byte {
return nil
}
+11
View File
@@ -0,0 +1,11 @@
// Copyright (c) 2024 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
func getTooBigSlice() []byte {
return make([]byte, maxStreamRWLen+1)
}
-11
View File
@@ -1,11 +0,0 @@
// 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
+14 -26
View File
@@ -7,7 +7,6 @@ import (
"unsafe"
"github.com/dblohm7/wingoes"
"github.com/dblohm7/wingoes/internal"
"golang.org/x/sys/windows"
)
@@ -44,63 +43,52 @@ var (
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")
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
procCoGetApartmentType = modole32.NewProc("CoGetApartmentType")
procCoIncrementMTAUsage = modole32.NewProc("CoIncrementMTAUsage")
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
procCoInitializeSecurity = modole32.NewProc("CoInitializeSecurity")
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)
r0, _, _ := syscall.SyscallN(procCoCreateInstance.Addr(), uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(unkOuter)), uintptr(clsctx), uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(ppv)))
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)
r0, _, _ := syscall.SyscallN(procCoGetApartmentType.Addr(), uintptr(unsafe.Pointer(aptType)), uintptr(unsafe.Pointer(qual)))
hr = wingoes.HRESULT(r0)
return
}
func coIncrementMTAUsage(cookie *coMTAUsageCookie) (hr wingoes.HRESULT) {
r0, _, _ := syscall.Syscall(procCoIncrementMTAUsage.Addr(), 1, uintptr(unsafe.Pointer(cookie)), 0, 0)
r0, _, _ := syscall.SyscallN(procCoIncrementMTAUsage.Addr(), uintptr(unsafe.Pointer(cookie)))
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)
r0, _, _ := syscall.SyscallN(procCoInitializeEx.Addr(), uintptr(reserved), uintptr(flags))
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)))
r0, _, _ := syscall.SyscallN(procCoInitializeSecurity.Addr(), 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 setOaNoCache() {
syscall.Syscall(procSetOaNoCache.Addr(), 0, 0, 0, 0)
syscall.SyscallN(procSetOaNoCache.Addr())
return
}
func shCreateMemStream(pInit *byte, cbInit uint32) (stream *IUnknownABI) {
r0, _, _ := syscall.Syscall(procSHCreateMemStream.Addr(), 2, uintptr(unsafe.Pointer(pInit)), uintptr(cbInit), 0)
r0, _, _ := syscall.SyscallN(procSHCreateMemStream.Addr(), uintptr(unsafe.Pointer(pInit)), uintptr(cbInit))
stream = (*IUnknownABI)(unsafe.Pointer(r0))
return
}
+19 -10
View File
@@ -27,7 +27,6 @@ const (
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
@@ -83,6 +82,10 @@ func (hr HRESULT) String() string {
return fmt.Sprintf("0x%08X", uint32(hr))
}
func (hr HRESULT) GoString() string {
return fmt.Sprintf("%T(0x%08X)", hr, uint32(hr))
}
func (hr HRESULT) isNT() bool {
return (hr & (hrCustomerBit | hrFacilityNTBit)) == hrFacilityNTBit
}
@@ -152,20 +155,26 @@ func ErrorFromHRESULT(hr HRESULT) Error {
return Error(hr)
}
// SupportedErrorTypes is a type constraint describing the possible types
// that may be used as inputs to [NewError].
type SupportedErrorTypes interface {
Error | HRESULT | windows.Errno | windows.NTStatus
}
// 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) {
func NewError[E SupportedErrorTypes](e E) (Error) {
switch v := any(e).(type) {
case Error:
return v, true
return v
case windows.NTStatus:
return ErrorFromNTStatus(v), true
return ErrorFromNTStatus(v)
case windows.Errno:
return ErrorFromErrno(v), true
return ErrorFromErrno(v)
case HRESULT:
return ErrorFromHRESULT(v), true
return ErrorFromHRESULT(v)
default:
return ErrorFromHRESULT(hrTYPE_E_WRONGTYPEKIND), false
panic(fmt.Sprintf("Unsupported error type %T", v))
}
}
@@ -221,7 +230,7 @@ func (e Error) toErrno(f errnoFailHandler) windows.Errno {
// 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)))
panic(fmt.Sprintf("wingoes.Error: Called AsErrno on a non-convertable %#v", hr))
return windows.ERROR_UNIDENTIFIED_ERROR
}
@@ -247,7 +256,7 @@ func (e Error) toNTStatus(f ntStatusFailHandler) windows.NTStatus {
// 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)))
panic(fmt.Sprintf("windows.Error: Called AsNTStatus on a non-NTSTATUS %#v", hr))
return windows.STATUS_UNSUCCESSFUL
}
-13
View File
@@ -1,13 +0,0 @@
// 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
+14
View File
@@ -0,0 +1,14 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
package wingoes
//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
// Appending a '?' to this declaration because it's very, very deprecated, in
// the (admittedly unlikely) chance it actually gets removed in a future version
// of Windows.
//sys getVersionEx(osv *_OSVERSIONINFOEX) (err error) [int32(failretval)==0] = kernel32.GetVersionExW?
+195 -66
View File
@@ -9,27 +9,33 @@ package wingoes
import (
"fmt"
"sync"
"sync/atomic"
"unsafe"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
var (
verOnce sync.Once
verInfo osVersionInfo // must access via getVersionInfo()
const (
_VER_MINORVERSION = 0x0000001
_VER_MAJORVERSION = 0x0000002
_VER_BUILDNUMBER = 0x0000004
_VER_PLATFORMID = 0x0000008
_VER_SERVICEPACKMINOR = 0x0000010
_VER_SERVICEPACKMAJOR = 0x0000020
_VER_SUITENAME = 0x0000040
_VER_PRODUCT_TYPE = 0x0000080
)
// 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_EQUAL = 1
_VER_GREATER = 2
_VER_GREATER_EQUAL = 3
_VER_LESS = 4
_VER_LESS_EQUAL = 5
_VER_AND = 6
_VER_OR = 7
)
const (
_VER_NT_WORKSTATION = 1
@@ -37,29 +43,124 @@ const (
_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,
// The definition in x/sys/windows doesn't export the OSVersionInfoSize field,
// which we need to set before making API calls.
type _OSVERSIONINFOEX struct {
OSVersionInfoSize uint32
MajorVersion uint32
MinorVersion uint32
BuildNumber uint32
PlatformId uint32
CSDVersion [128]uint16
ServicePackMajor uint16
ServicePackMinor uint16
SuiteMask uint16
ProductType byte
_ byte // Reserved
}
type osVersionInfo struct {
fallbackBuildMin atomic.Uint32
fallbackBuildMax atomic.Uint32
build uint32
str string
isDC bool
isServer bool
useFallback bool
}
var (
// We cannot use mksyscall for these because they pass uint64 by value on
// 32-bit CPU architectures.
procVerSetConditionMask = modkernel32.NewProc("VerSetConditionMask")
procVerifyVersionInfo = modkernel32.NewProc("VerifyVersionInfoW")
verOnce sync.Once
verInfo osVersionInfo // must access via getVersionInfo()
)
func getVersionInfoInternal() (*_OSVERSIONINFOEX, error) {
osv := _OSVERSIONINFOEX{
OSVersionInfoSize: uint32(unsafe.Sizeof(_OSVERSIONINFOEX{})),
}
// Using GetVersionEx to account for app manifest.
if err := getVersionEx(&osv); err != nil {
return nil, err
}
return &osv, nil
}
func (osv *osVersionInfo) initFlags() {
if isDC, err := verQueryProductType(_VER_NT_DOMAIN_CONTROLLER); err == nil {
osv.isDC = isDC
if isDC {
// Domain Controllers are also implicitly servers.
isServer: osv.ProductType == _VER_NT_DOMAIN_CONTROLLER || osv.ProductType == _VER_NT_SERVER,
osv.isServer = true
return
}
// 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)
}
}
if isServer, err := verQueryProductType(_VER_NT_SERVER); err == nil {
osv.isServer = isServer
}
}
func (osv *osVersionInfo) initFallback() {
osv.initFlags()
osv.useFallback = true
}
func verCheckManifest(osv *_OSVERSIONINFOEX) bool {
major, minor, build := windows.RtlGetNtVersionNumbers()
return major == osv.MajorVersion && minor == osv.MinorVersion && build == osv.BuildNumber
}
func (osv *osVersionInfo) init() {
osvx, err := getVersionInfoInternal()
if err != nil {
osv.initFallback()
return
}
// We only support 10.0.x.x
if osvx.MajorVersion != 10 || osvx.MinorVersion != 0 {
if verCheckManifest(osvx) {
panic("change in versioning scheme -- package must be updated to interpret")
} else {
panic("incoherent Windows version -- missing/outdated manifest?")
}
})
}
*osv = osVersionInfo{
build: osvx.BuildNumber,
str: versionString(osvx),
isDC: osvx.ProductType == _VER_NT_DOMAIN_CONTROLLER,
// Domain Controllers are also implicitly servers.
isServer: osvx.ProductType == _VER_NT_DOMAIN_CONTROLLER || osvx.ProductType == _VER_NT_SERVER,
}
}
func getVersionInfo() *osVersionInfo {
verOnce.Do(verInfo.init)
return &verInfo
}
func versionString(osv *_OSVERSIONINFOEX) string {
fmtstr := "%d.%d.%d.%d"
vers := append(make([]any, 0, 4),
uint(osv.MajorVersion),
uint(osv.MinorVersion),
uint(osv.BuildNumber),
)
if ubr, err := getUBR(); err == nil {
vers = append(vers, uint(ubr))
} else {
fmtstr = fmtstr[:len(fmtstr)-3]
}
return fmt.Sprintf(fmtstr, vers...)
}
// 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) {
@@ -82,8 +183,7 @@ func getUBR() (uint32, error) {
}
// 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.
// dotted-decimal form.
func GetOSVersionString() string {
return getVersionInfo().String()
}
@@ -100,31 +200,6 @@ 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
@@ -133,6 +208,7 @@ const (
Win10BuildRTM = Win10BuildConstant(10240)
Win10Build1511 = Win10BuildConstant(10586)
Win10Build1607 = Win10BuildConstant(14393)
WinServer2016 = Win10Build1607
Win10BuildAnniversary = Win10Build1607
Win10Build1703 = Win10BuildConstant(15063)
Win10BuildCreators = Win10Build1703
@@ -140,6 +216,7 @@ const (
Win10BuildFallCreators = Win10Build1709
Win10Build1803 = Win10BuildConstant(17134)
Win10Build1809 = Win10BuildConstant(17763)
WinServer2019 = Win10Build1809
Win10Build1903 = Win10BuildConstant(18362)
Win10Build1909 = Win10BuildConstant(18363)
Win10Build2004 = Win10BuildConstant(19041)
@@ -147,6 +224,7 @@ const (
Win10Build21H1 = Win10BuildConstant(19043)
Win10Build21H2 = Win10BuildConstant(19044)
Win10Build22H2 = Win10BuildConstant(19045)
WinServer2022 = Win10BuildConstant(20348)
)
// IsWin10BuildOrGreater returns true when running on the specified Windows 10
@@ -163,6 +241,10 @@ const (
Win11BuildRTM = Win11BuildConstant(22000)
Win11Build22H2 = Win11BuildConstant(22621)
Win11Build23H2 = Win11BuildConstant(22631)
Win11Build24H2 = Win11BuildConstant(26100)
WinServer2025 = Win11Build24H2
Win11Build25H2 = Win11BuildConstant(26200)
Win11Build26H1 = Win11BuildConstant(28000)
)
// IsWin11OrGreater returns true when running on any release of Windows 11,
@@ -184,16 +266,63 @@ func (osv *osVersionInfo) String() string {
}
func (osv *osVersionInfo) isWin10BuildOrGreater(build uint32) bool {
return osv.isVersionOrGreater(10, 0, build)
if !osv.useFallback {
return osv.build >= build
}
if osv.fallbackBuildMin.Load() >= build {
return true
}
if build >= osv.fallbackBuildMax.Load() {
return false
}
result, err := verQueryBuild(10, 0, build)
if err == nil {
if result {
osv.fallbackBuildMin.Store(build)
} else {
osv.fallbackBuildMax.Store(build)
}
}
return result
}
func (osv *osVersionInfo) isVersionOrGreater(major, minor, build uint32) bool {
return isVerGE(osv.major, major, osv.minor, minor, osv.build, build)
func verQueryBuild(major, minor, build uint32) (bool, error) {
var condMask uint64
condMask = verSetConditionMask(condMask, _VER_MAJORVERSION, _VER_GREATER_EQUAL)
condMask = verSetConditionMask(condMask, _VER_MINORVERSION, _VER_GREATER_EQUAL)
condMask = verSetConditionMask(condMask, _VER_BUILDNUMBER, _VER_GREATER_EQUAL)
typeMask := uint32(_VER_MAJORVERSION | _VER_MINORVERSION | _VER_BUILDNUMBER)
osv := _OSVERSIONINFOEX{
MajorVersion: major,
MinorVersion: minor,
BuildNumber: build,
}
return verVerify(&osv, typeMask, condMask)
}
func isVerGE(lmajor, rmajor, lminor, rminor, lbuild, rbuild uint32) bool {
return lmajor > rmajor ||
lmajor == rmajor &&
(lminor > rminor ||
lminor == rminor && lbuild >= rbuild)
func verQueryProductType(wantProdType byte) (bool, error) {
condMask := verSetConditionMask(0, _VER_PRODUCT_TYPE, _VER_EQUAL)
osv := _OSVERSIONINFOEX{
ProductType: wantProdType,
}
return verVerify(&osv, _VER_PRODUCT_TYPE, condMask)
}
func verVerify(osv *_OSVERSIONINFOEX, typeMask uint32, condMask uint64) (bool, error) {
osv.OSVersionInfoSize = uint32(unsafe.Sizeof(_OSVERSIONINFOEX{}))
err := verifyVersionInfo(osv, typeMask, condMask)
switch err {
case nil:
return true, nil
case windows.ERROR_OLD_WIN_VERSION:
return false, nil
default:
return false, err
}
}
+38
View File
@@ -0,0 +1,38 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
package wingoes
import (
"syscall"
"unsafe"
)
func verSetConditionMask(conditionMask uint64, typeMask uint32, condition byte) uint64 {
words := (*[2]uintptr)(unsafe.Pointer(&conditionMask))
r0, r1, _ := syscall.SyscallN(
procVerSetConditionMask.Addr(),
words[0],
words[1],
uintptr(typeMask),
uintptr(condition),
)
return uint64(r0) | (uint64(r1) << 32)
}
func verifyVersionInfo(versionInformation *_OSVERSIONINFOEX, typeMask uint32, conditionMask uint64) (err error) {
words := (*[2]uintptr)(unsafe.Pointer(&conditionMask))
rc, _, e := syscall.SyscallN(
procVerifyVersionInfo.Addr(),
uintptr(unsafe.Pointer(versionInformation)),
uintptr(typeMask),
words[0],
words[1],
)
if int32(rc) == 0 {
err = e
}
return err
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows && !386
package wingoes
import (
"syscall"
"unsafe"
)
func verSetConditionMask(conditionMask uint64, typeMask uint32, condition byte) uint64 {
r0, _, _ := syscall.SyscallN(
procVerSetConditionMask.Addr(),
uintptr(conditionMask),
uintptr(typeMask),
uintptr(condition),
)
return uint64(r0)
}
func verifyVersionInfo(versionInformation *_OSVERSIONINFOEX, typeMask uint32, conditionMask uint64) (err error) {
rc, _, e := syscall.SyscallN(
procVerifyVersionInfo.Addr(),
uintptr(unsafe.Pointer(versionInformation)),
uintptr(typeMask),
uintptr(conditionMask),
)
if int32(rc) == 0 {
err = e
}
return err
}
+79 -38
View File
@@ -13,6 +13,7 @@ import (
"errors"
"fmt"
"io"
"math"
"math/bits"
"os"
"reflect"
@@ -83,10 +84,14 @@ func (s *SectionHeader) NameString() string {
return string(s.Name[:])
}
type peReader interface {
io.Closer
type peReaderBase interface {
io.ReaderAt
io.ReadSeeker
}
type peReader interface {
peReaderBase
io.Closer
Base() uintptr
Limit() uintptr
}
@@ -136,7 +141,7 @@ func checkMagic(oh OptionalHeader, machine uint16) bool {
case dpe.IMAGE_FILE_MACHINE_AMD64, dpe.IMAGE_FILE_MACHINE_ARM64:
expectedMagic = 0x020B
default:
panic("unsupported machine")
panic(fmt.Sprintf("unsupported machine 0x%04X", machine))
}
return oh.GetMagic() == expectedMagic
@@ -147,22 +152,32 @@ type peBounds struct {
limit uintptr
}
func (peb *peBounds) Base() uintptr {
return peb.base
}
func (peb *peBounds) Limit() uintptr {
return peb.limit
}
type peFile struct {
*os.File
peReaderBase
peBounds
}
func (pef *peFile) Base() uintptr {
return pef.base
}
func (pef *peFile) Close() error {
if pef.peReaderBase == nil {
return nil
}
func (pef *peFile) Limit() uintptr {
if pef.limit == 0 {
if fi, err := pef.Stat(); err == nil {
pef.limit = uintptr(fi.Size())
if cl, ok := pef.peReaderBase.(io.Closer); ok {
if err := cl.Close(); err != nil {
return err
}
}
return pef.limit
pef.peReaderBase = nil
return nil
}
type peModule struct {
@@ -171,14 +186,6 @@ type peModule struct {
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.
@@ -193,8 +200,18 @@ func NewPEFromFileName(filename string) (*PEHeaders, error) {
}
func newPEFromFile(f *os.File) (*PEHeaders, error) {
// peBounds base is 0, limit is loaded lazily
pef := &peFile{File: f}
fi, err := f.Stat()
if err != nil {
f.Close()
return nil, err
}
return newPEFromReaderBase(f, uintptr(fi.Size()))
}
func newPEFromReaderBase(rb peReaderBase, size uintptr) (*PEHeaders, error) {
// peBounds base is 0, limit is size
pef := &peFile{peReaderBase: rb, peBounds: peBounds{limit: size}}
peh, err := loadHeaders(pef)
if err != nil {
pef.Close()
@@ -204,6 +221,15 @@ func newPEFromFile(f *os.File) (*PEHeaders, error) {
return peh, nil
}
// NewPEFromBlob parses the headers from contents of a PE file stored in blob.
// The blob must contain the contents of a file, not a module. 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 NewPEFromBlob(blob []byte) (*PEHeaders, error) {
return newPEFromReaderBase(bytes.NewReader(blob), uintptr(len(blob)))
}
// Close frees any resources that were opened when peh was created.
func (peh *PEHeaders) Close() error {
return peh.r.Close()
@@ -213,17 +239,20 @@ 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 {
// addOffset ensures that off neither overflows nor underflows base.
func addOffset[O rvaType](base uintptr, off O) (addr uintptr, ok bool) {
if off >= 0 {
return base + uintptr(off)
if upoff := uintptr(off); upoff <= (math.MaxUint - base) {
return base + upoff, true
}
return 0, false
}
negation := uintptr(-off)
if negation >= base {
return 0
return 0, false
}
return base - negation
return base - negation, true
}
func binaryRead(r io.Reader, data any) (err error) {
@@ -254,9 +283,12 @@ func readStruct[T any, R rvaType](r peReader, rva R) (*T, error) {
return result, nil
case *peModule:
addr := addOffset(r.Base(), rva)
szT := unsafe.Sizeof(*((*T)(nil)))
if addr+szT >= v.Limit() {
addr, ok := addOffset(r.Base(), rva)
if !ok {
return nil, ErrInvalidBinary
}
szT := uint32(unsafe.Sizeof(*((*T)(nil))))
if addr2, ok := addOffset(addr, szT); !ok || addr2 >= v.Limit() {
return nil, ErrInvalidBinary
}
@@ -285,9 +317,12 @@ func readStructArray[T any, R rvaType](r peReader, rva R, count int) ([]T, error
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() {
addr, ok := addOffset(r.Base(), rva)
if !ok {
return nil, ErrInvalidBinary
}
szT := uint32(reflect.ArrayOf(count, reflect.TypeFor[T]()).Size())
if addr2, ok := addOffset(addr, szT); !ok || addr2 >= v.Limit() {
return nil, ErrInvalidBinary
}
@@ -330,7 +365,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
if e_lfanew <= 0 {
return nil, ErrInvalidBinary
}
if addOffset(r.Base(), e_lfanew) >= r.Limit() {
if addr, ok := addOffset(r.Base(), e_lfanew); !ok || addr >= r.Limit() {
return nil, ErrInvalidBinary
}
@@ -352,7 +387,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
// Read the file header
fileHeaderOffset := uint32(e_lfanew) + uint32(unsafe.Sizeof(pe))
if r.Base()+uintptr(fileHeaderOffset) >= r.Limit() {
if addr, ok := addOffset(r.Base(), fileHeaderOffset); !ok || addr >= r.Limit() {
return nil, ErrInvalidBinary
}
@@ -368,7 +403,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
// Read the optional header
optionalHeaderOffset := uint32(fileHeaderOffset) + uint32(unsafe.Sizeof(*fileHeader))
if r.Base()+uintptr(optionalHeaderOffset) >= r.Limit() {
if addr, ok := addOffset(r.Base(), optionalHeaderOffset); !ok || addr >= r.Limit() {
return nil, ErrInvalidBinary
}
@@ -402,7 +437,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
// Read in the section table
sectionTableOffset := optionalHeaderOffset + uint32(fileHeader.SizeOfOptionalHeader)
if r.Base()+uintptr(sectionTableOffset) >= r.Limit() {
if addr, ok := addOffset(r.Base(), sectionTableOffset); !ok || addr >= r.Limit() {
return nil, ErrInvalidBinary
}
@@ -474,6 +509,8 @@ const (
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
)
const _IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
// 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
@@ -486,9 +523,13 @@ const (
// sophisticated return values, so be careful to structure your type assertions
// accordingly.
func (nfo *PEHeaders) DataDirectoryEntry(idx DataDirectoryIndex) (any, error) {
if ii := int(idx); ii < 0 || ii >= _IMAGE_NUMBEROF_DIRECTORY_ENTRIES {
return nil, ErrIndexOutOfRange
}
dd := nfo.optionalHeader.GetDataDirectory()
if int(idx) >= len(dd) {
return nil, ErrIndexOutOfRange
return nil, ErrNotPresent
}
dde := dd[idx]
+15 -1
View File
@@ -13,7 +13,17 @@ import (
)
func (pei *peModule) Close() error {
return windows.FreeLibrary(windows.Handle(pei.modLock))
if pei.modLock == 0 {
return nil
}
if err := windows.FreeLibrary(windows.Handle(pei.modLock)); err != nil {
return err
}
pei.Reader = nil
pei.modLock = 0
return nil
}
// NewPEFromBaseAddressAndSize parses the headers in a PE binary loaded
@@ -22,6 +32,10 @@ func (pei *peModule) Close() error {
// 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) {
if baseAddr == 0 || size == 0 {
return nil, os.ErrInvalid
}
// Grab a strong reference to the module until we're done with it.
var modLock windows.Handle
if err := windows.GetModuleHandleEx(
+1 -2
View File
@@ -48,7 +48,6 @@ const (
langEnUS = 0x0409
codePageUTF16LE = 0x04B0
langNeutral = 0
codePageNeutral = 0
)
// NewVersionInfo extracts any VERSIONINFO resource from filepath, parses its
@@ -111,7 +110,7 @@ func (vi *VersionInfo) maybeLoadTranslationIDs() {
},
langAndCodePage{
language: langNeutral,
codePage: codePageNeutral,
codePage: codePageUTF16LE,
},
}
+8
View File
@@ -8,11 +8,15 @@ package wingoes
import (
"errors"
"math"
"time"
"golang.org/x/sys/windows"
)
// Infinite is the [time.Duration] equivalent of [windows.INFINITE].
const Infinite = time.Duration(math.MaxInt64)
var (
// ErrDurationOutOfRange means that a time.Duration is too large to be able
// to be specified as a valid Win32 timeout value.
@@ -21,6 +25,10 @@ var (
// DurationToTimeoutMilliseconds converts d into a timeout usable by Win32 APIs.
func DurationToTimeoutMilliseconds(d time.Duration) (uint32, error) {
if d == Infinite {
return windows.INFINITE, nil
}
millis := d.Milliseconds()
if millis >= windows.INFINITE {
return 0, ErrDurationOutOfRange
+63
View File
@@ -7,9 +7,14 @@
package wingoes
import (
"fmt"
"os"
"strings"
"unicode/utf16"
"unsafe"
"golang.org/x/sys/windows"
_ "golang.org/x/sys/windows/registry"
)
// UserSIDs contains pointers to the SIDs for a user and their primary group.
@@ -78,3 +83,61 @@ func getTokenInfoFixedLen[T any](token windows.Token, infoClass uint32) (result
err := windows.GetTokenInformation(token, infoClass, (*byte)(unsafe.Pointer(&result)), uint32(unsafe.Sizeof(result)), &actualLen)
return result, err
}
// MultiSZDecode decodes a UTF-16 buffer containing either a [registry.MULTI_SZ]
// value or a Win32 environment block, and returns the contents as a slice of
// strings. It returns an empty slice if the value is empty, or nil if buf
// cannot be decoded.
func MultiSZDecode(buf []uint16) []string {
if buf == nil {
// Technically an empty value, but not a failure.
return []string{}
}
const doubleNulLen = 2
if len(buf) < doubleNulLen {
return nil
}
// We cannot rely on the len(buf) to determine the end of the data; registry
// APIs treat MULTI_SZ strings as double-nul-terminated C-style strings.
doubleNulIdx := int(-1)
fl := len(buf) - 1
for i, v := range buf {
if v == 0 && i < fl && buf[i+1] == 0 {
doubleNulIdx = i
break
}
}
if doubleNulIdx < 0 {
return nil
}
str := string(utf16.Decode(buf[:doubleNulIdx]))
if len(str) == 0 {
// Technically an empty value, but not a failure.
return []string{}
}
return strings.Split(str, "\x00")
}
// MultiSZEncode encodes strs into the format utilized by [registry.MULTI_SZ]
// values and Win32 environment blocks. strs may be nil or empty, but it must
// not contain any empty strings.
func MultiSZEncode(strs []string) ([]uint16, error) {
var buf strings.Builder
for _, v := range strs {
if len(v) == 0 {
return nil, fmt.Errorf("MultiSZEncode: %w: empty strings not permitted", os.ErrInvalid)
}
buf.WriteString(v)
buf.WriteByte(0)
}
if buf.Len() == 0 {
// So that we end with a double-nul in the empty case
buf.WriteByte(0)
}
buf.WriteByte(0)
return utf16.Encode([]rune(buf.String())), nil
}
+56
View File
@@ -0,0 +1,56 @@
// Code generated by 'go generate'; DO NOT EDIT.
package wingoes
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 (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procGetVersionExW = modkernel32.NewProc("GetVersionExW")
)
func getVersionEx(osv *_OSVERSIONINFOEX) (err error) {
err = procGetVersionExW.Find()
if err != nil {
return
}
r1, _, e1 := syscall.SyscallN(procGetVersionExW.Addr(), uintptr(unsafe.Pointer(osv)))
if int32(r1) == 0 {
err = errnoErr(e1)
}
return
}