在模拟时使用 Process.Start(Window 应用程序)
use Process.Start while Impersonating (Window Application)
我正在尝试在模拟下使用 Process.Start(),我已经 google 几天了,我遇到的大多数答案都是在 ASP.net 下,但我正在开发对于 Window 应用程序,所以我很难找到根本原因。
这是我的模拟代码
private void impersonateValidUser(string userName, string domain, string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if ( LogonUser( userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
mImpersonationContext = tempWindowsIdentity.Impersonate();
}
}
}
并且我正在尝试通过我的程序打开文档(none .exe,例如 .txt、.doc)
using (new Impersonator(DomainUserID, Domain, Password))
Process.Start(filePath);
到目前为止,我能够检测到 directory/file 与模拟用户的关系,这对我当前的登录用户来说是不可见的,因为我没有授予它访问权限。但是每当我尝试打开文档时,都会出现错误
System.ComponentModel.Win32Exception (0x80004005): Access is denied
如果我错了,请纠正我,所以在这种情况下,我不应该将 UseShellExecute 设置为 false(并且 processStartInfo 也需要输入用户名和密码?),因为它用于可执行文件(?),而且我确实也遇到过 CreateProcessAsUser 函数,但这个函数似乎也不适用于我的情况,从我读到的示例来看,它也是针对 .exe 文件的。
如有大神赐教,不胜感激
更新:模拟 class
public class Impersonator : IDisposable
{
#region P/Invoke
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int LogonUser( string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int DuplicateToken( IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseHandle(IntPtr handle);
#endregion
#region Constants
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
#endregion
#region Attributes
private WindowsImpersonationContext mImpersonationContext = null;
#endregion
#region Public methods.
public Impersonator( string userName, string domainName, string password)
{
impersonateValidUser(userName, domainName, password);
}
#endregion
#region IDisposable member.
public void Dispose()
{
undoImpersonation();
}
#endregion
#region Private member.
private void impersonateValidUser(string userName, string domain, string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if ( RevertToSelf() )
{
if ( LogonUser( userName, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
mImpersonationContext = tempWindowsIdentity.Impersonate();
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
finally
{
if ( token != IntPtr.Zero )
{
CloseHandle( token );
}
if ( tokenDuplicate != IntPtr.Zero )
{
CloseHandle( tokenDuplicate );
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void undoImpersonation()
{
if ( mImpersonationContext != null )
{
mImpersonationContext.Undo();
}
}
#endregion
}
这是我用来模拟用户的完整 class:
/// <summary>
/// Provide a context to impersonate operations.
/// </summary>
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public class Impersonate : IDisposable
{
/// <summary>
/// Initialize a new instance of the ImpersonateValidUser class with the specified user name, password, and domain.
/// </summary>
/// <param name="userName">The user name associated with the impersonation.</param>
/// <param name="password">The password for the user name associated with the impersonation.</param>
/// <param name="domain">The domain associated with the impersonation.</param>
/// <exception cref="ArgumentException">If the logon operation failed.</exception>
public Impersonate(string userName, SecureString password, string domain)
{
ValidateParameters(userName, password, domain);
WindowsIdentity tempWindowsIdentity;
IntPtr userAccountToken = IntPtr.Zero;
IntPtr passwordPointer = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if (Kernel32.RevertToSelf())
{
// Marshal the SecureString to unmanaged memory.
passwordPointer = Marshal.SecureStringToGlobalAllocUnicode(password);
if (Advapi32.LogonUser(userName, domain, passwordPointer, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref userAccountToken))
{
if (Advapi32.DuplicateToken(userAccountToken, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
return;
}
}
}
}
}
finally
{
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(passwordPointer);
// Close the token handle.
if (userAccountToken != IntPtr.Zero)
Kernel32.CloseHandle(userAccountToken);
if (tokenDuplicate != IntPtr.Zero)
Kernel32.CloseHandle(tokenDuplicate);
}
throw new ArgumentException(string.Format("Logon operation failed for userName {0}.", userName));
}
/// <summary>
/// Reverts the user context to the Windows user represented by the WindowsImpersonationContext.
/// </summary>
public void UndoImpersonation()
{
impersonationContext.Undo();
}
/// <summary>
/// Releases all resources used by <see cref="Impersonate"/> :
/// - Reverts the user context to the Windows user represented by this object : <see cref="System.Security.Principal.WindowsImpersonationContext"/>.Undo().
/// - Dispose the <see cref="System.Security.Principal.WindowsImpersonationContext"/>.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (impersonationContext != null)
{
//UndoImpersonation();
impersonationContext.Dispose();
impersonationContext = null;
}
}
}
private void ValidateParameters(string userName, SecureString password, string domain)
{
if (string.IsNullOrWhiteSpace(userName))
{
throw new ArgumentNullException("userName");
}
if (password == null || password.Length == 0)
{
throw new ArgumentNullException("password");
}
if (string.IsNullOrWhiteSpace(userName))
{
throw new ArgumentNullException("domain");
}
}
private WindowsImpersonationContext impersonationContext;
private const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
}
如何将字符串转换为安全字符串:
var secure = new SecureString();
foreach (char c in "myclearpassword")
{
secure.AppendChar(c);
}
所以你可以这样使用它:
using (var imp = new Impersonate(DomainUserID, SecurePassword, Domain))
{
Process.Start(filePath);
}
冒充时不能使用 UseShellExecute = true
。这与 shell 在 Windows 中的执行方式有关。基本上所有内容都传递给 shell,它查找如何处理动词(在您的情况下为 "open"),然后在拥有 shell 的用户下启动应用程序,即 [=25] =]不是模拟用户 - 如果没有会话,模拟用户实际上没有 shell!
尽管您使用不同的机制来模拟用户,documentation for UseShellExecute
仍然适用于您的情况:
UseShellExecute
must be false if the UserName
property is not null or an empty string, or an InvalidOperationException
will be thrown when the Process.Start(ProcessStartInfo)
method is called.
要解决此问题,最简单的方法可能是自己查找已注册的应用程序,如以下答案所述:Finding the default application for opening a particular file type on Windows。使用关联应用程序的路径,您可以以其他用户身份启动可执行文件。
我正在尝试在模拟下使用 Process.Start(),我已经 google 几天了,我遇到的大多数答案都是在 ASP.net 下,但我正在开发对于 Window 应用程序,所以我很难找到根本原因。
这是我的模拟代码
private void impersonateValidUser(string userName, string domain, string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if ( LogonUser( userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
mImpersonationContext = tempWindowsIdentity.Impersonate();
}
}
}
并且我正在尝试通过我的程序打开文档(none .exe,例如 .txt、.doc)
using (new Impersonator(DomainUserID, Domain, Password))
Process.Start(filePath);
到目前为止,我能够检测到 directory/file 与模拟用户的关系,这对我当前的登录用户来说是不可见的,因为我没有授予它访问权限。但是每当我尝试打开文档时,都会出现错误
System.ComponentModel.Win32Exception (0x80004005): Access is denied
如果我错了,请纠正我,所以在这种情况下,我不应该将 UseShellExecute 设置为 false(并且 processStartInfo 也需要输入用户名和密码?),因为它用于可执行文件(?),而且我确实也遇到过 CreateProcessAsUser 函数,但这个函数似乎也不适用于我的情况,从我读到的示例来看,它也是针对 .exe 文件的。
如有大神赐教,不胜感激
更新:模拟 class
public class Impersonator : IDisposable
{
#region P/Invoke
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int LogonUser( string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int DuplicateToken( IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseHandle(IntPtr handle);
#endregion
#region Constants
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
#endregion
#region Attributes
private WindowsImpersonationContext mImpersonationContext = null;
#endregion
#region Public methods.
public Impersonator( string userName, string domainName, string password)
{
impersonateValidUser(userName, domainName, password);
}
#endregion
#region IDisposable member.
public void Dispose()
{
undoImpersonation();
}
#endregion
#region Private member.
private void impersonateValidUser(string userName, string domain, string password)
{
WindowsIdentity tempWindowsIdentity = null;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if ( RevertToSelf() )
{
if ( LogonUser( userName, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
mImpersonationContext = tempWindowsIdentity.Impersonate();
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
else
{
throw new Win32Exception( Marshal.GetLastWin32Error() );
}
}
finally
{
if ( token != IntPtr.Zero )
{
CloseHandle( token );
}
if ( tokenDuplicate != IntPtr.Zero )
{
CloseHandle( tokenDuplicate );
}
}
}
/// <summary>
/// Reverts the impersonation.
/// </summary>
private void undoImpersonation()
{
if ( mImpersonationContext != null )
{
mImpersonationContext.Undo();
}
}
#endregion
}
这是我用来模拟用户的完整 class:
/// <summary>
/// Provide a context to impersonate operations.
/// </summary>
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public class Impersonate : IDisposable
{
/// <summary>
/// Initialize a new instance of the ImpersonateValidUser class with the specified user name, password, and domain.
/// </summary>
/// <param name="userName">The user name associated with the impersonation.</param>
/// <param name="password">The password for the user name associated with the impersonation.</param>
/// <param name="domain">The domain associated with the impersonation.</param>
/// <exception cref="ArgumentException">If the logon operation failed.</exception>
public Impersonate(string userName, SecureString password, string domain)
{
ValidateParameters(userName, password, domain);
WindowsIdentity tempWindowsIdentity;
IntPtr userAccountToken = IntPtr.Zero;
IntPtr passwordPointer = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
try
{
if (Kernel32.RevertToSelf())
{
// Marshal the SecureString to unmanaged memory.
passwordPointer = Marshal.SecureStringToGlobalAllocUnicode(password);
if (Advapi32.LogonUser(userName, domain, passwordPointer, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref userAccountToken))
{
if (Advapi32.DuplicateToken(userAccountToken, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
return;
}
}
}
}
}
finally
{
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(passwordPointer);
// Close the token handle.
if (userAccountToken != IntPtr.Zero)
Kernel32.CloseHandle(userAccountToken);
if (tokenDuplicate != IntPtr.Zero)
Kernel32.CloseHandle(tokenDuplicate);
}
throw new ArgumentException(string.Format("Logon operation failed for userName {0}.", userName));
}
/// <summary>
/// Reverts the user context to the Windows user represented by the WindowsImpersonationContext.
/// </summary>
public void UndoImpersonation()
{
impersonationContext.Undo();
}
/// <summary>
/// Releases all resources used by <see cref="Impersonate"/> :
/// - Reverts the user context to the Windows user represented by this object : <see cref="System.Security.Principal.WindowsImpersonationContext"/>.Undo().
/// - Dispose the <see cref="System.Security.Principal.WindowsImpersonationContext"/>.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (impersonationContext != null)
{
//UndoImpersonation();
impersonationContext.Dispose();
impersonationContext = null;
}
}
}
private void ValidateParameters(string userName, SecureString password, string domain)
{
if (string.IsNullOrWhiteSpace(userName))
{
throw new ArgumentNullException("userName");
}
if (password == null || password.Length == 0)
{
throw new ArgumentNullException("password");
}
if (string.IsNullOrWhiteSpace(userName))
{
throw new ArgumentNullException("domain");
}
}
private WindowsImpersonationContext impersonationContext;
private const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
}
如何将字符串转换为安全字符串:
var secure = new SecureString();
foreach (char c in "myclearpassword")
{
secure.AppendChar(c);
}
所以你可以这样使用它:
using (var imp = new Impersonate(DomainUserID, SecurePassword, Domain))
{
Process.Start(filePath);
}
冒充时不能使用 UseShellExecute = true
。这与 shell 在 Windows 中的执行方式有关。基本上所有内容都传递给 shell,它查找如何处理动词(在您的情况下为 "open"),然后在拥有 shell 的用户下启动应用程序,即 [=25] =]不是模拟用户 - 如果没有会话,模拟用户实际上没有 shell!
尽管您使用不同的机制来模拟用户,documentation for UseShellExecute
仍然适用于您的情况:
UseShellExecute
must be false if theUserName
property is not null or an empty string, or anInvalidOperationException
will be thrown when theProcess.Start(ProcessStartInfo)
method is called.
要解决此问题,最简单的方法可能是自己查找已注册的应用程序,如以下答案所述:Finding the default application for opening a particular file type on Windows。使用关联应用程序的路径,您可以以其他用户身份启动可执行文件。