I am working on one electron app. Electron app has child node js process which capture desktop stream using ffmpeg tool. To capture winlogon stream, I have used setThreadDesktop
API in child process to change thread desktop to winlogon.
Now, when I run this electron app as system process using PsExec
tool with command PsExec64.exe -sid <path-to-exe>
, Everything works as expected.
But when I run app as system process following these programmatic steps in windows service written in Go using package golang.org/x/sys/windows
, it does not work. ffmpeg
tool in child process exits with Error (5) means permission denined to winlogon desktop.
Get token using this code
wlPid, e := processID("winlogon.exe", uint32(sessionId))
if e != nil {
log.Printf("Error in getting winlogon pid: %s", e.Error())
return 0xFFFFFFFF, e
}
hProcess, eo := windows.OpenProcess(windows.MAXIMUM_ALLOWED, true, wlPid)
if eo != nil {
log.Printf("Error opening process: %s", eo.Error())
return 0xFFFFFFFF, eo
}
token := windows.Token(impersonationToken)
ope := windows.OpenProcessToken(hProcess, windows.TOKEN_ALL_ACCESS, &token)
if ope != nil {
log.Printf("OpenProcessToken failed %s", ope.Error())
return 0xFFFFFFFF, ope
}
systemSid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
// Create the trustee to add to an ACE
trustee := windows.TRUSTEE{
MultipleTrustee: nil,
MultipleTrusteeOperation: windows.NO_MULTIPLE_TRUSTEE,
TrusteeForm: windows.TRUSTEE_IS_SID,
TrusteeType: windows.TRUSTEE_IS_USER,
TrusteeValue: windows.TrusteeValueFromSID(systemSid),
}
ace := windows.EXPLICIT_ACCESS{
// AccessPermissions: windows.ACCESS_MASK(windows.READ_CONTROL), // WINSTA_CREATEDESKTOP | WINSTA_READSCREEN | WINSTA_ACCESSCLIPBOARD | WINSTA_WRITEATTRIBUTES | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_READATTRIBUTES |
// AccessMode: windows.SET_ACCESS,
AccessPermissions: windows.STANDARD_RIGHTS_ALL,
AccessMode: windows.GRANT_ACCESS,
Inheritance: windows.INHERIT_ONLY,
Trustee: trustee,
}
// Add the new ACE for the token user to the existing security descriptor for the window station
sd, err := windows.BuildSecurityDescriptor(nil, nil, []windows.EXPLICIT_ACCESS{ace}, nil, nil)
if err != nil {
return 0xFFFFFFFF, fmt.Errorf("there was an error calling windows.BuildSecurityDescriptor for the station: %s\n", err)
}
if returnCode, _, err := procDuplicateTokenEx.Call(uintptr(token), windows.MAXIMUM_ALLOWED, uintptr(unsafe.Pointer(&sd)), uintptr(SecurityImpersonation), uintptr(TokenPrimary), uintptr(unsafe.Pointer(&userToken))); returnCode == 0 {
// log.Printf("Dup Error: %s\n", err.Error())
return 0xFFFFFFFF, fmt.Errorf("call native DuplicateTokenEx: %s", err)
}
h := windows.Handle(token)
if err := windows.CloseHandle(h); err != nil {
// log.Printf("Close Error: %s\n", err.Error())
return 0xFFFFFFFF, fmt.Errorf("close windows handle used for token duplication: %s", err)
}
return userToken, nil
then, using this userToken
to create process
if returnCode, _, err := procCreateEnvironmentBlock.Call(uintptr(unsafe.Pointer(&envInfo)), uintptr(userToken), 0); returnCode == 0 {
return fmt.Errorf("create environment details for process: %s", err)
}
creationFlags := windows.CREATE_UNICODE_ENVIRONMENT | windows.CREATE_NEW_CONSOLE
startupInfo.ShowWindow = windows.SW_SHOW
startupInfo.Desktop = windows.StringToUTF16Ptr("winsta0\\default")
if len(cmdLine) > 0 {
commandLine = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cmdLine)))
}
if len(workDir) > 0 {
workingDir = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(workDir)))
}
log.Printf("Calling Create Process as user\n")
log.Println(userToken)
if returnCode, _, err := procCreateProcessAsUser.Call(
uintptr(userToken), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(appPath))), commandLine, 0, 0, 0,
uintptr(creationFlags), uintptr(envInfo), workingDir, uintptr(unsafe.Pointer(&startupInfo)), uintptr(unsafe.Pointer(&processInfo)),
); returnCode == 0 {
return fmt.Errorf("create process as user: %s", err)
}
So, Can some windows experts help me understand what is the diffrence in this two methods of running process as system ?