为什么这个进程一启动就崩溃了?

2024-03-28

我们有一个 IIS WCF 服务,它以不同的用户身份启动另一个进程 (app.exe)。我可以完全控制这两个应用程序(目前这是一个开发环境)。 IIS 应用程序池以我的身份运行,我是域用户 (DOMAIN\nirvin),同时也是机器上的本地管理员。第二个进程应该以本地用户 (svc-low) 身份运行。我在用System.Diagnostics.Process.Start(ProcessStartInfo)启动该进程。进程启动成功 - 我知道,因为没有抛出异常,并且我获得了进程 ID。但该进程立即终止,并且我在事件日志中收到如下错误:

错误应用程序名称:app.exe,版本:1.0.3.0,时间戳:0x514cd763

错误模块名称:KERNELBASE.dll,版本:6.2.9200.16451,时间戳:0x50988aa6

异常代码:0xc06d007e

故障偏移:0x000000000003811c

错误进程 ID:0x10a4

错误的应用程序启动时间:0x01ce274b3c83d62d

错误应用程序路径: C:\Program Files\company\app\app.exe

错误模块路径: C:\Windows\system32\KERNELBASE.dll

报告 ID:7a45cd1c-933e-11e2-93f8-005056b316dd

错误包全名:

错误包相关应用程序 ID:

我已经在 app.exe 中进行了相当彻底的登录(现在),所以我认为它不会在 .NET 代码中抛出错误(不再)。

这是真正令人讨厌的部分:我认为我只是错误地启动了进程,所以我复制了我的Process.Start()调用一个愚蠢的 WinForms 应用程序,并以我自己的身份在机器上运行它,希望能够进行修改,直到我获得正确的参数。当然,这第一次和此后的每一次都有效:我能够持续启动第二个进程并让它按预期运行。只能从 IIS 启动,不起作用。

我尝试授予 svc-low 权限“作为批处理作业登录”,并且尝试授予自己“替换进程级别令牌”的权限(在本地安全策略中),但似乎都没有任何区别。

Help!

环境详情

  • Windows 服务器 2012
  • .NET 4.5(提到的所有应用程序)

额外细节

最初 app.exe 是一个控制台应用程序。尝试启动导致 conhost.exe 在事件日志中生成错误,因此我将 app.exe 切换为 Windows 应用程序。这将 conhost 排除在等式之外,但给我留下了这里描述的情况。 (沿着这条路引导这个问题 https://stackoverflow.com/questions/677874/starting-a-process-with-credentials-from-a-windows-service.)

The ProcessStartInfo我使用的对象如下所示:

new ProcessStartInfo
{
    FileName = fileName,
    Arguments = allArguments,
    Domain = domainName,
    UserName = userName,  
    Password = securePassword,
    WindowStyle = ProcessWindowStyle.Hidden,
    CreateNoWindow = true,  
    UseShellExecute = false,
    RedirectStandardOutput = false
    //LoadUserProfile = true  //I've done it with and without this set
};

一个现有的问题 https://stackoverflow.com/questions/362419/using-process-start-to-start-a-process-as-a-different-user-from-within-a-windo说我应该深入到本机 API,但是 a) 该问题解决了不同的情况,b) 愚蠢的 WinForms 应用程序的成功表明Process.Start是这项工作的可行选择。


我最终向 Microsoft 立案,这是我得到的信息:

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

(这是支持工程师的逐字记录;重点是我的)

他们建议我使用CreateProcessAsUser反而。他们给了我一些有用的示例代码,然后我根据我的需要进行了调整,现在一切都很好!

最终结果是这样的:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;

public class ProcessHelper
{
    static ProcessHelper()
    {
        UserToken = IntPtr.Zero;
    }

    private static IntPtr UserToken { get; set; }

    public int StartProcess(ProcessStartInfo processStartInfo)
    {
        LogInOtherUser(processStartInfo);

        Native.STARTUPINFO startUpInfo = new Native.STARTUPINFO();
        startUpInfo.cb = Marshal.SizeOf(startUpInfo);
        startUpInfo.lpDesktop = string.Empty;

        Native.PROCESS_INFORMATION processInfo = new Native.PROCESS_INFORMATION();
        bool processStarted = Native.CreateProcessAsUser(UserToken, processStartInfo.FileName, processStartInfo.Arguments,
                                                         IntPtr.Zero, IntPtr.Zero, true, 0, IntPtr.Zero, null,
                                                         ref startUpInfo, out processInfo);

        if (!processStarted)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        uint processId = processInfo.dwProcessId;
        Native.CloseHandle(processInfo.hProcess);
        Native.CloseHandle(processInfo.hThread);
        return (int) processId;
    }

    private static void LogInOtherUser(ProcessStartInfo processStartInfo)
    {
        if (UserToken == IntPtr.Zero)
        {
            IntPtr tempUserToken = IntPtr.Zero;
            string password = SecureStringToString(processStartInfo.Password);
            bool loginResult = Native.LogonUser(processStartInfo.UserName, processStartInfo.Domain, password,
                                                Native.LOGON32_LOGON_BATCH, Native.LOGON32_PROVIDER_DEFAULT,
                                                ref tempUserToken);

            if (loginResult)
            {
                UserToken = tempUserToken;
            }
            else
            {
                Native.CloseHandle(tempUserToken);
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
        }
    }

    private static String SecureStringToString(SecureString value)
    {
        IntPtr stringPointer = Marshal.SecureStringToBSTR(value);
        try
        {
            return Marshal.PtrToStringBSTR(stringPointer);
        }
        finally
        {
            Marshal.FreeBSTR(stringPointer);
        }
    }

    public static void ReleaseUserToken()
    {
        Native.CloseHandle(UserToken);
    }
}

internal class Native
{
    internal const int LOGON32_LOGON_INTERACTIVE = 2;
    internal const int LOGON32_LOGON_BATCH = 4;
    internal const int LOGON32_PROVIDER_DEFAULT = 0;

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

    [StructLayout(LayoutKind.Sequential)]
    internal struct STARTUPINFO
    {
        public int cb;
        [MarshalAs(UnmanagedType.LPStr)]
        public string lpReserved;
        [MarshalAs(UnmanagedType.LPStr)]
        public string lpDesktop;
        [MarshalAs(UnmanagedType.LPStr)]
        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 SECURITY_ATTRIBUTES
    {
        public System.UInt32 nLength;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }

    [DllImport("advapi32.dll", EntryPoint = "LogonUserW", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    internal extern static bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUserA", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    internal extern static bool CreateProcessAsUser(IntPtr hToken, [MarshalAs(UnmanagedType.LPStr)] string lpApplicationName, 
                                                    [MarshalAs(UnmanagedType.LPStr)] string lpCommandLine, IntPtr lpProcessAttributes,
                                                    IntPtr lpThreadAttributes, bool bInheritHandle, uint dwCreationFlags, IntPtr lpEnvironment,
                                                    [MarshalAs(UnmanagedType.LPStr)] string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, 
                                                    out PROCESS_INFORMATION lpProcessInformation);      

    [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    internal extern static bool CloseHandle(IntPtr handle);
}

要使此代码正常工作,有一些先决条件。运行它的用户必须具有“替换进程级令牌”和“调整进程的内存配额”的用户权限,而“其他用户”必须具有“作为批处理作业登录”的用户权限。这些设置可以在本地安全策略 http://www.sysnative.com/forums/windows-tips-tricks/521-local-security-policy-windows-7-professional-above-versions.html(或者可能通过组策略)。如果更改它们,则需要重新启动。

UserToken是一个可以通过关闭的属性ReleaseUserToken因为我们会打电话StartProcess反复多次,我们被告知不要一次又一次地登录其他用户。

That SecureStringToString()方法取自这个问题 https://stackoverflow.com/questions/818704/how-to-convert-securestring-to-system-string. Using SecureString不属于 Microsoft 推荐的一部分;我这样做是为了不破坏与其他代码的兼容性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么这个进程一启动就崩溃了? 的相关文章

  • 如何让我的方法等待所有线程完成?

    我有一个方法可以触发线程来完成一些工作 将有 2 个线程异步运行一段时间 当调用它们的回调方法时 回调会触发另一个线程 直到所有工作完成 如何让我的方法等待所有这些线程完成并被触发 如果这是 Net 4 0 您可以使用CountdownEv
  • F1 2019 UDP解码

    我目前正在为 F1 方向盘开发自己的显示器 F1 2019 由codemasters提供 通过UDP发送数据 该数据存储在字节数组中 我在解码返回的数组时遇到一些问题 问题是我得到了很多信息 但我不知道如何处理它们 我将向您介绍我所尝试过的
  • 从对象中获取类型正在返回运行时类型[重复]

    这个问题在这里已经有答案了 我有一个简单的功能 public string getType object obj Type type obj getType return type FullName 如果您在运行时创建的字符串对象上使用此函
  • 将 MyGeneration 与 Fluent NHibernate 结合使用

    我在这里找到了一个使用 MyGeneration 生成 NHibernate 代码的绝佳模板 http vucetica blogspot com 2009 01 nhibernate template for my Generation
  • 如何更改 GridView 内 ListViewItemPresenter 中的 SelectedBackground

    我在 SubSection 中有一个 Clickable Gridview
  • 正确别名向量

    我无法在其他地方找到答案 所以我想我只需要问这个 我正在尝试获取向量 其中存储 int 指针 的别名 如下所示 void conversion Engine ENGINES The Engine class has a vector of
  • 为什么 C++11/Boost `unordered_map` 在擦除时不重新散列?

    我想知道为什么 C 11 和 Boost 的 hashmap 在通过迭代擦除元素时不会调整大小 即使这在技术上不是内存泄漏 我认为这可能是应用程序中的一个严重问题 这对我来说是一个隐藏的问题 很难追踪它 并且它实际上可能会影响许多应用程序
  • boost::unordered_map 是...有序的吗?

    我有一个 boost unordered map 但它看起来是有序的 给我一种压倒性的 你做错了 的感觉 为什么输出是这样的 我希望底层的哈希算法能够随机化这个顺序 include
  • C# 中的异步方法如何工作?

    我在我的一些项目中使用异步方法 我喜欢它 因为它使我的应用程序更具可扩展性 但是 我想知道异步方法如何在后台真正工作 NET 或 Windows 如何知道调用已完成 根据我进行的异步调用的数量 我可以看到创建了新线程 但并不总是 为什么 此
  • C++ 中的静态虚函数

    我有一个基类和一个派生类 我想更改基函数 同时保持它们静态 因为它们应该作为静态传递给其他函数 我怎样才能做到这一点 ATL 框架通过将基类设为模板 然后让派生类将其类类型作为模板参数传递 从而绕过了无虚拟静态的限制 然后 基类可以在需要时
  • 将 size_t 变量添加到指针

    我想向指针添加 size t 类型 有些像这样 void function size t sizeA size t sizeB void pointer pointer malloc sizeA pointer pointer sizeB
  • 在 Perl 中,如何从父进程向子进程发送消息(或信号),反之亦然?

    我正在编写一个管理多进程的程序 这就是我所做的 而且效果很好 但现在 我想将消息从子进程发送到父进程 反之亦然 从父进程到子进程 你知道最好的方法吗 你知道我所做的是否是我想要的正确方法 从子进程到父进程发送消息 信号或共享内存 反之亦然
  • Windows Phone 8.1 应用程序多语言

    我正在使用 Visual Studio 2015 在 SilverLight 中创建 Windows Phone 应用程序 8 1 我正在用英语和阿拉伯语创建多语言应用程序 为此 我在项目中创建了 Strings 文件夹 其中包含 en U
  • cuda中有模板化的数学函数吗? [复制]

    这个问题在这里已经有答案了 我一直在寻找 cuda 中的模板化数学函数 但似乎找不到 在普通的 C 中 如果我调用std sqrt它是模板化的 并且将根据参数是浮点数还是双精度数执行不同的版本 我想要这样的 CUDA 设备代码 我的内核将真
  • 实体框架..自引用表..获取深度=x的记录?

    我成功地在实体框架中使用自引用表 但我不知道如何获得所需深度的记录 这应该是什么逻辑 Model public class FamilyLabel public FamilyLabel this Children new Collectio
  • 二叉树实现C++

    二叉树插入 include stdafx h include
  • 使用 Entity Framework Core 在运行时迁移

    我正在将 PHP Illuminate 应用程序移植到 ASP NET Core EF Core 其中一部分由类似 Wordpress 的安装过程组成 该过程要求提供数据库凭据 然后创建应用程序运行所需的表 本质上 我想在运行时运行某种迁移
  • 使用本地系统帐户运行时,GetAccessControl 方法失败,出现意外错误代码 3

    我已经创建了 Windows 服务并使用本地系统帐户运行它 该服务正在读取用户文件并查找其所有者 在获取文件的访问权限以查找所有者时 它抛出以下异常 方法失败 出现意外错误代码 3 StackTrace 在 System Security
  • 使用全局 Web API 过滤器属性进行 Unity 依赖注入

    参考这个CodePlex 统一文章 http unity codeplex com discussions 446780我能够使用 WebAPI 控制器获取过滤器属性 如下所示 MyFilterAttribute public class
  • C++ 头文件包含

    我正在开发一个项目 每个头文件都有一个预处理器包含防护 我的包含是这样的 文件 gt 包含 main cpp gt header h 字符 h header h gt 矢量 iostream DataFiles h Character h

随机推荐