从Windows服务运行进程作为当前用户

时间:2013-03-13 11:13:24

标签: c# windows-services

我目前有一个在系统帐户下运行的Windows服务。我的问题是我需要从服务中启动某些进程作为当前登录用户。我有所有代码等来获取当前登录的用户/活动会话。

我的问题是我需要以登录用户的身份生成进程,但不知道用户凭据等。

该服务是.net编译服务,我希望我需要使用一些Pinvoke方法来获取当前用户进程之一的句柄,以便将其复制并作为句柄处理午餐。

不幸的是,我找不到任何关于如何实施它的好文档/解决方案?

如果有人能够给我一些指导/示例,我将非常感激。

*已更新* 我想我已经错误地解释了这一点,需要根据我的实际需要进行调整。我不一定想要启动一个新进程,我只想模拟登录用户。我已经非常专注于查看CreateProcess等我已经引导自己沿着创建新进程的路径作为当前登录用户(这不是我想要做的事情)。

反过来,我只想在当前用户上下文下运行一些代码(模拟当前登录用户)?

2 个答案:

答案 0 :(得分:10)

对于有关Windows服务的这些类型的问题非常常见,您在单用户操作系统的思维模式下运行。您决定将应用程序编写为服务的全部原因是因为您遇到了单用户操作系统的心智模型与多用户操作系统的现实之间的冲突。不幸的是,一项服务并没有解决你所有的问题,现在你正试图弄清楚如何在最终注定的黑客攻击设计中完成第二步。

事实是,您无法保证 是“登录用户”。如果没有人登录到工作站,则没有人登录,但您的服务仍将继续运行。

即使您通过确保某人始终登录(不可能)而以某种方式超越了这一点,那么您将遇到多个用户登录的情况。那你的服务应该以哪一个开始?它应该随机选择其中一个吗?

在您的情况下,是否有必要区分本地登录到控制台的用户和远程登录的用户?请记住,远程用户将没有本地控制台。

如果你能以某种方式克服所有这些障碍(不幸的是,可能是将你的头埋在沙中并继续假装Windows是单用户操作系统),你可以使用WTSGetActiveConsoleSessionId功能获取当前会话ID,WTSQueryUserToken函数获取与该会话ID对应的用户令牌,最后获取CreateProcessAsUser函数以在该用户的上下文中启动您的进程。如果有的话。他们有适当的特权。并且物理控制台未附加到虚拟会话。而且您没有运行允许多个活动控制台会话的服务器SKU。和...

如果您可以决定要使用其帐户启动辅助流程的特定用户,您可以登录该用户,操纵其用户令牌,执行流程,最后关闭流程并注销用户。 CreateProcessWithLogonUser function为你包装了很多这种苦差事,让代码变得更加苗条。但外观可能是欺骗性的,而且这仍然有一些大量的安全隐患,如果你首先提出这个问题,你可能并不完全理解。你真的不能不理解这样的安全风险。

此外,使用LogonUser登录的用户(在使用CreateProcessWithLogonUser功能时自动为您完成)缺少可以启动交互式进程的窗口站和桌面。因此,如果您希望在该用户的上下文中启动的进程将显示任何类型的UI,那么您将失去运气。 Windows会在尝试访问缺少必要权限的桌面时立即终止您的应用。从Windows服务中无法获得对您有用的桌面句柄(这有助于解释您可能已经知道的一般规则,即服务无法显示任何类型的UI)。 / p>

答案 1 :(得分:9)

一种选择是让后台应用程序在用户登录并通过WCF或thrift监听服务中的命令时自动启动,或者只监视某些文件并从那里读取命令。

另一个选择是执行您最初要求的操作 - 使用Windows API启动。但代码非常可怕。这是一个可以使用的示例。它将使用CreateProcessInConsoleSession方法执行当前活动用户会话下的任何命令行:

internal class ApplicationLauncher
{
    public enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin,
        MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum
    }

    public const int READ_CONTROL = 0x00020000;

    public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;

    public const int STANDARD_RIGHTS_READ = READ_CONTROL;
    public const int STANDARD_RIGHTS_WRITE = READ_CONTROL;
    public const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL;

    public const int STANDARD_RIGHTS_ALL = 0x001F0000;

    public const int SPECIFIC_RIGHTS_ALL = 0x0000FFFF;

    public const int TOKEN_ASSIGN_PRIMARY = 0x0001;
    public const int TOKEN_DUPLICATE = 0x0002;
    public const int TOKEN_IMPERSONATE = 0x0004;
    public const int TOKEN_QUERY = 0x0008;
    public const int TOKEN_QUERY_SOURCE = 0x0010;
    public const int TOKEN_ADJUST_PRIVILEGES = 0x0020;
    public const int TOKEN_ADJUST_GROUPS = 0x0040;
    public const int TOKEN_ADJUST_DEFAULT = 0x0080;
    public const int TOKEN_ADJUST_SESSIONID = 0x0100;

    public const int TOKEN_ALL_ACCESS_P = (STANDARD_RIGHTS_REQUIRED |
                                           TOKEN_ASSIGN_PRIMARY |
                                           TOKEN_DUPLICATE |
                                           TOKEN_IMPERSONATE |
                                           TOKEN_QUERY |
                                           TOKEN_QUERY_SOURCE |
                                           TOKEN_ADJUST_PRIVILEGES |
                                           TOKEN_ADJUST_GROUPS |
                                           TOKEN_ADJUST_DEFAULT);

    public const int TOKEN_ALL_ACCESS = TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID;

    public const int TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;

    public const int TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
                                   TOKEN_ADJUST_PRIVILEGES |
                                   TOKEN_ADJUST_GROUPS |
                                   TOKEN_ADJUST_DEFAULT;

    public const int TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE;

    public const uint MAXIMUM_ALLOWED = 0x2000000;

    public const int CREATE_NEW_PROCESS_GROUP = 0x00000200;
    public const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;

    public const int IDLE_PRIORITY_CLASS = 0x40;
    public const int NORMAL_PRIORITY_CLASS = 0x20;
    public const int HIGH_PRIORITY_CLASS = 0x80;
    public const int REALTIME_PRIORITY_CLASS = 0x100;

    public const int CREATE_NEW_CONSOLE = 0x00000010;

    public const string SE_DEBUG_NAME = "SeDebugPrivilege";
    public const string SE_RESTORE_NAME = "SeRestorePrivilege";
    public const string SE_BACKUP_NAME = "SeBackupPrivilege";

    public const int SE_PRIVILEGE_ENABLED = 0x0002;

    public const int ERROR_NOT_ALL_ASSIGNED = 1300;

    private const uint TH32CS_SNAPPROCESS = 0x00000002;

    public static int INVALID_HANDLE_VALUE = -1;

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpname,
        [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);

    [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi,
        CallingConvention = CallingConvention.StdCall)]
    public static extern bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine,
        ref SECURITY_ATTRIBUTES lpProcessAttributes,
        ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
        String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool DuplicateToken(IntPtr ExistingTokenHandle,
        int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

    [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
    public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
        ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
        int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges,
        ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
        ref uint TokenInformation, uint TokenInformationLength);

    [DllImport("userenv.dll", SetLastError = true)]
    public static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);

    public static bool CreateProcessInConsoleSession(String CommandLine, bool bElevate)
    {

        PROCESS_INFORMATION pi;

        bool bResult = false;
        uint dwSessionId, winlogonPid = 0;
        IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;

        Debug.Print("CreateProcessInConsoleSession");
        // Log the client on to the local computer.
        dwSessionId = WTSGetActiveConsoleSessionId();

        // Find the winlogon process
        var procEntry = new PROCESSENTRY32();

        uint hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (hSnap == INVALID_HANDLE_VALUE)
        {
            return false;
        }

        procEntry.dwSize = (uint) Marshal.SizeOf(procEntry); //sizeof(PROCESSENTRY32);

        if (Process32First(hSnap, ref procEntry) == 0)
        {
            return false;
        }

        String strCmp = "explorer.exe";
        do
        {
            if (strCmp.IndexOf(procEntry.szExeFile) == 0)
            {
                // We found a winlogon process...make sure it's running in the console session
                uint winlogonSessId = 0;
                if (ProcessIdToSessionId(procEntry.th32ProcessID, ref winlogonSessId) &&
                    winlogonSessId == dwSessionId)
                {
                    winlogonPid = procEntry.th32ProcessID;
                    break;
                }
            }
        }
        while (Process32Next(hSnap, ref procEntry) != 0);

        //Get the user token used by DuplicateTokenEx
        WTSQueryUserToken(dwSessionId, ref hUserToken);

        var si = new STARTUPINFO();
        si.cb = Marshal.SizeOf(si);
        si.lpDesktop = "winsta0\\default";
        var tp = new TOKEN_PRIVILEGES();
        var luid = new LUID();
        hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

        if (
            !OpenProcessToken(hProcess,
                TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY
                | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, ref hPToken))
        {
            Debug.Print(String.Format("CreateProcessInConsoleSession OpenProcessToken error: {0}",
                Marshal.GetLastWin32Error()));
        }

        if (!LookupPrivilegeValue(IntPtr.Zero, SE_DEBUG_NAME, ref luid))
        {
            Debug.Print(String.Format("CreateProcessInConsoleSession LookupPrivilegeValue error: {0}",
                Marshal.GetLastWin32Error()));
        }

        var sa = new SECURITY_ATTRIBUTES();
        sa.Length = Marshal.SizeOf(sa);

        if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa,
                (int) SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int) TOKEN_TYPE.TokenPrimary,
                ref hUserTokenDup))
        {
            Debug.Print(
                String.Format(
                    "CreateProcessInConsoleSession DuplicateTokenEx error: {0} Token does not have the privilege.",
                    Marshal.GetLastWin32Error()));
            CloseHandle(hProcess);
            CloseHandle(hUserToken);
            CloseHandle(hPToken);
            return false;
        }

        if (bElevate)
        {
            //tp.Privileges[0].Luid = luid;
            //tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            tp.PrivilegeCount = 1;
            tp.Privileges = new int[3];
            tp.Privileges[2] = SE_PRIVILEGE_ENABLED;
            tp.Privileges[1] = luid.HighPart;
            tp.Privileges[0] = luid.LowPart;

            //Adjust Token privilege
            if (
                !SetTokenInformation(hUserTokenDup, TOKEN_INFORMATION_CLASS.TokenSessionId, ref dwSessionId,
                    (uint) IntPtr.Size))
            {
                Debug.Print(
                    String.Format(
                        "CreateProcessInConsoleSession SetTokenInformation error: {0} Token does not have the privilege.",
                        Marshal.GetLastWin32Error()));
                //CloseHandle(hProcess);
                //CloseHandle(hUserToken);
                //CloseHandle(hPToken);
                //CloseHandle(hUserTokenDup);
                //return false;
            }
            if (
                !AdjustTokenPrivileges(hUserTokenDup, false, ref tp, Marshal.SizeOf(tp), /*(PTOKEN_PRIVILEGES)*/
                    IntPtr.Zero, IntPtr.Zero))
            {
                int nErr = Marshal.GetLastWin32Error();

                if (nErr == ERROR_NOT_ALL_ASSIGNED)
                {
                    Debug.Print(
                        String.Format(
                            "CreateProcessInConsoleSession AdjustTokenPrivileges error: {0} Token does not have the privilege.",
                            nErr));
                }
                else
                {
                    Debug.Print(String.Format("CreateProcessInConsoleSession AdjustTokenPrivileges error: {0}", nErr));
                }
            }
        }

        uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
        IntPtr pEnv = IntPtr.Zero;
        if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
        {
            dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
        }
        else
        {
            pEnv = IntPtr.Zero;
        }
        // Launch the process in the client's logon session.
        bResult = CreateProcessAsUser(hUserTokenDup, // client's access token
            null, // file to execute
            CommandLine, // command line
            ref sa, // pointer to process SECURITY_ATTRIBUTES
            ref sa, // pointer to thread SECURITY_ATTRIBUTES
            false, // handles are not inheritable
            (int) dwCreationFlags, // creation flags
            pEnv, // pointer to new environment block 
            null, // name of current directory 
            ref si, // pointer to STARTUPINFO structure
            out pi // receives information about new process
            );
        // End impersonation of client.

        //GetLastError should be 0
        int iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();

        //Close handles task
        CloseHandle(hProcess);
        CloseHandle(hUserToken);
        CloseHandle(hUserTokenDup);
        CloseHandle(hPToken);

        return (iResultOfCreateProcessAsUser == 0) ? true : false;
    }

    [DllImport("kernel32.dll")]
    private static extern int Process32First(uint hSnapshot, ref PROCESSENTRY32 lppe);

    [DllImport("kernel32.dll")]
    private static extern int Process32Next(uint hSnapshot, ref PROCESSENTRY32 lppe);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern uint CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr hSnapshot);

    [DllImport("kernel32.dll")]
    private static extern uint WTSGetActiveConsoleSessionId();

    [DllImport("Wtsapi32.dll")]
    private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);

    [DllImport("kernel32.dll")]
    private static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);

    [DllImport("kernel32.dll")]
    private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

    [DllImport("advapi32", SetLastError = true)]
    [SuppressUnmanagedCodeSecurity]
    private static extern bool OpenProcessToken(IntPtr ProcessHandle, // handle to process
        int DesiredAccess, // desired access to process
        ref IntPtr TokenHandle);

    #region Nested type: LUID

    [StructLayout(LayoutKind.Sequential)]
    internal struct LUID
    {
        public int LowPart;
        public int HighPart;
    }

    #endregion

    //end struct

    #region Nested type: LUID_AND_ATRIBUTES

    [StructLayout(LayoutKind.Sequential)]
    internal struct LUID_AND_ATRIBUTES
    {
        public LUID Luid;
        public int Attributes;
    }

    #endregion

    #region Nested type: PROCESSENTRY32

    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESSENTRY32
    {
        public uint dwSize;
        public readonly uint cntUsage;
        public readonly uint th32ProcessID;
        public readonly IntPtr th32DefaultHeapID;
        public readonly uint th32ModuleID;
        public readonly uint cntThreads;
        public readonly uint th32ParentProcessID;
        public readonly int pcPriClassBase;
        public readonly uint dwFlags;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public readonly string szExeFile;
    }

    #endregion

    #region Nested type: PROCESS_INFORMATION

    [StructLayout(LayoutKind.Sequential)]
    public struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public uint dwProcessId;
        public uint dwThreadId;
    }

    #endregion

    #region Nested type: SECURITY_ATTRIBUTES

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int Length;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }

    #endregion

    #region Nested type: SECURITY_IMPERSONATION_LEVEL

    private enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous = 0,
        SecurityIdentification = 1,
        SecurityImpersonation = 2,
        SecurityDelegation = 3,
    }

    #endregion

    #region Nested type: STARTUPINFO

    [StructLayout(LayoutKind.Sequential)]
    public struct STARTUPINFO
    {
        public int cb;
        public String lpReserved;
        public String lpDesktop;
        public String lpTitle;
        public uint dwX;
        public uint dwY;
        public uint dwXSize;
        public uint dwYSize;
        public uint dwXCountChars;
        public uint dwYCountChars;
        public uint dwFillAttribute;
        public uint dwFlags;
        public short wShowWindow;
        public short cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

    #endregion

    #region Nested type: TOKEN_PRIVILEGES

    [StructLayout(LayoutKind.Sequential)]
    internal struct TOKEN_PRIVILEGES
    {
        internal int PrivilegeCount;
        //LUID_AND_ATRIBUTES
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        internal int[] Privileges;
    }

    #endregion

    #region Nested type: TOKEN_TYPE

    private enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation = 2
    }

    #endregion

    // handle to open access token
}