2019-03-11 14:56:06 +08:00
|
|
|
package wincmd
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
2026-06-09 15:59:31 +08:00
|
|
|
"syscall"
|
2019-03-11 14:56:06 +08:00
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
|
|
"b612.me/win32api"
|
|
|
|
|
"golang.org/x/sys/windows"
|
|
|
|
|
"golang.org/x/sys/windows/registry"
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-09 15:59:31 +08:00
|
|
|
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 (
|
2026-06-09 15:59:31 +08:00
|
|
|
sessionId win32api.DWORD
|
|
|
|
|
userToken win32api.TOKEN
|
2019-03-11 14:56:06 +08:00
|
|
|
envInfo win32api.HANDLE
|
2026-06-09 15:59:31 +08:00
|
|
|
impersonationToken win32api.HANDLE
|
2019-03-11 14:56:06 +08:00
|
|
|
startupInfo win32api.StartupInfo
|
|
|
|
|
processInfo win32api.ProcessInformation
|
|
|
|
|
)
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
|
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
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)
|
|
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
2026-06-09 15:59:31 +08:00
|
|
|
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)
|
|
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
defer func() {
|
|
|
|
|
if envInfo != 0 {
|
|
|
|
|
_ = destroyEnvironmentBlock(envInfo)
|
|
|
|
|
}
|
|
|
|
|
}()
|
2019-03-11 14:56:06 +08:00
|
|
|
creationFlags := win32api.CREATE_UNICODE_ENVIRONMENT | win32api.CREATE_NEW_CONSOLE
|
2026-06-09 15:59:31 +08:00
|
|
|
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)
|
|
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 15:59:31 +08:00
|
|
|
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 {
|
2026-06-09 15:59:31 +08:00
|
|
|
return err
|
2019-03-11 14:56:06 +08:00
|
|
|
}
|
2026-06-09 15:59:31 +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 {
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
return err
|
2019-03-11 14:56:06 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-06-09 15:59:31 +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) {
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
|
|
}
|
2026-06-09 15:59:31 +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) {
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
|
|
}
|
2026-06-09 15:59:31 +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
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
return false, err
|
2019-03-11 14:56:06 +08:00
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
return elevated && inAdminGroup, nil
|
2019-03-11 14:56:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 15:59:31 +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 {
|
2026-06-09 15:59:31 +08:00
|
|
|
return false, err
|
2019-03-11 14:56:06 +08:00
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
member, err := token.IsMember(adminSID)
|
|
|
|
|
if err == nil {
|
|
|
|
|
return member, nil
|
2019-03-11 14:56:06 +08:00
|
|
|
}
|
2026-06-09 15:59:31 +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 {
|
2026-06-09 15:59:31 +08:00
|
|
|
elevated, err := IsElevated()
|
|
|
|
|
if err != nil {
|
2019-03-11 14:56:06 +08:00
|
|
|
return false
|
|
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
|
|
}
|
2026-06-09 15:59:31 +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
|
|
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
|
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
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
|
|
|
}
|
2026-06-09 15:59:31 +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
|
|
|
}
|
2026-06-09 15:59:31 +08:00
|
|
|
return sa == path, nil
|
2019-03-11 14:56:06 +08:00
|
|
|
}
|