我在 Windows 8 中面临一个问题,其中模拟登录用户的提升的应用程序/服务无法正确识别映射的驱动器路径。
我有一个 Windows 服务,用于将文件从 / 复制到不同的源路径/目标,包括映射的网络驱动器。路径通过 xml 文件提供给服务。然后服务从 xml 读取源和目标并复制文件。我从来没有遇到过在 Vista 和 7 中映射驱动器的问题,因为该服务总是通过获取资源管理器令牌和所有 my CreateFile
, ReadFile
s来模拟登录的用户,并且WriteFile
运行良好。
这就是我模仿用户的方式
首先,我使用以下代码获取会话令牌
DWORD GetActiveSessionId(DWORD& ret)
{
ret=0;
DWORD active_session_id = WTSGetActiveConsoleSessionId();
if (IsSessionActive(active_session_id))
{
return active_session_id;
}
DWORD console_session_ID = active_session_id;
active_session_id = -2;
WTS_SESSION_INFO* session_info = NULL;
DWORD num_sessions = 0;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1,
&session_info, &num_sessions))
{
// Pick the first active session we can find
for (DWORD i = 0 ; i < num_sessions; ++i)
{
if (session_info[i].State == WTSActive)
{
// There is a user logged on to the WinStation associated with the
// session.
active_session_id = session_info[i].SessionId;
break;
}
}
WTSFreeMemory(session_info);
return active_session_id;
}
ret=::GetLastError();
return -2;
}
BOOL GetSessionUserToken( HANDLE * phUserToken, DWORD& retCode )
{
if( NULL == phUserToken )
{
return FALSE;
}
BOOL bRet = FALSE;
HANDLE hImpersonationToken = NULL;
BOOL bWin2K = FALSE;
OSVERSIONINFOEX osv;
ZeroMemory( & osv, sizeof( OSVERSIONINFOEX ) );
osv.dwOSVersionInfoSize = sizeof( OSVERSIONINFOEX );
if( GetVersionEx( (OSVERSIONINFO*) & osv ) )
{
if( 0 == osv.dwMinorVersion && osv.dwMajorVersion == 5)
{
return FALSE;
}
}
DWORD dwActiveSession= CGSSystem::GetActiveSessionId(retCode);
if (dwActiveSession==GSInvalidSessionId)
return FALSE;
if( 0 != WTSQueryUserToken( dwActiveSession, & hImpersonationToken ) )
{
bRet = TRUE;
}
else
{
}
DWORD neededSize = 0;
HANDLE *realToken = new HANDLE;
if(GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize))
{
CloseHandle(hImpersonationToken);
hImpersonationToken = *realToken;
}
DWORD lastError = GetLastError();
delete realToken;
if( TRUE == bRet )
{
bRet = DuplicateTokenEx( hImpersonationToken,
0,
NULL,
SecurityImpersonation,
TokenPrimary,
phUserToken );
CloseHandle( hImpersonationToken );
}
return bRet;
}
然后我有我的CopyFile
函数,它是一个线程。这是一个巨大的功能,所以我只会提到重要的(模拟/安全)部分。
BOOL CopyFile(LPCTSTR source, LPCTSTR destination)
{
//Some variables initializations
//...
HRESULT hrInternal = CoInitializeSecurity(
NULL, // Allow *all* VSS writers to communicate back!
-1, // Default COM authentication service
NULL, // Default COM authorization service
NULL, // reserved parameter
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // Strongest COM authentication level
RPC_C_IMP_LEVEL_IDENTIFY, // Minimal impersonation abilities
NULL, // Default COM authentication settings
EOAC_NONE, // No special options
NULL // Reserved parameter
);
//Initialize security descriptors
SECURITY_DESCRIPTOR SD;
SECURITY_ATTRIBUTES copyMutexAttrib;
copyMutexAttrib.nLength = sizeof( SECURITY_ATTRIBUTES );
copyMutexAttrib.lpSecurityDescriptor = & SD;
copyMutexAttrib.bInheritHandle = TRUE;
if(!InitializeSecurityDescriptor( & SD, SECURITY_DESCRIPTOR_REVISION ) )
{
//Error handling;
}
// add a NULL disc. ACL to the security descriptor.
//
if( ! SetSecurityDescriptorDacl( & SD, TRUE, (PACL) NULL, FALSE ) )
{
//Error handling;
}
HRESULT hr=S_OK;
hr=ModifyThreadPrivilege( SE_BACKUP_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=S_OK;
hr=ModifyThreadPrivilege( SE_TCB_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=ModifyThreadPrivilege( SE_IMPERSONATE_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=ModifyThreadPrivilege( SE_MANAGE_VOLUME_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=ModifyThreadPrivilege( SE_SYSTEM_PROFILE_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
hr=ModifyThreadPrivilege( SE_DEBUG_NAME, TRUE , m_hUserToken==NULL ? FALSE : TRUE );
if (FAILED(hr))
{
//Error Handling and logs
}
//Other variable initializations
//...
//Create the destination file
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE hFile = ::CreateFile(destination, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, &sa,
CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH|FILE_FLAG_BACKUP_SEMANTICS, NULL); //---> creates the file in the wrong location
}
这是我的 ModifyThreadPrivilage 代码:
HRESULT ModifyThreadPrivilege(IN LPCTSTR szPrivilege,IN BOOL fEnable,IN BOOL OpenAsSelf)
{
HRESULT hr = S_OK;
TOKEN_PRIVILEGES NewState;
LUID luid;
HANDLE hToken = NULL;
// Open the process token for this process.
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
OpenAsSelf,
&hToken ))
{
int iLast=::GetLastError();
if (iLast != ERROR_NO_TOKEN)
{
return ERROR_FUNCTION_FAILED;
}
/*
* No access token for the thread so impersonate the security context
* of the process.
*/
if (!ImpersonateSelf(SecurityImpersonation))
{
return ERROR_FUNCTION_FAILED;
}
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
FALSE,
&hToken))
{
return ERROR_FUNCTION_FAILED;
}
}
// Get the local unique ID for the privilege.
if ( !LookupPrivilegeValue( NULL,
szPrivilege,
&luid ))
{
CloseHandle( hToken );
printf("Failed LookupPrivilegeValue\n");
return ERROR_FUNCTION_FAILED;
}
// Assign values to the TOKEN_PRIVILEGE structure.
NewState.PrivilegeCount = 1;
NewState.Privileges[0].Luid = luid;
NewState.Privileges[0].Attributes =
(fEnable ? SE_PRIVILEGE_ENABLED : 0);
// Adjust the token privilege.
if (!AdjustTokenPrivileges(hToken,
FALSE,
&NewState,
0,
NULL,
NULL))
{
hr = ERROR_FUNCTION_FAILED;
}
// Close the handle.
CloseHandle(hToken);
return hr;
}
在 Windows 8 中,当目标是映射驱动器时,"Z:\MyFile.txt"
它会将文件写入错误的位置,如下所示:
我已经映射了
Z:
映射到\\nsa\public\myfolder1\subfolder\
函数的网络驱动器将文件写入\\nsa\public\
我在 Windows Vista 或 7 中从未遇到过此类行为,但似乎 MS 引入了一些导致此类行为的新特权或证券。
我注意到很多人抱怨 Windows 8 中的映射驱动器,特别是对于提升的进程,但所有解决方案都建议使用 UNC 路径而不是映射驱动器号。
我还注意到启用/禁用 UAC 对此没有影响。
有人可以解释我如何实现复制文件的目标吗?