├── .gitignore ├── .gitlab-ci.yml ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── linox └── clone_newuser_linux.go ├── macox ├── bundles_darwin.go └── bundles_darwin_test.go ├── preallocate_others.go ├── preallocate_test.go ├── preallocate_windows.go ├── runtime.go ├── scripts └── ci.sh ├── syscallex ├── advapi32_windows.go ├── exec_windows.go ├── kernel32_windows.go ├── kernel32_windows_386.go ├── kernel32_windows_amd64.go ├── netapi32_windows.go ├── neterrors_windows.go ├── shell32_windows.go ├── user32_windows.go ├── userenv_windows.go ├── wellknownsid_windows.go └── wintrust_windows.go └── winox ├── execas └── execas_windows.go ├── osex └── exec_windows.go ├── permissions_windows.go ├── users_windows.go ├── users_windows_test.go └── verifytrust_windows.go /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage.txt 2 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | 2 | stages: 3 | - test 4 | 5 | test:windows: 6 | stage: test 7 | tags: 8 | - windows 9 | script: 10 | - scripts/ci.sh 11 | 12 | test:darwin: 13 | stage: test 14 | tags: 15 | - darwin 16 | script: 17 | - scripts/ci.sh 18 | 19 | test:linux: 20 | stage: test 21 | tags: 22 | - linux 23 | script: 24 | - scripts/ci.sh -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2018 Leaf Corcoran and Amos Wenger, https://itch.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ox 2 | 3 | [![build status](https://git.itch.ovh/itchio/ox/badges/master/build.svg)](https://git.itch.ovh/itchio/ox/commits/master) 4 | [![codecov](https://codecov.io/gh/itchio/ox/branch/master/graph/badge.svg)](https://codecov.io/gh/itchio/ox) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/itchio/ox)](https://goreportcard.com/report/github.com/itchio/ox) 6 | [![GoDoc](https://godoc.org/github.com/itchio/ox?status.svg)](https://godoc.org/github.com/itchio/ox) 7 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/itchio/ox/blob/master/LICENSE) 8 | 9 | ox contains: 10 | 11 | * Package `syscallex`: the missing parts of `syscall` 12 | * Package `winox`: convenient wrappers for some Win32 APIs 13 | * Package `macox`: convenient wrappers for some Cocoa APIs 14 | 15 | ## License 16 | 17 | Licensed under MIT License, see `LICENSE` for details. 18 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/itchio/ox 2 | 3 | require ( 4 | github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e 5 | github.com/itchio/headway v0.0.0-20191015112415-46f64dd4d524 6 | github.com/kr/text v0.2.0 // indirect 7 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 8 | github.com/pkg/errors v0.9.1 9 | github.com/stretchr/testify v1.5.1 10 | golang.org/x/sys v0.0.0-20200301153931-2f85c7ec1e52 11 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect 12 | gopkg.in/yaml.v2 v2.2.8 // indirect 13 | ) 14 | 15 | go 1.13 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e h1:lj77EKYUpYXTd8CD/+QMIf8b6OIOTsfEBSXiAzuEHTU= 7 | github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e/go.mod h1:3ZQK6DMPSz/QZ73jlWxBtUhNA8xZx7LzUFSq/OfP8vk= 8 | github.com/itchio/headway v0.0.0-20191015112415-46f64dd4d524 h1:eROPirGQCZXnzRiwJw3bOE4OB5CctMF+zsnH91bmv9o= 9 | github.com/itchio/headway v0.0.0-20191015112415-46f64dd4d524/go.mod h1:Iif+7HeesRB0PvTYf0gOIFX8lj0za0SUsWryENQYt1E= 10 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 11 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 12 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 13 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 14 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 15 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 16 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 17 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 18 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 19 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 20 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 21 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 22 | github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709 h1:Ko2LQMrRU+Oy/+EDBwX7eZ2jp3C47eDBB8EIhKTun+I= 23 | github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 24 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 25 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 26 | golang.org/x/sys v0.0.0-20200301153931-2f85c7ec1e52 h1:Fe2jtNSBRfG8Aj/TMNSxQtNw3TQns0pulbct3LgZ1oI= 27 | golang.org/x/sys v0.0.0-20200301153931-2f85c7ec1e52/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 28 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 29 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 30 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 31 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 32 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 33 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 34 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 35 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 36 | -------------------------------------------------------------------------------- /linox/clone_newuser_linux.go: -------------------------------------------------------------------------------- 1 | package linox 2 | 3 | import ( 4 | "os/exec" 5 | "syscall" 6 | ) 7 | 8 | // SupportsUnprivilegedCloneNewUser returns true if 9 | // the Linux kernel allows unprivileged users to call the clone() 10 | // syscall with `CLONE_NEWUSER`. 11 | // It is useful, for example to establish whether the Electron 5.0+ suid sandbox 12 | // can be used, or if it needs to be disabled. 13 | // cf. https://github.com/electron/electron/issues/17972 14 | func SupportsUnprivilegedCloneNewUser() bool { 15 | cmd := exec.Command("/bin/true") 16 | cmd.SysProcAttr = &syscall.SysProcAttr{} 17 | cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER 18 | if err := cmd.Run(); err != nil { 19 | return false 20 | } 21 | return true 22 | } 23 | -------------------------------------------------------------------------------- /macox/bundles_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | package macox 4 | 5 | /* 6 | #cgo CFLAGS: -x objective-c 7 | #cgo LDFLAGS: -framework Cocoa 8 | #import 9 | #include 10 | 11 | char *GetExecutablePath(char *cBundlePath) { 12 | NSString* bundlePath = [NSString stringWithUTF8String:cBundlePath]; 13 | if (!bundlePath) { 14 | return NULL; 15 | } 16 | 17 | NSBundle* bundle = [NSBundle bundleWithPath:bundlePath]; 18 | if (!bundle) { 19 | return NULL; 20 | } 21 | 22 | const char *tempString = [[bundle executablePath] UTF8String]; 23 | char *ret = malloc(strlen(tempString) + 1); 24 | memcpy(ret, tempString, strlen(tempString) + 1); 25 | return ret; 26 | } 27 | 28 | char *GetLibraryPath() { 29 | NSArray* paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); 30 | for (NSString* path in paths) { 31 | const char *tempString = [path UTF8String]; 32 | char *ret = malloc(strlen(tempString) + 1); 33 | memcpy(ret, tempString, strlen(tempString) + 1); 34 | return ret; 35 | } 36 | return NULL; 37 | } 38 | 39 | char *GetHomeDirectory() { 40 | id path = NSHomeDirectory(); 41 | const char *tempString = [path UTF8String]; 42 | char *ret = malloc(strlen(tempString) + 1); 43 | memcpy(ret, tempString, strlen(tempString) + 1); 44 | return ret; 45 | } 46 | 47 | char *GetApplicationSupportPath() { 48 | NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); 49 | for (NSString* path in paths) { 50 | const char *tempString = [path UTF8String]; 51 | char *ret = malloc(strlen(tempString) + 1); 52 | memcpy(ret, tempString, strlen(tempString) + 1); 53 | return ret; 54 | } 55 | return NULL; 56 | } 57 | */ 58 | import "C" 59 | 60 | import ( 61 | "fmt" 62 | "unsafe" 63 | ) 64 | 65 | func GetExecutablePath(bundlePath string) (string, error) { 66 | cPath := C.GetExecutablePath(C.CString(bundlePath)) 67 | if uintptr(unsafe.Pointer(cPath)) == 0 { 68 | return "", fmt.Errorf("Could not get executable path for app bundle (%s)", bundlePath) 69 | } 70 | defer C.free(unsafe.Pointer(cPath)) 71 | 72 | return C.GoString(cPath), nil 73 | } 74 | 75 | func GetLibraryPath() (string, error) { 76 | cPath := C.GetLibraryPath() 77 | if uintptr(unsafe.Pointer(cPath)) == 0 { 78 | return "", fmt.Errorf("Could not get library path") 79 | } 80 | defer C.free(unsafe.Pointer(cPath)) 81 | 82 | return C.GoString(cPath), nil 83 | } 84 | 85 | func GetHomeDirectory() (string, error) { 86 | cPath := C.GetHomeDirectory() 87 | if uintptr(unsafe.Pointer(cPath)) == 0 { 88 | return "", fmt.Errorf("Could not get home directory") 89 | } 90 | defer C.free(unsafe.Pointer(cPath)) 91 | 92 | return C.GoString(cPath), nil 93 | } 94 | 95 | func GetApplicationSupportPath() (string, error) { 96 | cPath := C.GetApplicationSupportPath() 97 | if uintptr(unsafe.Pointer(cPath)) == 0 { 98 | return "", fmt.Errorf("Could not get application support path") 99 | } 100 | defer C.free(unsafe.Pointer(cPath)) 101 | 102 | return C.GoString(cPath), nil 103 | } 104 | -------------------------------------------------------------------------------- /macox/bundles_darwin_test.go: -------------------------------------------------------------------------------- 1 | package macox_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/itchio/ox/macox" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_GetExecutablePath(t *testing.T) { 11 | s, err := macox.GetExecutablePath("/Applications/Safari.app") 12 | assert.NoError(t, err) 13 | assert.NotEmpty(t, s) 14 | } 15 | 16 | func Test_GetLibraryPath(t *testing.T) { 17 | s, err := macox.GetLibraryPath() 18 | assert.NoError(t, err) 19 | assert.NotEmpty(t, s) 20 | } 21 | 22 | func Test_GetHomeDirectory(t *testing.T) { 23 | s, err := macox.GetHomeDirectory() 24 | assert.NoError(t, err) 25 | assert.NotEmpty(t, s) 26 | } 27 | 28 | func Test_GetApplicationSupportPath(t *testing.T) { 29 | s, err := macox.GetApplicationSupportPath() 30 | assert.NoError(t, err) 31 | assert.NotEmpty(t, s) 32 | } 33 | -------------------------------------------------------------------------------- /preallocate_others.go: -------------------------------------------------------------------------------- 1 | //+build !windows 2 | 3 | package ox 4 | 5 | import ( 6 | "io" 7 | "os" 8 | "syscall" 9 | 10 | "github.com/detailyang/go-fallocate" 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | var SIMULATE_FALLOCATE_NOT_SUPPORTED = false 15 | 16 | func Fallocate(file *os.File, offset int64, length int64) error { 17 | if SIMULATE_FALLOCATE_NOT_SUPPORTED { 18 | return syscall.ENOTSUP 19 | } else { 20 | return fallocate.Fallocate(file, offset, length) 21 | } 22 | } 23 | 24 | // Reserve `size` bytes of space for f, in the 25 | // quickest way possible. f must be opened with O_RDWR. 26 | func Preallocate(f *os.File, size int64) error { 27 | currentSize, err := f.Seek(0, io.SeekEnd) 28 | if err != nil { 29 | return errors.WithStack(err) 30 | } 31 | 32 | remaining := size - currentSize 33 | if remaining <= 0 { 34 | return nil 35 | } 36 | 37 | err = Fallocate(f, currentSize, remaining) 38 | if err != nil { 39 | if errors.Is(err, syscall.ENOTSUP) { 40 | // as of July 2020, we've seen this error condition happpen 41 | // on Linux with NTFS, eCryptFS, and ZFS partitions. ext* are fine. 42 | 43 | // this is the slower fallback: 44 | _, err = f.Seek(currentSize, io.SeekStart) 45 | if err != nil { 46 | return errors.Wrapf(err, "while pre-allocating %v bytes with fallback", size) 47 | } 48 | 49 | _, err = io.Copy(f, io.LimitReader(&zeroReader{}, remaining)) 50 | if err != nil { 51 | return errors.Wrapf(err, "while pre-allocating %v bytes with fallback", size) 52 | } 53 | 54 | return nil 55 | } 56 | 57 | if err != nil { 58 | return errors.Wrapf(err, "while pre-allocating %v bytes with fallocate", size) 59 | } 60 | } 61 | 62 | return nil 63 | } 64 | 65 | type zeroReader struct{} 66 | 67 | var _ io.Reader = (*zeroReader)(nil) 68 | 69 | func (zr *zeroReader) Read(p []byte) (int, error) { 70 | for i := 0; i < len(p); i++ { 71 | p[i] = 0 72 | } 73 | return len(p), nil 74 | } 75 | -------------------------------------------------------------------------------- /preallocate_test.go: -------------------------------------------------------------------------------- 1 | package ox_test 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "testing" 9 | 10 | "github.com/itchio/ox" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func doTestPreallocate(t *testing.T) { 15 | assert := assert.New(t) 16 | f, err := ioutil.TempFile("", "") 17 | must(err) 18 | defer f.Close() 19 | defer os.Remove(f.Name()) 20 | 21 | assertSize := func(expected int64) { 22 | s, err := f.Stat() 23 | must(err) 24 | 25 | assert.Equal(expected, s.Size()) 26 | } 27 | assertSize(0) 28 | 29 | must(ox.Preallocate(f, 2048)) 30 | 31 | assertSize(2048) 32 | 33 | _, err = f.Seek(0, io.SeekStart) 34 | must(err) 35 | 36 | _, err = f.Write([]byte("hello")) 37 | must(err) 38 | 39 | assertSize(2048) 40 | 41 | must(ox.Preallocate(f, 4096)) 42 | assertSize(4096) 43 | 44 | buf := make([]byte, 5) 45 | n, err := f.ReadAt(buf, 0) 46 | must(err) 47 | assert.Equal(5, n) 48 | 49 | assert.Equal("hello", string(buf)) 50 | } 51 | 52 | func Test_Preallocate(t *testing.T) { 53 | t.Logf("With fallocate...") 54 | ox.SIMULATE_FALLOCATE_NOT_SUPPORTED = true 55 | doTestPreallocate(t) 56 | 57 | t.Logf("Without fallocate...") 58 | ox.SIMULATE_FALLOCATE_NOT_SUPPORTED = false 59 | doTestPreallocate(t) 60 | } 61 | 62 | func must(err error) { 63 | if err != nil { 64 | panic(fmt.Sprintf("%+v", err)) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /preallocate_windows.go: -------------------------------------------------------------------------------- 1 | //+build windows 2 | 3 | package ox 4 | 5 | import ( 6 | "io" 7 | "os" 8 | 9 | "github.com/pkg/errors" 10 | "golang.org/x/sys/windows" 11 | ) 12 | 13 | // Note: this does nothing on Windows 14 | var SIMULATE_FALLOCATE_NOT_SUPPORTED = false 15 | 16 | // Reserve `size` bytes of space for f, in the 17 | // quickest way possible. f must be opened with O_RDWR. 18 | func Preallocate(f *os.File, size int64) error { 19 | _, err := f.Seek(size, io.SeekStart) 20 | if err != nil { 21 | return errors.WithStack(err) 22 | } 23 | 24 | err = windows.SetEndOfFile(windows.Handle(f.Fd())) 25 | if err != nil { 26 | return errors.WithStack(err) 27 | } 28 | 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /runtime.go: -------------------------------------------------------------------------------- 1 | package ox 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | type Platform string 12 | 13 | // these coincide with the namings used in the itch.io backend 14 | const ( 15 | PlatformOSX Platform = "osx" 16 | PlatformWindows Platform = "windows" 17 | PlatformLinux Platform = "linux" 18 | PlatformUnknown Platform = "unknown" 19 | ) 20 | 21 | // Runtime describes an os-arch combo in a convenient way 22 | type Runtime struct { 23 | Platform Platform `json:"platform"` 24 | Is64 bool `json:"is64"` 25 | } 26 | 27 | type Runtimes []Runtime 28 | 29 | func (rs Runtimes) HasPlatform(platform Platform) bool { 30 | for _, r := range rs { 31 | if r.Platform == platform { 32 | return true 33 | } 34 | } 35 | return false 36 | } 37 | 38 | func (r Runtime) String() string { 39 | var arch string 40 | if r.Is64 { 41 | arch = "64-bit" 42 | } else { 43 | arch = "32-bit" 44 | } 45 | var platform = "Unknown" 46 | switch r.Platform { 47 | case PlatformLinux: 48 | platform = "Linux" 49 | case PlatformOSX: 50 | platform = "macOS" 51 | case PlatformWindows: 52 | platform = "Windows" 53 | } 54 | return fmt.Sprintf("%s %s", arch, platform) 55 | } 56 | 57 | // OS returns the operating system in GOOS format 58 | func (r Runtime) OS() string { 59 | switch r.Platform { 60 | case PlatformLinux: 61 | return "linux" 62 | case PlatformOSX: 63 | return "darwin" 64 | case PlatformWindows: 65 | return "windows" 66 | default: 67 | return "unknown" 68 | } 69 | } 70 | 71 | // Arch returns the architecture in GOARCH format 72 | func (r Runtime) Arch() string { 73 | if r.Is64 { 74 | return "amd64" 75 | } 76 | return "386" 77 | } 78 | 79 | func (r Runtime) Equals(other Runtime) bool { 80 | return r.Is64 == other.Is64 && r.Platform == other.Platform 81 | } 82 | 83 | var cachedRuntime *Runtime 84 | 85 | func CurrentRuntime() Runtime { 86 | if cachedRuntime == nil { 87 | var is64 = is64Bit() 88 | var platform Platform 89 | switch runtime.GOOS { 90 | case "linux": 91 | platform = PlatformLinux 92 | case "darwin": 93 | platform = PlatformOSX 94 | case "windows": 95 | platform = PlatformWindows 96 | default: 97 | platform = PlatformUnknown 98 | } 99 | 100 | cachedRuntime = &Runtime{ 101 | Is64: is64, 102 | Platform: platform, 103 | } 104 | } 105 | return *cachedRuntime 106 | } 107 | 108 | var win64Arches = map[string]bool{ 109 | "AMD64": true, 110 | "IA64": true, 111 | } 112 | 113 | var hasDeterminedLinux64 = false 114 | var cachedIsLinux64 bool 115 | 116 | func is64Bit() bool { 117 | switch runtime.GOOS { 118 | case "darwin": 119 | // we don't ship for 32-bit mac 120 | return true 121 | case "linux": 122 | if !hasDeterminedLinux64 { 123 | cachedIsLinux64 = determineLinux64() 124 | hasDeterminedLinux64 = true 125 | } 126 | return cachedIsLinux64 127 | case "windows": 128 | // if we're currently running as a 64-bit executable then, 129 | // yeah, we're on 64-bit windows 130 | if runtime.GOARCH == "amd64" { 131 | return true 132 | } 133 | 134 | // otherwise, check environment variables 135 | // any value not in the map will return false (the zero value for bool () 136 | return win64Arches[os.Getenv("PROCESSOR_ARCHITECTURE")] || 137 | win64Arches[os.Getenv("PROCESSOR_ARCHITEW6432")] 138 | } 139 | 140 | // unsupported platform eh :( 141 | return false 142 | } 143 | 144 | func determineLinux64() bool { 145 | unameOutput, err := exec.Command("uname", "-m").Output() 146 | if err == nil { 147 | return strings.TrimSpace(string(unameOutput)) == "x86_64" 148 | } 149 | 150 | archOutput, err := exec.Command("arch").Output() 151 | if err == nil { 152 | return strings.TrimSpace(string(archOutput)) == "x86_64" 153 | } 154 | 155 | // if we're lacking uname AND arch, honestly, our chances are slim. 156 | // but in doubt, let's just assume the architecture of the current binary is the 157 | // same as the os 158 | return runtime.GOARCH == "amd64" 159 | } 160 | -------------------------------------------------------------------------------- /scripts/ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | go version 4 | go test -v -cover -coverprofile=coverage.txt -race ./... 5 | curl -s https://codecov.io/bash | bash 6 | 7 | -------------------------------------------------------------------------------- /syscallex/advapi32_windows.go: -------------------------------------------------------------------------------- 1 | package syscallex 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "golang.org/x/sys/windows" 8 | ) 9 | 10 | // logon flags 11 | const ( 12 | LOGON_WITH_PROFILE = 1 13 | LOGON_CREDENTIALS_ONLY = 2 14 | ) 15 | 16 | // logon type 17 | const ( 18 | LOGON32_LOGON_INTERACTIVE = 2 19 | ) 20 | 21 | // logon provider 22 | const ( 23 | LOGON32_PROVIDER_DEFAULT = 0 24 | ) 25 | 26 | // security impersonation level 27 | const ( 28 | SecurityAnonymous = iota 29 | SecurityIdentification 30 | SecurityImpersonation 31 | SecurityDelegation 32 | ) 33 | 34 | // token types 35 | const ( 36 | TokenPrimary = 1 37 | TokenImpersonation = 2 38 | ) 39 | 40 | var ( 41 | modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") 42 | 43 | procCreateProcessWithLogonW = modadvapi32.NewProc("CreateProcessWithLogonW") 44 | procLogonUserW = modadvapi32.NewProc("LogonUserW") 45 | procImpersonateLoggedOnUser = modadvapi32.NewProc("ImpersonateLoggedOnUser") 46 | procRevertToSelf = modadvapi32.NewProc("RevertToSelf") 47 | procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") 48 | procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW") 49 | procCreateWellKnownSid = modadvapi32.NewProc("CreateWellKnownSid") 50 | 51 | procGetNamedSecurityInfoW = modadvapi32.NewProc("GetNamedSecurityInfoW") 52 | procSetNamedSecurityInfoW = modadvapi32.NewProc("SetNamedSecurityInfoW") 53 | procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW") 54 | procMakeAbsoluteSD = modadvapi32.NewProc("MakeAbsoluteSD") 55 | procSetSecurityDescriptorDacl = modadvapi32.NewProc("SetSecurityDescriptorDacl") 56 | procGetFileSecurityW = modadvapi32.NewProc("GetFileSecurityW") 57 | procSetFileSecurityW = modadvapi32.NewProc("SetFileSecurityW") 58 | procAccessCheck = modadvapi32.NewProc("AccessCheck") 59 | procMapGenericMask = modadvapi32.NewProc("MapGenericMask") 60 | 61 | procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") 62 | procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") 63 | ) 64 | 65 | func CreateProcessWithLogon( 66 | username *uint16, 67 | domain *uint16, 68 | password *uint16, 69 | logonFlags uint32, 70 | appName *uint16, 71 | commandLine *uint16, 72 | creationFlags uint32, 73 | env *uint16, 74 | currentDir *uint16, 75 | startupInfo *syscall.StartupInfo, 76 | outProcInfo *syscall.ProcessInformation, 77 | ) (err error) { 78 | r1, _, e1 := syscall.Syscall12( 79 | procCreateProcessWithLogonW.Addr(), 80 | 11, 81 | uintptr(unsafe.Pointer(username)), 82 | uintptr(unsafe.Pointer(domain)), 83 | uintptr(unsafe.Pointer(password)), 84 | uintptr(logonFlags), 85 | uintptr(unsafe.Pointer(appName)), 86 | uintptr(unsafe.Pointer(commandLine)), 87 | uintptr(creationFlags), 88 | uintptr(unsafe.Pointer(env)), 89 | uintptr(unsafe.Pointer(currentDir)), 90 | uintptr(unsafe.Pointer(startupInfo)), 91 | uintptr(unsafe.Pointer(outProcInfo)), 92 | 0, 93 | ) 94 | if r1 == 0 { 95 | if e1 != 0 { 96 | err = e1 97 | } else { 98 | err = syscall.EINVAL 99 | } 100 | } 101 | return 102 | } 103 | 104 | func LogonUser( 105 | username *uint16, 106 | domain *uint16, 107 | password *uint16, 108 | logonType uint32, 109 | logonProvider uint32, 110 | outToken *syscall.Token, 111 | ) (err error) { 112 | r1, _, e1 := syscall.Syscall6( 113 | procLogonUserW.Addr(), 114 | 6, 115 | uintptr(unsafe.Pointer(username)), 116 | uintptr(unsafe.Pointer(domain)), 117 | uintptr(unsafe.Pointer(password)), 118 | uintptr(logonType), 119 | uintptr(logonProvider), 120 | uintptr(unsafe.Pointer(outToken)), 121 | ) 122 | if r1 == 0 { 123 | if e1 != 0 { 124 | err = e1 125 | } else { 126 | err = syscall.EINVAL 127 | } 128 | } 129 | return 130 | } 131 | 132 | func ImpersonateLoggedOnUser( 133 | token syscall.Token, 134 | ) (err error) { 135 | r1, _, e1 := syscall.Syscall( 136 | procImpersonateLoggedOnUser.Addr(), 137 | 1, 138 | uintptr(token), 139 | 0, 0, 140 | ) 141 | if r1 == 0 { 142 | if e1 != 0 { 143 | err = e1 144 | } else { 145 | err = syscall.EINVAL 146 | } 147 | } 148 | return 149 | } 150 | 151 | func RevertToSelf() (err error) { 152 | r1, _, e1 := syscall.Syscall( 153 | procRevertToSelf.Addr(), 154 | 0, 155 | 0, 0, 0, 156 | ) 157 | if r1 == 0 { 158 | if e1 != 0 { 159 | err = e1 160 | } else { 161 | err = syscall.EINVAL 162 | } 163 | } 164 | return 165 | } 166 | 167 | func LookupAccountName( 168 | systemName *uint16, 169 | accountName *uint16, 170 | sid uintptr, 171 | cbSid *uint32, 172 | referencedDomainName *uint16, 173 | cchReferencedDomainName *uint32, 174 | use *uint32, 175 | ) (err error) { 176 | r1, _, e1 := syscall.Syscall9( 177 | procLookupAccountNameW.Addr(), 178 | 7, 179 | uintptr(unsafe.Pointer(systemName)), 180 | uintptr(unsafe.Pointer(accountName)), 181 | sid, 182 | uintptr(unsafe.Pointer(cbSid)), 183 | uintptr(unsafe.Pointer(referencedDomainName)), 184 | uintptr(unsafe.Pointer(cchReferencedDomainName)), 185 | uintptr(unsafe.Pointer(use)), 186 | 0, 0, 187 | ) 188 | if r1 == 0 { 189 | if e1 != 0 { 190 | err = e1 191 | } else { 192 | err = syscall.EINVAL 193 | } 194 | } 195 | return 196 | } 197 | 198 | func LookupAccountSid( 199 | systemName *uint16, 200 | sid uintptr, 201 | name *uint16, 202 | cchName *uint32, 203 | referencedDomainName *uint16, 204 | cchReferencedDomainName *uint32, 205 | use *uint32, 206 | ) (err error) { 207 | r1, _, e1 := syscall.Syscall9( 208 | procLookupAccountSidW.Addr(), 209 | 7, 210 | uintptr(unsafe.Pointer(systemName)), 211 | sid, 212 | uintptr(unsafe.Pointer(name)), 213 | uintptr(unsafe.Pointer(cchName)), 214 | uintptr(unsafe.Pointer(referencedDomainName)), 215 | uintptr(unsafe.Pointer(cchReferencedDomainName)), 216 | uintptr(unsafe.Pointer(use)), 217 | 0, 0, 218 | ) 219 | if r1 == 0 { 220 | if e1 != 0 { 221 | err = e1 222 | } else { 223 | err = syscall.EINVAL 224 | } 225 | } 226 | return 227 | } 228 | 229 | func CreateWellKnownSid( 230 | wellKnownSidType int, 231 | domainSid uintptr, 232 | sid uintptr, 233 | cbSid *uint32, 234 | ) (err error) { 235 | r1, _, e1 := syscall.Syscall6( 236 | procCreateWellKnownSid.Addr(), 237 | 4, 238 | uintptr(wellKnownSidType), 239 | domainSid, 240 | sid, 241 | uintptr(unsafe.Pointer(cbSid)), 242 | 0, 0, 243 | ) 244 | if r1 == 0 { 245 | if e1 != 0 { 246 | err = e1 247 | } else { 248 | err = syscall.EINVAL 249 | } 250 | } 251 | return 252 | } 253 | 254 | // SE_OBJECT_TYPE, cf. 255 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379593(v=vs.85).aspx 256 | // do not reorder 257 | const ( 258 | SE_UNKNOWN_OBJECT_TYPE = iota 259 | SE_FILE_OBJECT 260 | SE_SERVICE 261 | SE_PRINTER 262 | SE_REGISTRY_KEY 263 | SE_LMSHARE 264 | SE_KERNEL_OBJECT 265 | SE_WINDOW_OBJECT 266 | SE_DS_OBJECT 267 | SE_DS_OBJECT_ALL 268 | SE_PROVIDER_DEFINED_OBJECT 269 | SE_WMIGUID_OBJECT 270 | SE_REGISTRY_WOW64_32KEY 271 | ) 272 | 273 | // see 274 | // https://raw.githubusercontent.com/mirror/reactos/master/reactos/include/xdk/setypes.h 275 | const ( 276 | DELETE = 0x00010000 277 | READ_CONTROL = 0x00020000 278 | WRITE_DAC = 0x00040000 279 | WRITE_OWNER = 0x00080000 280 | SYNCHRONIZE = 0x00100000 281 | STANDARD_RIGHTS_REQUIRED = 0x000F0000 282 | STANDARD_RIGHTS_READ = READ_CONTROL 283 | STANDARD_RIGHTS_WRITE = READ_CONTROL 284 | STANDARD_RIGHTS_EXECUTE = READ_CONTROL 285 | STANDARD_RIGHTS_ALL = 0x001F0000 286 | SPECIFIC_RIGHTS_ALL = 0x0000FFFF 287 | ACCESS_SYSTEM_SECURITY = 0x01000000 288 | MAXIMUM_ALLOWED = 0x02000000 289 | GENERIC_READ = 0x80000000 290 | GENERIC_WRITE = 0x40000000 291 | GENERIC_EXECUTE = 0x20000000 292 | GENERIC_ALL = 0x10000000 293 | 294 | // cf. https://www.codeproject.com/script/Content/ViewAssociatedFile.aspx?rzp=%2FKB%2Fasp%2Fuseraccesscheck%2Fuseraccesscheck_demo.zip&zep=ASPDev%2FMasks.txt&obid=1881&obtid=2&ovid=1 295 | FILE_READ_DATA = (0x0001) // file & pipe 296 | FILE_LIST_DIRECTORY = (0x0001) // directory 297 | 298 | FILE_WRITE_DATA = (0x0002) // file & pipe 299 | FILE_ADD_FILE = (0x0002) // directory 300 | 301 | FILE_APPEND_DATA = (0x0004) // file 302 | FILE_ADD_SUBDIRECTORY = (0x0004) // directory 303 | FILE_CREATE_PIPE_INSTANCE = (0x0004) // named pipe 304 | 305 | FILE_READ_EA = (0x0008) // file & directory 306 | 307 | FILE_WRITE_EA = (0x0010) // file & directory 308 | 309 | FILE_EXECUTE = (0x0020) // file 310 | FILE_TRAVERSE = (0x0020) // directory 311 | 312 | FILE_DELETE_CHILD = (0x0040) // directory 313 | 314 | FILE_READ_ATTRIBUTES = (0x0080) // all 315 | 316 | FILE_WRITE_ATTRIBUTES = (0x0100) // all 317 | 318 | FILE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF) 319 | 320 | FILE_GENERIC_READ = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE) 321 | FILE_GENERIC_WRITE = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE) 322 | FILE_GENERIC_EXECUTE = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE) 323 | ) 324 | 325 | // ACCESS_MODE, cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa374899(v=vs.85).aspx 326 | // do not reorder 327 | const ( 328 | NOT_USED_ACCESS = iota 329 | GRANT_ACCESS 330 | SET_ACCESS 331 | DENY_ACCESS 332 | REVOKE_ACCESS 333 | SET_AUDIT_SUCCESS 334 | SET_AUDIT_FAILURE 335 | ) 336 | 337 | // SECURITY_INFORMATION, cf. 338 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379573(v=vs.85).aspx 339 | // and 340 | // https://raw.githubusercontent.com/mirror/reactos/master/reactos/include/xdk/setypes.h 341 | const ( 342 | OWNER_SECURITY_INFORMATION = 0x00000001 343 | GROUP_SECURITY_INFORMATION = 0x00000002 344 | DACL_SECURITY_INFORMATION = 0x00000004 345 | SACL_SECURITY_INFORMATION = 0x00000008 346 | LABEL_SECURITY_INFORMATION = 0x00000010 347 | 348 | PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000 349 | PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000 350 | UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000 351 | UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000 352 | ) 353 | 354 | // struct _ACL, cf. 355 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa374931(v=vs.85).aspx 356 | type ACL struct { 357 | AclRevision byte 358 | Sbz1 byte 359 | AclSize int16 360 | AceCount int16 361 | Sbz2 int16 362 | } 363 | 364 | func GetNamedSecurityInfo( 365 | objectName *uint16, 366 | objectType uint32, 367 | securityInfo uint32, 368 | ppsidOwner uintptr, 369 | ppsidGroup uintptr, 370 | ppDacl **ACL, 371 | ppSacl **ACL, 372 | ppSecurityDescriptor uintptr, 373 | ) (err error) { 374 | r1, _, _ := syscall.Syscall9( 375 | procGetNamedSecurityInfoW.Addr(), 376 | 8, 377 | uintptr(unsafe.Pointer(objectName)), 378 | uintptr(objectType), 379 | uintptr(securityInfo), 380 | ppsidOwner, 381 | ppsidGroup, 382 | uintptr(unsafe.Pointer(ppDacl)), 383 | uintptr(unsafe.Pointer(ppSacl)), 384 | ppSecurityDescriptor, 385 | 0, 386 | ) 387 | // cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa446645(v=vs.85).aspx 388 | // If the function succeeds, the return value is ERROR_SUCCESS. 389 | // If the function fails, the return value is a nonzero error code defined in WinError.h. 390 | if r1 != 0 { 391 | err = syscall.Errno(r1) 392 | } 393 | return 394 | } 395 | 396 | func SetNamedSecurityInfo( 397 | objectName *uint16, 398 | objectType uint32, 399 | securityInfo uint32, 400 | psidOwner uintptr, 401 | psidGroup uintptr, 402 | pDacl *ACL, 403 | pSacl *ACL, 404 | ) (err error) { 405 | r1, _, _ := syscall.Syscall9( 406 | procSetNamedSecurityInfoW.Addr(), 407 | 7, 408 | uintptr(unsafe.Pointer(objectName)), 409 | uintptr(objectType), 410 | uintptr(securityInfo), 411 | psidOwner, 412 | psidGroup, 413 | uintptr(unsafe.Pointer(pDacl)), 414 | uintptr(unsafe.Pointer(pSacl)), 415 | 0, 0, 416 | ) 417 | // cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa379579(v=vs.85).aspx 418 | // If the function succeeds, the return value is ERROR_SUCCESS. 419 | // If the function fails, the return value is a nonzero error code defined in WinError.h. 420 | if r1 != 0 { 421 | err = syscall.Errno(r1) 422 | } 423 | return 424 | } 425 | 426 | // TRUSTEE_FORM, cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa379638(v=vs.85).aspx 427 | // do not reorder 428 | const ( 429 | TRUSTEE_IS_SID = iota 430 | TRUSTEE_IS_NAME 431 | TRUSTEE_BAD_FORM 432 | TRUSTEE_IS_OBJECTS_AND_SID 433 | TRUSTEE_IS_OBJECTS_AND_NAME 434 | ) 435 | 436 | // struct _EXPLICIT_ACCESS, cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa446627(v=vs.85).aspx 437 | type ExplicitAccess struct { 438 | AccessPermissions uint32 439 | AccessMode uint32 // ACCESS_MODE 440 | Inheritance uint32 441 | Trustee Trustee 442 | } 443 | 444 | // dwInheritance flags in EXPLICIT_ACCESS 445 | const ( 446 | NO_INHERITANCE = 0 447 | OBJECT_INHERIT_ACE = 1 // (OI) 448 | CONTAINER_INHERIT_ACE = 2 // (CI) 449 | NO_PROPAGATE_INHERIT_ACE = 4 450 | ) 451 | 452 | // MULTIPLE_TRUSTEE_OPERATION enum, cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa379284(v=vs.85).aspx 453 | // do not reorder. 454 | const ( 455 | NO_MULTIPLE_TRUSTEE = iota 456 | TRUSTEE_IS_IMPERSONATE 457 | ) 458 | 459 | // TRUSTEE_TYPE enum, cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa379639(v=vs.85).aspx 460 | const ( 461 | TRUSTEE_IS_UNKNOWN = iota 462 | TRUSTEE_IS_USER 463 | TRUSTEE_IS_GROUP 464 | TRUSTEE_IS_DOMAIN 465 | TRUSTEE_IS_ALIAS 466 | TRUSTEE_IS_WELL_KNOWN_GROUP 467 | TRUSTEE_IS_DELETED 468 | TRUSTEE_IS_INVALID 469 | TRUSTEE_IS_COMPUTER 470 | ) 471 | 472 | // struct _TRUSTEE, cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa379636(v=vs.85).aspx 473 | type Trustee struct { 474 | MultipleTrustee *Trustee 475 | MultipleTrusteeOperation uint32 // MULTIPLE_TRUSTEE_OPERATION 476 | TrusteeForm uint32 // TRUSTEE_FORM 477 | TrusteeType uint32 // TRUSTEE_TYPE 478 | Name *uint16 479 | } 480 | 481 | func SetEntriesInAcl( 482 | countOfExplicitEntries uint32, 483 | listOfExplicitEntries uintptr, 484 | oldAcl *ACL, 485 | newAcl **ACL, 486 | ) (err error) { 487 | r1, _, _ := syscall.Syscall6( 488 | procSetEntriesInAclW.Addr(), 489 | 4, 490 | uintptr(countOfExplicitEntries), 491 | listOfExplicitEntries, 492 | uintptr(unsafe.Pointer(oldAcl)), 493 | uintptr(unsafe.Pointer(newAcl)), 494 | 0, 0, 495 | ) 496 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379576(v=vs.85).aspx 497 | // If the function succeeds, the return value is ERROR_SUCCESS. 498 | // If the function fails, the return value is a nonzero error code defined in WinError.h. 499 | if r1 != 0 { 500 | err = syscall.Errno(r1) 501 | } 502 | return 503 | } 504 | 505 | // here be dragons 506 | func MakeAbsoluteSD( 507 | pSelfRelativeSd uintptr, 508 | pAbsoluteSD uintptr, 509 | lpdwAbsoluteSDSize *uint32, 510 | pDacl *ACL, 511 | lpdwDaclSize *uint32, 512 | pSacl *ACL, 513 | lpdwSaclSize *uint32, 514 | pOwner uintptr, 515 | lpdwOwnerSize *uint32, 516 | pPrimaryGroup uintptr, 517 | lpdwPrimaryGroupSize *uint32, 518 | ) (err error) { 519 | r1, _, e1 := syscall.Syscall12( 520 | procMakeAbsoluteSD.Addr(), 521 | 11, 522 | pSelfRelativeSd, 523 | pAbsoluteSD, 524 | uintptr(unsafe.Pointer(lpdwAbsoluteSDSize)), 525 | uintptr(unsafe.Pointer(pDacl)), 526 | uintptr(unsafe.Pointer(lpdwDaclSize)), 527 | uintptr(unsafe.Pointer(pSacl)), 528 | uintptr(unsafe.Pointer(lpdwSaclSize)), 529 | pOwner, 530 | uintptr(unsafe.Pointer(lpdwOwnerSize)), 531 | pPrimaryGroup, 532 | uintptr(unsafe.Pointer(lpdwPrimaryGroupSize)), 533 | 0, 534 | ) 535 | if r1 == 0 { 536 | if e1 != 0 { 537 | err = e1 538 | } else { 539 | err = syscall.EINVAL 540 | } 541 | } 542 | return 543 | } 544 | 545 | func SetSecurityDescriptorDacl( 546 | pSecurityDescriptor uintptr, 547 | bDaclPresent uint32, // BOOL 548 | pDacl *ACL, 549 | bDaclDefaulted uint32, // BOOL 550 | ) (err error) { 551 | r1, _, e1 := syscall.Syscall6( 552 | procSetSecurityDescriptorDacl.Addr(), 553 | 4, 554 | pSecurityDescriptor, 555 | uintptr(bDaclPresent), 556 | uintptr(unsafe.Pointer(pDacl)), 557 | uintptr(bDaclDefaulted), 558 | 0, 0, 559 | ) 560 | if r1 == 0 { 561 | if e1 != 0 { 562 | err = e1 563 | } else { 564 | err = syscall.EINVAL 565 | } 566 | } 567 | return 568 | } 569 | 570 | func GetFileSecurity( 571 | fileName *uint16, 572 | requestedInformation uint32, 573 | pSecurityDescriptor uintptr, 574 | nLength uint32, 575 | nLengthNeeded *uint32, 576 | ) (err error) { 577 | r1, _, e1 := syscall.Syscall6( 578 | procGetFileSecurityW.Addr(), 579 | 5, 580 | uintptr(unsafe.Pointer(fileName)), 581 | uintptr(requestedInformation), 582 | pSecurityDescriptor, 583 | uintptr(nLength), 584 | uintptr(unsafe.Pointer(nLengthNeeded)), 585 | 0, 586 | ) 587 | if r1 == 0 { 588 | if e1 != 0 { 589 | err = e1 590 | } else { 591 | err = syscall.EINVAL 592 | } 593 | } 594 | return 595 | } 596 | 597 | func SetFileSecurity( 598 | fileName *uint16, 599 | securityInformation uint32, 600 | pSecurityDescriptor uintptr, 601 | ) (err error) { 602 | r1, _, e1 := syscall.Syscall( 603 | procSetFileSecurityW.Addr(), 604 | 3, 605 | uintptr(unsafe.Pointer(fileName)), 606 | uintptr(securityInformation), 607 | pSecurityDescriptor, 608 | ) 609 | if r1 == 0 { 610 | if e1 != 0 { 611 | err = e1 612 | } else { 613 | err = syscall.EINVAL 614 | } 615 | } 616 | return 617 | } 618 | 619 | // struct _GENERIC_MAPPING 620 | // cf. https://msdn.microsoft.com/en-us/library/windows/desktop/aa446633(v=vs.85).aspx 621 | type GenericMapping struct { 622 | GenericRead uint32 623 | GenericWrite uint32 624 | GenericExecute uint32 625 | GenericAll uint32 626 | } 627 | 628 | func AccessCheck( 629 | securityDescriptor uintptr, 630 | clientToken syscall.Token, 631 | desiredAccess uint32, 632 | genericMapping *GenericMapping, 633 | privilegeSet uintptr, 634 | privilegeSetLength *uint32, 635 | grantedAccess *uint32, 636 | accessStatus *bool, 637 | ) (err error) { 638 | r1, _, e1 := syscall.Syscall9( 639 | procAccessCheck.Addr(), 640 | 8, 641 | securityDescriptor, 642 | uintptr(clientToken), 643 | uintptr(desiredAccess), 644 | uintptr(unsafe.Pointer(genericMapping)), 645 | privilegeSet, 646 | uintptr(unsafe.Pointer(privilegeSetLength)), 647 | uintptr(unsafe.Pointer(grantedAccess)), 648 | uintptr(unsafe.Pointer(accessStatus)), 649 | 0, 650 | ) 651 | if r1 == 0 { 652 | if e1 != 0 { 653 | err = e1 654 | } else { 655 | err = syscall.EINVAL 656 | } 657 | } 658 | return 659 | } 660 | 661 | func MapGenericMask( 662 | accessMask *uint32, 663 | genericMapping *GenericMapping, 664 | ) { 665 | syscall.Syscall( 666 | procMapGenericMask.Addr(), 667 | 2, 668 | uintptr(unsafe.Pointer(accessMask)), 669 | uintptr(unsafe.Pointer(genericMapping)), 670 | 0, 671 | ) 672 | } 673 | 674 | func LookupPrivilegeValue(systemname *uint16, name *uint16, luid *LUID) (err error) { 675 | r1, _, e1 := syscall.Syscall( 676 | procLookupPrivilegeValueW.Addr(), 677 | 3, 678 | uintptr(unsafe.Pointer(systemname)), 679 | uintptr(unsafe.Pointer(name)), 680 | uintptr(unsafe.Pointer(luid)), 681 | ) 682 | if r1 == 0 { 683 | if e1 != 0 { 684 | err = e1 685 | } else { 686 | err = syscall.EINVAL 687 | } 688 | } 689 | return 690 | } 691 | 692 | func AdjustTokenPrivileges(token syscall.Token, disableAllPrivileges bool, newstate *TOKEN_PRIVILEGES, buflen uint32, prevstate *TOKEN_PRIVILEGES, returnlen *uint32) (ret uint32, err error) { 693 | var _p0 uint32 694 | if disableAllPrivileges { 695 | _p0 = 1 696 | } else { 697 | _p0 = 0 698 | } 699 | r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(newstate)), uintptr(buflen), uintptr(unsafe.Pointer(prevstate)), uintptr(unsafe.Pointer(returnlen))) 700 | ret = uint32(r0) 701 | if true { 702 | if e1 != 0 { 703 | err = e1 704 | } else { 705 | err = syscall.EINVAL 706 | } 707 | } 708 | return 709 | } 710 | 711 | type LUID struct { 712 | LowPart uint32 713 | HighPart int32 714 | } 715 | 716 | type LUID_AND_ATTRIBUTES struct { 717 | Luid LUID 718 | Attributes uint32 719 | } 720 | 721 | type TOKEN_PRIVILEGES struct { 722 | PrivilegeCount uint32 723 | Privileges [1]LUID_AND_ATTRIBUTES 724 | } 725 | 726 | const ( 727 | TOKEN_ADJUST_PRIVILEGES = 0x0020 728 | SE_PRIVILEGE_ENABLED = 0x00000002 729 | ) 730 | 731 | var ( 732 | SE_DEBUG_NAME = syscall.StringToUTF16Ptr("SeAuditPrivilege") 733 | ) 734 | -------------------------------------------------------------------------------- /syscallex/exec_windows.go: -------------------------------------------------------------------------------- 1 | package syscallex 2 | 3 | import ( 4 | "syscall" 5 | "unicode/utf16" 6 | "unsafe" 7 | ) 8 | 9 | // makeCmdLine builds a command line out of args by escaping "special" 10 | // characters and joining the arguments with spaces. 11 | func makeCmdLine(args []string) string { 12 | var s string 13 | for _, v := range args { 14 | if s != "" { 15 | s += " " 16 | } 17 | s += syscall.EscapeArg(v) 18 | } 19 | return s 20 | } 21 | 22 | // createEnvBlock converts an array of environment strings into 23 | // the representation required by CreateProcess: a sequence of NUL 24 | // terminated strings followed by a nil. 25 | // Last bytes are two UCS-2 NULs, or four NUL bytes. 26 | func createEnvBlock(envv []string) *uint16 { 27 | if len(envv) == 0 { 28 | return &utf16.Encode([]rune("\x00\x00"))[0] 29 | } 30 | length := 0 31 | for _, s := range envv { 32 | length += len(s) + 1 33 | } 34 | length += 1 35 | 36 | b := make([]byte, length) 37 | i := 0 38 | for _, s := range envv { 39 | l := len(s) 40 | copy(b[i:i+l], []byte(s)) 41 | copy(b[i+l:i+l+1], []byte{0}) 42 | i = i + l + 1 43 | } 44 | copy(b[i:i+1], []byte{0}) 45 | 46 | return &utf16.Encode([]rune(string(b)))[0] 47 | } 48 | 49 | func isSlash(c uint8) bool { 50 | return c == '\\' || c == '/' 51 | } 52 | 53 | func normalizeDir(dir string) (name string, err error) { 54 | ndir, err := syscall.FullPath(dir) 55 | if err != nil { 56 | return "", err 57 | } 58 | if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) { 59 | // dir cannot have \\server\share\path form 60 | return "", syscall.EINVAL 61 | } 62 | return ndir, nil 63 | } 64 | 65 | func volToUpper(ch int) int { 66 | if 'a' <= ch && ch <= 'z' { 67 | ch += 'A' - 'a' 68 | } 69 | return ch 70 | } 71 | 72 | func joinExeDirAndFName(dir, p string) (name string, err error) { 73 | if len(p) == 0 { 74 | return "", syscall.EINVAL 75 | } 76 | if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) { 77 | // \\server\share\path form 78 | return p, nil 79 | } 80 | if len(p) > 1 && p[1] == ':' { 81 | // has drive letter 82 | if len(p) == 2 { 83 | return "", syscall.EINVAL 84 | } 85 | if isSlash(p[2]) { 86 | return p, nil 87 | } else { 88 | d, err := normalizeDir(dir) 89 | if err != nil { 90 | return "", err 91 | } 92 | if volToUpper(int(p[0])) == volToUpper(int(d[0])) { 93 | return syscall.FullPath(d + "\\" + p[2:]) 94 | } else { 95 | return syscall.FullPath(p) 96 | } 97 | } 98 | } else { 99 | // no drive letter 100 | d, err := normalizeDir(dir) 101 | if err != nil { 102 | return "", err 103 | } 104 | if isSlash(p[0]) { 105 | return syscall.FullPath(d[:2] + p) 106 | } else { 107 | return syscall.FullPath(d + "\\" + p) 108 | } 109 | } 110 | } 111 | 112 | type ProcAttr struct { 113 | Dir string 114 | Env []string 115 | Files []uintptr 116 | Sys *SysProcAttr 117 | } 118 | 119 | type SysProcAttr struct { 120 | HideWindow bool 121 | CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess 122 | CreationFlags uint32 123 | LogonFlags uint32 124 | ProcessHandle syscall.Handle 125 | ThreadHandle syscall.Handle 126 | } 127 | 128 | var zeroProcAttr ProcAttr 129 | var zeroSysProcAttr SysProcAttr 130 | 131 | func StartProcessWithLogon(argv0 string, argv []string, username string, domain string, password string, attr *ProcAttr) (pid int, handle uintptr, err error) { 132 | if len(argv0) == 0 { 133 | return 0, 0, syscall.EWINDOWS 134 | } 135 | if attr == nil { 136 | attr = &zeroProcAttr 137 | } 138 | sys := attr.Sys 139 | if sys == nil { 140 | sys = &zeroSysProcAttr 141 | } 142 | 143 | if len(attr.Files) > 3 { 144 | return 0, 0, syscall.EWINDOWS 145 | } 146 | if len(attr.Files) < 3 { 147 | return 0, 0, syscall.EINVAL 148 | } 149 | 150 | if len(attr.Dir) != 0 { 151 | // StartProcess assumes that argv0 is relative to attr.Dir, 152 | // because it implies Chdir(attr.Dir) before executing argv0. 153 | // Windows CreateProcess assumes the opposite: it looks for 154 | // argv0 relative to the current directory, and, only once the new 155 | // process is started, it does Chdir(attr.Dir). We are adjusting 156 | // for that difference here by making argv0 absolute. 157 | var err error 158 | argv0, err = joinExeDirAndFName(attr.Dir, argv0) 159 | if err != nil { 160 | return 0, 0, err 161 | } 162 | } 163 | argv0p, err := syscall.UTF16PtrFromString(argv0) 164 | if err != nil { 165 | return 0, 0, err 166 | } 167 | 168 | var cmdline string 169 | // Windows CreateProcess takes the command line as a single string: 170 | // use attr.CmdLine if set, else build the command line by escaping 171 | // and joining each argument with spaces 172 | if sys.CmdLine != "" { 173 | cmdline = sys.CmdLine 174 | } else { 175 | cmdline = makeCmdLine(argv) 176 | } 177 | 178 | var argvp *uint16 179 | if len(cmdline) != 0 { 180 | argvp, err = syscall.UTF16PtrFromString(cmdline) 181 | if err != nil { 182 | return 0, 0, err 183 | } 184 | } 185 | 186 | var dirp *uint16 187 | if len(attr.Dir) != 0 { 188 | dirp, err = syscall.UTF16PtrFromString(attr.Dir) 189 | if err != nil { 190 | return 0, 0, err 191 | } 192 | } 193 | 194 | // Acquire the fork lock so that no other threads 195 | // create new fds that are not yet close-on-exec 196 | // before we fork. 197 | syscall.ForkLock.Lock() 198 | defer syscall.ForkLock.Unlock() 199 | 200 | p, _ := syscall.GetCurrentProcess() 201 | fd := make([]syscall.Handle, len(attr.Files)) 202 | for i := range attr.Files { 203 | if attr.Files[i] > 0 { 204 | err := syscall.DuplicateHandle(p, syscall.Handle(attr.Files[i]), p, &fd[i], 0, true, syscall.DUPLICATE_SAME_ACCESS) 205 | if err != nil { 206 | return 0, 0, err 207 | } 208 | defer syscall.CloseHandle(syscall.Handle(fd[i])) 209 | } 210 | } 211 | si := new(syscall.StartupInfo) 212 | si.Cb = uint32(unsafe.Sizeof(*si)) 213 | si.Flags = syscall.STARTF_USESTDHANDLES 214 | if sys.HideWindow { 215 | si.Flags |= syscall.STARTF_USESHOWWINDOW 216 | si.ShowWindow = syscall.SW_HIDE 217 | } 218 | si.StdInput = fd[0] 219 | si.StdOutput = fd[1] 220 | si.StdErr = fd[2] 221 | 222 | if sys.CreationFlags&CREATE_NEW_CONSOLE != 0 { 223 | si.Flags &^= syscall.STARTF_USESTDHANDLES 224 | si.StdInput = 0 225 | si.StdOutput = 0 226 | si.StdErr = 0 227 | } 228 | 229 | pi := new(syscall.ProcessInformation) 230 | 231 | if username == "" { 232 | creationFlags := sys.CreationFlags | syscall.CREATE_UNICODE_ENVIRONMENT 233 | err = syscall.CreateProcess( 234 | argv0p, argvp, nil, nil, true, creationFlags, createEnvBlock(attr.Env), dirp, si, pi, 235 | ) 236 | if err != nil { 237 | return 0, 0, err 238 | } 239 | } else { 240 | usernamep := syscall.StringToUTF16Ptr(username) 241 | domainp := syscall.StringToUTF16Ptr(domain) 242 | passwordp := syscall.StringToUTF16Ptr(password) 243 | 244 | creationFlags := sys.CreationFlags | syscall.CREATE_UNICODE_ENVIRONMENT 245 | logonFlags := sys.LogonFlags 246 | err = CreateProcessWithLogon( 247 | usernamep, domainp, passwordp, logonFlags, 248 | argv0p, argvp, creationFlags, createEnvBlock(attr.Env), dirp, si, pi, 249 | ) 250 | if err != nil { 251 | return 0, 0, err 252 | } 253 | } 254 | attr.Sys.ThreadHandle = syscall.Handle(pi.Thread) 255 | attr.Sys.ProcessHandle = syscall.Handle(pi.Process) 256 | 257 | return int(pi.ProcessId), uintptr(pi.Process), nil 258 | } 259 | -------------------------------------------------------------------------------- /syscallex/kernel32_windows.go: -------------------------------------------------------------------------------- 1 | package syscallex 2 | 3 | import ( 4 | "runtime" 5 | "syscall" 6 | "unsafe" 7 | 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | var ( 12 | modkernel32 = windows.NewLazySystemDLL("kernel32.dll") 13 | 14 | procCreateJobObject = modkernel32.NewProc("CreateJobObjectW") 15 | procSetInformationJobObject = modkernel32.NewProc("SetInformationJobObject") 16 | procQueryInformationJobObject = modkernel32.NewProc("QueryInformationJobObject") 17 | procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") 18 | 19 | procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") 20 | procOpenThreadToken = modkernel32.NewProc("OpenThreadToken") 21 | procGetDiskFreeSpaceExW = modkernel32.NewProc("GetDiskFreeSpaceExW") 22 | 23 | procOpenThread = modkernel32.NewProc("OpenThread") 24 | procSuspendThread = modkernel32.NewProc("SuspendThread") 25 | procResumeThread = modkernel32.NewProc("ResumeThread") 26 | procThread32First = modkernel32.NewProc("Thread32First") 27 | procThread32Next = modkernel32.NewProc("Thread32Next") 28 | 29 | procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot") 30 | procProcess32FirstW = modkernel32.NewProc("Process32FirstW") 31 | procProcess32NextW = modkernel32.NewProc("Process32NextW") 32 | 33 | procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW") 34 | 35 | procVirtualAllocEx = modkernel32.NewProc("VirtualAllocEx") 36 | procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory") 37 | procCreateRemoteThread = modkernel32.NewProc("CreateRemoteThread") 38 | procVirtualFreeEx = modkernel32.NewProc("VirtualFreeEx") 39 | procGetExitCodeThread = modkernel32.NewProc("GetExitCodeThread") 40 | ) 41 | 42 | // JobObjectInfoClass 43 | // cf. https://msdn.microsoft.com/en-us/library/windows/desktop/ms686216%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 44 | const ( 45 | JobObjectInfoClass_JobObjectBasicProcessIdList = 3 46 | JobObjectInfoClass_JobObjectAssociateCompletionPortInformation = 7 47 | JobObjectInfoClass_JobObjectExtendedLimitInformation = 9 48 | ) 49 | 50 | // JobObjectBasicLimitInformation.LimitFlags 51 | const ( 52 | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000 53 | ) 54 | 55 | // job object completion statuses, thanks wine! 56 | // cf. https://www.winehq.org/pipermail/wine-cvs/2013-October/097834.html 57 | const ( 58 | JOB_OBJECT_MSG_END_OF_JOB_TIME = 1 59 | JOB_OBJECT_MSG_END_OF_PROCESS_TIME = 2 60 | JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT = 3 61 | JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO = 4 62 | JOB_OBJECT_MSG_NEW_PROCESS = 6 63 | JOB_OBJECT_MSG_EXIT_PROCESS = 7 64 | JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS = 8 65 | JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT = 9 66 | JOB_OBJECT_MSG_JOB_MEMORY_LIMIT = 10 67 | ) 68 | 69 | type JobObjectAssociateCompletionPort struct { 70 | CompletionKey syscall.Handle 71 | CompletionPort syscall.Handle 72 | } 73 | 74 | const ( 75 | CREATE_SUSPENDED = 0x00000004 76 | CREATE_NEW_CONSOLE = 0x00000010 77 | PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff 78 | THREAD_SUSPEND_RESUME = 0x0002 79 | 80 | TH32CS_SNAPPROCESS = 0x00000002 81 | ) 82 | 83 | type ThreadEntry32 struct { 84 | Size uint32 85 | TUsage uint32 86 | ThreadID uint32 87 | OwnerProcessID uint32 88 | BasePri int32 89 | DeltaPri int32 90 | Flags uint32 91 | } 92 | 93 | type ProcessEntry32 struct { 94 | Size uint32 95 | CntUsage uint32 96 | ProcessID uint32 97 | DefaultHeapID uintptr 98 | ModuleID uint32 99 | CntThreads uint32 100 | ParentProcessID uint32 101 | PriorityClassBase int32 102 | Flags uint32 103 | ExeFile [MAX_PATH]uint16 104 | } 105 | 106 | func CreateJobObject( 107 | jobAttributes *syscall.SecurityAttributes, 108 | name *uint16, 109 | ) (handle syscall.Handle, err error) { 110 | r1, _, e1 := syscall.Syscall( 111 | procCreateJobObject.Addr(), 112 | 2, 113 | uintptr(unsafe.Pointer(jobAttributes)), 114 | uintptr(unsafe.Pointer(name)), 115 | 0, 116 | ) 117 | handle = syscall.Handle(r1) 118 | if r1 == 0 { 119 | if e1 != 0 { 120 | err = e1 121 | } else { 122 | err = syscall.EINVAL 123 | } 124 | } 125 | return 126 | } 127 | 128 | type IoCounters struct { 129 | ReadOperationCount uint64 130 | WriteOperationCount uint64 131 | OtherOperationCount uint64 132 | ReadTransferCount uint64 133 | WriteTransferCount uint64 134 | OtherTransferCount uint64 135 | } 136 | 137 | func SetInformationJobObject( 138 | jobObject syscall.Handle, 139 | jobObjectInfoClass uint32, 140 | jobObjectInfo uintptr, 141 | jobObjectInfoLength uintptr, 142 | ) (err error) { 143 | r1, _, e1 := syscall.Syscall6( 144 | procSetInformationJobObject.Addr(), 145 | 4, 146 | uintptr(jobObject), 147 | uintptr(jobObjectInfoClass), 148 | jobObjectInfo, 149 | jobObjectInfoLength, 150 | 0, 0, 151 | ) 152 | if r1 == 0 { 153 | if e1 != 0 { 154 | err = e1 155 | } else { 156 | err = syscall.EINVAL 157 | } 158 | } 159 | return 160 | } 161 | 162 | func QueryInformationJobObject( 163 | jobObject syscall.Handle, 164 | jobObjectInfoClass uint32, 165 | jobObjectInfo uintptr, 166 | jobObjectInfoLength uintptr, 167 | returnLength uintptr, 168 | ) (err error) { 169 | r1, _, e1 := syscall.Syscall6( 170 | procQueryInformationJobObject.Addr(), 171 | 5, 172 | uintptr(jobObject), 173 | uintptr(jobObjectInfoClass), 174 | jobObjectInfo, 175 | jobObjectInfoLength, 176 | returnLength, 177 | 0, 178 | ) 179 | if r1 == 0 { 180 | if e1 != 0 { 181 | err = e1 182 | } else { 183 | err = syscall.EINVAL 184 | } 185 | } 186 | return 187 | } 188 | 189 | func AssignProcessToJobObject( 190 | jobObject syscall.Handle, 191 | process syscall.Handle, 192 | ) (err error) { 193 | r1, _, e1 := syscall.Syscall( 194 | procAssignProcessToJobObject.Addr(), 195 | 2, 196 | uintptr(jobObject), 197 | uintptr(process), 198 | 0, 199 | ) 200 | if r1 == 0 { 201 | if e1 != 0 { 202 | err = e1 203 | } else { 204 | err = syscall.EINVAL 205 | } 206 | } 207 | return 208 | } 209 | 210 | func GetCurrentThread() syscall.Handle { 211 | r1, _, _ := syscall.Syscall( 212 | procGetCurrentThread.Addr(), 213 | 0, 214 | 0, 0, 0, 215 | ) 216 | return syscall.Handle(r1) 217 | } 218 | 219 | func OpenThreadToken( 220 | threadHandle syscall.Handle, 221 | desiredAccess uint32, 222 | openAsSelf uint32, 223 | tokenHandle *syscall.Token, 224 | ) (err error) { 225 | r1, _, e1 := syscall.Syscall6( 226 | procOpenThreadToken.Addr(), 227 | 4, 228 | uintptr(threadHandle), 229 | uintptr(desiredAccess), 230 | uintptr(openAsSelf), 231 | uintptr(unsafe.Pointer(tokenHandle)), 232 | 0, 0, 233 | ) 234 | if r1 == 0 { 235 | if e1 != 0 { 236 | err = e1 237 | } else { 238 | err = syscall.EINVAL 239 | } 240 | } 241 | return 242 | } 243 | 244 | type DiskFreeSpace struct { 245 | FreeBytesAvailable uint64 246 | TotalNumberOfBytes uint64 247 | TotalNumberOfFreeBytes uint64 248 | } 249 | 250 | func GetDiskFreeSpaceEx(path *uint16) (dfs *DiskFreeSpace, err error) { 251 | var buf DiskFreeSpace 252 | dfs = &buf 253 | 254 | r1, _, e1 := syscall.Syscall6( 255 | procGetDiskFreeSpaceExW.Addr(), 256 | 4, 257 | uintptr(unsafe.Pointer(path)), 258 | uintptr(unsafe.Pointer(&buf.FreeBytesAvailable)), 259 | uintptr(unsafe.Pointer(&buf.TotalNumberOfBytes)), 260 | uintptr(unsafe.Pointer(&buf.TotalNumberOfFreeBytes)), 261 | 0, 0, 262 | ) 263 | if r1 == 0 { 264 | if e1 != 0 { 265 | err = e1 266 | } else { 267 | err = syscall.EINVAL 268 | } 269 | } 270 | return dfs, err 271 | } 272 | 273 | func OpenThread( 274 | desiredAccess uint32, 275 | inheritHandle uint32, 276 | threadId uint32, 277 | ) (handle syscall.Handle, err error) { 278 | r1, _, e1 := syscall.Syscall( 279 | procOpenThread.Addr(), 280 | 3, 281 | uintptr(desiredAccess), 282 | uintptr(inheritHandle), 283 | uintptr(threadId), 284 | ) 285 | handle = syscall.Handle(r1) 286 | if r1 == 0 { 287 | if e1 != 0 { 288 | err = e1 289 | } else { 290 | err = syscall.EINVAL 291 | } 292 | } 293 | return 294 | } 295 | 296 | func SuspendThread( 297 | thread syscall.Handle, 298 | ) (retCount uint32, err error) { 299 | r1, _, e1 := syscall.Syscall( 300 | procSuspendThread.Addr(), 301 | 1, 302 | uintptr(thread), 303 | 0, 304 | 0, 305 | ) 306 | 307 | minusOne := int(-1) 308 | if r1 == uintptr(minusOne) { 309 | if e1 != 0 { 310 | err = e1 311 | } else { 312 | err = syscall.EINVAL 313 | } 314 | } else { 315 | retCount = uint32(r1) 316 | } 317 | return 318 | } 319 | 320 | func ResumeThread( 321 | thread syscall.Handle, 322 | ) (retCount uint32, err error) { 323 | r1, _, e1 := syscall.Syscall( 324 | procResumeThread.Addr(), 325 | 1, 326 | uintptr(thread), 327 | 0, 328 | 0, 329 | ) 330 | 331 | minusOne := int(-1) 332 | if r1 == uintptr(minusOne) { 333 | if e1 != 0 { 334 | err = e1 335 | } else { 336 | err = syscall.EINVAL 337 | } 338 | } else { 339 | retCount = uint32(r1) 340 | } 341 | return 342 | } 343 | 344 | func Thread32First( 345 | snapshot syscall.Handle, 346 | pThreadEntry *ThreadEntry32, 347 | ) (err error) { 348 | r1, _, e1 := syscall.Syscall( 349 | procThread32First.Addr(), 350 | 2, 351 | uintptr(snapshot), 352 | uintptr(unsafe.Pointer(pThreadEntry)), 353 | 0, 354 | ) 355 | if r1 == 0 { 356 | if e1 != 0 { 357 | err = e1 358 | } else { 359 | err = syscall.EINVAL 360 | } 361 | } 362 | return 363 | } 364 | 365 | func Thread32Next( 366 | snapshot syscall.Handle, 367 | pThreadEntry *ThreadEntry32, 368 | ) (err error) { 369 | r1, _, e1 := syscall.Syscall( 370 | procThread32Next.Addr(), 371 | 2, 372 | uintptr(snapshot), 373 | uintptr(unsafe.Pointer(pThreadEntry)), 374 | 0, 375 | ) 376 | if r1 == 0 { 377 | if e1 != 0 { 378 | err = e1 379 | } else { 380 | err = syscall.EINVAL 381 | } 382 | } 383 | return 384 | } 385 | 386 | func CreateToolhelp32Snapshot( 387 | flags uint32, 388 | processID uint32, 389 | ) (handle syscall.Handle, err error) { 390 | r1, _, e1 := syscall.Syscall( 391 | procCreateToolhelp32Snapshot.Addr(), 392 | 2, 393 | uintptr(flags), 394 | uintptr(processID), 395 | 0, 396 | ) 397 | handle = syscall.Handle(r1) 398 | if r1 == 0 { 399 | if e1 != 0 { 400 | err = e1 401 | } else { 402 | err = syscall.EINVAL 403 | } 404 | } 405 | return 406 | } 407 | 408 | func Process32First( 409 | snapshot syscall.Handle, 410 | pProcessEntry *ProcessEntry32, 411 | ) (err error) { 412 | r1, _, e1 := syscall.Syscall( 413 | procProcess32FirstW.Addr(), 414 | 2, 415 | uintptr(snapshot), 416 | uintptr(unsafe.Pointer(pProcessEntry)), 417 | 0, 418 | ) 419 | if r1 == 0 { 420 | if e1 != 0 { 421 | err = e1 422 | } else { 423 | err = syscall.EINVAL 424 | } 425 | } 426 | return 427 | } 428 | 429 | func Process32Next( 430 | snapshot syscall.Handle, 431 | pProcessEntry *ProcessEntry32, 432 | ) (err error) { 433 | r1, _, e1 := syscall.Syscall( 434 | procProcess32NextW.Addr(), 435 | 2, 436 | uintptr(snapshot), 437 | uintptr(unsafe.Pointer(pProcessEntry)), 438 | 0, 439 | ) 440 | if r1 == 0 { 441 | if e1 != 0 { 442 | err = e1 443 | } else { 444 | err = syscall.EINVAL 445 | } 446 | } 447 | return 448 | } 449 | 450 | func QueryFullProcessImageName( 451 | process syscall.Handle, 452 | flags uint32, 453 | ) (s string, err error) { 454 | var bufferSize uint32 = 32 * 1024 455 | buffer := make([]uint16, bufferSize) 456 | 457 | r1, _, e1 := syscall.Syscall6( 458 | procQueryFullProcessImageNameW.Addr(), 459 | 4, 460 | uintptr(process), 461 | uintptr(flags), 462 | uintptr(unsafe.Pointer(&buffer[0])), 463 | uintptr(unsafe.Pointer(&bufferSize)), 464 | 0, 465 | 0, 466 | ) 467 | if r1 == 0 { 468 | if e1 != 0 { 469 | err = e1 470 | } else { 471 | err = syscall.EINVAL 472 | } 473 | } 474 | if err == nil { 475 | s = syscall.UTF16ToString(buffer[:bufferSize]) 476 | } 477 | return 478 | } 479 | 480 | const ( 481 | MEM_COMMIT = 0x00001000 482 | MEM_RESERVE = 0x00002000 483 | PAGE_READWRITE = 0x04 484 | ) 485 | 486 | func VirtualAllocEx( 487 | process syscall.Handle, 488 | address uintptr, 489 | size uintptr, 490 | allocationType uint32, 491 | protect uint32, 492 | ) (res uintptr, err error) { 493 | r1, _, e1 := syscall.Syscall6( 494 | procVirtualAllocEx.Addr(), 495 | 5, 496 | uintptr(process), 497 | address, 498 | uintptr(size), 499 | uintptr(allocationType), 500 | uintptr(protect), 501 | 0, 502 | ) 503 | res = uintptr(r1) 504 | if r1 == 0 { 505 | if e1 != 0 { 506 | err = e1 507 | } else { 508 | err = syscall.EINVAL 509 | } 510 | } 511 | return 512 | } 513 | 514 | func WriteProcessMemory(process syscall.Handle, addr uintptr, buf unsafe.Pointer, size uint32) (nLength uint32, err error) { 515 | r1, _, e1 := syscall.Syscall6( 516 | procWriteProcessMemory.Addr(), 517 | 5, 518 | uintptr(process), 519 | addr, 520 | uintptr(buf), 521 | uintptr(size), 522 | uintptr(unsafe.Pointer(&nLength)), 523 | 0, 524 | ) 525 | if r1 == 0 { 526 | if e1 != 0 { 527 | err = e1 528 | } else { 529 | err = syscall.EINVAL 530 | } 531 | } 532 | return 533 | } 534 | 535 | func CreateRemoteThread(process syscall.Handle, sa *syscall.SecurityAttributes, stackSize uint32, startAddress, 536 | parameter uintptr, creationFlags uint32) (ret syscall.Handle, threadId uint32, err error) { 537 | r1, _, e1 := syscall.Syscall9( 538 | procCreateRemoteThread.Addr(), 539 | 7, 540 | uintptr(process), 541 | uintptr(unsafe.Pointer(sa)), 542 | uintptr(stackSize), 543 | startAddress, 544 | parameter, 545 | uintptr(creationFlags), 546 | uintptr(unsafe.Pointer(&threadId)), 547 | 0, 0, 548 | ) 549 | runtime.KeepAlive(sa) 550 | ret = syscall.Handle(r1) 551 | if r1 == 0 { 552 | if e1 != 0 { 553 | err = e1 554 | } else { 555 | err = syscall.EINVAL 556 | } 557 | } 558 | return 559 | } 560 | 561 | const ( 562 | MEM_RELEASE = 0x8000 563 | ) 564 | 565 | func VirtualFreeEx(process syscall.Handle, addr uintptr, size, freeType uint32) (err error) { 566 | r1, _, e1 := syscall.Syscall6( 567 | procVirtualFreeEx.Addr(), 568 | 4, 569 | uintptr(process), 570 | addr, 571 | uintptr(size), 572 | uintptr(freeType), 573 | 0, 0, 574 | ) 575 | if r1 == 0 { 576 | if e1 != 0 { 577 | err = e1 578 | } else { 579 | err = syscall.EINVAL 580 | } 581 | } 582 | return 583 | } 584 | 585 | func GetExitCodeThread(thread syscall.Handle) (exitCode uint32, err error) { 586 | r1, _, e1 := syscall.Syscall( 587 | procGetExitCodeThread.Addr(), 588 | 2, 589 | uintptr(thread), 590 | uintptr(unsafe.Pointer(&exitCode)), 591 | 0, 592 | ) 593 | if r1 == 0 { 594 | if e1 != 0 { 595 | err = e1 596 | } else { 597 | err = syscall.EINVAL 598 | } 599 | } 600 | return 601 | } 602 | -------------------------------------------------------------------------------- /syscallex/kernel32_windows_386.go: -------------------------------------------------------------------------------- 1 | // +build windows,386 2 | 3 | package syscallex 4 | 5 | type JobObjectExtendedLimitInformation struct { 6 | BasicLimitInformation JobObjectBasicLimitInformation 7 | IoInfo IoCounters 8 | ProcessMemoryLimit uintptr 9 | JobMemoryLimit uintptr 10 | PeakProcessMemoryUsed uintptr 11 | PeakJobMemoryUsed uintptr 12 | } 13 | 14 | type JobObjectBasicLimitInformation struct { 15 | PerProcessUserTimeLimit uint64 // LARGE_INTEGER 16 | PerJobUserTimeLimit uint64 // LARGE_INTEGER 17 | LimitFlags uint32 // DWORD 18 | MinimumWorkingSetSize uintptr // SIZE_T 19 | MaximumWorkingSetSize uintptr // SIZE_T 20 | ActiveProcessLimit uint32 // DWORD 21 | Affinity uintptr // originally ULONG_PTR 22 | PriorityClass uint32 // DWORD 23 | SchedulingClass uint32 // DWORD 24 | _ [4]byte // padding 25 | } 26 | 27 | type JobObjectBasicProcessIdList struct { 28 | NumberOfAssignedProcesses uint32 29 | NumberOfProcessIdsInList uint32 30 | ProcessIdList [32]uint32 // ULONG_PTR[1] 31 | } 32 | -------------------------------------------------------------------------------- /syscallex/kernel32_windows_amd64.go: -------------------------------------------------------------------------------- 1 | // +build windows,amd64 2 | 3 | package syscallex 4 | 5 | type JobObjectExtendedLimitInformation struct { 6 | BasicLimitInformation JobObjectBasicLimitInformation 7 | IoInfo IoCounters 8 | ProcessMemoryLimit uintptr 9 | JobMemoryLimit uintptr 10 | PeakProcessMemoryUsed uintptr 11 | PeakJobMemoryUsed uintptr 12 | } 13 | 14 | type JobObjectBasicLimitInformation struct { 15 | PerProcessUserTimeLimit uint64 // LARGE_INTEGER 16 | PerJobUserTimeLimit uint64 // LARGE_INTEGER 17 | LimitFlags uint32 // DWORD 18 | MinimumWorkingSetSize uintptr // SIZE_T 19 | MaximumWorkingSetSize uintptr // SIZE_T 20 | ActiveProcessLimit uint32 // DWORD 21 | Affinity uintptr // originally ULONG_PTR 22 | PriorityClass uint32 // DWORD 23 | SchedulingClass uint32 // DWORD 24 | } 25 | 26 | type JobObjectBasicProcessIdList struct { 27 | NumberOfAssignedProcesses uint32 28 | NumberOfProcessIdsInList uint32 29 | ProcessIdList [32]uint64 // ULONG_PTR[1] 30 | } 31 | -------------------------------------------------------------------------------- /syscallex/netapi32_windows.go: -------------------------------------------------------------------------------- 1 | package syscallex 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "golang.org/x/sys/windows" 8 | ) 9 | 10 | var ( 11 | modnetapi32 = windows.NewLazySystemDLL("netapi32.dll") 12 | 13 | procNetUserAdd = modnetapi32.NewProc("NetUserAdd") 14 | procNetUserSetInfo = modnetapi32.NewProc("NetUserSetInfo") 15 | procNetLocalGroupDelMembers = modnetapi32.NewProc("NetLocalGroupDelMembers") 16 | procNetUserChangePassword = modnetapi32.NewProc("NetUserChangePassword") 17 | ) 18 | 19 | // struct _USER_INFO_1, cf. 20 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa371109(v=vs.85).aspx 21 | type UserInfo1 struct { 22 | Name *uint16 23 | Password *uint16 24 | PasswordAge uint32 25 | Priv uint32 26 | HomeDir *uint16 27 | Comment *uint16 28 | Flags uint32 29 | ScriptPath *uint16 30 | } 31 | 32 | // struct _USER_INFO_1003, cf. 33 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa370963(v=vs.85).aspx 34 | type UserInfo1003 struct { 35 | Password *uint16 36 | } 37 | 38 | // struct LOCALGROUP_MEMBERS_INFO_3, cf. 39 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa370281(v=vs.85).aspx 40 | type LocalGroupMembersInfo3 struct { 41 | DomainAndName *uint16 42 | } 43 | 44 | // see http://www.rensselaer.org/dept/cis/software/g77-mingw32/include/lmaccess.h 45 | const ( 46 | UF_SCRIPT = 1 47 | ) 48 | 49 | const ( 50 | USER_PRIV_GUEST = 0 51 | USER_PRIV_USER = 1 52 | USER_PRIV_ADMIN = 2 53 | ) 54 | 55 | func NetUserAdd( 56 | servername *uint16, 57 | level uint32, 58 | buf uintptr, 59 | parmErr *uint32, 60 | ) (err error) { 61 | r1, _, _ := syscall.Syscall6( 62 | procNetUserAdd.Addr(), 63 | 4, 64 | uintptr(unsafe.Pointer(servername)), 65 | uintptr(level), 66 | buf, 67 | uintptr(unsafe.Pointer(parmErr)), 68 | 0, 0, 69 | ) 70 | if r1 != 0 { 71 | err = syscall.Errno(r1) 72 | } 73 | return 74 | } 75 | 76 | func NetUserSetInfo( 77 | servername *uint16, 78 | username *uint16, 79 | level uint32, 80 | buf uintptr, 81 | parmErr *uint32, 82 | ) (err error) { 83 | r1, _, _ := syscall.Syscall6( 84 | procNetUserSetInfo.Addr(), 85 | 5, 86 | uintptr(unsafe.Pointer(servername)), 87 | uintptr(unsafe.Pointer(username)), 88 | uintptr(level), 89 | buf, 90 | uintptr(unsafe.Pointer(parmErr)), 91 | 0, 92 | ) 93 | if r1 != 0 { 94 | err = syscall.Errno(r1) 95 | } 96 | return 97 | } 98 | 99 | func NetLocalGroupDelMembers( 100 | servername *uint16, 101 | groupname *uint16, 102 | level uint32, 103 | buf uintptr, 104 | totalentries uint32, 105 | ) (err error) { 106 | r1, _, _ := syscall.Syscall6( 107 | procNetLocalGroupDelMembers.Addr(), 108 | 5, 109 | uintptr(unsafe.Pointer(servername)), 110 | uintptr(unsafe.Pointer(groupname)), 111 | uintptr(level), 112 | buf, 113 | uintptr(totalentries), 114 | 0, 115 | ) 116 | if r1 != 0 { 117 | err = syscall.Errno(r1) 118 | } 119 | return 120 | } 121 | 122 | // cf. https://www.rpi.edu/dept/cis/software/g77-mingw32/include/winerror.h 123 | const ( 124 | ERROR_INVALID_PASSWORD syscall.Errno = 86 125 | ERROR_PASSWORD_EXPIRED syscall.Errno = 1330 126 | ERROR_PASSWORD_MUST_CHANGE syscall.Errno = 1907 127 | ERROR_MEMBER_NOT_IN_ALIAS syscall.Errno = 1377 128 | ) 129 | 130 | func NetUserChangePassword( 131 | domainname *uint16, 132 | username *uint16, 133 | oldpassword *uint16, 134 | newpassword *uint16, 135 | ) (err error) { 136 | r1, _, _ := syscall.Syscall6( 137 | procNetUserChangePassword.Addr(), 138 | 4, 139 | uintptr(unsafe.Pointer(domainname)), 140 | uintptr(unsafe.Pointer(username)), 141 | uintptr(unsafe.Pointer(oldpassword)), 142 | uintptr(unsafe.Pointer(newpassword)), 143 | 0, 0, 144 | ) 145 | if r1 != 0 { 146 | err = syscall.Errno(r1) 147 | } 148 | return 149 | } 150 | -------------------------------------------------------------------------------- /syscallex/neterrors_windows.go: -------------------------------------------------------------------------------- 1 | // MACHINE GENERATED BY https://github.com/fasterthanlime/neterrors; DO NOT EDIT 2 | 3 | // !build windows 4 | 5 | package syscallex 6 | 7 | import "syscall" 8 | 9 | const ( 10 | NERR_NetNotStarted syscall.Errno = 2102 11 | NERR_UnknownServer syscall.Errno = 2103 12 | NERR_ShareMem syscall.Errno = 2104 13 | NERR_NoNetworkResource syscall.Errno = 2105 14 | NERR_RemoteOnly syscall.Errno = 2106 15 | NERR_DevNotRedirected syscall.Errno = 2107 16 | NERR_ServerNotStarted syscall.Errno = 2114 17 | NERR_ItemNotFound syscall.Errno = 2115 18 | NERR_UnknownDevDir syscall.Errno = 2116 19 | NERR_RedirectedPath syscall.Errno = 2117 20 | NERR_DuplicateShare syscall.Errno = 2118 21 | NERR_NoRoom syscall.Errno = 2119 22 | NERR_TooManyItems syscall.Errno = 2121 23 | NERR_InvalidMaxUsers syscall.Errno = 2122 24 | NERR_BufTooSmall syscall.Errno = 2123 25 | NERR_RemoteErr syscall.Errno = 2127 26 | NERR_LanmanIniError syscall.Errno = 2131 27 | NERR_NetworkError syscall.Errno = 2136 28 | NERR_WkstaInconsistentState syscall.Errno = 2137 29 | NERR_WkstaNotStarted syscall.Errno = 2138 30 | NERR_BrowserNotStarted syscall.Errno = 2139 31 | NERR_InternalError syscall.Errno = 2140 32 | NERR_BadTransactConfig syscall.Errno = 2141 33 | NERR_InvalidAPI syscall.Errno = 2142 34 | NERR_BadEventName syscall.Errno = 2143 35 | NERR_DupNameReboot syscall.Errno = 2144 36 | NERR_CfgCompNotFound syscall.Errno = 2146 37 | NERR_CfgParamNotFound syscall.Errno = 2147 38 | NERR_LineTooLong syscall.Errno = 2149 39 | NERR_QNotFound syscall.Errno = 2150 40 | NERR_JobNotFound syscall.Errno = 2151 41 | NERR_DestNotFound syscall.Errno = 2152 42 | NERR_DestExists syscall.Errno = 2153 43 | NERR_QExists syscall.Errno = 2154 44 | NERR_QNoRoom syscall.Errno = 2155 45 | NERR_JobNoRoom syscall.Errno = 2156 46 | NERR_DestNoRoom syscall.Errno = 2157 47 | NERR_DestIdle syscall.Errno = 2158 48 | NERR_DestInvalidOp syscall.Errno = 2159 49 | NERR_ProcNoRespond syscall.Errno = 2160 50 | NERR_SpoolerNotLoaded syscall.Errno = 2161 51 | NERR_DestInvalidState syscall.Errno = 2162 52 | NERR_QinvalidState syscall.Errno = 2163 53 | NERR_JobInvalidState syscall.Errno = 2164 54 | NERR_SpoolNoMemory syscall.Errno = 2165 55 | NERR_DriverNotFound syscall.Errno = 2166 56 | NERR_DataTypeInvalid syscall.Errno = 2167 57 | NERR_ProcNotFound syscall.Errno = 2168 58 | NERR_ServiceTableLocked syscall.Errno = 2180 59 | NERR_ServiceTableFull syscall.Errno = 2181 60 | NERR_ServiceInstalled syscall.Errno = 2182 61 | NERR_ServiceEntryLocked syscall.Errno = 2183 62 | NERR_ServiceNotInstalled syscall.Errno = 2184 63 | NERR_BadServiceName syscall.Errno = 2185 64 | NERR_ServiceCtlTimeout syscall.Errno = 2186 65 | NERR_ServiceCtlBusy syscall.Errno = 2187 66 | NERR_BadServiceProgName syscall.Errno = 2188 67 | NERR_ServiceNotCtrl syscall.Errno = 2189 68 | NERR_ServiceKillProc syscall.Errno = 2190 69 | NERR_ServiceCtlNotValid syscall.Errno = 2191 70 | NERR_NotInDispatchTbl syscall.Errno = 2192 71 | NERR_BadControlRecv syscall.Errno = 2193 72 | NERR_ServiceNotStarting syscall.Errno = 2194 73 | NERR_AlreadyLoggedOn syscall.Errno = 2200 74 | NERR_NotLoggedOn syscall.Errno = 2201 75 | NERR_BadUsername syscall.Errno = 2202 76 | NERR_BadPassword syscall.Errno = 2203 77 | NERR_UnableToAddName_W syscall.Errno = 2204 78 | NERR_UnableToAddName_F syscall.Errno = 2205 79 | NERR_UnableToDelName_W syscall.Errno = 2206 80 | NERR_UnableToDelName_F syscall.Errno = 2207 81 | NERR_LogonsPaused syscall.Errno = 2209 82 | NERR_LogonServerConflict syscall.Errno = 2210 83 | NERR_LogonNoUserPath syscall.Errno = 2211 84 | NERR_LogonScriptError syscall.Errno = 2212 85 | NERR_StandaloneLogon syscall.Errno = 2214 86 | NERR_LogonServerNotFound syscall.Errno = 2215 87 | NERR_LogonDomainExists syscall.Errno = 2216 88 | NERR_NonValidatedLogon syscall.Errno = 2217 89 | NERR_ACFNotFound syscall.Errno = 2219 90 | NERR_GroupNotFound syscall.Errno = 2220 91 | NERR_UserNotFound syscall.Errno = 2221 92 | NERR_ResourceNotFound syscall.Errno = 2222 93 | NERR_GroupExists syscall.Errno = 2223 94 | NERR_UserExists syscall.Errno = 2224 95 | NERR_ResourceExists syscall.Errno = 2225 96 | NERR_NotPrimary syscall.Errno = 2226 97 | NERR_ACFNotLoaded syscall.Errno = 2227 98 | NERR_ACFNoRoom syscall.Errno = 2228 99 | NERR_ACFFileIOFail syscall.Errno = 2229 100 | NERR_ACFTooManyLists syscall.Errno = 2230 101 | NERR_UserLogon syscall.Errno = 2231 102 | NERR_ACFNoParent syscall.Errno = 2232 103 | NERR_CanNotGrowSegment syscall.Errno = 2233 104 | NERR_SpeGroupOp syscall.Errno = 2234 105 | NERR_NotInCache syscall.Errno = 2235 106 | NERR_UserInGroup syscall.Errno = 2236 107 | NERR_UserNotInGroup syscall.Errno = 2237 108 | NERR_AccountUndefined syscall.Errno = 2238 109 | NERR_AccountExpired syscall.Errno = 2239 110 | NERR_InvalidWorkstation syscall.Errno = 2240 111 | NERR_InvalidLogonHours syscall.Errno = 2241 112 | NERR_PasswordExpired syscall.Errno = 2242 113 | NERR_PasswordCantChange syscall.Errno = 2243 114 | NERR_PasswordHistConflict syscall.Errno = 2244 115 | NERR_PasswordTooShort syscall.Errno = 2245 116 | NERR_PasswordTooRecent syscall.Errno = 2246 117 | NERR_InvalidDatabase syscall.Errno = 2247 118 | NERR_DatabaseUpToDate syscall.Errno = 2248 119 | NERR_SyncRequired syscall.Errno = 2249 120 | NERR_UseNotFound syscall.Errno = 2250 121 | NERR_BadAsgType syscall.Errno = 2251 122 | NERR_DeviceIsShared syscall.Errno = 2252 123 | NERR_NoComputerName syscall.Errno = 2270 124 | NERR_MsgAlreadyStarted syscall.Errno = 2271 125 | NERR_MsgInitFailed syscall.Errno = 2272 126 | NERR_NameNotFound syscall.Errno = 2273 127 | NERR_AlreadyForwarded syscall.Errno = 2274 128 | NERR_AddForwarded syscall.Errno = 2275 129 | NERR_AlreadyExists syscall.Errno = 2276 130 | NERR_TooManyNames syscall.Errno = 2277 131 | NERR_DelComputerName syscall.Errno = 2278 132 | NERR_LocalForward syscall.Errno = 2279 133 | NERR_GrpMsgProcessor syscall.Errno = 2280 134 | NERR_PausedRemote syscall.Errno = 2281 135 | NERR_BadReceive syscall.Errno = 2282 136 | NERR_NameInUse syscall.Errno = 2283 137 | NERR_MsgNotStarted syscall.Errno = 2284 138 | NERR_NotLocalName syscall.Errno = 2285 139 | NERR_NoForwardName syscall.Errno = 2286 140 | NERR_RemoteFull syscall.Errno = 2287 141 | NERR_NameNotForwarded syscall.Errno = 2288 142 | NERR_TruncatedBroadcast syscall.Errno = 2289 143 | NERR_InvalidDevice syscall.Errno = 2294 144 | NERR_WriteFault syscall.Errno = 2295 145 | NERR_DuplicateName syscall.Errno = 2297 146 | NERR_DeleteLater syscall.Errno = 2298 147 | NERR_IncompleteDel syscall.Errno = 2299 148 | NERR_MultipleNets syscall.Errno = 2300 149 | NERR_NetNameNotFound syscall.Errno = 2310 150 | NERR_DeviceNotShared syscall.Errno = 2311 151 | NERR_ClientNameNotFound syscall.Errno = 2312 152 | NERR_FileIdNotFound syscall.Errno = 2314 153 | NERR_ExecFailure syscall.Errno = 2315 154 | NERR_TmpFile syscall.Errno = 2316 155 | NERR_TooMuchData syscall.Errno = 2317 156 | NERR_DeviceShareConflict syscall.Errno = 2318 157 | NERR_BrowserTableIncomplete syscall.Errno = 2319 158 | NERR_NotLocalDomain syscall.Errno = 2320 159 | NERR_IsDfsShare syscall.Errno = 2321 160 | NERR_DevInvalidOpCode syscall.Errno = 2331 161 | NERR_DevNotFound syscall.Errno = 2332 162 | NERR_DevNotOpen syscall.Errno = 2333 163 | NERR_BadQueueDevString syscall.Errno = 2334 164 | NERR_BadQueuePriority syscall.Errno = 2335 165 | NERR_NoCommDevs syscall.Errno = 2337 166 | NERR_QueueNotFound syscall.Errno = 2338 167 | NERR_BadDevString syscall.Errno = 2340 168 | NERR_BadDev syscall.Errno = 2341 169 | NERR_InUseBySpooler syscall.Errno = 2342 170 | NERR_CommDevInUse syscall.Errno = 2343 171 | NERR_InvalidComputer syscall.Errno = 2351 172 | NERR_MaxLenExceeded syscall.Errno = 2354 173 | NERR_BadComponent syscall.Errno = 2356 174 | NERR_CantType syscall.Errno = 2357 175 | NERR_TooManyEntries syscall.Errno = 2362 176 | NERR_ProfileFileTooBig syscall.Errno = 2370 177 | NERR_ProfileOffset syscall.Errno = 2371 178 | NERR_ProfileCleanup syscall.Errno = 2372 179 | NERR_ProfileUnknownCmd syscall.Errno = 2373 180 | NERR_ProfileLoadErr syscall.Errno = 2374 181 | NERR_ProfileSaveErr syscall.Errno = 2375 182 | NERR_LogOverflow syscall.Errno = 2377 183 | NERR_LogFileChanged syscall.Errno = 2378 184 | NERR_LogFileCorrupt syscall.Errno = 2379 185 | NERR_SourceIsDir syscall.Errno = 2380 186 | NERR_BadSource syscall.Errno = 2381 187 | NERR_BadDest syscall.Errno = 2382 188 | NERR_DifferentServers syscall.Errno = 2383 189 | NERR_RunSrvPaused syscall.Errno = 2385 190 | NERR_ErrCommRunSrv syscall.Errno = 2389 191 | NERR_ErrorExecingGhost syscall.Errno = 2391 192 | NERR_ShareNotFound syscall.Errno = 2392 193 | NERR_InvalidLana syscall.Errno = 2400 194 | NERR_OpenFiles syscall.Errno = 2401 195 | NERR_ActiveConns syscall.Errno = 2402 196 | NERR_BadPasswordCore syscall.Errno = 2403 197 | NERR_DevInUse syscall.Errno = 2404 198 | NERR_LocalDrive syscall.Errno = 2405 199 | NERR_AlertExists syscall.Errno = 2430 200 | NERR_TooManyAlerts syscall.Errno = 2431 201 | NERR_NoSuchAlert syscall.Errno = 2432 202 | NERR_BadRecipient syscall.Errno = 2433 203 | NERR_AcctLimitExceeded syscall.Errno = 2434 204 | NERR_InvalidLogSeek syscall.Errno = 2440 205 | NERR_BadUasConfig syscall.Errno = 2450 206 | NERR_InvalidUASOp syscall.Errno = 2451 207 | NERR_LastAdmin syscall.Errno = 2452 208 | NERR_DCNotFound syscall.Errno = 2453 209 | NERR_LogonTrackingError syscall.Errno = 2454 210 | NERR_NetlogonNotStarted syscall.Errno = 2455 211 | NERR_CanNotGrowUASFile syscall.Errno = 2456 212 | NERR_TimeDiffAtDC syscall.Errno = 2457 213 | NERR_PasswordMismatch syscall.Errno = 2458 214 | NERR_NoSuchServer syscall.Errno = 2460 215 | NERR_NoSuchSession syscall.Errno = 2461 216 | NERR_NoSuchConnection syscall.Errno = 2462 217 | NERR_TooManyServers syscall.Errno = 2463 218 | NERR_TooManySessions syscall.Errno = 2464 219 | NERR_TooManyConnections syscall.Errno = 2465 220 | NERR_TooManyFiles syscall.Errno = 2466 221 | NERR_NoAlternateServers syscall.Errno = 2467 222 | NERR_TryDownLevel syscall.Errno = 2470 223 | NERR_UPSDriverNotStarted syscall.Errno = 2480 224 | NERR_UPSInvalidConfig syscall.Errno = 2481 225 | NERR_UPSInvalidCommPort syscall.Errno = 2482 226 | NERR_UPSSignalAsserted syscall.Errno = 2483 227 | NERR_UPSShutdownFailed syscall.Errno = 2484 228 | NERR_BadDosRetCode syscall.Errno = 2500 229 | NERR_ProgNeedsExtraMem syscall.Errno = 2501 230 | NERR_BadDosFunction syscall.Errno = 2502 231 | NERR_RemoteBootFailed syscall.Errno = 2503 232 | NERR_BadFileCheckSum syscall.Errno = 2504 233 | NERR_NoRplBootSystem syscall.Errno = 2505 234 | NERR_RplLoadrNetBiosErr syscall.Errno = 2506 235 | NERR_RplLoadrDiskErr syscall.Errno = 2507 236 | NERR_ImageParamErr syscall.Errno = 2508 237 | NERR_TooManyImageParams syscall.Errno = 2509 238 | NERR_NonDosFloppyUsed syscall.Errno = 2510 239 | NERR_RplBootRestart syscall.Errno = 2511 240 | NERR_RplSrvrCallFailed syscall.Errno = 2512 241 | NERR_CantConnectRplSrvr syscall.Errno = 2513 242 | NERR_CantOpenImageFile syscall.Errno = 2514 243 | NERR_CallingRplSrvr syscall.Errno = 2515 244 | NERR_StartingRplBoot syscall.Errno = 2516 245 | NERR_RplBootServiceTerm syscall.Errno = 2517 246 | NERR_RplBootStartFailed syscall.Errno = 2518 247 | NERR_RPL_CONNECTED syscall.Errno = 2519 248 | NERR_BrowserConfiguredToNotRun syscall.Errno = 2550 249 | NERR_RplNoAdaptersStarted syscall.Errno = 2610 250 | NERR_RplBadRegistry syscall.Errno = 2611 251 | NERR_RplBadDatabase syscall.Errno = 2612 252 | NERR_RplRplfilesShare syscall.Errno = 2613 253 | NERR_RplNotRplServer syscall.Errno = 2614 254 | NERR_RplCannotEnum syscall.Errno = 2615 255 | NERR_RplWkstaInfoCorrupted syscall.Errno = 2616 256 | NERR_RplWkstaNotFound syscall.Errno = 2617 257 | NERR_RplWkstaNameUnavailable syscall.Errno = 2618 258 | NERR_RplProfileInfoCorrupted syscall.Errno = 2619 259 | NERR_RplProfileNotFound syscall.Errno = 2620 260 | NERR_RplProfileNameUnavailable syscall.Errno = 2621 261 | NERR_RplProfileNotEmpty syscall.Errno = 2622 262 | NERR_RplConfigInfoCorrupted syscall.Errno = 2623 263 | NERR_RplConfigNotFound syscall.Errno = 2624 264 | NERR_RplAdapterInfoCorrupted syscall.Errno = 2625 265 | NERR_RplInternal syscall.Errno = 2626 266 | NERR_RplVendorInfoCorrupted syscall.Errno = 2627 267 | NERR_RplBootInfoCorrupted syscall.Errno = 2628 268 | NERR_RplWkstaNeedsUserAcct syscall.Errno = 2629 269 | NERR_RplNeedsRPLUSERAcct syscall.Errno = 2630 270 | NERR_RplBootNotFound syscall.Errno = 2631 271 | NERR_RplIncompatibleProfile syscall.Errno = 2632 272 | NERR_RplAdapterNameUnavailable syscall.Errno = 2633 273 | NERR_RplConfigNotEmpty syscall.Errno = 2634 274 | NERR_RplBootInUse syscall.Errno = 2635 275 | NERR_RplBackupDatabase syscall.Errno = 2636 276 | NERR_RplAdapterNotFound syscall.Errno = 2637 277 | NERR_RplVendorNotFound syscall.Errno = 2638 278 | NERR_RplVendorNameUnavailable syscall.Errno = 2639 279 | NERR_RplBootNameUnavailable syscall.Errno = 2640 280 | NERR_RplConfigNameUnavailable syscall.Errno = 2641 281 | NERR_DfsInternalCorruption syscall.Errno = 2660 282 | NERR_DfsVolumeDataCorrupt syscall.Errno = 2661 283 | NERR_DfsNoSuchVolume syscall.Errno = 2662 284 | NERR_DfsVolumeAlreadyExists syscall.Errno = 2663 285 | NERR_DfsAlreadyShared syscall.Errno = 2664 286 | NERR_DfsNoSuchShare syscall.Errno = 2665 287 | NERR_DfsNotALeafVolume syscall.Errno = 2666 288 | NERR_DfsLeafVolume syscall.Errno = 2667 289 | NERR_DfsVolumeHasMultipleServers syscall.Errno = 2668 290 | NERR_DfsCantCreateJunctionPoint syscall.Errno = 2669 291 | NERR_DfsServerNotDfsAware syscall.Errno = 2670 292 | NERR_DfsBadRenamePath syscall.Errno = 2671 293 | NERR_DfsVolumeIsOffline syscall.Errno = 2672 294 | NERR_DfsNoSuchServer syscall.Errno = 2673 295 | NERR_DfsCyclicalName syscall.Errno = 2674 296 | NERR_DfsNotSupportedInServerDfs syscall.Errno = 2675 297 | NERR_DfsDuplicateService syscall.Errno = 2676 298 | NERR_DfsCantRemoveLastServerShare syscall.Errno = 2677 299 | NERR_DfsVolumeIsInterDfs syscall.Errno = 2678 300 | NERR_DfsInconsistent syscall.Errno = 2679 301 | NERR_DfsServerUpgraded syscall.Errno = 2680 302 | NERR_DfsDataIsIdentical syscall.Errno = 2681 303 | NERR_DfsCantRemoveDfsRoot syscall.Errno = 2682 304 | NERR_DfsChildOrParentInDfs syscall.Errno = 2683 305 | NERR_DfsInternalError syscall.Errno = 2690 306 | NERR_SetupAlreadyJoined syscall.Errno = 2691 307 | NERR_SetupNotJoined syscall.Errno = 2692 308 | NERR_SetupDomainController syscall.Errno = 2693 309 | NERR_DefaultJoinRequired syscall.Errno = 2694 310 | NERR_InvalidWorkgroupName syscall.Errno = 2695 311 | NERR_NameUsesIncompatibleCodePage syscall.Errno = 2696 312 | NERR_ComputerAccountNotFound syscall.Errno = 2697 313 | NERR_PersonalSku syscall.Errno = 2698 314 | NERR_PasswordMustChange syscall.Errno = 2701 315 | NERR_AccountLockedOut syscall.Errno = 2702 316 | NERR_PasswordTooLong syscall.Errno = 2703 317 | NERR_PasswordNotComplexEnough syscall.Errno = 2704 318 | NERR_PasswordFilterError syscall.Errno = 2705 319 | NERR_NoOfflineJoinInfo syscall.Errno = 2709 320 | NERR_BadOfflineJoinInfo syscall.Errno = 2710 321 | NERR_CantCreateJoinInfo syscall.Errno = 2711 322 | NERR_BadDomainJoinInfo syscall.Errno = 2712 323 | NERR_JoinPerformedMustRestart syscall.Errno = 2713 324 | NERR_NoJoinPending syscall.Errno = 2714 325 | NERR_ValuesNotSet syscall.Errno = 2715 326 | NERR_CantVerifyHostname syscall.Errno = 2716 327 | NERR_CantLoadOfflineHive syscall.Errno = 2717 328 | NERR_ConnectionInsecure syscall.Errno = 2718 329 | NERR_ProvisioningBlobUnsupported syscall.Errno = 2719 330 | ) 331 | 332 | -------------------------------------------------------------------------------- /syscallex/shell32_windows.go: -------------------------------------------------------------------------------- 1 | package syscallex 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "golang.org/x/sys/windows" 8 | ) 9 | 10 | const ( 11 | SHGFP_TYPE_CURRENT = 0 12 | ) 13 | 14 | // see http://svnpenn.blogspot.com/2011/01/csidl-constants.html 15 | const ( 16 | CSIDL_FLAG_CREATE = 0x8000 17 | CSIDL_APPDATA = 0x001a 18 | CSIDL_PROFILE = 0x0028 19 | CSIDL_LOCAL_APPDATA = 0x001c 20 | CSIDL_STARTMENU = 0x000b 21 | CSIDL_PROGRAMS = 0x0002 22 | ) 23 | 24 | const MAX_PATH = 260 25 | 26 | var ( 27 | modshell32 = windows.NewLazySystemDLL("shell32.dll") 28 | 29 | procSHGetFolderPathW = modshell32.NewProc("SHGetFolderPathW") 30 | ) 31 | 32 | func SHGetFolderPath( 33 | owner syscall.Handle, 34 | folder uint32, 35 | token syscall.Token, 36 | flags uint32, 37 | ) (s string, err error) { 38 | buffer := make([]uint16, MAX_PATH+1) 39 | 40 | r1, _, e1 := syscall.Syscall6( 41 | procSHGetFolderPathW.Addr(), 42 | 5, 43 | uintptr(owner), 44 | uintptr(folder), 45 | uintptr(token), 46 | uintptr(flags), 47 | uintptr(unsafe.Pointer(&buffer[0])), 48 | 0, 49 | ) 50 | if FAILED(r1) { 51 | if e1 != 0 { 52 | err = e1 53 | } else { 54 | err = syscall.EINVAL 55 | } 56 | } 57 | if err == nil { 58 | s = syscall.UTF16ToString(buffer) 59 | } 60 | return 61 | } 62 | 63 | const ERROR_SUCCESS = 0 64 | 65 | func FAILED(r1 uintptr) bool { 66 | return r1 != ERROR_SUCCESS 67 | } 68 | -------------------------------------------------------------------------------- /syscallex/user32_windows.go: -------------------------------------------------------------------------------- 1 | package syscallex 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "golang.org/x/sys/windows" 8 | ) 9 | 10 | var ( 11 | moduser32 = windows.NewLazySystemDLL("user32.dll") 12 | 13 | procEnumWindows = moduser32.NewProc("EnumWindows") 14 | procGetWindowThreadProcessId = moduser32.NewProc("GetWindowThreadProcessId") 15 | procSetForegroundWindow = moduser32.NewProc("SetForegroundWindow") 16 | procShowWindow = moduser32.NewProc("ShowWindow") 17 | procIsWindowVisible = moduser32.NewProc("IsWindowVisible") 18 | procSwitchToThisWindow = moduser32.NewProc("SwitchToThisWindow") 19 | procFindWindow = moduser32.NewProc("FindWindowW") 20 | procSendInput = moduser32.NewProc("SendInput") 21 | procGetWindowRect = moduser32.NewProc("GetWindowRect") 22 | procGetSystemMetrics = moduser32.NewProc("GetSystemMetrics") 23 | ) 24 | 25 | func EnumWindows( 26 | cb uintptr, 27 | lparam uintptr, 28 | ) (err error) { 29 | r1, _, e1 := syscall.Syscall( 30 | procEnumWindows.Addr(), 31 | 2, 32 | cb, 33 | lparam, 34 | 0, 35 | ) 36 | if r1 == 0 { 37 | if e1 != 0 { 38 | err = e1 39 | } else { 40 | err = syscall.EINVAL 41 | } 42 | } 43 | return 44 | } 45 | 46 | func GetWindowThreadProcessId( 47 | hwnd syscall.Handle, 48 | pProcessId *uint32, 49 | ) uint32 { 50 | r1, _, _ := syscall.Syscall( 51 | procGetWindowThreadProcessId.Addr(), 52 | 2, 53 | uintptr(hwnd), 54 | uintptr(unsafe.Pointer(pProcessId)), 55 | 0, 56 | ) 57 | return uint32(r1) 58 | } 59 | 60 | func SetForegroundWindow( 61 | hwnd syscall.Handle, 62 | ) (err error) { 63 | r1, _, e1 := syscall.Syscall( 64 | procSetForegroundWindow.Addr(), 65 | 1, 66 | uintptr(hwnd), 67 | 0, 68 | 0, 69 | ) 70 | if r1 == 0 { 71 | if e1 != 0 { 72 | err = e1 73 | } else { 74 | err = syscall.EINVAL 75 | } 76 | } 77 | return 78 | } 79 | 80 | func ShowWindow( 81 | hwnd syscall.Handle, 82 | flags int, 83 | ) (err error) { 84 | r1, _, e1 := syscall.Syscall( 85 | procShowWindow.Addr(), 86 | 2, 87 | uintptr(hwnd), 88 | uintptr(flags), 89 | 0, 90 | ) 91 | if r1 == 0 { 92 | if e1 != 0 { 93 | err = e1 94 | } else { 95 | err = syscall.EINVAL 96 | } 97 | } 98 | return 99 | } 100 | 101 | func IsWindowVisible( 102 | hwnd syscall.Handle, 103 | ) bool { 104 | ret, _, _ := syscall.Syscall( 105 | procIsWindowVisible.Addr(), 106 | 1, 107 | uintptr(hwnd), 108 | 0, 109 | 0, 110 | ) 111 | 112 | return ret != 0 113 | } 114 | 115 | func SwitchToThisWindow( 116 | hwnd syscall.Handle, 117 | altTab bool, 118 | ) { 119 | altTabInt := 0 120 | if altTab { 121 | altTabInt = 1 122 | } 123 | 124 | syscall.Syscall( 125 | procSwitchToThisWindow.Addr(), 126 | 2, 127 | uintptr(hwnd), 128 | uintptr(altTabInt), 129 | 0, 130 | ) 131 | } 132 | 133 | func FindWindow(cls string, win string) (syscall.Handle, error) { 134 | r0, _, e1 := syscall.Syscall( 135 | procFindWindow.Addr(), 2, 136 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(cls))), 137 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(win))), 138 | 0, 139 | ) 140 | var err error 141 | if r0 == 0 { 142 | if e1 != 0 { 143 | err = error(e1) 144 | } else { 145 | err = syscall.EINVAL 146 | } 147 | } 148 | return syscall.Handle(r0), err 149 | } 150 | 151 | func FindWindowByClass(cls string) (syscall.Handle, error) { 152 | r0, _, e1 := syscall.Syscall( 153 | procFindWindow.Addr(), 2, 154 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(cls))), 155 | 0, 156 | 0, 157 | ) 158 | var err error 159 | if r0 == 0 { 160 | if e1 != 0 { 161 | err = error(e1) 162 | } else { 163 | err = syscall.EINVAL 164 | } 165 | } 166 | return syscall.Handle(r0), err 167 | } 168 | 169 | type INPUT struct { 170 | Type uint32 171 | Input MOUSEINPUT 172 | } 173 | 174 | var ( 175 | INPUT_MOUSE uint32 = 0 176 | INPUT_KEYBOARD uint32 = 1 177 | INPUT_HARDWARE uint32 = 2 178 | ) 179 | 180 | func (i *INPUT) SetKeyboardInput(ki KEYBDINPUT) { 181 | i.Type = INPUT_KEYBOARD 182 | var mip = (&i.Input) 183 | *mip = MOUSEINPUT{} 184 | 185 | var p = (*KEYBDINPUT)((unsafe.Pointer)(mip)) 186 | *p = ki 187 | } 188 | 189 | func (i *INPUT) SetMouseInput(mi MOUSEINPUT) { 190 | i.Type = INPUT_MOUSE 191 | var mip = (&i.Input) 192 | *mip = mi 193 | } 194 | 195 | type MOUSEINPUT struct { 196 | X int32 197 | Y int32 198 | MouseData uint32 199 | Flags uint32 200 | Time uint32 201 | ExtraInfo *uint32 202 | } 203 | 204 | type KEYBDINPUT struct { 205 | Vk uint16 206 | Scan uint16 207 | Flags uint32 208 | Time uint32 209 | ExtraInfo *uint32 210 | } 211 | 212 | type HARDWAREINPUT struct { 213 | UMsg uint32 214 | ParamL int16 215 | ParamH int16 216 | } 217 | 218 | var ( 219 | KEYEVENTF_EXTENDEDKEY uint32 = 0x0001 220 | KEYEVENTF_KEYUP uint32 = 0x0002 221 | KEYEVENTF_UNICODE uint32 = 0x0004 222 | KEYEVENTF_SCANCODE uint32 = 0x0008 223 | ) 224 | 225 | var ( 226 | MOUSEEVENTF_ABSOLUTE uint32 = 0x8000 227 | MOUSEEVENTF_HWHEEL uint32 = 0x1000 228 | MOUSEEVENTF_MOVE uint32 = 0x0001 229 | MOUSEEVENTF_MOVE_NOCOALESCE uint32 = 0x2000 230 | MOUSEEVENTF_LEFTDOWN uint32 = 0x0002 231 | MOUSEEVENTF_LEFTUP uint32 = 0x0004 232 | MOUSEEVENTF_RIGHTDOWN uint32 = 0x0008 233 | MOUSEEVENTF_RIGHTUP uint32 = 0x0010 234 | MOUSEEVENTF_MIDDLEDOWN uint32 = 0x0020 235 | MOUSEEVENTF_MIDDLEUP uint32 = 0x0040 236 | MOUSEEVENTF_VIRTUALDESK uint32 = 0x4000 237 | MOUSEEVENTF_WHEEL uint32 = 0x0800 238 | MOUSEEVENTF_XDOWN uint32 = 0x0080 239 | MOUSEEVENTF_XUP uint32 = 0x0100 240 | ) 241 | 242 | func SendMouseInput(mi MOUSEINPUT) (err error) { 243 | var i INPUT 244 | i.SetMouseInput(mi) 245 | return SendInput(i) 246 | } 247 | 248 | func SendKeyboardInput(ki KEYBDINPUT) (err error) { 249 | var i INPUT 250 | i.SetKeyboardInput(ki) 251 | return SendInput(i) 252 | } 253 | 254 | func SendInput(input INPUT) (err error) { 255 | r0, _, e1 := syscall.Syscall( 256 | procSendInput.Addr(), 3, 257 | 1, 258 | uintptr(unsafe.Pointer(&input)), 259 | unsafe.Sizeof(INPUT{}), 260 | ) 261 | if r0 == 0 { 262 | if e1 != 0 { 263 | err = error(e1) 264 | } else { 265 | err = syscall.EINVAL 266 | } 267 | } 268 | return 269 | } 270 | 271 | type RECT struct { 272 | Left int32 273 | Top int32 274 | Right int32 275 | Bottom int32 276 | } 277 | 278 | func GetWindowRect(hwnd syscall.Handle) (rect RECT, err error) { 279 | r0, _, e1 := syscall.Syscall( 280 | procGetWindowRect.Addr(), 2, 281 | uintptr(hwnd), 282 | uintptr(unsafe.Pointer(&rect)), 283 | 0, 284 | ) 285 | if r0 == 0 { 286 | if e1 != 0 { 287 | err = error(e1) 288 | } else { 289 | err = syscall.EINVAL 290 | } 291 | } 292 | return 293 | } 294 | 295 | var ( 296 | SM_CXSCREEN int = 0 297 | SM_CYSCREEN = 1 298 | ) 299 | 300 | func GetSystemMetrics(nIndex int) (ret int) { 301 | r0, _, _ := syscall.Syscall( 302 | procGetSystemMetrics.Addr(), 1, 303 | uintptr(nIndex), 304 | 0, 0, 305 | ) 306 | ret = int(r0) 307 | return 308 | } 309 | -------------------------------------------------------------------------------- /syscallex/userenv_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package syscallex 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "golang.org/x/sys/windows" 10 | ) 11 | 12 | var ( 13 | moduserenv = windows.NewLazySystemDLL("userenv.dll") 14 | 15 | procLoadUserProfileW = moduserenv.NewProc("LoadUserProfileW") 16 | procUnloadUserProfile = moduserenv.NewProc("UnloadUserProfile") 17 | ) 18 | 19 | // flags for the ProfileInfo struct 20 | const ( 21 | // Prevents the display of profile error messages. 22 | PI_NOUI = 1 23 | ) 24 | 25 | // struct _PROFILEINFO, cf. 26 | // https://msdn.microsoft.com/en-us/library/windows/desktop/bb773378(v=vs.85).aspx 27 | type ProfileInfo struct { 28 | Size uint32 29 | Flags uint32 30 | UserName *uint16 31 | ProfilePath *uint16 32 | Defaultpath *uint16 33 | ServerName *uint16 34 | PolicyPath *uint16 35 | Profile syscall.Handle 36 | } 37 | 38 | func LoadUserProfile( 39 | token syscall.Token, 40 | profileInfo *ProfileInfo, 41 | ) (err error) { 42 | r1, _, e1 := syscall.Syscall( 43 | procLoadUserProfileW.Addr(), 44 | 2, 45 | uintptr(token), 46 | uintptr(unsafe.Pointer(profileInfo)), 47 | 0, 48 | ) 49 | if r1 == 0 { 50 | if e1 != 0 { 51 | err = e1 52 | } else { 53 | err = syscall.EINVAL 54 | } 55 | } 56 | return 57 | } 58 | 59 | func UnloadUserProfile( 60 | token syscall.Token, 61 | profile syscall.Handle, 62 | ) (err error) { 63 | r1, _, e1 := syscall.Syscall( 64 | procUnloadUserProfile.Addr(), 65 | 2, 66 | uintptr(token), 67 | uintptr(profile), 68 | 0, 69 | ) 70 | if r1 == 0 { 71 | if e1 != 0 { 72 | err = e1 73 | } else { 74 | err = syscall.EINVAL 75 | } 76 | } 77 | return 78 | } 79 | -------------------------------------------------------------------------------- /syscallex/wellknownsid_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package syscallex 4 | 5 | const ( 6 | WinNullSid = 0 7 | WinWorldSid = 1 8 | WinLocalSid = 2 9 | WinCreatorOwnerSid = 3 10 | WinCreatorGroupSid = 4 11 | WinCreatorOwnerServerSid = 5 12 | WinCreatorGroupServerSid = 6 13 | WinNtAuthoritySid = 7 14 | WinDialupSid = 8 15 | WinNetworkSid = 9 16 | WinBatchSid = 10 17 | WinInteractiveSid = 11 18 | WinServiceSid = 12 19 | WinAnonymousSid = 13 20 | WinProxySid = 14 21 | WinEnterpriseControllersSid = 15 22 | WinSelfSid = 16 23 | WinAuthenticatedUserSid = 17 24 | WinRestrictedCodeSid = 18 25 | WinTerminalServerSid = 19 26 | WinRemoteLogonIdSid = 20 27 | WinLogonIdsSid = 21 28 | WinLocalSystemSid = 22 29 | WinLocalServiceSid = 23 30 | WinNetworkServiceSid = 24 31 | WinBuiltinDomainSid = 25 32 | WinBuiltinAdministratorsSid = 26 33 | WinBuiltinUsersSid = 27 34 | WinBuiltinGuestsSid = 28 35 | WinBuiltinPowerUsersSid = 29 36 | WinBuiltinAccountOperatorsSid = 30 37 | WinBuiltinSystemOperatorsSid = 31 38 | WinBuiltinPrintOperatorsSid = 32 39 | WinBuiltinBackupOperatorsSid = 33 40 | WinBuiltinReplicatorSid = 34 41 | WinBuiltinPreWindows2000CompatibleAccessSid = 35 42 | WinBuiltinRemoteDesktopUsersSid = 36 43 | WinBuiltinNetworkConfigurationOperatorsSid = 37 44 | WinAccountAdministratorSid = 38 45 | WinAccountGuestSid = 39 46 | WinAccountKrbtgtSid = 40 47 | WinAccountDomainAdminsSid = 41 48 | WinAccountDomainUsersSid = 42 49 | WinAccountDomainGuestsSid = 43 50 | WinAccountComputersSid = 44 51 | WinAccountControllersSid = 45 52 | WinAccountCertAdminsSid = 46 53 | WinAccountSchemaAdminsSid = 47 54 | WinAccountEnterpriseAdminsSid = 48 55 | WinAccountPolicyAdminsSid = 49 56 | WinAccountRasAndIasServersSid = 50 57 | WinNTLMAuthenticationSid = 51 58 | WinDigestAuthenticationSid = 52 59 | WinSChannelAuthenticationSid = 53 60 | WinThisOrganizationSid = 54 61 | WinOtherOrganizationSid = 55 62 | WinBuiltinIncomingForestTrustBuildersSid = 56 63 | WinBuiltinPerfMonitoringUsersSid = 57 64 | WinBuiltinPerfLoggingUsersSid = 58 65 | WinBuiltinAuthorizationAccessSid = 59 66 | WinBuiltinTerminalServerLicenseServersSid = 60 67 | WinBuiltinDCOMUsersSid = 61 68 | WinBuiltinIUsersSid = 62 69 | WinIUserSid = 63 70 | WinBuiltinCryptoOperatorsSid = 64 71 | WinUntrustedLabelSid = 65 72 | WinLowLabelSid = 66 73 | WinMediumLabelSid = 67 74 | WinHighLabelSid = 68 75 | WinSystemLabelSid = 69 76 | WinWriteRestrictedCodeSid = 70 77 | WinCreatorOwnerRightsSid = 71 78 | WinCacheablePrincipalsGroupSid = 72 79 | WinNonCacheablePrincipalsGroupSid = 73 80 | WinEnterpriseReadonlyControllersSid = 74 81 | WinAccountReadonlyControllersSid = 75 82 | WinBuiltinEventLogReadersGroup = 76 83 | WinNewEnterpriseReadonlyControllersSid = 77 84 | WinBuiltinCertSvcDComAccessGroup = 78 85 | WinMediumPlusLabelSid = 79 86 | WinLocalLogonSid = 80 87 | WinConsoleLogonSid = 81 88 | WinThisOrganizationCertificateSid = 82 89 | WinApplicationPackageAuthoritySid = 83 90 | WinBuiltinAnyPackageSid = 84 91 | WinCapabilityInternetClientSid = 85 92 | WinCapabilityInternetClientServerSid = 86 93 | WinCapabilityPrivateNetworkClientServerSid = 87 94 | WinCapabilityPicturesLibrarySid = 88 95 | WinCapabilityVideosLibrarySid = 89 96 | WinCapabilityMusicLibrarySid = 90 97 | WinCapabilityDocumentsLibrarySid = 91 98 | WinCapabilitySharedUserCertificatesSid = 92 99 | WinCapabilityEnterpriseAuthenticationSid = 93 100 | WinCapabilityRemovableStorageSid = 94 101 | ) 102 | -------------------------------------------------------------------------------- /syscallex/wintrust_windows.go: -------------------------------------------------------------------------------- 1 | package syscallex 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "golang.org/x/sys/windows" 8 | ) 9 | 10 | var ( 11 | modwintrust = windows.NewLazySystemDLL("wintrust.dll") 12 | 13 | procWinVerifyTrust = modwintrust.NewProc("WinVerifyTrust") 14 | ) 15 | 16 | type WinTrustData struct { 17 | CbStruct uint32 18 | PolicyCallbackData uintptr 19 | SIPClientData uintptr 20 | UIChoice uint32 21 | RevocationChecks uint32 22 | UnionChoice uint32 23 | FileOrCatalogOrBlobOrSgnrOrCert uintptr 24 | StateAction uint32 25 | StateData syscall.Handle 26 | URLReference *uint16 27 | ProvFlags uint32 28 | UIContext uint32 29 | SignatureSettings *WintrustSignatureSettings 30 | } 31 | 32 | // cf. https://msdn.microsoft.com/en-us/library/aa388205(v=vs.85).aspx 33 | const ( 34 | WTD_UI_ALL = 1 35 | WTD_UI_NONE = 2 36 | WTD_UI_NOBAD = 3 37 | WTD_UI_NOGOOD = 4 38 | ) 39 | 40 | const ( 41 | WTD_REVOKE_NONE = 0 42 | WTD_REVOKE_WHOLECHAIN = 1 43 | ) 44 | 45 | const ( 46 | WTD_CHOICE_FILE = 1 47 | WTD_CHOICE_CATALOG = 2 48 | WTD_CHOICE_BLOB = 3 49 | WTD_CHOICE_SIGNER = 4 50 | WTD_CHOICE_CERT = 5 51 | ) 52 | 53 | const ( 54 | WTD_STATEACTION_IGNORE = 0x00000000 55 | WTD_STATEACTION_VERIFY = 0x00000001 56 | WTD_STATEACTION_CLOSE = 0x00000002 57 | WTD_STATEACTION_AUTO_CACHE = 0x00000003 58 | WTD_STATEACTION_AUTO_CACHE_FLUSH = 0x00000004 59 | ) 60 | 61 | const ( 62 | WTD_USE_IE4_TRUST_FLAG = 0x1 63 | WTD_NO_IE4_CHAIN_FLAG = 0x2 64 | WTD_NO_POLICY_USAGE_FLAG = 0x4 65 | WTD_REVOCATION_CHECK_NONE = 0x10 66 | WTD_REVOCATION_CHECK_END_CERT = 0x20 67 | WTD_REVOCATION_CHECK_CHAIN = 0x40 68 | WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT = 0x80 69 | WTD_SAFER_FLAG = 0x100 70 | WTD_HASH_ONLY_FLAG = 0x200 71 | WTD_USE_DEFAULT_OSVER_CHECK = 0x400 72 | WTD_LIFETIME_SIGNING_FLAG = 0x800 73 | WTD_CACHE_ONLY_URL_RETRIEVAL = 0x1000 74 | WTD_DISABLE_MD2_MD4 = 0x2000 75 | WTD_MOTW = 0x4000 76 | ) 77 | 78 | const ( 79 | TRUST_E_NOSIGNATURE = 0x800B0100 80 | TRUST_E_EXPLICIT_DISTRUST = 0x800B0111 81 | TRUST_E_SUBJECT_NOT_TRUSTED = 0x800B0004 82 | CRYPT_E_SECURITY_SETTINGS = 0x80092026 83 | ) 84 | 85 | const ( 86 | WTD_UICONTEXT_EXECUTE = 0 87 | WTD_UICONTEXT_INSTALL = 1 88 | ) 89 | 90 | var WINTRUST_ACTION_GENERIC_VERIFY_V2 = syscall.GUID{ 91 | Data1: 0xaac56b, 92 | Data2: 0xcd44, 93 | Data3: 0x11d0, 94 | Data4: [8]byte{0x8c, 0xc2, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee}, 95 | } 96 | 97 | type WinTrustFileInfo struct { 98 | CbStruct uint32 99 | FilePath *uint16 100 | File syscall.Handle 101 | KnownSubject *syscall.GUID 102 | } 103 | 104 | type WintrustSignatureSettings struct { 105 | CbStruct uint32 106 | Index uint32 107 | Flags uint32 108 | SecondarySigs uint32 109 | VerifiedSigIndex uint32 110 | CryptoPolicy *CertStrongSignPara 111 | } 112 | 113 | type CertStrongSignPara struct { 114 | CbStruct uint32 115 | InfoChoice uint32 116 | InfoOrSerializedInfoOrOID uintptr 117 | } 118 | 119 | func WinVerifyTrust( 120 | hWnd syscall.Handle, 121 | actionId *syscall.GUID, 122 | data *WinTrustData, 123 | ) (err error) { 124 | r1, _, e1 := syscall.Syscall( 125 | procWinVerifyTrust.Addr(), 126 | 3, 127 | uintptr(hWnd), 128 | uintptr(unsafe.Pointer(actionId)), 129 | uintptr(unsafe.Pointer(data)), 130 | ) 131 | if r1 == 0 { 132 | // success! 133 | } else { 134 | err = e1 135 | } 136 | return 137 | } 138 | -------------------------------------------------------------------------------- /winox/execas/execas_windows.go: -------------------------------------------------------------------------------- 1 | package execas 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "errors" 7 | "io" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "runtime" 12 | "strconv" 13 | "strings" 14 | "sync" 15 | 16 | "github.com/itchio/ox/syscallex" 17 | "github.com/itchio/ox/winox/osex" 18 | ) 19 | 20 | // Cmd represents an external command being prepared or run. 21 | // 22 | // A Cmd cannot be reused after calling its Run, Output or CombinedOutput 23 | // methods. 24 | type Cmd struct { 25 | // Path is the path of the command to run. 26 | // 27 | // This is the only field that must be set to a non-zero 28 | // value. If Path is relative, it is evaluated relative 29 | // to Dir. 30 | Path string 31 | 32 | // Args holds command line arguments, including the command as Args[0]. 33 | // If the Args field is empty or nil, Run uses {Path}. 34 | // 35 | // In typical use, both Path and Args are set by calling Command. 36 | Args []string 37 | 38 | // Env specifies the environment of the process. 39 | // Each entry is of the form "key=value". 40 | // If Env is nil, the new process uses the current process's 41 | // environment. 42 | // If Env contains duplicate environment keys, only the last 43 | // value in the slice for each duplicate key is used. 44 | Env []string 45 | 46 | // Dir specifies the working directory of the command. 47 | // If Dir is the empty string, Run runs the command in the 48 | // calling process's current directory. 49 | Dir string 50 | 51 | // Username specifies the username to log on as when starting 52 | // the command 53 | Username string 54 | 55 | // Domain specifies the domain to log on as when starting 56 | // the command 57 | Domain string 58 | 59 | // Domain specifies the domain to log on as when starting 60 | // the command 61 | Password string 62 | 63 | // Stdin specifies the process's standard input. 64 | // If Stdin is nil, the process reads from the null device (os.DevNull). 65 | // If Stdin is an *os.File, the process's standard input is connected 66 | // directly to that file. 67 | // Otherwise, during the execution of the command a separate 68 | // goroutine reads from Stdin and delivers that data to the command 69 | // over a pipe. In this case, Wait does not complete until the goroutine 70 | // stops copying, either because it has reached the end of Stdin 71 | // (EOF or a read error) or because writing to the pipe returned an error. 72 | Stdin io.Reader 73 | 74 | // Stdout and Stderr specify the process's standard output and error. 75 | // 76 | // If either is nil, Run connects the corresponding file descriptor 77 | // to the null device (os.DevNull). 78 | // 79 | // If Stdout and Stderr are the same writer, and have a type that can be compared with ==, 80 | // at most one goroutine at a time will call Write. 81 | Stdout io.Writer 82 | Stderr io.Writer 83 | 84 | // ExtraFiles specifies additional open files to be inherited by the 85 | // new process. It does not include standard input, standard output, or 86 | // standard error. If non-nil, entry i becomes file descriptor 3+i. 87 | ExtraFiles []*os.File 88 | 89 | // SysProcAttr holds optional, operating system-specific attributes. 90 | // Run passes it to os.StartProcess as the os.ProcAttr's Sys field. 91 | SysProcAttr *syscallex.SysProcAttr 92 | 93 | // Process is the underlying process, once started. 94 | Process *os.Process 95 | 96 | // ProcessState contains information about an exited process, 97 | // available after a call to Wait or Run. 98 | ProcessState *os.ProcessState 99 | 100 | ctx context.Context // nil means none 101 | lookPathErr error // LookPath error, if any. 102 | finished bool // when Wait was called 103 | childFiles []*os.File 104 | closeAfterStart []io.Closer 105 | closeAfterWait []io.Closer 106 | goroutine []func() error 107 | errch chan error // one send per goroutine 108 | waitDone chan struct{} 109 | } 110 | 111 | // Command returns the Cmd struct to execute the named program with 112 | // the given arguments. 113 | // 114 | // It sets only the Path and Args in the returned structure. 115 | // 116 | // If name contains no path separators, Command uses LookPath to 117 | // resolve name to a complete path if possible. Otherwise it uses name 118 | // directly as Path. 119 | // 120 | // The returned Cmd's Args field is constructed from the command name 121 | // followed by the elements of arg, so arg should not include the 122 | // command name itself. For example, Command("echo", "hello"). 123 | // Args[0] is always name, not the possibly resolved Path. 124 | func Command(name string, arg ...string) *Cmd { 125 | cmd := &Cmd{ 126 | Path: name, 127 | Args: append([]string{name}, arg...), 128 | } 129 | if filepath.Base(name) == name { 130 | if lp, err := exec.LookPath(name); err != nil { 131 | cmd.lookPathErr = err 132 | } else { 133 | cmd.Path = lp 134 | } 135 | } 136 | return cmd 137 | } 138 | 139 | // CommandContext is like Command but includes a context. 140 | // 141 | // The provided context is used to kill the process (by calling 142 | // os.Process.Kill) if the context becomes done before the command 143 | // completes on its own. 144 | func CommandContext(ctx context.Context, name string, arg ...string) *Cmd { 145 | if ctx == nil { 146 | panic("nil Context") 147 | } 148 | cmd := Command(name, arg...) 149 | cmd.ctx = ctx 150 | return cmd 151 | } 152 | 153 | func (c *Cmd) envv() []string { 154 | if c.Env != nil { 155 | return c.Env 156 | } 157 | return os.Environ() 158 | } 159 | 160 | func (c *Cmd) argv() []string { 161 | if len(c.Args) > 0 { 162 | return c.Args 163 | } 164 | return []string{c.Path} 165 | } 166 | 167 | // skipStdinCopyError optionally specifies a function which reports 168 | // whether the provided the stdin copy error should be ignored. 169 | // It is non-nil everywhere but Plan 9, which lacks EPIPE. See exec_posix.go. 170 | var skipStdinCopyError func(error) bool 171 | 172 | func (c *Cmd) stdin() (f *os.File, err error) { 173 | if c.Stdin == nil { 174 | f, err = os.Open(os.DevNull) 175 | if err != nil { 176 | return 177 | } 178 | c.closeAfterStart = append(c.closeAfterStart, f) 179 | return 180 | } 181 | 182 | if f, ok := c.Stdin.(*os.File); ok { 183 | return f, nil 184 | } 185 | 186 | pr, pw, err := os.Pipe() 187 | if err != nil { 188 | return 189 | } 190 | 191 | c.closeAfterStart = append(c.closeAfterStart, pr) 192 | c.closeAfterWait = append(c.closeAfterWait, pw) 193 | c.goroutine = append(c.goroutine, func() error { 194 | _, err := io.Copy(pw, c.Stdin) 195 | if skip := skipStdinCopyError; skip != nil && skip(err) { 196 | err = nil 197 | } 198 | if err1 := pw.Close(); err == nil { 199 | err = err1 200 | } 201 | return err 202 | }) 203 | return pr, nil 204 | } 205 | 206 | func (c *Cmd) stdout() (f *os.File, err error) { 207 | return c.writerDescriptor(c.Stdout) 208 | } 209 | 210 | func (c *Cmd) stderr() (f *os.File, err error) { 211 | if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) { 212 | return c.childFiles[1], nil 213 | } 214 | return c.writerDescriptor(c.Stderr) 215 | } 216 | 217 | func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) { 218 | if w == nil { 219 | f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0) 220 | if err != nil { 221 | return 222 | } 223 | c.closeAfterStart = append(c.closeAfterStart, f) 224 | return 225 | } 226 | 227 | if f, ok := w.(*os.File); ok { 228 | return f, nil 229 | } 230 | 231 | pr, pw, err := os.Pipe() 232 | if err != nil { 233 | return 234 | } 235 | 236 | c.closeAfterStart = append(c.closeAfterStart, pw) 237 | c.closeAfterWait = append(c.closeAfterWait, pr) 238 | c.goroutine = append(c.goroutine, func() error { 239 | _, err := io.Copy(w, pr) 240 | pr.Close() // in case io.Copy stopped due to write error 241 | return err 242 | }) 243 | return pw, nil 244 | } 245 | 246 | func (c *Cmd) closeDescriptors(closers []io.Closer) { 247 | for _, fd := range closers { 248 | fd.Close() 249 | } 250 | } 251 | 252 | // Run starts the specified command and waits for it to complete. 253 | // 254 | // The returned error is nil if the command runs, has no problems 255 | // copying stdin, stdout, and stderr, and exits with a zero exit 256 | // status. 257 | // 258 | // If the command starts but does not complete successfully, the error is of 259 | // type *ExitError. Other error types may be returned for other situations. 260 | func (c *Cmd) Run() error { 261 | if err := c.Start(); err != nil { 262 | return err 263 | } 264 | return c.Wait() 265 | } 266 | 267 | // lookExtensions finds windows executable by its dir and path. 268 | // It uses LookPath to try appropriate extensions. 269 | // lookExtensions does not search PATH, instead it converts `prog` into `.\prog`. 270 | func lookExtensions(path, dir string) (string, error) { 271 | if filepath.Base(path) == path { 272 | path = filepath.Join(".", path) 273 | } 274 | if dir == "" { 275 | return exec.LookPath(path) 276 | } 277 | if filepath.VolumeName(path) != "" { 278 | return exec.LookPath(path) 279 | } 280 | if len(path) > 1 && os.IsPathSeparator(path[0]) { 281 | return exec.LookPath(path) 282 | } 283 | dirandpath := filepath.Join(dir, path) 284 | // We assume that LookPath will only add file extension. 285 | lp, err := exec.LookPath(dirandpath) 286 | if err != nil { 287 | return "", err 288 | } 289 | ext := strings.TrimPrefix(lp, dirandpath) 290 | return path + ext, nil 291 | } 292 | 293 | // interfaceEqual protects against panics from doing equality tests on 294 | // two interfaces with non-comparable underlying types. 295 | func interfaceEqual(a, b interface{}) bool { 296 | defer func() { 297 | recover() 298 | }() 299 | return a == b 300 | } 301 | 302 | // Start starts the specified command but does not wait for it to complete. 303 | // 304 | // The Wait method will return the exit code and release associated resources 305 | // once the command exits. 306 | func (c *Cmd) Start() error { 307 | if c.lookPathErr != nil { 308 | c.closeDescriptors(c.closeAfterStart) 309 | c.closeDescriptors(c.closeAfterWait) 310 | return c.lookPathErr 311 | } 312 | if runtime.GOOS == "windows" { 313 | lp, err := lookExtensions(c.Path, c.Dir) 314 | if err != nil { 315 | c.closeDescriptors(c.closeAfterStart) 316 | c.closeDescriptors(c.closeAfterWait) 317 | return err 318 | } 319 | c.Path = lp 320 | } 321 | if c.Process != nil { 322 | return errors.New("exec: already started") 323 | } 324 | if c.ctx != nil { 325 | select { 326 | case <-c.ctx.Done(): 327 | c.closeDescriptors(c.closeAfterStart) 328 | c.closeDescriptors(c.closeAfterWait) 329 | return c.ctx.Err() 330 | default: 331 | } 332 | } 333 | 334 | type F func(*Cmd) (*os.File, error) 335 | for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} { 336 | fd, err := setupFd(c) 337 | if err != nil { 338 | c.closeDescriptors(c.closeAfterStart) 339 | c.closeDescriptors(c.closeAfterWait) 340 | return err 341 | } 342 | c.childFiles = append(c.childFiles, fd) 343 | } 344 | c.childFiles = append(c.childFiles, c.ExtraFiles...) 345 | 346 | var err error 347 | c.Process, err = osex.StartProcessWithLogon(c.Path, c.argv(), c.Username, c.Domain, c.Password, &osex.ProcAttr{ 348 | Dir: c.Dir, 349 | Files: c.childFiles, 350 | Env: dedupEnv(c.envv()), 351 | Sys: c.SysProcAttr, 352 | }) 353 | if err != nil { 354 | c.closeDescriptors(c.closeAfterStart) 355 | c.closeDescriptors(c.closeAfterWait) 356 | return err 357 | } 358 | 359 | c.closeDescriptors(c.closeAfterStart) 360 | 361 | c.errch = make(chan error, len(c.goroutine)) 362 | for _, fn := range c.goroutine { 363 | go func(fn func() error) { 364 | c.errch <- fn() 365 | }(fn) 366 | } 367 | 368 | if c.ctx != nil { 369 | c.waitDone = make(chan struct{}) 370 | go func() { 371 | select { 372 | case <-c.ctx.Done(): 373 | c.Process.Kill() 374 | case <-c.waitDone: 375 | } 376 | }() 377 | } 378 | 379 | return nil 380 | } 381 | 382 | // Wait waits for the command to exit and waits for any copying to 383 | // stdin or copying from stdout or stderr to complete. 384 | // 385 | // The command must have been started by Start. 386 | // 387 | // The returned error is nil if the command runs, has no problems 388 | // copying stdin, stdout, and stderr, and exits with a zero exit 389 | // status. 390 | // 391 | // If the command fails to run or doesn't complete successfully, the 392 | // error is of type *ExitError. Other error types may be 393 | // returned for I/O problems. 394 | // 395 | // If c.Stdin is not an *os.File, Wait also waits for the I/O loop 396 | // copying from c.Stdin into the process's standard input 397 | // to complete. 398 | // 399 | // Wait releases any resources associated with the Cmd. 400 | func (c *Cmd) Wait() error { 401 | if c.Process == nil { 402 | return errors.New("exec: not started") 403 | } 404 | if c.finished { 405 | return errors.New("exec: Wait was already called") 406 | } 407 | c.finished = true 408 | 409 | state, err := c.Process.Wait() 410 | if c.waitDone != nil { 411 | close(c.waitDone) 412 | } 413 | c.ProcessState = state 414 | 415 | var copyError error 416 | for range c.goroutine { 417 | if err := <-c.errch; err != nil && copyError == nil { 418 | copyError = err 419 | } 420 | } 421 | 422 | c.closeDescriptors(c.closeAfterWait) 423 | 424 | if err != nil { 425 | return err 426 | } else if !state.Success() { 427 | return &exec.ExitError{ProcessState: state} 428 | } 429 | 430 | return copyError 431 | } 432 | 433 | // Output runs the command and returns its standard output. 434 | // Any returned error will usually be of type *ExitError. 435 | // If c.Stderr was nil, Output populates ExitError.Stderr. 436 | func (c *Cmd) Output() ([]byte, error) { 437 | if c.Stdout != nil { 438 | return nil, errors.New("exec: Stdout already set") 439 | } 440 | var stdout bytes.Buffer 441 | c.Stdout = &stdout 442 | 443 | captureErr := c.Stderr == nil 444 | if captureErr { 445 | c.Stderr = &prefixSuffixSaver{N: 32 << 10} 446 | } 447 | 448 | err := c.Run() 449 | if err != nil && captureErr { 450 | if ee, ok := err.(*exec.ExitError); ok { 451 | ee.Stderr = c.Stderr.(*prefixSuffixSaver).Bytes() 452 | } 453 | } 454 | return stdout.Bytes(), err 455 | } 456 | 457 | // CombinedOutput runs the command and returns its combined standard 458 | // output and standard error. 459 | func (c *Cmd) CombinedOutput() ([]byte, error) { 460 | if c.Stdout != nil { 461 | return nil, errors.New("exec: Stdout already set") 462 | } 463 | if c.Stderr != nil { 464 | return nil, errors.New("exec: Stderr already set") 465 | } 466 | var b bytes.Buffer 467 | c.Stdout = &b 468 | c.Stderr = &b 469 | err := c.Run() 470 | return b.Bytes(), err 471 | } 472 | 473 | // StdinPipe returns a pipe that will be connected to the command's 474 | // standard input when the command starts. 475 | // The pipe will be closed automatically after Wait sees the command exit. 476 | // A caller need only call Close to force the pipe to close sooner. 477 | // For example, if the command being run will not exit until standard input 478 | // is closed, the caller must close the pipe. 479 | func (c *Cmd) StdinPipe() (io.WriteCloser, error) { 480 | if c.Stdin != nil { 481 | return nil, errors.New("exec: Stdin already set") 482 | } 483 | if c.Process != nil { 484 | return nil, errors.New("exec: StdinPipe after process started") 485 | } 486 | pr, pw, err := os.Pipe() 487 | if err != nil { 488 | return nil, err 489 | } 490 | c.Stdin = pr 491 | c.closeAfterStart = append(c.closeAfterStart, pr) 492 | wc := &closeOnce{File: pw} 493 | c.closeAfterWait = append(c.closeAfterWait, closerFunc(wc.safeClose)) 494 | return wc, nil 495 | } 496 | 497 | type closeOnce struct { 498 | *os.File 499 | 500 | writers sync.RWMutex // coordinate safeClose and Write 501 | once sync.Once 502 | err error 503 | } 504 | 505 | func (c *closeOnce) Close() error { 506 | c.once.Do(c.close) 507 | return c.err 508 | } 509 | 510 | func (c *closeOnce) close() { 511 | c.err = c.File.Close() 512 | } 513 | 514 | type closerFunc func() error 515 | 516 | func (f closerFunc) Close() error { return f() } 517 | 518 | // safeClose closes c being careful not to race with any calls to c.Write. 519 | // See golang.org/issue/9307 and TestEchoFileRace in exec_test.go. 520 | // In theory other calls could also be excluded (by writing appropriate 521 | // wrappers like c.Write's implementation below), but since c is most 522 | // commonly used as a WriteCloser, Write is the main one to worry about. 523 | // See also #7970, for which this is a partial fix for this specific instance. 524 | // The idea is that we return a WriteCloser, and so the caller can be 525 | // relied upon not to call Write and Close simultaneously, but it's less 526 | // obvious that cmd.Wait calls Close and that the caller must not call 527 | // Write and cmd.Wait simultaneously. In fact that seems too onerous. 528 | // So we change the use of Close in cmd.Wait to use safeClose, which will 529 | // synchronize with any Write. 530 | // 531 | // It's important that we know this won't block forever waiting for the 532 | // operations being excluded. At the point where this is called, 533 | // the invoked command has exited and the parent copy of the read side 534 | // of the pipe has also been closed, so there should really be no read side 535 | // of the pipe left. Any active writes should return very shortly with an EPIPE, 536 | // making it reasonable to wait for them. 537 | // Technically it is possible that the child forked a sub-process or otherwise 538 | // handed off the read side of the pipe before exiting and the current holder 539 | // is not reading from the pipe, and the pipe is full, in which case the close here 540 | // might block waiting for the write to complete. That's probably OK. 541 | // It's a small enough problem to be outweighed by eliminating the race here. 542 | func (c *closeOnce) safeClose() error { 543 | c.writers.Lock() 544 | err := c.Close() 545 | c.writers.Unlock() 546 | return err 547 | } 548 | 549 | func (c *closeOnce) Write(b []byte) (int, error) { 550 | c.writers.RLock() 551 | n, err := c.File.Write(b) 552 | c.writers.RUnlock() 553 | return n, err 554 | } 555 | 556 | func (c *closeOnce) WriteString(s string) (int, error) { 557 | c.writers.RLock() 558 | n, err := c.File.WriteString(s) 559 | c.writers.RUnlock() 560 | return n, err 561 | } 562 | 563 | // StdoutPipe returns a pipe that will be connected to the command's 564 | // standard output when the command starts. 565 | // 566 | // Wait will close the pipe after seeing the command exit, so most callers 567 | // need not close the pipe themselves; however, an implication is that 568 | // it is incorrect to call Wait before all reads from the pipe have completed. 569 | // For the same reason, it is incorrect to call Run when using StdoutPipe. 570 | // See the example for idiomatic usage. 571 | func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { 572 | if c.Stdout != nil { 573 | return nil, errors.New("exec: Stdout already set") 574 | } 575 | if c.Process != nil { 576 | return nil, errors.New("exec: StdoutPipe after process started") 577 | } 578 | pr, pw, err := os.Pipe() 579 | if err != nil { 580 | return nil, err 581 | } 582 | c.Stdout = pw 583 | c.closeAfterStart = append(c.closeAfterStart, pw) 584 | c.closeAfterWait = append(c.closeAfterWait, pr) 585 | return pr, nil 586 | } 587 | 588 | // StderrPipe returns a pipe that will be connected to the command's 589 | // standard error when the command starts. 590 | // 591 | // Wait will close the pipe after seeing the command exit, so most callers 592 | // need not close the pipe themselves; however, an implication is that 593 | // it is incorrect to call Wait before all reads from the pipe have completed. 594 | // For the same reason, it is incorrect to use Run when using StderrPipe. 595 | // See the StdoutPipe example for idiomatic usage. 596 | func (c *Cmd) StderrPipe() (io.ReadCloser, error) { 597 | if c.Stderr != nil { 598 | return nil, errors.New("exec: Stderr already set") 599 | } 600 | if c.Process != nil { 601 | return nil, errors.New("exec: StderrPipe after process started") 602 | } 603 | pr, pw, err := os.Pipe() 604 | if err != nil { 605 | return nil, err 606 | } 607 | c.Stderr = pw 608 | c.closeAfterStart = append(c.closeAfterStart, pw) 609 | c.closeAfterWait = append(c.closeAfterWait, pr) 610 | return pr, nil 611 | } 612 | 613 | // prefixSuffixSaver is an io.Writer which retains the first N bytes 614 | // and the last N bytes written to it. The Bytes() methods reconstructs 615 | // it with a pretty error message. 616 | type prefixSuffixSaver struct { 617 | N int // max size of prefix or suffix 618 | prefix []byte 619 | suffix []byte // ring buffer once len(suffix) == N 620 | suffixOff int // offset to write into suffix 621 | skipped int64 622 | 623 | // TODO(bradfitz): we could keep one large []byte and use part of it for 624 | // the prefix, reserve space for the '... Omitting N bytes ...' message, 625 | // then the ring buffer suffix, and just rearrange the ring buffer 626 | // suffix when Bytes() is called, but it doesn't seem worth it for 627 | // now just for error messages. It's only ~64KB anyway. 628 | } 629 | 630 | func (w *prefixSuffixSaver) Write(p []byte) (n int, err error) { 631 | lenp := len(p) 632 | p = w.fill(&w.prefix, p) 633 | 634 | // Only keep the last w.N bytes of suffix data. 635 | if overage := len(p) - w.N; overage > 0 { 636 | p = p[overage:] 637 | w.skipped += int64(overage) 638 | } 639 | p = w.fill(&w.suffix, p) 640 | 641 | // w.suffix is full now if p is non-empty. Overwrite it in a circle. 642 | for len(p) > 0 { // 0, 1, or 2 iterations. 643 | n := copy(w.suffix[w.suffixOff:], p) 644 | p = p[n:] 645 | w.skipped += int64(n) 646 | w.suffixOff += n 647 | if w.suffixOff == w.N { 648 | w.suffixOff = 0 649 | } 650 | } 651 | return lenp, nil 652 | } 653 | 654 | // fill appends up to len(p) bytes of p to *dst, such that *dst does not 655 | // grow larger than w.N. It returns the un-appended suffix of p. 656 | func (w *prefixSuffixSaver) fill(dst *[]byte, p []byte) (pRemain []byte) { 657 | if remain := w.N - len(*dst); remain > 0 { 658 | add := minInt(len(p), remain) 659 | *dst = append(*dst, p[:add]...) 660 | p = p[add:] 661 | } 662 | return p 663 | } 664 | 665 | func (w *prefixSuffixSaver) Bytes() []byte { 666 | if w.suffix == nil { 667 | return w.prefix 668 | } 669 | if w.skipped == 0 { 670 | return append(w.prefix, w.suffix...) 671 | } 672 | var buf bytes.Buffer 673 | buf.Grow(len(w.prefix) + len(w.suffix) + 50) 674 | buf.Write(w.prefix) 675 | buf.WriteString("\n... omitting ") 676 | buf.WriteString(strconv.FormatInt(w.skipped, 10)) 677 | buf.WriteString(" bytes ...\n") 678 | buf.Write(w.suffix[w.suffixOff:]) 679 | buf.Write(w.suffix[:w.suffixOff]) 680 | return buf.Bytes() 681 | } 682 | 683 | func minInt(a, b int) int { 684 | if a < b { 685 | return a 686 | } 687 | return b 688 | } 689 | 690 | // dedupEnv returns a copy of env with any duplicates removed, in favor of 691 | // later values. 692 | // Items not of the normal environment "key=value" form are preserved unchanged. 693 | func dedupEnv(env []string) []string { 694 | return dedupEnvCase(runtime.GOOS == "windows", env) 695 | } 696 | 697 | // dedupEnvCase is dedupEnv with a case option for testing. 698 | // If caseInsensitive is true, the case of keys is ignored. 699 | func dedupEnvCase(caseInsensitive bool, env []string) []string { 700 | out := make([]string, 0, len(env)) 701 | saw := map[string]int{} // key => index into out 702 | for _, kv := range env { 703 | eq := strings.Index(kv, "=") 704 | if eq < 0 { 705 | out = append(out, kv) 706 | continue 707 | } 708 | k := kv[:eq] 709 | if caseInsensitive { 710 | k = strings.ToLower(k) 711 | } 712 | if dupIdx, isDup := saw[k]; isDup { 713 | out[dupIdx] = kv 714 | continue 715 | } 716 | saw[k] = len(out) 717 | out = append(out, kv) 718 | } 719 | return out 720 | } 721 | -------------------------------------------------------------------------------- /winox/osex/exec_windows.go: -------------------------------------------------------------------------------- 1 | package osex 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "runtime" 7 | "unsafe" 8 | 9 | "github.com/itchio/ox/syscallex" 10 | ) 11 | 12 | // ProcAttr holds the attributes that will be applied to a new process 13 | // started by StartProcess. 14 | type ProcAttr struct { 15 | // If Dir is non-empty, the child changes into the directory before 16 | // creating the process. 17 | Dir string 18 | // If Env is non-nil, it gives the environment variables for the 19 | // new process in the form returned by Environ. 20 | // If it is nil, the result of Environ will be used. 21 | Env []string 22 | // Files specifies the open files inherited by the new process. The 23 | // first three entries correspond to standard input, standard output, and 24 | // standard error. An implementation may support additional entries, 25 | // depending on the underlying operating system. A nil entry corresponds 26 | // to that file being closed when the process starts. 27 | Files []*os.File 28 | 29 | // Operating system-specific process creation attributes. 30 | // Note that setting this field means that your program 31 | // may not execute properly or even compile on some 32 | // operating systems. 33 | Sys *syscallex.SysProcAttr 34 | } 35 | 36 | // StartProcess starts a new process with the program, arguments and attributes 37 | // specified by name, argv and attr. 38 | // 39 | // StartProcess is a low-level interface. The os/exec package provides 40 | // higher-level interfaces. 41 | // 42 | // If there is an error, it will be of type *PathError. 43 | func StartProcessWithLogon(name string, argv []string, username string, domain string, password string, attr *ProcAttr) (*os.Process, error) { 44 | return startProcessWithLogon(name, argv, username, domain, password, attr) 45 | } 46 | 47 | func startProcessWithLogon(name string, argv []string, username string, domain string, password string, attr *ProcAttr) (p *os.Process, err error) { 48 | // If there is no SysProcAttr (ie. no Chroot or changed 49 | // UID/GID), double-check existence of the directory we want 50 | // to chdir into. We can make the error clearer this way. 51 | if attr != nil && attr.Sys == nil && attr.Dir != "" { 52 | if _, err := os.Stat(attr.Dir); err != nil { 53 | pe := err.(*os.PathError) 54 | pe.Op = "chdir" 55 | return nil, pe 56 | } 57 | } 58 | 59 | sysattr := &syscallex.ProcAttr{ 60 | Dir: attr.Dir, 61 | Env: attr.Env, 62 | Sys: attr.Sys, 63 | } 64 | if sysattr.Env == nil { 65 | sysattr.Env = os.Environ() 66 | } 67 | for _, f := range attr.Files { 68 | sysattr.Files = append(sysattr.Files, f.Fd()) 69 | } 70 | 71 | pid, h, e := syscallex.StartProcessWithLogon(name, argv, username, domain, password, sysattr) 72 | if e != nil { 73 | return nil, &os.PathError{"fork/exec", name, e} 74 | } 75 | return newProcess(pid, h), nil 76 | } 77 | 78 | func newProcess(pid int, handle uintptr) *os.Process { 79 | p := &os.Process{Pid: pid} 80 | 81 | // /!\ Danger zone /!\ 82 | // set private field handle via reflection 83 | // see: https://stackoverflow.com/a/17982725 84 | pointerVal := reflect.ValueOf(p) 85 | val := reflect.Indirect(pointerVal) 86 | member := val.FieldByName("handle") 87 | ptrToHandle := unsafe.Pointer(member.UnsafeAddr()) 88 | realPtrToHandle := (*uintptr)(ptrToHandle) 89 | *realPtrToHandle = handle 90 | 91 | runtime.SetFinalizer(p, (*os.Process).Release) 92 | return p 93 | } 94 | -------------------------------------------------------------------------------- /winox/permissions_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package winox 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | "syscall" 9 | "unsafe" 10 | 11 | "github.com/itchio/ox/syscallex" 12 | "github.com/itchio/headway/state" 13 | "github.com/pkg/errors" 14 | ) 15 | 16 | type PermissionChange int 17 | 18 | const ( 19 | PermissionChangeGrant = iota 20 | PermissionChangeRevoke 21 | ) 22 | 23 | type InheritanceMode int 24 | 25 | const ( 26 | InheritanceModeNone = iota 27 | InheritanceModeFull 28 | ) 29 | 30 | type Rights uint32 31 | 32 | const ( 33 | RightsRead = syscallex.GENERIC_READ 34 | RightsWrite = syscallex.GENERIC_WRITE 35 | RightsExecute = syscallex.GENERIC_EXECUTE 36 | RightsAll = syscallex.GENERIC_ALL 37 | 38 | RightsFull = RightsRead | RightsWrite | RightsExecute | RightsAll 39 | ) 40 | 41 | type SetFilePermissionsParams struct { 42 | FilePath string 43 | Trustee string 44 | PermissionChange PermissionChange 45 | 46 | AccessRights Rights 47 | Inheritance InheritanceMode 48 | } 49 | 50 | func SetFilePermissions(params *SetFilePermissionsParams) error { 51 | if params.FilePath == "" { 52 | return errors.New("FilePath cannot be empty") 53 | } 54 | if params.Trustee == "" { 55 | return errors.New("Trustee cannot be empty") 56 | } 57 | 58 | objectName := syscall.StringToUTF16Ptr(params.FilePath) 59 | var objectType uint32 = syscallex.SE_FILE_OBJECT 60 | 61 | var accessMode uint32 62 | switch params.PermissionChange { 63 | case PermissionChangeGrant: 64 | accessMode = syscallex.GRANT_ACCESS 65 | case PermissionChangeRevoke: 66 | accessMode = syscallex.REVOKE_ACCESS 67 | default: 68 | return errors.New("unknown PermissionChange value") 69 | } 70 | 71 | var inheritance uint32 72 | switch params.Inheritance { 73 | case InheritanceModeNone: 74 | inheritance = syscallex.NO_INHERITANCE 75 | case InheritanceModeFull: 76 | inheritance = syscallex.CONTAINER_INHERIT_ACE | syscallex.OBJECT_INHERIT_ACE 77 | default: 78 | return errors.New("unknown Inheritance value") 79 | } 80 | 81 | // Get a pointer to the existing DACL 82 | var pOldDACL *syscallex.ACL 83 | var pSD uintptr 84 | err := syscallex.GetNamedSecurityInfo( 85 | objectName, 86 | objectType, 87 | syscallex.DACL_SECURITY_INFORMATION, 88 | 0, // ppsidOwner 89 | 0, // ppsidGroup 90 | &pOldDACL, // ppDacl 91 | nil, // ppSacl 92 | uintptr(unsafe.Pointer(&pSD)), // ppSecurityDescriptor 93 | ) 94 | if err != nil { 95 | return errors.WithStack(err) 96 | } 97 | defer SafeRelease(pSD) 98 | 99 | // Initialize an EXPLICIT_ACCESS structure for the new ACE 100 | var ea syscallex.ExplicitAccess 101 | ea.AccessPermissions = uint32(params.AccessRights) 102 | ea.AccessMode = accessMode 103 | ea.Inheritance = inheritance 104 | ea.Trustee.TrusteeForm = syscallex.TRUSTEE_IS_NAME 105 | ea.Trustee.Name = syscall.StringToUTF16Ptr(params.Trustee) 106 | 107 | // Create a new ACL that merges the new ACE 108 | // into the existing DACL. 109 | var pNewDACL *syscallex.ACL 110 | err = syscallex.SetEntriesInAcl( 111 | 1, // number of items 112 | uintptr(unsafe.Pointer(&ea)), // pointer to first element of array 113 | pOldDACL, 114 | &pNewDACL, 115 | ) 116 | if err != nil { 117 | return errors.WithStack(err) 118 | } 119 | defer SafeRelease(uintptr(unsafe.Pointer(pNewDACL))) 120 | 121 | switch params.Inheritance { 122 | case InheritanceModeNone: 123 | // use legacy SetFileSecurity call, which doesn't propagaate 124 | 125 | // But first, convert the (self-relative) security descriptor to absolute format 126 | var absoluteSDSize uint32 127 | var daclSize uint32 128 | var saclSize uint32 129 | var ownerSize uint32 130 | var groupSize uint32 131 | 132 | // sic. ignoring err on purpose 133 | err = syscallex.MakeAbsoluteSD( 134 | pSD, 135 | 0, 136 | &absoluteSDSize, 137 | nil, 138 | &daclSize, 139 | nil, 140 | &saclSize, 141 | 0, 142 | &ownerSize, 143 | 0, 144 | &groupSize, 145 | ) 146 | if err != nil { 147 | rescued := false 148 | if en, ok := AsErrno(err); ok { 149 | if en == syscall.ERROR_INSUFFICIENT_BUFFER { 150 | // cool, that's expected! 151 | rescued = true 152 | } 153 | } 154 | 155 | if !rescued { 156 | return errors.WithStack(err) 157 | } 158 | } 159 | 160 | // allocate everything 161 | // avoid 0-length allocations because then the 162 | // uintptr(unsafe.Pointer(&slice[0])) doesn't work 163 | pAbsoluteSD := make([]byte, absoluteSDSize+1) 164 | pDacl := make([]byte, daclSize+1) 165 | pSacl := make([]byte, saclSize+1) 166 | pOwner := make([]byte, ownerSize+1) 167 | pGroup := make([]byte, groupSize+1) 168 | 169 | err = syscallex.MakeAbsoluteSD( 170 | pSD, 171 | uintptr(unsafe.Pointer(&pAbsoluteSD[0])), 172 | &absoluteSDSize, 173 | (*syscallex.ACL)(unsafe.Pointer(&pDacl[0])), 174 | &daclSize, 175 | (*syscallex.ACL)(unsafe.Pointer(&pSacl[0])), 176 | &saclSize, 177 | uintptr(unsafe.Pointer(&pOwner[0])), 178 | &ownerSize, 179 | uintptr(unsafe.Pointer(&pGroup[0])), 180 | &groupSize, 181 | ) 182 | if err != nil { 183 | return errors.WithStack(err) 184 | } 185 | 186 | // Attach the new ACL as the object's DACL 187 | err = syscallex.SetSecurityDescriptorDacl( 188 | uintptr(unsafe.Pointer(&pAbsoluteSD[0])), 189 | 1, // bDaclPresent 190 | pNewDACL, 191 | 0, // not defaulted 192 | ) 193 | if err != nil { 194 | return errors.WithStack(err) 195 | } 196 | err = syscallex.SetFileSecurity( 197 | objectName, 198 | syscallex.DACL_SECURITY_INFORMATION, 199 | uintptr(unsafe.Pointer(&pAbsoluteSD[0])), 200 | ) 201 | if err != nil { 202 | return errors.WithStack(err) 203 | } 204 | case InheritanceModeFull: 205 | // use new SetNamedSecurityInfo call, which propagates 206 | err = syscallex.SetNamedSecurityInfo( 207 | objectName, 208 | objectType, 209 | syscallex.DACL_SECURITY_INFORMATION, 210 | 0, // psidOwner 211 | 0, // psidGroup 212 | pNewDACL, // pDacl 213 | nil, // pSacl 214 | ) 215 | if err != nil { 216 | return errors.WithStack(err) 217 | } 218 | } 219 | 220 | return nil 221 | } 222 | 223 | func SafeRelease(handle uintptr) { 224 | if handle != 0 { 225 | syscall.Close(syscall.Handle(handle)) 226 | } 227 | } 228 | 229 | type ShareEntry struct { 230 | Path string 231 | Inheritance InheritanceMode 232 | Rights Rights 233 | } 234 | 235 | func (se *ShareEntry) Grant(trustee string) error { 236 | err := SetFilePermissions(se.params(PermissionChangeGrant, trustee)) 237 | if err != nil { 238 | return errors.WithStack(err) 239 | } 240 | 241 | return nil 242 | } 243 | 244 | func (se *ShareEntry) Revoke(trustee string) error { 245 | err := SetFilePermissions(se.params(PermissionChangeRevoke, trustee)) 246 | if err != nil { 247 | return errors.WithStack(err) 248 | } 249 | 250 | return nil 251 | } 252 | 253 | func (se *ShareEntry) params(change PermissionChange, trustee string) *SetFilePermissionsParams { 254 | return &SetFilePermissionsParams{ 255 | FilePath: se.Path, 256 | AccessRights: se.Rights, 257 | PermissionChange: change, 258 | Trustee: trustee, 259 | Inheritance: se.Inheritance, 260 | } 261 | } 262 | 263 | type SharingPolicy struct { 264 | Trustee string 265 | Entries []*ShareEntry 266 | } 267 | 268 | func (sp *SharingPolicy) Grant(consumer *state.Consumer) error { 269 | ec := &errorCoalescer{ 270 | operation: "granting permissions", 271 | consumer: consumer, 272 | } 273 | for _, se := range sp.Entries { 274 | ec.Record(se.Grant(sp.Trustee)) 275 | } 276 | return ec.Result() 277 | } 278 | 279 | func (sp *SharingPolicy) Revoke(consumer *state.Consumer) error { 280 | ec := &errorCoalescer{ 281 | operation: "revoking permissions", 282 | consumer: consumer, 283 | } 284 | for _, se := range sp.Entries { 285 | ec.Record(se.Revoke(sp.Trustee)) 286 | } 287 | return ec.Result() 288 | } 289 | 290 | func (sp *SharingPolicy) String() string { 291 | var entries []string 292 | 293 | for _, e := range sp.Entries { 294 | perms := "" 295 | if e.Rights&RightsRead > 0 { 296 | perms += "R" 297 | } 298 | if e.Rights&RightsWrite > 0 { 299 | perms += "W" 300 | } 301 | if e.Rights&RightsExecute > 0 { 302 | perms += "X" 303 | } 304 | if e.Rights&RightsAll > 0 { 305 | perms += "*" 306 | } 307 | 308 | inherit := "" 309 | if e.Inheritance == InheritanceModeFull { 310 | inherit = "(CI)(OI)" 311 | } else { 312 | inherit = "" 313 | } 314 | 315 | entries = append(entries, fmt.Sprintf(" → (%s)(%s)%s", e.Path, perms, inherit)) 316 | } 317 | 318 | var entriesString = " (no sharing entries)" 319 | if len(entries) > 0 { 320 | entriesString = strings.Join(entries, "\n") 321 | } 322 | 323 | return fmt.Sprintf("for %s\n%s", sp.Trustee, entriesString) 324 | } 325 | 326 | type errorCoalescer struct { 327 | operation string 328 | consumer *state.Consumer 329 | 330 | // internal 331 | errors []error 332 | } 333 | 334 | func (ec *errorCoalescer) Record(err error) { 335 | if err != nil { 336 | ec.errors = append(ec.errors, err) 337 | ec.consumer.Warnf("While %s: %+v", ec.operation, err) 338 | } 339 | } 340 | 341 | func (ec *errorCoalescer) Result() error { 342 | if len(ec.errors) > 0 { 343 | var messages []string 344 | for _, e := range ec.errors { 345 | messages = append(messages, e.Error()) 346 | } 347 | return fmt.Errorf("%d errors while %s: %s", len(messages), ec.operation, strings.Join(messages, " ; ")) 348 | } 349 | return nil 350 | } 351 | 352 | func GetImpersonationToken(username string, domain string, password string) (syscall.Token, error) { 353 | var impersonationToken syscall.Token 354 | err := Impersonate(username, domain, password, func() error { 355 | currentThread := syscallex.GetCurrentThread() 356 | 357 | err := syscallex.OpenThreadToken( 358 | currentThread, 359 | syscall.TOKEN_ALL_ACCESS, 360 | 1, 361 | &impersonationToken, 362 | ) 363 | if err != nil { 364 | return errors.WithStack(err) 365 | } 366 | return nil 367 | }) 368 | if err != nil { 369 | return 0, errors.WithStack(err) 370 | } 371 | 372 | return impersonationToken, nil 373 | } 374 | 375 | func UserHasPermission(impersonationToken syscall.Token, accessDesired uint32, path string) (bool, error) { 376 | // cf. http://blog.aaronballman.com/2011/08/how-to-check-access-rights/ 377 | // (more or less) 378 | 379 | // get the security descriptor for the file 380 | var securityDescriptorLength uint32 381 | syscallex.GetFileSecurity( 382 | syscall.StringToUTF16Ptr(path), 383 | syscallex.OWNER_SECURITY_INFORMATION|syscallex.GROUP_SECURITY_INFORMATION|syscallex.DACL_SECURITY_INFORMATION, 384 | 0, 385 | 0, 386 | &securityDescriptorLength, 387 | ) 388 | 389 | // allow 0-length allocations 390 | securityDescriptor := make([]byte, securityDescriptorLength+1) 391 | err := syscallex.GetFileSecurity( 392 | syscall.StringToUTF16Ptr(path), 393 | syscallex.OWNER_SECURITY_INFORMATION|syscallex.GROUP_SECURITY_INFORMATION|syscallex.DACL_SECURITY_INFORMATION, 394 | uintptr(unsafe.Pointer(&securityDescriptor[0])), 395 | securityDescriptorLength, 396 | &securityDescriptorLength, 397 | ) 398 | if err != nil { 399 | return false, errors.WithStack(err) 400 | } 401 | 402 | var accessStatus bool 403 | 404 | var mapping syscallex.GenericMapping 405 | mapping.GenericRead = syscallex.FILE_GENERIC_READ 406 | mapping.GenericWrite = syscallex.FILE_GENERIC_WRITE 407 | mapping.GenericExecute = syscallex.FILE_GENERIC_EXECUTE 408 | mapping.GenericAll = syscallex.FILE_ALL_ACCESS 409 | syscallex.MapGenericMask(&accessDesired, &mapping) 410 | 411 | var grantedAccess uint32 412 | var privilegeSetLength uint32 413 | 414 | // get length of privilegeSet 415 | syscallex.AccessCheck( 416 | uintptr(unsafe.Pointer(&securityDescriptor[0])), 417 | impersonationToken, 418 | accessDesired, 419 | &mapping, 420 | 0, 421 | &privilegeSetLength, 422 | &grantedAccess, 423 | &accessStatus, 424 | ) 425 | 426 | // avoid 0-byte allocation 427 | privilegeSet := make([]byte, privilegeSetLength+1) 428 | 429 | err = syscallex.AccessCheck( 430 | uintptr(unsafe.Pointer(&securityDescriptor[0])), 431 | impersonationToken, 432 | accessDesired, 433 | &mapping, 434 | uintptr(unsafe.Pointer(&privilegeSet[0])), 435 | &privilegeSetLength, 436 | &grantedAccess, 437 | &accessStatus, 438 | ) 439 | if err != nil { 440 | return false, errors.WithStack(err) 441 | } 442 | 443 | return accessStatus, nil 444 | } 445 | -------------------------------------------------------------------------------- /winox/users_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package winox 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/itchio/ox/syscallex" 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | type FolderType int 14 | 15 | const ( 16 | FolderTypeProfile FolderType = iota 17 | FolderTypeAppData 18 | FolderTypeLocalAppData 19 | FolderTypeStartMenu 20 | FolderTypePrograms 21 | ) 22 | 23 | func GetFolderPath(folderType FolderType) (string, error) { 24 | var csidl uint32 25 | switch folderType { 26 | case FolderTypeProfile: 27 | csidl = syscallex.CSIDL_PROFILE 28 | case FolderTypeAppData: 29 | csidl = syscallex.CSIDL_APPDATA 30 | case FolderTypeLocalAppData: 31 | csidl = syscallex.CSIDL_LOCAL_APPDATA 32 | case FolderTypeStartMenu: 33 | csidl = syscallex.CSIDL_STARTMENU 34 | case FolderTypePrograms: 35 | csidl = syscallex.CSIDL_PROGRAMS 36 | default: 37 | return "", errors.Errorf("Unknown folder type: %d", folderType) 38 | } 39 | csidl |= syscallex.CSIDL_FLAG_CREATE 40 | 41 | ret, err := syscallex.SHGetFolderPath( 42 | 0, 43 | csidl, 44 | 0, 45 | syscallex.SHGFP_TYPE_CURRENT, 46 | ) 47 | if err != nil { 48 | return "", errors.WithStack(err) 49 | } 50 | return ret, nil 51 | } 52 | 53 | type ImpersonateCallback func() error 54 | 55 | func Logon(username string, domain string, password string) (syscall.Token, error) { 56 | var token syscall.Token 57 | err := syscallex.LogonUser( 58 | syscall.StringToUTF16Ptr(username), 59 | syscall.StringToUTF16Ptr(domain), 60 | syscall.StringToUTF16Ptr(password), 61 | syscallex.LOGON32_LOGON_INTERACTIVE, 62 | syscallex.LOGON32_PROVIDER_DEFAULT, 63 | &token, 64 | ) 65 | if err != nil { 66 | return 0, errors.WithStack(err) 67 | } 68 | 69 | return token, nil 70 | } 71 | 72 | func Impersonate(username string, domain string, password string, cb ImpersonateCallback) error { 73 | token, err := Logon(username, domain, password) 74 | if err != nil { 75 | return errors.WithStack(err) 76 | } 77 | defer syscall.CloseHandle(syscall.Handle(token)) 78 | 79 | _, err = syscall.GetEnvironmentStrings() 80 | if err != nil { 81 | return errors.WithStack(err) 82 | } 83 | 84 | err = syscallex.ImpersonateLoggedOnUser(token) 85 | if err != nil { 86 | return errors.WithStack(err) 87 | } 88 | 89 | defer syscallex.RevertToSelf() 90 | 91 | return cb() 92 | } 93 | 94 | func AddUser(username string, password string, comment string) error { 95 | var usri1 = syscallex.UserInfo1{ 96 | Name: syscall.StringToUTF16Ptr(username), 97 | Password: syscall.StringToUTF16Ptr(password), 98 | Priv: syscallex.USER_PRIV_USER, 99 | Flags: syscallex.UF_SCRIPT, 100 | Comment: syscall.StringToUTF16Ptr(comment), 101 | } 102 | 103 | err := syscallex.NetUserAdd( 104 | nil, 105 | 1, 106 | uintptr(unsafe.Pointer(&usri1)), 107 | nil, 108 | ) 109 | if err != nil { 110 | return errors.WithMessage(err, "NetUserAdd") 111 | } 112 | 113 | return nil 114 | } 115 | 116 | func ForceSetPassword(username string, password string) error { 117 | var usri1003 = syscallex.UserInfo1003{ 118 | Password: syscall.StringToUTF16Ptr(password), 119 | } 120 | 121 | err := syscallex.NetUserSetInfo( 122 | nil, 123 | syscall.StringToUTF16Ptr(username), 124 | 1003, 125 | uintptr(unsafe.Pointer(&usri1003)), 126 | nil, 127 | ) 128 | if err != nil { 129 | return errors.WithMessage(err, "NetUserSetInfo (password)") 130 | } 131 | 132 | return nil 133 | } 134 | 135 | // Remove "username" from the "Users" group if needed 136 | func RemoveUserFromUsersGroup(username string) error { 137 | var arbitrarySize = 2048 138 | var sidSize uint32 = uint32(arbitrarySize) 139 | sid := make([]byte, sidSize) 140 | 141 | err := syscallex.CreateWellKnownSid( 142 | syscallex.WinBuiltinUsersSid, 143 | 0, // domainSid 144 | uintptr(unsafe.Pointer(&sid[0])), 145 | &sidSize, 146 | ) 147 | if err != nil { 148 | return errors.WithStack(err) 149 | } 150 | 151 | var cchName = uint32(arbitrarySize) 152 | name := make([]uint16, cchName) 153 | 154 | var cchReferencedDomainName = uint32(arbitrarySize) 155 | referencedDomainName := make([]uint16, cchReferencedDomainName) 156 | 157 | var sidUse uint32 158 | 159 | err = syscallex.LookupAccountSid( 160 | nil, // systemName 161 | uintptr(unsafe.Pointer(&sid[0])), 162 | &name[0], 163 | &cchName, 164 | &referencedDomainName[0], 165 | &cchReferencedDomainName, 166 | &sidUse, 167 | ) 168 | if err != nil { 169 | return errors.WithStack(err) 170 | } 171 | 172 | usersGroupName := &name[0] 173 | 174 | var gmi [1]syscallex.LocalGroupMembersInfo3 175 | gmi[0].DomainAndName = syscall.StringToUTF16Ptr(username) 176 | 177 | err = syscallex.NetLocalGroupDelMembers( 178 | nil, // servername 179 | usersGroupName, // groupName 180 | 3, // level 181 | uintptr(unsafe.Pointer(&gmi[0])), 182 | 1, // totalentries 183 | ) 184 | if err != nil { 185 | if en, ok := err.(syscall.Errno); ok { 186 | if en == syscallex.ERROR_MEMBER_NOT_IN_ALIAS { 187 | // User wasn't in Users group. That's ok! 188 | return nil 189 | } 190 | } 191 | return errors.WithStack(err) 192 | } 193 | 194 | return nil 195 | } 196 | 197 | func LoadProfileOnce(username string, domain string, password string) error { 198 | token, err := Logon(username, password, password) 199 | if err != nil { 200 | return errors.WithStack(err) 201 | } 202 | 203 | defer syscall.CloseHandle(syscall.Handle(token)) 204 | 205 | var profileInfo syscallex.ProfileInfo 206 | profileInfo.Size = uint32(unsafe.Sizeof(profileInfo)) 207 | profileInfo.UserName = syscall.StringToUTF16Ptr(username) 208 | profileInfo.Flags = syscallex.PI_NOUI 209 | 210 | err = syscallex.LoadUserProfile(token, &profileInfo) 211 | if err != nil { 212 | return errors.WithStack(err) 213 | } 214 | 215 | err = syscallex.UnloadUserProfile(token, profileInfo.Profile) 216 | if err != nil { 217 | return errors.WithStack(err) 218 | } 219 | 220 | return nil 221 | } 222 | 223 | type causer interface { 224 | Cause() error 225 | } 226 | 227 | func AsErrno(err error) (syscall.Errno, bool) { 228 | if se, ok := err.(causer); ok { 229 | return AsErrno(se.Cause()) 230 | } 231 | 232 | en, ok := err.(syscall.Errno) 233 | return en, ok 234 | } 235 | -------------------------------------------------------------------------------- /winox/users_windows_test.go: -------------------------------------------------------------------------------- 1 | package winox_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/itchio/ox/winox" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_GetFolderPath(t *testing.T) { 11 | type tcase struct { 12 | name string 13 | typ winox.FolderType 14 | } 15 | 16 | cases := []tcase{ 17 | tcase{name: "appData", typ: winox.FolderTypeAppData}, 18 | tcase{name: "localAppData", typ: winox.FolderTypeLocalAppData}, 19 | tcase{name: "profile", typ: winox.FolderTypeProfile}, 20 | tcase{name: "startMenu", typ: winox.FolderTypeStartMenu}, 21 | tcase{name: "programs", typ: winox.FolderTypePrograms}, 22 | } 23 | 24 | for _, cas := range cases { 25 | t.Run(cas.name, func(t *testing.T) { 26 | s, err := winox.GetFolderPath(cas.typ) 27 | assert.NoError(t, err) 28 | assert.NotEmpty(t, s) 29 | }) 30 | } 31 | 32 | { 33 | s, err := winox.GetFolderPath(winox.FolderType(-1)) 34 | assert.Error(t, err) 35 | assert.Empty(t, s) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /winox/verifytrust_windows.go: -------------------------------------------------------------------------------- 1 | package winox 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "github.com/itchio/ox/syscallex" 8 | ) 9 | 10 | func VerifyTrust(path string) error { 11 | policyGUID := syscallex.WINTRUST_ACTION_GENERIC_VERIFY_V2 12 | 13 | fileData := new(syscallex.WinTrustFileInfo) 14 | fileData.CbStruct = uint32(unsafe.Sizeof(*fileData)) 15 | fileData.FilePath = syscall.StringToUTF16Ptr(path) 16 | 17 | winTrustData := new(syscallex.WinTrustData) 18 | winTrustData.CbStruct = uint32(unsafe.Sizeof(*winTrustData)) 19 | winTrustData.UIChoice = syscallex.WTD_UI_NONE 20 | winTrustData.RevocationChecks = syscallex.WTD_REVOKE_NONE 21 | winTrustData.UnionChoice = syscallex.WTD_CHOICE_FILE 22 | winTrustData.StateAction = syscallex.WTD_STATEACTION_VERIFY 23 | winTrustData.FileOrCatalogOrBlobOrSgnrOrCert = uintptr(unsafe.Pointer(fileData)) 24 | 25 | trustErr := syscallex.WinVerifyTrust(syscall.Handle(0), &policyGUID, winTrustData) 26 | 27 | winTrustData.StateAction = syscallex.WTD_STATEACTION_CLOSE 28 | syscallex.WinVerifyTrust(syscall.Handle(0), &policyGUID, winTrustData) 29 | 30 | return trustErr 31 | } 32 | --------------------------------------------------------------------------------