c# - 来自 C# 的 Runas/NETONLY

标签 c# winapi runas

我正在尝试编写一个与 runas/netonly 执行相同操作的 C# 程序。 做与 runas 相同的事情很容易,我找到了很多例子。然而,做一个/netonly,就像 runas 一样,似乎并不容易。

我从各种答案中找到了大部分代码,但它需要标准用户没有的一些权限。 我的问题是,runas 命令行如何在没有任何特权(甚至没有管理员权限)的情况下工作,而我的程序却不能? runas 使用什么 API 使其无需任何特权即可工作? 我应该在这段代码中更改什么,以使其在没有特权的情况下工作?

进一步解释一下该方法: 我使用 LogonUser API 创建带有“NEW_CREDENTIALS”参数的 NetOnly token 。 然后,我使用 CreateProcessAsUser 使用之前的 token 运行任何外部 exe。

我尝试的其他方法失败了,例如: 将进程启动与来自LogonUser的模拟 token 结合使用不起作用,因为进程启动不会继承模拟 token ,而是使用来自父进程的原始 token 。 p>

这是代码:

using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace runas_manager
{
    internal class ImpersonatedProcess
    {

        //private const string CommandLine = @"C:\createjobobject.exe";
        private NativeMethods.ProcessInformation _processInfo;
        private readonly ManualResetEvent _exited = new ManualResetEvent(false);


        public IntPtr Handle { get; private set; }
        public event EventHandler? Exited;
        public TextReader StandardOutput { get; private set; }
        public TextReader StandardError { get; private set; }
        public TextWriter StandardInput { get; private set; }

        public void WaitForExit()
        {
            WaitForExit(-1);
        }

        public bool WaitForExit(int milliseconds)
        {
            return _exited.WaitOne(milliseconds);
        }

        public bool Start(string username, string password, string domain, string executablePath)
        {
            _processInfo = new NativeMethods.ProcessInformation();
            var startInfo = new NativeMethods.StartupInfo();
            bool success;

            SafeFileHandle hToken, hReadOut, hWriteOut, hReadErr, hWriteErr, hReadIn, hWriteIn;

            var securityAttributes = new NativeMethods.SecurityAttributes();
            securityAttributes.bInheritHandle = true;

            success = NativeMethods.CreatePipe(out hReadOut, out hWriteOut, securityAttributes, 0);
            if (!success)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            success = NativeMethods.CreatePipe(out hReadErr, out hWriteErr, securityAttributes, 0);
            if (!success)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            success = NativeMethods.CreatePipe(out hReadIn, out hWriteIn, securityAttributes, 0);
            if (!success)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            success = NativeMethods.SetHandleInformation(hReadOut, NativeMethods.Constants.HANDLE_FLAG_INHERIT, 0);
            if (!success)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            // Logon user
            success = NativeMethods.LogonUser(
                username,
                domain,
                password,
                NativeMethods.LogonType.LOGON32_LOGON_NEW_CREDENTIALS,
                NativeMethods.LogonProvider.LOGON32_PROVIDER_DEFAULT,
                out hToken
            );
            if (!success)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            if (!NativeMethods.CreateEnvironmentBlock(out IntPtr unmanagedEnv, hToken.DangerousGetHandle(), false))
            {
                int lastError = Marshal.GetLastWin32Error();
                throw new Win32Exception(lastError, "Error calling CreateEnvironmentBlock: " + lastError);
            }

            // Create process
            startInfo.cb = Marshal.SizeOf(startInfo);
            startInfo.dwFlags = NativeMethods.Constants.STARTF_USESTDHANDLES;
            startInfo.hStdOutput = hWriteOut;
            startInfo.hStdError = hWriteErr;
            startInfo.hStdInput = hReadIn;

            success = NativeMethods.CreateProcessAsUser(
                hToken,
                null,
                executablePath,
                IntPtr.Zero,
                IntPtr.Zero,
                true,
                NativeMethods.CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT,
                unmanagedEnv,
                null,
                ref startInfo,
                out _processInfo
            );

            if (!success)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            Handle = _processInfo.hProcess;

            startInfo.hStdOutput.Close();
            startInfo.hStdError.Close();
            startInfo.hStdInput.Close();
            StandardOutput = new StreamReader(new FileStream(hReadOut, FileAccess.Read), Console.OutputEncoding);
            StandardError = new StreamReader(new FileStream(hReadErr, FileAccess.Read), Console.OutputEncoding);
            StandardInput = new StreamWriter(new FileStream(hWriteIn, FileAccess.Write), Console.InputEncoding);

            WaitForExitAsync();

            return success;
        }

        private void WaitForExitAsync()
        {
            var thr = new Thread(() =>
            {
                _ = NativeMethods.WaitForSingleObject(_processInfo.hProcess, NativeMethods.Constants.INFINITE);
                Exited?.Invoke(this, EventArgs.Empty);
                _exited.Set();
            });
            thr.Start();
        }
    }
}

和 native 方法声明

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;

namespace runas_manager
{
    class NativeMethods
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct ProcessInformation
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public Int32 dwProcessId;
            public Int32 dwThreadId;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct StartupInfo
        {
            public Int32 cb;
            public String lpReserved;
            public String lpDesktop;

            public String lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwYSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public uint dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public SafeFileHandle hStdInput;
            public SafeFileHandle hStdOutput;
            public SafeFileHandle hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public class SecurityAttributes
        {
            public Int32 Length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;

            public SecurityAttributes()
            {
                this.Length = Marshal.SizeOf(this);
            }
        }

        [Flags]
        public enum LogonType
        {
            LOGON32_LOGON_INTERACTIVE = 2,
            LOGON32_LOGON_NETWORK = 3,
            LOGON32_LOGON_BATCH = 4,
            LOGON32_LOGON_SERVICE = 5,
            LOGON32_LOGON_UNLOCK = 7,
            LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
            LOGON32_LOGON_NEW_CREDENTIALS = 9
        }

        [Flags]
        public enum LogonProvider
        {
            LOGON32_PROVIDER_DEFAULT = 0,
            LOGON32_PROVIDER_WINNT35,
            LOGON32_PROVIDER_WINNT40,
            LOGON32_PROVIDER_WINNT50
        }

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


        public class Constants
        {
            public const int HANDLE_FLAG_INHERIT = 1;
            public static uint STARTF_USESTDHANDLES = 0x00000100;
            public const UInt32 INFINITE = 0xFFFFFFFF;

        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool CreatePipe(out SafeFileHandle phReadPipe, out SafeFileHandle phWritePipe,
            SecurityAttributes lpPipeAttributes, uint nSize);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetHandleInformation(SafeFileHandle hObject, int dwMask, uint dwFlags);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern Boolean LogonUser(
            String lpszUserName,
            String lpszDomain,
            String lpszPassword,
            LogonType dwLogonType,
            LogonProvider dwLogonProvider,
            out SafeFileHandle phToken);

        [DllImport("userenv.dll", SetLastError = true)]
        public static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean CreateProcessAsUser
        (
            SafeFileHandle hToken,
            String? lpApplicationName,
            String lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            Boolean bInheritHandles,
            CreateProcessFlags dwCreationFlags,
            IntPtr lpEnvironment,
            String? lpCurrentDirectory,
            ref StartupInfo lpStartupInfo,
            out ProcessInformation lpProcessInformation
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);


    }
}

最佳答案

我找到了一种让它发挥作用的方法。 在上面的代码中,我没有使用 LogonUser 和 CreateProcessAsUser,而是仅使用了 CreateProcessWithLogonW。这需要知道密码,您通常在使用 runas/netonly 时知道密码,但好处是除了标准用户之外您不需要任何权限!

使用以下代码替换 LogonUser、环境创建和 CreateProcessAsUser。

success = NativeMethods.CreateProcessWithLogonW(
                username,
                domain,
                password,
                NativeMethods.LogonFlags.LOGON_NETCREDENTIALS_ONLY,
                null,
                executablePath,
                NativeMethods.CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT,
                IntPtr.Zero,//unmanagedEnv,
                null,
                ref startInfo,
                out _processInfo
                );

添加到 dllimport 定义

        [Flags]
        public enum LogonFlags
        {
            LOGON_WITH_PROFILE = 1,
            LOGON_NETCREDENTIALS_ONLY = 2
        }


        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool CreateProcessWithLogonW(
            String userName,
            String domain,
            String password,
            LogonFlags logonFlags,
            String? applicationName,
            String commandLine,
            CreateProcessFlags creationFlags,
            IntPtr environment,
            String? currentDirectory,
            ref StartupInfo startupInfo,
            out ProcessInformation processInformation);

关于c# - 来自 C# 的 Runas/NETONLY,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71647220/

相关文章:

c# - 检查打开的窗口是否已关闭

c++ - Winapi 专用鼠标/键盘

c++ - 如何在 Windows 上使用 C++ 监听终端外的击键

permissions - 使用 NSIS 设置文件夹权限

c++ - 如果应用程序以管理员身份运行,则奇怪的文件权限

C# - 从 WebRequest 获取响应并处理状态代码

c# - 返回 Enumerable.Empty<T>().AsQueryable() 是个坏主意吗?

c++ - 检查是否可以删除目录树而不实际开始删除它

.net - 程序 (.NET) 如何判断它是否以其他用户身份运行?

c# - C# 3.0 中的命名/可选参数?