将 SessionId 转换为用户帐户 SID _without_ WTSQuerySessionInformation?
Posted
技术标签:
【中文标题】将 SessionId 转换为用户帐户 SID _without_ WTSQuerySessionInformation?【英文标题】:Convert SessionId to User Account SID _without_ WTSQuerySessionInformation? 【发布时间】:2020-12-02 19:47:30 【问题描述】:我正在开发一个接收会话更改通知(特别是 SessionLogon)的服务 (C#)。我通过该通知获得的唯一信息是SessionId
。
我的最终目标是检查登录用户的配置文件(本地/漫游 AppData/MyCorp/MyApp 文件夹)是否有特定设置,如果存在则执行任务。
我需要从 SessionId 转到我可以映射到本地用户配置文件的东西,或者直接映射到 用户帐户 SID 或映射到可以映射到SID,(例如 "
我在 SO 上找到的解决方案依赖于 Windows 终端服务 (WTS) API(例如 WTSQuerySessionInformation
),但远程桌面服务在 Windows 10 家庭版中不可用,所以这是一个非首发。
有谁知道如何从 SessionId 映射到不涉及 WTS API 的本地用户帐户?
(编辑#1)澄清:
在 .NET 中,ServiceBase 类有一个 OnSessionChange
覆盖,用于登录/注销/解锁/锁定事件。我最初认为这是针对所有此类事件(来自物理机或终端服务器)。
看起来这个仅适用于终端服务器会话(?)所以,显然,我得到的sessionId
是终端服务器特定的东西。正如@RbMm 在下面指出的那样,这个覆盖可能不会在 Windows Home 版上被首先调用。不过,这是一个有争议的问题,因为我感兴趣的是本地(物理)登录事件,这与终端服务会话完全不同。
在我看来,服务基类会有这样一个有用的事件,但它与 Terminal Services 相关联,而不是适用于所有情况,这对我来说似乎很奇怪。也许有人对此有所了解?
(编辑#2)实现:
@RbMm 的 cmets 消除了我一开始的一些误解。这是更新:
OnSessionChange 事件仅用于终端服务,与本地(物理)登录会话无关(我将两者混为一谈)。 我只对本地登录会话感兴趣,因此我将寻找一种方法来在我的服务中获得有关它们的通知。如果没有此类通知,我将不得不设置一个计时器并进行投票。 我需要从收到的任何信息以及此类通知(或定期致电LsaGetLogonSessionData
)中获取用户帐户 SID
【问题讨论】:
如果远程桌面服务不可用或未运行 - 根本不会更改会话。结果没有通知,在这种情况下你永远不会打电话给WTSQuerySessionInformationW
。从另一边这个 api 总是被导出,所以你总是可以导入它
我也认为文档非常糟糕且不正确。 如果远程桌面服务未运行,则对 WTSQuerySessionInformation 的调用失败 - 不清楚这仅用于检索会话 ID 或任何情况。但在我的系统上,远程桌面服务 (TermService) 没有运行,但 WTSQuerySessionInformation
不会失败 并返回实际信息
OnSessionChange
- 我如何理解您准确注册终端会话通知。不是登录会话。这里你得到WM_WTSSESSION_CHANGE
消息的wParam
参数和指向WTSSESSION_NOTIFICATION
的指针。所以你需要准确地打电话给WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, dwSessionId, WTSSessionInfo, (PWSTR*)&pp, &dwSessionId)
,我确定这个电话不会失败。
我认为您不了解终端和登录会话之间的区别。在单个终端会话中可以有几个不同的登录会话。例如运行this 看看输出
注册SERVICE_ACCEPT_SESSIONCHANGE
时无论如何服务都会收到SERVICE_CONTROL_SESSIONCHANGE
通知。它在用户登录,注销,锁定,解锁......等本地用户时调用,不仅适用于远程用户。如果本地用户 WinStationName
将是 Console
【参考方案1】:
我认为您可以通过 WMI 检索此信息
Win32_LogonSession
class Win32_LogonSession : Win32_Session
string Caption;
string Description;
datetime InstallDate;
string Name;
string Status;
datetime StartTime;
string AuthenticationPackage;
string LogonId;
uint32 LogonType;
;
此外:
LogonId
数据类型:字符串
访问类型:只读
限定符:关键
分配给登录会话的 ID。
示例
var scope = new ManagementScope(ManagementPath.DefaultPath);
var query = new SelectQuery($"Select * from Win32_LogonSession where LogonId = SessionId");
var searcher = new ManagementObjectSearcher(scope, query);
var results = searcher.Get();
foreach (ManagementObject mo in results)
请注意,这是完全未经测试的
【讨论】:
关于登录会话(LUID)或终端会话(ULONG)的问题?这是绝对不同的事情。您查询有关登录会话的信息。为此存在LsaGetLogonSessionData。使用 wmi 总是不好的解决方案。这只是对 wmi 服务进程的远程调用。但是这个过程需要什么 api 来获取信息?为什么不直接调用这个api呢? @RbMm 如果 LsaGetLogonSessionData 符合 OP 的要求。也许你可以回答这个问题:)我的回答只是一个猜测,如果它错了,我很高兴删除 我不明白原来的问题 - 关于他问的是哪个会话。但我只是注意——如果信息可以通过 wmi 获得——这意味着我们可以在没有 wmi 的情况下获得相同的信息(这只是非常慢的远程调用)。从某种意义上说,最好使用LsaGetLogonSessionData
,它返回SECURITY_LOGON_SESSION_DATA
,然后是wmi,它内部无论如何调用LsaGetLogonSessionData
LogonId 和SessionId 绝对不同。在同一个 SessionId 中可以是多个不同的 LogonId 会话。我认为 OP 询问他在 HandlerEx
- SERVICE_CONTROL_SESSIONCHANGE
和 WTSSESSION_NOTIFICATION
中收到的终端会话 ID。他在哪里得到DWORD dwSessionId
。你,我想,这与登录会话混淆
抱歉耽搁了,但我想this comment解释情况以上是关于将 SessionId 转换为用户帐户 SID _without_ WTSQuerySessionInformation?的主要内容,如果未能解决你的问题,请参考以下文章