aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/elevate/shellexecute.go
diff options
context:
space:
mode:
Diffstat (limited to 'elevate/shellexecute.go')
-rw-r--r--elevate/shellexecute.go126
1 files changed, 126 insertions, 0 deletions
diff --git a/elevate/shellexecute.go b/elevate/shellexecute.go
new file mode 100644
index 00000000..c3dc84eb
--- /dev/null
+++ b/elevate/shellexecute.go
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package elevate
+
+import (
+ "path/filepath"
+ "runtime"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/registry"
+)
+
+const (
+ releaseOffset = 2
+ shellExecuteOffset = 9
+
+ cSEE_MASK_DEFAULT = 0
+)
+
+func ShellExecute(program string, arguments string, directory string, show int32) (err error) {
+ var (
+ program16 *uint16
+ arguments16 *uint16
+ directory16 *uint16
+ )
+
+ if len(program) > 0 {
+ program16, _ = windows.UTF16PtrFromString(program)
+ }
+ if len(arguments) > 0 {
+ arguments16, _ = windows.UTF16PtrFromString(arguments)
+ }
+ if len(directory) > 0 {
+ directory16, _ = windows.UTF16PtrFromString(directory)
+ }
+
+ defer func() {
+ if err != nil {
+ err = windows.ShellExecute(0, windows.StringToUTF16Ptr("runas"), program16, arguments16, directory16, show)
+ }
+ }()
+
+ processToken, err := windows.OpenCurrentProcessToken()
+ if err != nil {
+ return
+ }
+ defer processToken.Close()
+ if processToken.IsElevated() {
+ err = windows.ERROR_SUCCESS
+ return
+ }
+
+ key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\UAC\\COMAutoApprovalList", registry.QUERY_VALUE)
+ if err == nil {
+ var autoApproved uint64
+ autoApproved, _, err = key.GetIntegerValue("{3E5FC7F9-9A51-4367-9063-A120244FBEC7}")
+ key.Close()
+ if err != nil {
+ return
+ }
+ if uint32(autoApproved) == 0 {
+ err = windows.ERROR_ACCESS_DENIED
+ return
+ }
+ }
+
+ moduleHandle, err := getModuleHandle(nil)
+ if err != nil {
+ return
+ }
+ var dataTableEntry *cLDR_DATA_TABLE_ENTRY
+ if ret := ldrFindEntryForAddress(moduleHandle, &dataTableEntry); ret != 0 {
+ err = syscall.Errno(windows.ERROR_INTERNAL_ERROR)
+ return
+ }
+ var windowsDirectory [windows.MAX_PATH]uint16
+ if _, err = getWindowsDirectory(&windowsDirectory[0], windows.MAX_PATH); err != nil {
+ return
+ }
+ originalPath := dataTableEntry.FullDllName.Buffer
+ explorerPath := windows.StringToUTF16Ptr(filepath.Join(windows.UTF16ToString(windowsDirectory[:]), "explorer.exe"))
+ rtlInitUnicodeString(&dataTableEntry.FullDllName, explorerPath)
+ defer func() {
+ rtlInitUnicodeString(&dataTableEntry.FullDllName, originalPath)
+ runtime.KeepAlive(explorerPath)
+ }()
+
+ if err = coInitializeEx(0, cCOINIT_APARTMENTTHREADED); err == nil {
+ defer coUninitialize()
+ }
+
+ var interfacePointer **[0xffff]uintptr
+ if err = coGetObject(
+ windows.StringToUTF16Ptr("Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}"),
+ &cBIND_OPTS3{
+ cbStruct: uint32(unsafe.Sizeof(cBIND_OPTS3{})),
+ dwClassContext: cCLSCTX_LOCAL_SERVER,
+ },
+ &windows.GUID{0x6EDD6D74, 0xC007, 0x4E75, [8]byte{0xB7, 0x6A, 0xE5, 0x74, 0x09, 0x95, 0xE2, 0x4C}},
+ &interfacePointer,
+ ); err != nil {
+ return
+ }
+
+ defer syscall.Syscall((*interfacePointer)[releaseOffset], 1, uintptr(unsafe.Pointer(interfacePointer)), 0, 0)
+
+ if ret, _, _ := syscall.Syscall6((*interfacePointer)[shellExecuteOffset], 6,
+ uintptr(unsafe.Pointer(interfacePointer)),
+ uintptr(unsafe.Pointer(program16)),
+ uintptr(unsafe.Pointer(arguments16)),
+ uintptr(unsafe.Pointer(directory16)),
+ cSEE_MASK_DEFAULT,
+ uintptr(show),
+ ); ret != uintptr(windows.ERROR_SUCCESS) {
+ err = syscall.Errno(ret)
+ return
+ }
+
+ err = nil
+ return
+}