I'm trying to run the c# watchdog windows service application from this stackoverflow thread
The issue I'm having is that WtsApi32.WTSQueryUserToken
is return false with the following message The operation completed successfully
in this snippet of code:
// Retrieve the active session ID and its related user token.
var sessionId = Kernel32.WTSGetActiveConsoleSessionId();
var userTokenPtr = new IntPtr();
if (!WtsApi32.WTSQueryUserToken(sessionId, out userTokenPtr))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
After some hunting around, I found a common issue is that the privilege SE_TCB_NAME
needs to be granted but I don't believe this to be the issue since I'm running the application with Admin privilege in debug mode and the error code doesn't match the typical error code when SE_TCB_NAME
is missing (ERROR_PRIVILEGE_NOT_HELD
)
if SE_TCB_NAME
is the issue, can you provide a sample of code to grant SE_TCB_NAME
. The only sample I could find was here but to grant SE_TCB_NAME
I need to run Impersonation.ExecuteAppAsLoggedOnUser("applicationName", null);
and I'm not sure what parameter to put in for "applicationName"
in the context of my codes since I'm currently starting my process with ProcessStartInfo
.
here is the complete code:
ProcessExtensions.cs:
namespace WindowsService1
{
public static class ProcessExtensions
{
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
[StructLayout(LayoutKind.Sequential)]
public class SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
[Flags]
public enum CREATE_PROCESS_FLAGS : uint
{
NONE = 0x00000000,
DEBUG_PROCESS = 0x00000001,
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
CREATE_SUSPENDED = 0x00000004,
DETACHED_PROCESS = 0x00000008,
CREATE_NEW_CONSOLE = 0x00000010,
NORMAL_PRIORITY_CLASS = 0x00000020,
IDLE_PRIORITY_CLASS = 0x00000040,
HIGH_PRIORITY_CLASS = 0x00000080,
REALTIME_PRIORITY_CLASS = 0x00000100,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_SHARED_WOW_VDM = 0x00001000,
CREATE_FORCEDOS = 0x00002000,
BELOW_NORMAL_PRIORITY_CLASS = 0x00004000,
ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000,
INHERIT_PARENT_AFFINITY = 0x00010000,
INHERIT_CALLER_PRIORITY = 0x00020000,
CREATE_PROTECTED_PROCESS = 0x00040000,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
PROCESS_MODE_BACKGROUND_BEGIN = 0x00100000,
PROCESS_MODE_BACKGROUND_END = 0x00200000,
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
CREATE_NO_WINDOW = 0x08000000,
PROFILE_USER = 0x10000000,
PROFILE_KERNEL = 0x20000000,
PROFILE_SERVER = 0x40000000,
CREATE_IGNORE_SYSTEM_DEFAULT = 0x80000000,
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
public class Kernel32
{
[DllImport("kernel32.dll", EntryPoint = "WTSGetActiveConsoleSessionId")]
public static extern uint WTSGetActiveConsoleSessionId();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
}
public class WtsApi32
{
[DllImport("Wtsapi32.dll", EntryPoint = "WTSQueryUserToken")]
public static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr phToken);
}
public class AdvApi32
{
public const uint MAXIMUM_ALLOWED = 0x2000000;
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx
(
IntPtr hExistingToken,
uint dwDesiredAccess,
SECURITY_ATTRIBUTES lpTokenAttributes,
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
TOKEN_TYPE TokenType,
out IntPtr phNewToken
);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CreateProcessAsUser
(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
SECURITY_ATTRIBUTES lpProcessAttributes,
SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
CREATE_PROCESS_FLAGS dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
}
public class UserEnv
{
[DllImport("userenv.dll", SetLastError = true)]
public static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
}
public static void StartAsActiveUser(this Process process)
{
// Sanity check.
if (process.StartInfo == null)
{
throw new InvalidOperationException("The StartInfo property must be defined");
}
if (string.IsNullOrEmpty(process.StartInfo.FileName))
{
throw new InvalidOperationException("The StartInfo.FileName property must be defined");
}
// Retrieve the active session ID and its related user token.
var sessionId = Kernel32.WTSGetActiveConsoleSessionId();
var userTokenPtr = new IntPtr();
if (!WtsApi32.WTSQueryUserToken(sessionId, out userTokenPtr))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// Duplicate the user token so that it can be used to create a process.
var duplicateUserTokenPtr = new IntPtr();
if (!AdvApi32.DuplicateTokenEx(userTokenPtr, AdvApi32.MAXIMUM_ALLOWED, null, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out duplicateUserTokenPtr))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// Create an environment block for the interactive process.
var environmentPtr = new IntPtr();
if (!UserEnv.CreateEnvironmentBlock(out environmentPtr, duplicateUserTokenPtr, false))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// Create the process under the target user’s context.
var processFlags = CREATE_PROCESS_FLAGS.NORMAL_PRIORITY_CLASS | CREATE_PROCESS_FLAGS.CREATE_NEW_CONSOLE | CREATE_PROCESS_FLAGS.CREATE_UNICODE_ENVIRONMENT;
var processInfo = new PROCESS_INFORMATION();
var startupInfo = new STARTUPINFO();
startupInfo.cb = Marshal.SizeOf(startupInfo);
if (!AdvApi32.CreateProcessAsUser
(
duplicateUserTokenPtr,
process.StartInfo.FileName,
process.StartInfo.Arguments,
null,
null,
false,
processFlags,
environmentPtr,
null,
ref startupInfo,
out processInfo
))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// Free used resources.
Kernel32.CloseHandle(processInfo.hProcess);
Kernel32.CloseHandle(processInfo.hThread);
if (userTokenPtr != null)
{
Kernel32.CloseHandle(userTokenPtr);
}
if (duplicateUserTokenPtr != null)
{
Kernel32.CloseHandle(duplicateUserTokenPtr);
}
if (environmentPtr != null)
{
UserEnv.DestroyEnvironmentBlock(environmentPtr);
}
}
}
}
Service1.cs:
namespace WindowsService1
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
internal void OnDebug()
{
OnStart(null);
}
protected override void OnStart(string[] args)
{
var process = new Process();
process.StartInfo = new ProcessStartInfo { FileName = @"C:\Program Files (x86)\DigiParent\DigiParent.exe", Arguments = "-arg1 -arg2" };
process.StartAsActiveUser();
}
public void onDebug()
{
OnStart(null);
}
protected override void OnStop()
{
}
}
}
Program.cs:
{
static class Program
{
static void Main()
{
#if DEBUG
Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
#endif
}
}
}
User contributions licensed under CC BY-SA 3.0