CreateProcessAsUser与ShellExecute的区别

4
我需要作为另一个用户使用ShellExecute,目前我使用CreateProcessAsUser启动帮助进程来调用ShellExecute,但那似乎太过hack(错误的父进程等)。有更好的方法吗?
@PabloG:ImpersonateLoggedOnUser不起作用: HANDLE hTok; VERIFY(LogonUser("otheruser",0,"password",LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&hTok)); VERIFY(ImpersonateLoggedOnUser(hTok)); ShellExecute(0,0,"calc.exe",0,0,SW_SHOW); RevertToSelf(); CloseHandle(hTok);
将以登录的用户身份启动计算器,而不是“otheruser”。
@1800 INFORMATION: CreateProcess/CreateProcessAsUserShellExecute不同,在Vista上启用UAC时,当您无法控制用户执行的程序时,CreateProcess是无用的(如果您给它一个标记为requireAdmin的清单文件,则CreateProcess会返回一个错误)。
@Brian R. Bondy:我已经知道这个信息(不要误解,这是好东西),但这是离题了(在我看来),我正在寻求ShellExecuteAsUser,而不是关于启动进程作为另一个用户的内容,我已经知道如何做到这一点。
4个回答

9
这个解决方案实际上取决于你的需求,可能会非常复杂(感谢Windows Vista)。这可能超出了你的需求范围,但这将帮助通过搜索找到此页面的其他人。
  1. 如果您不需要进程在GUI下运行并且不需要提升权限
  2. 如果您想要运行的用户已经登录到会话中
  3. 如果您需要在GUI下运行进程,并且用户可能已经登录或未登录
  4. 如果您需要以提升权限的方式运行进程

关于1: 在Windows Vista中存在一个称为Session 0隔离的东西。所有服务都在Session 0中运行,而您不应该在Session 0中具有GUI。第一个登录的用户登录到会话1中。在Windows的早期版本(Vista之前),第一个登录的用户也完全在Session 0中运行。

您可以在同一会话中使用不同的用户名运行多个不同的进程。您可以在这里找到有关Session 0隔离的良好文档。

由于我们正在处理选项1),您不需要图形用户界面。 因此,您可以在会话0中启动进程。
您需要类似于以下的调用序列: LogonUser、ExpandEnvironmentStringsForUser、GetLogonSID、LoadUserProfile、CreateEnvironmentBlock、CreateProcessAsUser。
可以通过任何搜索引擎或通过Google code search找到此示例代码。
关于2: 如果您想要运行进程的用户已经登录,则可以简单地使用:WTSEnumerateSessions和WTSQuerySessionInformation获取会话ID,然后使用WTSQueryUserToken获取用户令牌。从那里,您只需在CreateProcessAsUser Win32 API中使用用户令牌即可。
这是一个很好的方法,因为您甚至不需要作为用户登录,也不需要知道用户的用户名/密码。我认为这只有通过以本地系统帐户运行的服务才可能实现。
您可以通过WTSGetActiveConsoleSessionId获取当前会话。
关于第3点: 您需要按照与#1相同的步骤操作,但是还需要使用STARTUPINFO的lpDesktop字段。将其设置为winsta0\Default。您还需要尝试使用OpenDesktop Win32 API,如果失败,则可以使用CreateDesktop。在使用站和桌面句柄之前,您应该对它们中的每一个使用SetSecurityInfo,并使用SE_WINDOW_OBJECT、GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION。
如果涉及的用户稍后尝试登录,则实际上会看到正在运行的进程。
关于第4点: 这也可以完成,但是需要您已经在运行提升的进程。以本地系统帐户运行的服务确实会被提升。我还只能通过具有要启动的Authenticode签名进程来使其工作。您要启动的进程还必须具有与之关联的manifest文件,其中requestedExecutionLevel level="requireAdministrator"。
其他注意事项: - 您可以通过SetTokenInformation和TokenSessionId设置令牌的会话。 - 您无法更改已经运行的进程的会话ID。 - 如果没有Vista,整个过程将大大简化。

如果您能更详细地阐述第4种情况下如何实现,那就太好了。需要使用ShellExecute吗?结合ImpersonateLoggedOnUser使用吗? - Ignitor
WTSQueryUserToken()需要“本地系统”帐户权限。这意味着应用程序应作为服务启动。 - Alex

2
如果你需要ShellExecute的语义,可以输入以下内容:

C:\windwos\system32\cmd.exe /k" start <your_target_to_be_ShellExecuted>"传递给CreateProcessAsUser,然后就完成了。


我已经在使用一个辅助进程,我想要移除它,以便我可以得到正确的父进程等。 - Anders

0

我非常确定我尝试过这个,但它没有起作用,因为新的进程是使用当前进程的令牌而不是它的线程创建的。 - Anders
如果在进程的主线程中执行,这可能有效,但如果在单独的线程中执行,则无法正常工作,因为ShellExecute使用主线程的令牌/安全上下文。你懂的。现在...如果你的应用程序有多个线程,在主线程中执行模拟/还原可能会导致应用程序的其他部分出现问题。 - quickly_now

0

为什么不直接使用CreateProcessAsUser指定要运行的进程呢?

您还可以尝试使用SHCreateProcessAsUserW。


因为我需要ShellExecute对非可执行文件的处理能力。 - Anders
SHCreateProcessAsUserW 在 XP 及以后的版本中未实现。 - quickly_now

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接