我有一个在本地系统帐户下运行的 Windows 服务,它使用 DefineDosDevice 函数创建 DOS 设备。该服务在 W2K8 远程桌面服务器上运行。如果设备是使用服务的凭据创建的,则它们是在 GLOBAL 设备命名空间中创建的,因此对所有用户可见。我需要仅对特定交互会话可见的设备。
我通过模拟我希望驱动器出现在其会话中的用户来实现这一点。这相当简单,只要会话 id 可用。这是我为说明问题而编写的一个简单的测试应用程序:
int _tmain(int argc, _TCHAR* argv[])
{
BOOL result = TRUE;
if(argc > 3 && !wcscmp(argv[2], L"/i"))
{
HANDLE hToken = 0;
DWORD dwSessionId = _wtoi(argv[3]);
result = WTSQueryUserToken(dwSessionId, &hToken);
if(result) result = ImpersonateLoggedOnUser(hToken);
}
if(result)
{
LPTSTR drive = argv[1];
DefineDosDevice(DDD_REMOVE_DEFINITION, drive, NULL);
result = DefineDosDevice(0, drive, L"C:\\test");
}
if(!result)
{
printf("Error: %d\n", GetLastError());
}
return 0;
}
为了测试这段代码,我创建了一个在 LocalSystem 帐户下启动命令 shell 的服务:
sc create test_svc binpath=“cmd /K start”类型=自己的类型=交互
此服务无法启动,但在它失败之前,它会生成一个在 LocalSystem 帐户下运行的命令 shell。
从 LocalSystem cmd.exe,我运行:
MySubst.exe x: /i 2
它调用 ImpersonateLoggedOnUser(),然后调用 DefineDosDevice()
从用户会话中运行的 cmd.exe,我运行:
MySubst.exe y:
在不调用 ImpersonateLoggedOnUser() 的情况下调用DefineDosDevice 。
这行得通。从 cmd.exe 我可以访问两个驱动器 X: 和 Y:。我可以从开始菜单启动 notepad.exe,然后查看 X: 和 Y: 驱动器。此外,如果我与其他用户创建新的终端服务会话,我看不到 X: 或 Y:。
但是,资源管理器仅在“所有计算机”下显示 Y:驱动器。Y:是通过在目标会话中运行的 cmd.exe 运行我的测试应用程序创建的驱动器,即未完成模拟。如果我从任务管理器重新启动 explorer.exe,X: 和 Y: 驱动器都会出现。
我还使用 SysInternals 的 WinObj.exe 来检查定义的 Win NT 设备。我看到的是:
- Sessions
- 0
- DosDevices
00000000-000057607
(57607 是与我正在模拟的会话关联的登录会话的 ID)
“00000000-000057607”的内容为:
Global SymbolicLink \Global??
X: SymbolicLink \\??\C:\test
Y: SymbolicLink \\??\C:\test
根据 WinObj,这两个 dos 设备是相同的。它们属于同一个会话和登录会话。它们是指向同一个 NT 对象的符号链接。
怎么可能其中一个出现在资源管理器中而另一个没有。