C# CreateProcessAsUser:服务获取;5:访问被拒绝”;正在尝试访问网络共享
我使用Windows服务中的CreateProcessAsUser为当前活动用户启动应用程序。到目前为止,它与本地驱动器上的应用程序配合得非常好 但是,如果可执行文件存在于网络共享上,则当我使用完整的服务器名(\myserver\path\app.exe)时,服务将生成5:ERROR\u ACCESS\u DENIED。如果改用映射驱动器(P:\path\app.exe),我还可以生成2:ERROR\u FILE\u NOT\u FOUND 我可以从资源管理器启动应用程序。听起来好像我无法获得正确的令牌副本,因为服务无法在服务器上正确模拟我 我在不同的帖子中尝试了CreateProcessAsUser的几种不同实现,但都没有成功。这对我来说是全新的(迷幻的)东西,坦率地说,我迫不及待地想回到.NET:)我想这里有一句冒犯的话:C# CreateProcessAsUser:服务获取;5:访问被拒绝”;正在尝试访问网络共享,c#,networking,windows-services,impersonation,C#,Networking,Windows Services,Impersonation,我使用Windows服务中的CreateProcessAsUser为当前活动用户启动应用程序。到目前为止,它与本地驱动器上的应用程序配合得非常好 但是,如果可执行文件存在于网络共享上,则当我使用完整的服务器名(\myserver\path\app.exe)时,服务将生成5:ERROR\u ACCESS\u DENIED。如果改用映射驱动器(P:\path\app.exe),我还可以生成2:ERROR\u FILE\u NOT\u FOUND 我可以从资源管理器启动应用程序。听起来好像我无法获得正
DuplicateTokenEx(
hUserToken,
(Int32)MAXIMUM_ALLOWED,
ref sa,
(Int32)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(Int32)TOKEN_TYPE.TokenPrimary,
ref hUserTokenDup);
CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true);
Int32 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
PROCESS_INFORMATION pi;
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "winsta0\\default";
CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
commandLine, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
workingDirectory, // name of current directory
ref si, // pointer to STARTUPINFO structure
out pi); // receives information about new process
以下是完整的示例代码,我想它可能很有用:
using System;
using System.Text;
using System.Security;
using System.Management;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Win32
{
public class Win32API
{
[StructLayout(LayoutKind.Sequential)]
struct SECURITY_ATTRIBUTES
{
public Int32 Length;
public IntPtr lpSecurityDescriptor;
public Boolean bInheritHandle;
}
enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
[StructLayout(LayoutKind.Sequential)]
struct STARTUPINFO
{
public Int32 cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public UInt32 dwX;
public UInt32 dwY;
public UInt32 dwXSize;
public UInt32 dwYSize;
public UInt32 dwXCountChars;
public UInt32 dwYCountChars;
public UInt32 dwFillAttribute;
public UInt32 dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public UInt32 dwProcessId;
public UInt32 dwThreadId;
}
enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
const UInt32 MAXIMUM_ALLOWED = 0x2000000;
const Int32 CREATE_UNICODE_ENVIRONMENT = 0x00000400;
const Int32 NORMAL_PRIORITY_CLASS = 0x20;
const Int32 CREATE_NEW_CONSOLE = 0x00000010;
[DllImport("kernel32.dll", SetLastError = true)]
static extern Boolean CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
public static extern UInt32 WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
static extern UInt32 WTSQueryUserToken(UInt32 SessionId, ref IntPtr phToken);
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
extern static Boolean CreateProcessAsUser(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
Boolean bInheritHandle,
Int32 dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
extern static Boolean DuplicateTokenEx(
IntPtr ExistingTokenHandle,
UInt32 dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
Int32 TokenType,
Int32 ImpersonationLevel,
ref IntPtr DuplicateTokenHandle);
[DllImport("userenv.dll", SetLastError = true)]
static extern Boolean CreateEnvironmentBlock(
ref IntPtr lpEnvironment,
IntPtr hToken,
Boolean bInherit);
[DllImport("userenv.dll", SetLastError = true)]
static extern Boolean DestroyEnvironmentBlock(IntPtr lpEnvironment);
/// <summary>
/// Creates the process in the interactive desktop with credentials of the logged in user.
/// </summary>
public static Boolean CreateProcessAsUser(String commandLine, String workingDirectory, out StringBuilder output)
{
Boolean processStarted = false;
output = new StringBuilder();
try
{
UInt32 dwSessionId = WTSGetActiveConsoleSessionId();
output.AppendLine(String.Format("Active console session Id: {0}", dwSessionId));
IntPtr hUserToken = IntPtr.Zero;
WTSQueryUserToken(dwSessionId, ref hUserToken);
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
IntPtr hUserTokenDup = IntPtr.Zero;
DuplicateTokenEx(
hUserToken,
(Int32)MAXIMUM_ALLOWED,
ref sa,
(Int32)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(Int32)TOKEN_TYPE.TokenPrimary,
ref hUserTokenDup);
if (hUserTokenDup != IntPtr.Zero)
{
output.AppendLine(String.Format("DuplicateTokenEx() OK (hToken: {0})", hUserTokenDup));
Int32 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
IntPtr pEnv = IntPtr.Zero;
if (CreateEnvironmentBlock(ref pEnv, hUserTokenDup, true))
{
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
output.AppendLine(String.Format("CreateEnvironmentBlock() success."));
}
else
{
output.AppendLine(String.Format("CreateEnvironmentBlock() FAILED (Last Error: {0})", Marshal.GetLastWin32Error()));
pEnv = IntPtr.Zero;
}
// Launch the process in the client's logon session.
PROCESS_INFORMATION pi;
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "winsta0\\default";
output.AppendLine(String.Format("CreateProcess (Path:{0}, CurrDir:{1})", commandLine, workingDirectory));
if (CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
commandLine, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
workingDirectory, // name of current directory
ref si, // pointer to STARTUPINFO structure
out pi // receives information about new process
))
{
processStarted = true;
output.AppendLine(String.Format("CreateProcessAsUser() OK (PID: {0})", pi.dwProcessId));
}
else
{
output.AppendLine(String.Format("CreateProcessAsUser() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
if (DestroyEnvironmentBlock(pEnv))
{
output.AppendLine("DestroyEnvironmentBlock: Success");
}
else
{
output.AppendLine(String.Format("DestroyEnvironmentBlock() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
}
else
{
output.AppendLine(String.Format("DuplicateTokenEx() failed (Last Error: {0})", Marshal.GetLastWin32Error()));
}
CloseHandle(hUserTokenDup);
CloseHandle(hUserToken);
}
catch (Exception ex)
{
output.AppendLine("Exception occurred: " + ex.Message);
}
return processStarted;
}
}
}
我的问题:需要调整什么才能使用重复令牌正确访问网络共享?当您对允许来宾访问的共享(即没有用户名/密码)使用此命令时,该命令正常工作,但当您对需要身份验证才能使用的共享使用此命令时,该命令不起作用 UI调用会涉及重定向器,它会自动建立到执行所需的远程服务器的连接 请注意,一种解决方法是使用基于
cmd
的中继来访问可执行文件,因此对于命令行,您可以将其设置为:
CreateProcessAsUser(@"cmd /c ""start \\server\share\binary.exe""", @"C:\Windows", out result);
然后使用以下方法将startupinfo更改为SW_HIDE
cmd窗口:
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "winsta0\\default";
si.dwFlags = 0x1; // STARTF_USESHOWWINDOW
si.wShowWindow = 0; // SW_HIDE
cmd
调用是在启动命令之前完全进入用户环境的一部分-这将利用访问服务器的所有凭据
请注意,您可能需要有一点逻辑来防止直接调用应用程序的
SW_HIDE
(例如,在命令行字符串的开头检查cmd?)检查DuplicateTokenEx和CreateProcessAsUser的返回值,如果无法了解失败原因,则获取最后一个错误。@500 InternalServerError-DuplicateTokenEx工作正常,但CreateProcessAsUser给我5:错误\u访问\u拒绝。你对我可以改变哪些选项来更接近真实用户有什么建议吗?让它发挥作用似乎并不简单。可能会提供一些线索。@500内部服务器错误谢谢!它确实提供了线索,现在我创建/销毁了一个环境块。但不幸的是,我仍然无法使它在网络共享上工作(但它在本地工作)。我想我已经找出了原因,所以我用新的解释和代码示例完全更新了我的问题。谢谢Petesh,“cmd”解决方案成功了!但是在CreateProcessAsUser()期间,带引号的语法在我的端部不起作用。当我将lpApplicationName和lpCurrentDirectory设置为null,并将lpCommandName格式设置为“cmd/c\\\\myserver\\path\\myapp.exe”,去掉“start”和引号后,它就工作了。是的,引号很难正确引用。启动的原因是在远程应用程序启动后终止shell。我会尝试添加它,谢谢你的帮助。在假期之前,我就一直被这个问题困扰着:Pindeded,start也可以。最后一条命令行:“cmd/c start\\\\myserver\\mypath\\myapp.exe”。
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "winsta0\\default";
si.dwFlags = 0x1; // STARTF_USESHOWWINDOW
si.wShowWindow = 0; // SW_HIDE