使用凭据从Windows服务环境中运行时,使用Powershell的Start-Process命令退出码为-1073741502。

13

我在使用PowerShell的Start-Process调用时遇到了一种奇怪的行为。

这是调用的代码:

$process = start-process `
    "C:\somepath\MyBinary.exe" `
    -PassThru `
    -Credential $defaultCredential `
    -Wait `
    -WorkingDirectory  "C:\somepath" `
    -LoadUserProfile
if ($process.ExitCode -ne 0)
{
#do something
}

这个调用总是返回退出代码-1073741502
经过快速搜索,发现该退出代码似乎与程序无法加载所需的dll(也称为STATUS_DLL_INIT_FAILED)相关。

当我没有使用-Credential $credential时,程序可以正常运行。

为了分离问题,我手动使用目标凭据在提示符中启动了some.exe,它可以平稳运行。

因此,问题似乎只来自start-process cmdlet有效地启动进程的方式。

我找到了一些潜在的解决方案,但尝试应用没有成功:linklink

你有什么想法吗?

编辑1:
我运行了一个proc mon来监视直接启动或通过powershell脚本启动程序的活动。问题似乎出现在加载kernelbase.dll时。

本地procmon转储(工作):

9:06:35.3837439 AM  MyBinary.exe    2620    Load Image  C:\Windows\SysWOW64\kernelbase.dll  SUCCESS Image Base: 0x76270000, Image Size: 0x47000
9:06:35.4317417 AM  MyBinary.exe    2620    RegOpenKey  HKLM\System\CurrentControlSet\Control\Nls\Sorting\Versions  REPARSE Desired Access: Read
9:06:35.4317751 AM  MyBinary.exe    2620    RegOpenKey  HKLM\System\CurrentControlSet\Control\Nls\Sorting\Versions  SUCCESS Desired Access: Read
9:06:35.4318016 AM  MyBinary.exe    2620    RegSetInfoKey   HKLM\System\CurrentControlSet\Control\Nls\Sorting\Versions  SUCCESS KeySetInformationClass: KeySetHandleTagsInformation, Length: 0
9:06:35.4318152 AM  MyBinary.exe    2620    RegQueryValue   HKLM\System\CurrentControlSet\Control\Nls\Sorting\Versions\(Default)    SUCCESS Type: REG_SZ, Length: 36, Data: 00060101.00060101
...

Powershell procmon(失败,查看线程退出和进程退出代码-1073741502):

9:35:07.9455191 AM  MyBinary.exe    2276    Load Image  C:\Windows\SysWOW64\kernelbase.dll  SUCCESS Image Base: 0x76270000, Image Size: 0x47000
9:35:07.9537146 AM  MyBinary.exe    2276    Thread Exit     SUCCESS Thread ID: 5112, User Time: 0.0000000, Kernel Time: 0.0000000
9:35:07.9537386 AM  MyBinary.exe    2276    QueryNameInformationFile    C:\Windows\System32\apisetschema.dll    SUCCESS Name: \Windows\System32\apisetschema.dll
9:35:07.9537686 AM  MyBinary.exe    2276    QueryNameInformationFile    C:\somepath\MyBinary\MyBinary.exe   SUCCESS Name: \somepath\MyBinary\MyBinary.exe
9:35:07.9537914 AM  MyBinary.exe    2276    QueryNameInformationFile    C:\Windows\System32\wow64cpu.dll    SUCCESS Name: \Windows\System32\wow64cpu.dll
9:35:07.9538134 AM  MyBinary.exe    2276    QueryNameInformationFile    C:\Windows\System32\wow64win.dll    SUCCESS Name: \Windows\System32\wow64win.dll
9:35:07.9538349 AM  MyBinary.exe    2276    QueryNameInformationFile    C:\Windows\System32\wow64.dll   SUCCESS Name: \Windows\System32\wow64.dll
9:35:07.9538579 AM  MyBinary.exe    2276    QueryNameInformationFile    C:\Windows\System32\ntdll.dll   SUCCESS Name: \Windows\System32\ntdll.dll
9:35:07.9538796 AM  MyBinary.exe    2276    QueryNameInformationFile    C:\Windows\SysWOW64\ntdll.dll   SUCCESS Name: \Windows\SysWOW64\ntdll.dll
9:35:07.9539425 AM  MyBinary.exe    2276    Process Exit        SUCCESS Exit Status: -1073741502, User Time: 0.0000000 seconds, Kernel Time: 0.0000000 seconds, Private Bytes: 339,968, Peak Private Bytes: 401,408, Working Set: 1,523,712, Peak Working Set: 1,826,816

编辑 2:
我应该提到powershell脚本是从服务中运行的(它是一个bamboo服务代理)。我刚刚发现这个线程说:

当指定凭据时,Process.Start内部调用CreateProcessWithLogonW(CPLW)。不能从Windows服务环境(例如IIS WCF服务)中调用CreateProcessWithLogonW。它只能从交互式进程(通过CTRL-ALT-DELETE登录的用户启动的应用程序)中调用。

我的猜测是powershell start-process调用正在使用CreateProcessWithLogonW...

编辑 3:
我的服务是以自定义用户身份运行的(因为我无法从System中模拟),所以根据这个链接进行了测试。我测试确保启用了“允许服务与桌面交互”。因为它仅适用于非自定义帐户,所以我通过手动更改HKLM\System\CurrentControlSet\Services\%myservice%类型键来在注册表上设置它(如此处此处所述)。


您使用的帐户凭据可能没有访问某些 DLL 所在位置的访问权限。 - EBGreen
检查程序所在文件夹的文件系统权限,确保您使用的凭据用户至少具有整个文件夹链和相关DLL(如果已编写)或整个文件夹(如果未编写)的读取访问权限。如果存在访问权限问题,请授予该用户读取+执行权限。 - Vesper
谢谢,我已经检查过了,权限似乎已经正确设置。该用户是“用户”和“管理员”的成员,并且对包含二进制文件的文件夹拥有完全控制权。 - John-Philip
如果是这样,请启动Process Monitor(来自Sysinternals的工具),然后启动您的进程,然后调试抛出了哪个DLL的错误。恐怕我不能再提供更多帮助了。 - Vesper
1
@Vesper,你刚才没看到我的编辑,对吧 :p? - John-Philip
从这个 ProcMon 日志中,我怎么知道哪个 dll 无法加载(或者哪个文件夹无法访问)?最后一行显示 ntdll.dll 加载成功。我正在解决同样的问题。我找到的唯一解决方案是禁用 UAC(将 EnableLUA 设置为 0)- 然后它就可以工作了! - serializer
3个回答

9

start-processSystem.Diagnostics.Process.Start() 的 'alias',因此它确实使用了 CreateProcessWithLogonW()。如上所述,该方法不能从服务进程中调用,只能从“交互式”进程中调用。但是,你已经发现了一个注意点 - 当你不更改凭据时,它至少可以启动进程。(这可能实际上是一个漏洞 - 我与一位关于这个问题的微软支持工程师交谈时,“惊讶”它居然起作用)

从服务进程内部启动另一个进程的唯一(受支持的)方式是使用本机 Win32 API 方法 CreateProcessAsUser()。在 C#.NET 中如何执行此操作的示例可在 答案 中找到。

Windows 进程必须作为用户会话的一部分启动。如果启动进程作为交互式会话的一部分运行 - 即你使用 CTRL+ALT+DELETE 登录并打开桌面的那种会话 - 那么你可以自动使用当前用户会话使用 CreateProcessWithLogonW()。如果启动进程是一个服务或“批处理”进程(如计划任务),则启动进程必须创建一个新的用户会话(或标识一个现有的用户会话)来启动新的进程(这就是前面提到的答案中的代码所做的事情)。


1
感谢@nateirvin,记录一下,我们成功将Bamboo代理作为一个简单的进程运行而不是服务。这种方法更加容易地解决了问题。 - John-Philip

0

到目前为止,我找到的唯一解决方案是禁用UAC(将EnableLUA设置为0 =本地安全策略中的管理员批准模式)。因此,当禁用UAC时,它肯定似乎是一个文件/文件夹/注册表访问问题,而UAC会忽略它。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接