生成在 Microsoft ASP.NET 中模拟用户的上下文下运行的进程

本文介绍如何生成在模拟用户的上下文下运行的进程。

原始产品版本: ASP.NET
原始 KB 编号: 889251

简介

本分步文章介绍如何生成在 ASP.NET 页面中模拟用户的上下文下运行的进程。 若要生成在模拟用户的上下文下运行的进程,不能使用 System.Diagnostics.Process.Start 方法。 这是因为在 ASP.NET 中,模拟是在线程级别而不是进程级别执行的。 因此,从 ASP.NET 生成的任何进程都将在 ASP.NET 工作进程的上下文中运行,而不是在模拟的上下文中运行。

技术说明

若要解决此问题,必须将平台调用 (P/Invoke) 调用以下 Win32 API:

  • 函数 CreateProcessAsUser 在某个参数中指定的安全令牌的标识下创建一个进程。 但是,对 函数的 CreateProcessAsUser 调用需要主令牌。 因此,必须将模拟令牌转换为主令牌。

  • 函数 DuplicateTokenEx 将模拟令牌转换为主令牌。 对 函数的 CreateProcessAsUser 调用使用此主令牌。

注意

若要使对函数的 CreateProcessAsUser 调用正常工作,必须将以下安全用户权限分配给模拟用户:

  • 在运行 Windows 的计算机上,将 “替换进程级令牌 ”用户权限和 “增加配额 ”用户权限分配给模拟用户。
  • 在运行 Windows Server 或 Windows XP 的计算机上,将 “替换进程级令牌 ”用户权限分配给模拟用户。

生成进程的步骤

Microsoft 提供的编程示例仅用于进行说明,而不提供明示或默示担保。 这包括但不限于适销性或对特定用途的适用性的默示担保。 本文假设您熟悉正在演示的编程语言和用于创建和调试过程的工具。 Microsoft 支持工程师可以帮助解释特定过程的功能,但他们不会修改这些示例以提供新增功能或构建步骤以满足你的特定需要。 若要生成在模拟用户的上下文下运行的进程,请执行以下步骤:

  1. Visual Studio .NET 中,创建新的 Visual C# ASP.NET Web 应用程序,然后将该应用程序命名 为Q889251

  2. 代码视图中打开 WebForm1.aspx 文件。

  3. WebForm1.aspx 文件中,将以下语句添加到 using 代码块。

    using System.Runtime.InteropServices;
    using System.Security.Principal;
    
  4. 找到类似于以下代码行的代码行。

    public class WebForm1 : System.Web.UI.Page
    
  5. 在步骤 4 中标识的代码行之后,添加以下代码。

    [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;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public uint dwProcessId;
        public uint dwThreadId;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int Length;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }
    [DllImport("kernel32.dll", EntryPoint="CloseHandle", SetLastError=true, CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
    public extern static bool CloseHandle(IntPtr handle);
    [DllImport("advapi32.dll", EntryPoint="CreateProcessAsUser", SetLastError=true, CharSet=CharSet.Ansi, CallingConvention=CallingConvention.StdCall)]
    public extern static 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", EntryPoint="DuplicateTokenEx")]
    public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType, int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
    
  6. “解决方案资源管理器”中,右键单击“WebForm1.aspx”,然后单击“打开”。 Web 窗体在 设计视图中打开。

  7. 在“ 视图 ”菜单上,单击“ HTML 源”。

  8. “HTML 源 ”窗口中的所有现有代码替换为以下代码。

    <%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="Q889251.WebForm1" %>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
    <HTML>
       <HEAD>
          <title>WebForm1</title>
          <meta name="GENERATOR" Content="Microsoft Visual Studio 7.0">
          <meta name="CODE_LANGUAGE" Content="C#">
          <meta name="vs_defaultClientScript" content="JavaScript">
          <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
       </HEAD>
       <body>
          <form id="Form1" method="post" runat="server">
             <P>&nbsp;</P>
             <P>
                Enter Path of process to be run (with relevant parameters)
                <asp:TextBox id="TextBox1" runat="server"></asp:TextBox>
             </P>
             <P>
                <asp:Button id="Button1" runat="server" Text="CreateProcess"></asp:Button>
             </P>
             <P>
                <asp:Label id="Label1" runat="server">Status:</asp:Label>
             </P>
             <P>
                <asp:Label id="Label2" runat="server">Impersonated Identity:</asp:Label>
             </P>
          </form>
       </body>
    </HTML>
    
  9. 在“ 视图 ”菜单上,单击“ 设计”。

  10. 双击“ 创建进程”。 方法 Button1_Click 插入代码中,代码将显示在内容窗格中。

  11. 将现有 Button1_Click 方法替换为以下代码。

    private void Button1_Click(object sender, System.EventArgs e)
    {
        IntPtr Token = new IntPtr(0);
        IntPtr DupedToken = new IntPtr(0);
        bool ret;
        Label2.Text+=WindowsIdentity.GetCurrent().Name.ToString();
        SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
        sa.bInheritHandle = false;
        sa.Length = Marshal.SizeOf(sa);
        sa.lpSecurityDescriptor = (IntPtr)0;
        Token = WindowsIdentity.GetCurrent().Token;
        const uint GENERIC_ALL = 0x10000000;
        const int SecurityImpersonation = 2;
        const int TokenType = 1;
        ret = DuplicateTokenEx(Token, GENERIC_ALL, ref sa, SecurityImpersonation, TokenType, ref DupedToken);
        if (ret == false)
        {
            Label1.Text +="DuplicateTokenEx failed with " + Marshal.GetLastWin32Error();
        }
        else
        {
            Label1.Text+= "DuplicateTokenEx SUCCESS";
            STARTUPINFO si = new STARTUPINFO();
            si.cb = Marshal.SizeOf(si);
            si.lpDesktop = "";
            string commandLinePath;
            commandLinePath = TextBox1.Text;
            PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
            ret = CreateProcessAsUser(DupedToken,null,commandLinePath, ref sa, ref sa, false, 0, (IntPtr)0, "c:\\", ref si, out pi);
            if (ret == false)
            {
                Label1.Text +="CreateProcessAsUser failed with " + Marshal.GetLastWin32Error();
            }
            else
            {
                Label1.Text +="CreateProcessAsUser SUCCESS. The child PID is" + pi.dwProcessId;
                CloseHandle(pi.hProcess);
                CloseHandle(pi.hThread);
            }
            ret = CloseHandle(DupedToken);
            if (ret == false)
            {
                Label1.Text+=Marshal.GetLastWin32Error();
            }
            else
            {
                Label1.Text+="CloseHandle SUCCESS";
            }
        }
    }
    
  12. 在"构建"菜单上,单击"构建解决方案"。

  13. “调试” 菜单上,单击“ 启动”。

  14. WebForm1 页中,键入要启动的进程的路径。 例如,键入 SystemDriver \Windows\Notepad.exe

  15. 单击“ 创建”“进程”。

References