C# 从C调用CreateProcessAsUser#

C# 从C调用CreateProcessAsUser#,c#,winapi,process,privileges,createprocessasuser,C#,Winapi,Process,Privileges,Createprocessasuser,我一直试图使用Windows API的CreateProcessAsUser函数在特定用户的上下文中创建一个新进程,但似乎遇到了一个相当严重的安全问题 在我进一步解释之前,以下是我当前用于启动新进程的代码(具体来说是控制台进程-PowerShell,但这不重要) 为了解决这里的问题,请忽略处理环境变量的代码(我已经独立测试了该部分,它似乎可以工作) 现在,我得到的错误如下(在调用CreateProcessAsUSer后的行中抛出): “客户端未持有所需的权限”(错误代码1314) (错误消息是通

我一直试图使用Windows API的
CreateProcessAsUser
函数在特定用户的上下文中创建一个新进程,但似乎遇到了一个相当严重的安全问题

在我进一步解释之前,以下是我当前用于启动新进程的代码(具体来说是控制台进程-PowerShell,但这不重要)

为了解决这里的问题,请忽略处理环境变量的代码(我已经独立测试了该部分,它似乎可以工作)

现在,我得到的错误如下(在调用
CreateProcessAsUSer
后的行中抛出):

“客户端未持有所需的权限”(错误代码1314)

(错误消息是通过从Win32Exception构造函数中删除message参数发现的。无可否认,我这里的错误处理代码可能不是最好的,但这是一个有点无关的问题。但是,如果您愿意,欢迎您对此进行评论。)我真的很困惑这种情况下出现这种模糊错误的原因。MSDN文档和各种论坛线程只给了我这么多建议,特别是考虑到这些错误的原因似乎千差万别,我不知道我需要修改哪一段代码。也许这只是我需要更改的一个参数,但就我所知,我可能进行了错误/不够的WinAPI调用。让我非常困惑的是,以前版本的代码使用了普通的
CreateProcess
函数(除了user-token参数之外的等效函数),工作得非常好。据我所知,只需调用登录用户函数来接收适当的令牌句柄,然后复制它,以便将其传递给
CreateProcessAsUser

欢迎对守则提出任何修改建议和解释

笔记 我主要指的是MSDN文档(以及C#function/struct/enum声明)。特别是以下几页的备注部分似乎包含了大量信息,其中一些信息可能很重要,但我无法理解:

编辑 我刚刚尝试了Mitch的建议,但不幸的是旧错误被新错误所取代:“系统无法找到指定的文件。”(错误代码2)

先前对
CreateProcessAsUser
的调用已替换为以下内容:

retValue = WinApi.CreateProcessWithTokenW(hUserToken, LogonFlags.WithProfile, null,
    this.CommandLine, CreationFlags.CREATE_NEW_CONSOLE |
    CreationFlags.CREATE_SUSPENDED | CreationFlags.CREATE_UNICODE_ENVIRONMENT,
    pNewEnvironmentBlock, null, ref startupInfo, out _processInfo);
请注意,正如MSDN文档所示,此代码不再使用重复标记,而是使用原始标记

下面是另一个使用
CreateProcessWithLogonW
的尝试。这次的错误是“登录失败:未知用户名或错误密码”(错误代码1326)

我还尝试以UPN格式指定用户名(“Alex@Alex-PC”)并将域作为第二个参数独立传递,但均无效(相同错误)。

来源:

通常,调用 CreateProcessAsUser函数必须具有 SE_ASSIGNPRIMARYTOKEN_名称和 请增加您的配额和姓名权限。如果 此函数在以下情况下失败: 错误\u权限\u未保持(1314),请使用 CreateProcessWithLogonW函数 相反CreateProcessWithLogonW 不需要特殊特权,但 指定的用户帐户必须为空 允许以交互方式登录。 一般来说,最好使用 CreateProcessWithLogonW创建一个 使用备用凭据处理


请看这篇博文

啊。。。似乎我已经被WinAPI互操作编程中最大的一个问题所困扰。此外,在这种情况下,发布函数声明的代码将是一个明智的想法

无论如何,我所需要做的就是在函数的DllImport属性中添加一个参数,指定
CharSet=CharSet.Unicode
。这对
CreateProcessWithLogonW
CreateProcessWithTokenW
函数都起到了作用。我想我终于明白了,函数名的W后缀指的是Unicode,我需要在C#中显式地指定它!如果有人感兴趣,以下是正确的函数声明:

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithLogonW(string principal, string authority,
    string password, LogonFlags logonFlags, string appName, string cmdLine,
    CreationFlags creationFlags, IntPtr environmentBlock, string currentDirectory,
    ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInfo);

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithTokenW(IntPtr hToken, LogonFlags dwLogonFlags,
    string lpApplicationName, string lpCommandLine, CreationFlags dwCreationFlags,
    IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation);
乔纳森佩珀斯 提供了这段伟大的代码,解决了我的问题


谢谢你指出这一点。不幸的是,CreateProcessWithLogonW不是我的解决方案,因为我也需要在Windows服务中执行此代码,正如该博客帖子上的一位评论员所指出的,这是不可能的。(续)也许尝试一些本地安全策略规则会有所帮助,根据你文章中引用的段落判断。注意:我已经更新了原始文章,尝试了你提出的解决方案。刚刚注意到我尝试了CreateProcessWithTokenW。CreateProcessAsLogonW不成功:错误1326(登录凭据无效)。这不是很好吗?。。。这三个函数中的每一个都有一个完全唯一的错误。@Noldorin:invalid logon credentials错误似乎相当确定。您确定域\用户名和密码正确吗?永远不要将您自己的描述传递给Win32Exception。相反,将默认构造的Win32Exception作为更适合您的函数的某个异常类型的InnerException传递,您的描述将在外部异常中显示。通过这种方式,调用者可以同时获取您的信息(失败的内容)、Win32错误代码(失败的原因)和完全匹配的错误消息。是否有任何方法可以从此进程获取stdin/stdout?应始终避免仅链接的答案。所以有一个反对的政策。如果没有链接断开(存档等),那么这个答案将变得毫无用处,如果没有对存在的内容和代码示例的总结,这个代码就是神奇的!没有任何机构发布这段代码来实现以用户身份无密码启动进程。非常感谢ermagana&他自己——Jonathan Peppers!:)
retValue = WinApi.CreateProcessWithLogonW("Alex", null, "password",
    LogonFlags.WithProfile, null, this.CommandLine,
    CreationFlags.CREATE_NEW_CONSOLE | CreationFlags.CREATE_SUSPENDED |
    CreationFlags.CREATE_UNICODE_ENVIRONMENT, pNewEnvironmentBlock,
    null, ref startupInfo, out _processInfo);
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithLogonW(string principal, string authority,
    string password, LogonFlags logonFlags, string appName, string cmdLine,
    CreationFlags creationFlags, IntPtr environmentBlock, string currentDirectory,
    ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInfo);

[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessWithTokenW(IntPtr hToken, LogonFlags dwLogonFlags,
    string lpApplicationName, string lpCommandLine, CreationFlags dwCreationFlags,
    IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation);