1

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 ?

Hiren patel
  • 971
  • 8
  • 25
  • for what you duplicate winlogon token ? this not need do – RbMm Apr 03 '23 at 22:49
  • Also, you should try to narrow down the possibilities. Such as [are there the interactive window station and desktop which the process can access?](https://stackoverflow.com/questions/15158888/createprocessasuser-from-service-and-user-security-issues) which is [Mandatory Integrity Control](https://learn.microsoft.com/en-us/windows/win32/secauthz/mandatory-integrity-control#process-creation) of your process? – YangXiaoPo-MSFT Apr 04 '23 at 06:23

0 Answers0