使用ANCI C和Windows API:
我正在调用函数CreateProcessAsUser(…)从一个Windows 7服务应用程序(运行在会话0)启动一个应用程序作为用户登录到会话1。
这个效果很好。应用程序在被服务以这种方式启动后表现出的所有行为都是正常的除了用户通常可访问的网络位置(即如果应用程序从会话1启动)不再可用
所以我的问题是:
-
如果我已经为正确登录到会话1的用户复制了令牌,为什么当应用程序使用此令牌启动时,它不能访问该用户通常可以访问的网络位置中的数据?
-
除了使用CreateProcessAsUser函数外,我是否缺少恢复网络可访问性的额外步骤?
注意:我特别选择了CreateProcessAsUser(与CreateProcessWithLogonW等其他选择相反),以避免必须在源代码中维护和传递用户凭据。
有什么建议吗?
下面是我用来作为会话1用户从Windows服务启动应用程序的源代码:
BOOL LaunchAppIntoDifferentSession(char *AtsAppPathName)
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL bResult = FALSE;
DWORD dwSessionId;
DWORD winlogonPid = 0;
HANDLE hUserToken,hUserTokenDup,hPToken,hProcess;
DWORD dwCreationFlags;
// Log the client on to the local computer.
dwSessionId = WTSGetActiveConsoleSessionId();
// Find the winlogon process
PROCESSENTRY32 procEntry;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE)
{
return 1 ;
}
procEntry.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hSnap, &procEntry))
{
CloseHandle(hSnap);
return 1 ;
}
do
{
if (stricmp(procEntry.szExeFile, "winlogon.exe") == 0)
{
// found a winlogon process...make sure it's running in the console session
DWORD winlogonSessId = 0;
if (ProcessIdToSessionId(procEntry.th32ProcessID, &winlogonSessId) && winlogonSessId == dwSessionId)
{
winlogonPid = procEntry.th32ProcessID;
break;
}
}
} while (Process32Next(hSnap, &procEntry));
WTSQueryUserToken(dwSessionId,&hUserToken);
dwCreationFlags = NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb= sizeof(STARTUPINFO);
si.lpDesktop = "winsta0\default";
ZeroMemory(&pi, sizeof(pi));
TOKEN_PRIVILEGES tp;
LUID luid;
hProcess = OpenProcess(MAXIMUM_ALLOWED,FALSE,winlogonPid);
if(!OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY
|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY|TOKEN_ADJUST_SESSIONID
|TOKEN_READ|TOKEN_WRITE,&hPToken))
{
sprintf(gTempBuf, "Process token open Error: %u",GetLastError());
if(LOG_ERR==1) WriteToLog("gTempBuf");
}
if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))
{
sprintf(gTempBuf, "Lookup Privilege value Error: %u",GetLastError());
if(LOG_ERR==1) WriteToLog("gTempBuf");
}
tp.PrivilegeCount =1;
tp.Privileges[0].Luid =luid;
tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;
if(!DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityImpersonation,TokenPrimary,&hUserTokenDup))
{
sprintf(gTempBuf, "DuplicateTokenEx return Error: %u",GetLastError());
if(LOG_ERR==1) WriteToLog("gTempBuf");
}
//Adjust Token privilege
SetTokenInformation(hUserTokenDup,TokenSessionId,(void*)dwSessionId,sizeof(DWORD));
if (!AdjustTokenPrivileges(hUserTokenDup,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),(PTOKEN_PRIVILEGES)NULL,NULL))
{
sprintf(gTempBuf, "Adjust Privilege value Error: %u",GetLastError());
if(LOG_ERR==1) WriteToLog("gTempBuf");
}
if (GetLastError()== ERROR_NOT_ALL_ASSIGNED)
{
sprintf(gTempBuf, "Token does not have the privilege");
if(LOG_ERR==1) WriteToLog("gTempBuf");
}
LPVOID pEnv =NULL;
if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))
{
dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT;
}
else
pEnv=NULL;
// Launch the process in the client's logon session.
bResult = CreateProcessAsUser(
hUserTokenDup, // client's access token
AtsAppPathName, // file to execute
NULL, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
// End impersonation of client.
//Perform All the Close Handles task
CloseHandle(hProcess);
CloseHandle(hUserToken);
CloseHandle(hUserTokenDup);
CloseHandle(hPToken);
CloseHandle(hSnap);
//return iResultOfCreateProcessAsUser;
return 0;
}
是否使用映射驱动器访问这些网络位置?
映射的驱动器以登录会话存储。由于用户的配置文件没有加载,他们将无法访问他们的"映射驱动器"。
您可以使用完整的UNC路径。
问题是您正在获得管理(提升)令牌的副本。这可能是有意为之,因为您启用了调试权限。不幸的是,用户的提升令牌与过滤令牌不在同一个登录会话中,因此没有任何用户的映射驱动器。
更多信息,包括各种解决方案,可以在MSDN文章"如果UAC启用,将文件从映射驱动器复制到本地目录失败"中找到"位置不可用"。通常最简单的解决方案是使用UNC路径或根据需要重新映射驱动器。