wincmd/permission.go

326 lines
8.6 KiB
Go
Raw Normal View History

2019-03-11 14:56:06 +08:00
package wincmd
import (
"fmt"
"strconv"
"strings"
"syscall"
2019-03-11 14:56:06 +08:00
"unsafe"
"b612.me/win32api"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
func getActiveSessionID() (win32api.DWORD, error) {
sessionID, err := win32api.ActiveSessionID()
if err != nil {
return 0, fmt.Errorf("resolve active session id: %w", err)
}
if sessionID == win32api.WTS_CURRENT_SESSION {
return 0, fmt.Errorf("active session id is invalid: %#x", sessionID)
}
return sessionID, nil
}
func destroyEnvironmentBlock(env win32api.HANDLE) error {
proc, err := syscall.LoadDLL("userenv.dll")
if err != nil {
return err
}
defer proc.Release()
destroy, err := proc.FindProc("DestroyEnvironmentBlock")
if err != nil {
return err
}
r, _, errno := syscall.Syscall(destroy.Addr(), 1, uintptr(env), 0, 0)
if r == 0 {
if errno != 0 {
return error(errno)
}
return syscall.EINVAL
}
return nil
}
2019-03-11 14:56:06 +08:00
func StartProcessWithSYS(appPath, cmdLine, workDir string, runas bool) error {
var (
sessionId win32api.DWORD
userToken win32api.TOKEN
2019-03-11 14:56:06 +08:00
envInfo win32api.HANDLE
impersonationToken win32api.HANDLE
2019-03-11 14:56:06 +08:00
startupInfo win32api.StartupInfo
processInfo win32api.ProcessInformation
)
sessionId, err := getActiveSessionID()
if err != nil {
return fmt.Errorf("get active session id: %w", err)
2019-03-11 14:56:06 +08:00
}
if err := win32api.WTSQueryUserToken(sessionId, &impersonationToken); err != nil {
return err
}
defer func() {
if impersonationToken != 0 {
_ = win32api.CloseHandle(impersonationToken)
}
}()
2019-03-11 14:56:06 +08:00
if err := win32api.DuplicateTokenEx(impersonationToken, 0, 0, int(win32api.SecurityImpersonation), win32api.TokenPrimary, &userToken); err != nil {
return fmt.Errorf("call native DuplicateTokenEx: %s", err)
}
defer func() {
if userToken != 0 {
_ = win32api.CloseHandle(win32api.HANDLE(userToken))
}
}()
2019-03-11 14:56:06 +08:00
if runas {
var admin win32api.TOKEN_LINKED_TOKEN
var dt uintptr = 0
if err := win32api.GetTokenInformation(impersonationToken, 19, uintptr(unsafe.Pointer(&admin)), uintptr(unsafe.Sizeof(admin)), &dt); err == nil && admin.LinkedToken != 0 {
if userToken != 0 && userToken != admin.LinkedToken {
_ = win32api.CloseHandle(win32api.HANDLE(userToken))
}
2019-03-11 14:56:06 +08:00
userToken = admin.LinkedToken
}
}
if err := win32api.CreateEnvironmentBlock(&envInfo, userToken, 0); err != nil {
return fmt.Errorf("create environment details for process: %s", err)
}
defer func() {
if envInfo != 0 {
_ = destroyEnvironmentBlock(envInfo)
}
}()
2019-03-11 14:56:06 +08:00
creationFlags := win32api.CREATE_UNICODE_ENVIRONMENT | win32api.CREATE_NEW_CONSOLE
startupInfo.Cb = uint32(unsafe.Sizeof(startupInfo))
startupInfo.ShowWindow = uint16(win32api.SW_SHOW)
2019-03-11 14:56:06 +08:00
startupInfo.Desktop = windows.StringToUTF16Ptr("winsta0\\default")
if err := win32api.CreateProcessAsUser(userToken, appPath, cmdLine, 0, 0, 0,
creationFlags, envInfo, workDir, &startupInfo, &processInfo); err != nil {
return fmt.Errorf("create process as user: %s", err)
}
if processInfo.Process != 0 {
_ = win32api.CloseHandle(processInfo.Process)
}
if processInfo.Thread != 0 {
_ = win32api.CloseHandle(processInfo.Thread)
}
2019-03-11 14:56:06 +08:00
return nil
}
func processImageName(proc windows.ProcessEntry32) string {
return windows.UTF16ToString(proc.ExeFile[:])
}
func walkProcesses(fn func(proc windows.ProcessEntry32) (bool, error)) error {
if fn == nil {
return nil
}
pHandle, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
2019-03-11 14:56:06 +08:00
if err != nil {
return err
2019-03-11 14:56:06 +08:00
}
defer func() {
_ = windows.CloseHandle(pHandle)
}()
var proc windows.ProcessEntry32
proc.Size = uint32(unsafe.Sizeof(proc))
if err := windows.Process32First(pHandle, &proc); err != nil {
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_NO_MORE_FILES {
return nil
}
return err
}
2019-03-11 14:56:06 +08:00
for {
stop, err := fn(proc)
if err != nil {
return err
}
if stop {
return nil
}
if err := windows.Process32Next(pHandle, &proc); err != nil {
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_NO_MORE_FILES {
return nil
2019-03-11 14:56:06 +08:00
}
return err
2019-03-11 14:56:06 +08:00
}
}
}
func GetRunningProcess() ([]map[string]string, error) {
result := []map[string]string{}
err := walkProcesses(func(proc windows.ProcessEntry32) (bool, error) {
result = append(result, map[string]string{
"name": processImageName(proc),
"pid": strconv.Itoa(int(proc.ProcessID)),
"ppid": fmt.Sprint(int(proc.ParentProcessID)),
})
return false, nil
})
return result, err
2019-03-11 14:56:06 +08:00
}
2022-03-22 09:46:55 +08:00
func IsProcessRunningByPID(pid int) (bool, error) {
found := false
err := walkProcesses(func(proc windows.ProcessEntry32) (bool, error) {
if int(proc.ProcessID) == pid {
found = true
return true, nil
2019-03-11 14:56:06 +08:00
}
return false, nil
})
return found, err
2019-03-11 14:56:06 +08:00
}
2022-03-22 09:46:55 +08:00
func IsProcessRunning(name string) (bool, error) {
target := strings.TrimSpace(name)
found := false
err := walkProcesses(func(proc windows.ProcessEntry32) (bool, error) {
if strings.EqualFold(strings.TrimSpace(processImageName(proc)), target) {
found = true
return true, nil
}
return false, nil
})
return found, err
}
func GetProcessCount(name string) (int, error) {
var count int
target := strings.TrimSpace(name)
err := walkProcesses(func(proc windows.ProcessEntry32) (bool, error) {
if strings.EqualFold(strings.TrimSpace(processImageName(proc)), target) {
count++
}
return false, nil
})
return count, err
}
// IsElevated reports whether the current process token is elevated and belongs to local Administrators.
func IsElevated() (bool, error) {
var token windows.Token
if err := windows.OpenProcessToken(windows.CurrentProcess(), windows.TOKEN_QUERY, &token); err != nil {
2022-03-22 09:46:55 +08:00
return false, err
2019-03-11 14:56:06 +08:00
}
defer token.Close()
elevated := token.IsElevated()
inAdminGroup, err := isCurrentUserInAdminGroup(token)
if err != nil {
if elevated {
return true, nil
2019-03-11 14:56:06 +08:00
}
return false, err
2019-03-11 14:56:06 +08:00
}
return elevated && inAdminGroup, nil
2019-03-11 14:56:06 +08:00
}
func isCurrentUserInAdminGroup(token windows.Token) (bool, error) {
adminSID, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
2019-03-11 14:56:06 +08:00
if err != nil {
return false, err
2019-03-11 14:56:06 +08:00
}
member, err := token.IsMember(adminSID)
if err == nil {
return member, nil
2019-03-11 14:56:06 +08:00
}
// CheckTokenMembership supports Token(0) fallback to caller's effective token.
return windows.Token(0).IsMember(adminSID)
2019-03-11 14:56:06 +08:00
}
func Isas() bool {
elevated, err := IsElevated()
if err != nil {
2019-03-11 14:56:06 +08:00
return false
}
return elevated
2019-03-11 14:56:06 +08:00
}
2022-03-22 09:46:55 +08:00
func StartProcess(appPath, cmdLine, wordDir string, runas bool, ShowWindow int) error {
2019-03-11 14:56:06 +08:00
var cst string
if runas {
cst = "runas"
} else {
cst = "open"
}
r := win32api.ShellExecute(0, cst, appPath, cmdLine, wordDir, ShowWindow)
2022-03-22 09:46:55 +08:00
return r
2019-03-11 14:56:06 +08:00
}
2022-03-22 09:46:55 +08:00
func StartProcessWithPID(appPath, cmdLine, workDir string, runas bool, ShowWindow int) (int, error) {
2019-03-11 14:56:06 +08:00
var sakura win32api.SHELLEXECUTEINFOW
sakura.Hwnd = 0
sakura.NShow = int32(ShowWindow)
2019-03-11 14:56:06 +08:00
sakura.FMask = 0x00000040
2020-10-19 21:17:15 +08:00
sakura.LpParameters = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cmdLine)))
2019-03-11 14:56:06 +08:00
sakura.LpFile = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(appPath)))
sakura.LpDirectory = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(workDir)))
if runas {
sakura.LpVerb = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("runas")))
} else {
sakura.LpVerb = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("open")))
}
sakura.CbSize = win32api.DWORD(unsafe.Sizeof(sakura))
if err := win32api.ShellExecuteEx(&sakura); err != nil {
2022-03-22 09:46:55 +08:00
return 0, err
2019-03-11 14:56:06 +08:00
}
pid := int(win32api.GetProcessId(sakura.HProcess))
if sakura.HProcess != 0 {
_ = win32api.CloseHandle(sakura.HProcess)
}
return pid, nil
2019-03-11 14:56:06 +08:00
}
func AutoRun(key, path string) (bool, error) {
reg, errs := registry.OpenKey(registry.LOCAL_MACHINE, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.ALL_ACCESS)
if errs != nil {
return false, errs
}
defer reg.Close()
2019-03-11 14:56:06 +08:00
if errs = reg.SetStringValue(key, path); errs != nil {
return false, errs
}
return true, nil
}
func DeleteAutoRun(key string) (bool, error) {
reg, errs := registry.OpenKey(registry.LOCAL_MACHINE, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.ALL_ACCESS)
if errs != nil {
return false, errs
}
defer reg.Close()
if _, _, err := reg.GetStringValue(key); err != nil {
if err == registry.ErrNotExist {
return true, nil
}
return false, err
2019-03-11 14:56:06 +08:00
}
if errs = reg.DeleteValue(key); errs != nil {
return false, errs
}
return true, nil
}
2022-03-22 09:46:55 +08:00
func IsAutoRun(key, path string) (bool, error) {
reg, err := registry.OpenKey(registry.LOCAL_MACHINE, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.ALL_ACCESS)
if err != nil {
return false, err
2019-03-11 14:56:06 +08:00
}
defer reg.Close()
sa, _, err := reg.GetStringValue(key)
if err != nil {
if err == registry.ErrNotExist {
return false, nil
}
return false, err
2019-03-11 14:56:06 +08:00
}
return sa == path, nil
2019-03-11 14:56:06 +08:00
}