我正在尝试编写一个与 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/