Session 在特定用户会话中运行批处理文件(不允许PSExec)

Session 在特定用户会话中运行批处理文件(不允许PSExec),session,powershell,batch-file,psexec,Session,Powershell,Batch File,Psexec,我有一个用户登录到虚拟机,打开了3个会话(会话2、3和4)。我需要在特定会话中运行批处理文件,例如:会话2中的“C:\test.bat” 我已经在用PSExec做这件事了,但我想知道是否有办法让它在没有PSExec的情况下工作 我的实际代码: $session = $args[0] $user = "user01" $pass = "change123" C:\PSTools\PsExec.exe -i $session -d \\$env:COMPUTERNAME -u $user -p $

我有一个用户登录到虚拟机,打开了3个会话(会话2、3和4)。我需要在特定会话中运行批处理文件,例如:会话2中的“C:\test.bat”

我已经在用PSExec做这件事了,但我想知道是否有办法让它在没有PSExec的情况下工作

我的实际代码:

$session = $args[0]
$user = "user01"
$pass = "change123"

C:\PSTools\PsExec.exe -i $session -d \\$env:COMPUTERNAME -u $user -p $pass C:\test.bat        

我一直在想。。。如果我是会话2,有办法切换到会话3(使用tscon),然后在会话3中运行命令?

简短回答:是,这是可能的。但是

详细回答:仅作为示例,我在C#on中发现了一些类似的代码。它将枚举所有会话,查找登录到控制台的会话,并在会话上下文中执行命令。在PowerShell中包装代码、添加一些using语句并将几个类和类型更改为public之后,它现在可以工作了。它从系统DLL导入完成任务所需的所有函数

$C =  @"
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    using System.Security;

    public 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)]
        public 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)]
        public 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
    }
"@
Add-Type -TypeDefinition $C -ReferencedAssemblies mscorlib

[ApplicationLauncher]::CreateProcessInConsoleSession("cmd.exe",$false)
因此,实现您所追求的目标的一种方法是重写C代码以找到您需要的特定会话。第二种方法是直接在PowerShell中导入DLL和函数,使用Add Type并重写PowerShell中的逻辑。两者都不容易。不管是哪种方式,我怀疑这“太难了”,因为您已经有了一种使用PSExec的方法。

尝试以下方法。
您是否考虑过运行
Invoke命令
New PsSession
。我会试试这个,但是PowerShell的会话就像一个用户会话?我需要在每个用户会话中创建新的PsSession?是的,我只想使用纯PowerShell,如果有一种简单的方法可以完成PSExec的相同任务的话。谢谢你!如果您稍微扩展一下您的问题,并提供一个您目前拥有的PSExec命令行/脚本的示例,可能会更好。实际上,您似乎正在尝试在已登录的会话中模拟用户。没有简单的PowerShell方法可以做到这一点。@Lucas,谢谢你。正如我所想。“PSExec–i”所做的是用户模拟。我不是一个.NET程序员,但从我在这里和其他地方所看到的讨论来看,在.NET中根本没有办法实现这一点。所有解决方案都求助于使用非托管代码(类似于我回答中的代码)。由于PowerShell是.NET的使用者,这意味着在PowerShell中没有简单的本机方法来实现这一点。抱歉。这将使用登录到该会话的用户的凭据在特定会话中运行程序。如果要使用帐户/系统运行,则需要更改代码。
Add-Type -TypeDefinition @"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

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

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
    public uint 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 STARTF dwFlags;
    public ShowWindow wShowWindow;
    public short cbReserved2;
    public IntPtr lpReserved2;
    public IntPtr hStdInput;
    public IntPtr hStdOutput;
    public IntPtr hStdError;
}

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

[Flags]
public enum CreationFlags : int
{
    NONE = 0,
    DEBUG_PROCESS = 0x00000001,
    DEBUG_ONLY_THIS_PROCESS = 0x00000002,
    CREATE_SUSPENDED = 0x00000004,
    DETACHED_PROCESS = 0x00000008,
    CREATE_NEW_CONSOLE = 0x00000010,
    CREATE_NEW_PROCESS_GROUP = 0x00000200,
    CREATE_UNICODE_ENVIRONMENT = 0x00000400,
    CREATE_SEPARATE_WOW_VDM = 0x00000800,
    CREATE_SHARED_WOW_VDM = 0x00001000,
    CREATE_PROTECTED_PROCESS = 0x00040000,
    EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
    CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
    CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
    CREATE_DEFAULT_ERROR_MODE = 0x04000000,
    CREATE_NO_WINDOW = 0x08000000,
}

[Flags]
public enum STARTF : uint
{
    STARTF_USESHOWWINDOW = 0x00000001,
    STARTF_USESIZE = 0x00000002,
    STARTF_USEPOSITION = 0x00000004,
    STARTF_USECOUNTCHARS = 0x00000008,
    STARTF_USEFILLATTRIBUTE = 0x00000010,
    STARTF_RUNFULLSCREEN = 0x00000020,  // ignored for non-x86 platforms
    STARTF_FORCEONFEEDBACK = 0x00000040,
    STARTF_FORCEOFFFEEDBACK = 0x00000080,
    STARTF_USESTDHANDLES = 0x00000100,
}

public enum ShowWindow : short
{
    SW_HIDE = 0,
    SW_SHOWNORMAL = 1,
    SW_NORMAL = 1,
    SW_SHOWMINIMIZED = 2,
    SW_SHOWMAXIMIZED = 3,
    SW_MAXIMIZE = 3,
    SW_SHOWNOACTIVATE = 4,
    SW_SHOW = 5,
    SW_MINIMIZE = 6,
    SW_SHOWMINNOACTIVE = 7,
    SW_SHOWNA = 8,
    SW_RESTORE = 9,
    SW_SHOWDEFAULT = 10,
    SW_FORCEMINIMIZE = 11,
    SW_MAX = 11
}

public static class wtsapi32
{
    [DllImport("wtsapi32.dll", SetLastError = true)]
    public static extern bool WTSQueryUserToken(Int32 sessionId, out IntPtr Token);
}

public static class advapi32
{
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool CreateProcessAsUser(
    IntPtr hToken,
    string lpApplicationName, 
    string lpCommandLine, 
    ref SECURITY_ATTRIBUTES lpProcessAttributes, 
    ref SECURITY_ATTRIBUTES lpThreadAttributes,
    bool bInheritHandles, 
    CreationFlags creationFlags, 
    IntPtr lpEnvironment, 
    string lpCurrentDirectory, 
    ref STARTUPINFO lpStartupInfo, 
    out PROCESS_INFORMATION lpProcessInformation);
}
"@


$sessionID = 2
$token = [IntPtr]::Zero

$ret = [wtsapi32]::WTSQueryUserToken($sessionID, [ref] $token)

$si = New-Object STARTUPINFO
$pi = New-Object PROCESS_INFORMATION

$si.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($si)
$si.wShowWindow = [ShowWindow]::SW_SHOW

$pSec = New-Object SECURITY_ATTRIBUTES
$tSec = New-Object SECURITY_ATTRIBUTES
$pSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($pSec)
$tSec.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($tSec)

$dwCreationFlags = [CreationFlags]::NORMAL_PRIORITY_CLASS -bor [CreationFlags]::CREATE_NEW_CONSOLE

[advapi32]::CreateProcessAsUser($token, "c:\windows\notepad.exe", $null, [ref] $pSec, [ref] $tSec, $false, $dwCreationFlags, [IntPtr]::Zero, "c:", [ref] $si, [ref] $pi)