Я работаю в сервисе, который должен выдавать себя за зарегистрированного пользователя.
Мой код до сих пор, с основной обработкой ошибок:
// get the active console session ID of the logged on user
if ( !WTSQueryUserToken( WTSGetActiveConsoleSessionId(), &hToken ) )
{
ShowErrorText( "WTSQueryUserToken failed.", GetLastError( ), true );
return;
}
HANDLE hDuplicated;
// duplicate the token
if ( !DuplicateToken( hToken, SecurityImpersonation, &hDuplicated ) )
{
ShowErrorText( "DuplicateToken failed.", GetLastError( ), true );
}
else
{
ShowErrorText( "DuplicateToken succeeded.", 0, true );
}
// impersonate the logged on user
if ( !ImpersonateLoggedOnUser( hToken ) )
{
ShowErrorText( "ImpersonateLoggedOnUser failed.", GetLastError(), true );
return;
}
// retrieve the DC name
if ( !GetPrimaryDC( DC ) )
{
ShowErrorText( "GetPrimaryDC failed.", 0, true );
}
PROFILEINFO lpProfileInfo;
ZeroMemory( &lpProfileInfo, sizeof( PROFILEINFO ) );
lpProfileInfo.dwSize = sizeof( PROFILEINFO );
lpProfileInfo.lpUserName = CurrentUser;
// get type of profile. roaming, mandatory or temporary
int ret = GetTypeOfProfile();
if ( ret == 2 )
{
// if roaming profile get the path of it
if ( !GetRoamingProfilePath( DC, CurrentUser, RoamingProfilePath ) )
{
ShowErrorText( "Failed to retrieve roaming profile path.", GetLastError(), true );
}
}
if ( RevertToSelf( ) )
{
ShowErrorText( "Impersonation ended successfully.", 0, true );
}
if ( !LoadUserProfile( hDuplicated, &lpProfileInfo ) )
{
ShowErrorText( "LoadUserProfile failed.", GetLastError(), true );
}
else
{
ShowErrorText( "LoadUserProfile succeeded.", 0, true );
}
//do some stuffif ( !UnloadUserProfile( hDuplicated, lpProfileInfo.hProfile ) )
{
ShowErrorText( "UnloadUserProfile failed.", GetLastError( ), true );
}
else
{
ShowErrorText( "UnloadUserProfile succeeded.", 0, true );
}
if ( !ImpersonateLoggedOnUser( hToken ) )
{
ShowErrorText( "ImpersonateLoggedOnUser failed.", GetLastError( ), true );
return;
}
По данным MSDN:
Когда пользователь входит в систему в интерактивном режиме, система автоматически загружает профиль пользователя. Если служба или приложение олицетворяет пользователя, система не загружает профиль пользователя. Поэтому служба или приложение должны загрузить профиль пользователя с помощью LoadUserProfile.
Службы и приложения, которые вызывают LoadUserProfile, должны проверить, есть ли у пользователя профиль в роуминге. Если у пользователя есть перемещаемый профиль, укажите его путь в качестве члена lpProfilePath в PROFILEINFO. Чтобы получить путь к перемещаемому профилю пользователя, вы можете вызвать функцию NetUserGetInfo, указав уровень информации 3 или 4.
После успешного возврата член hProfile в PROFILEINFO является дескриптором ключа реестра, открытым для корня куста пользователя. Он был открыт с полным доступом (KEY_ALL_ACCESS). Если службе, которая олицетворяет пользователя, необходимо выполнить чтение или запись в файл реестра пользователя, используйте этот дескриптор вместо HKEY_CURRENT_USER. Не закрывайте ручку hProfile. Вместо этого передайте его в функцию UnloadUserProfile.
Если я использую свой код, как сейчас, то он работает. Однако это немного странно, потому что сначала я должен выдать себя за вошедшего в систему пользователя, а затем прекратить олицетворение, чтобы загрузить профиль пользователя. Если я не заканчиваю олицетворение, тогда LoadUserProfile завершится с ошибкой 5 (доступ запрещен). И после успешного завершения LoadUserProfile я должен снова выдать себя за пользователя?
Итак, мой вопрос, это должно быть сделано таким образом, или я делаю что-то не так?
Другой вопрос заключается в том, что если LoadUserProfile преуспел, я мог бы использовать hProfile в качестве дескриптора для реестра пользователей, вошедших в систему. Вопрос как? Потому что для использования RegOpenKeyEy и RegSetValueEx мне нужно передать HKEY, а не HANDLE. Так как я могу использовать эту ручку?
Поблагодарить!
Вам не нужно звонить ImpersonateLoggedOnUser()
так как вы передаете токен пользователя LoadUserProfile()
, Вызов ImpersonateLoggedOnUser()
только если вам нужно вызывать API, которые не позволяют вам передавать пользовательский токен.
Если вы читаете остальную часть LoadUserProfile()
В документации сказано:
Вызывающий процесс должен иметь привилегии SE_RESTORE_NAME и SE_BACKUP_NAME.
Выдавая себя за пользователя, для которого вы пытаетесь загрузить профиль, вы, вероятно, теряете эти привилегии. Так что не выдавайте себя за пользователя.
Обновить: Попробуйте что-то вроде этого:
// get the active console session ID of the logged on user
DWORD dwSessionID = WTSGetActiveConsoleSessionId();
if ( dwSessionID == 0xFFFFFFFF )
{
ShowErrorText( "WTSGetActiveConsoleSessionId failed.", GetLastError( ), true );
return;
}
if ( !WTSQueryUserToken( dwSessionID, &hToken ) )
{
ShowErrorText( "WTSQueryUserToken failed.", GetLastError( ), true );
return;
}
// duplicate the token
HANDLE hDuplicated = NULL;
if ( !DuplicateToken( hToken, SecurityImpersonation, &hDuplicated ) )
{
ShowErrorText( "DuplicateToken failed.", GetLastError( ), true );
CloseHandle( hToken );
return;
}
// retrieve the DC name
if ( !GetPrimaryDC( DC ) )
{
ShowErrorText( "GetPrimaryDC failed.", 0, true );
CloseHandle( hDuplicated );
CloseHandle( hToken );
return;
}
PROFILEINFO lpProfileInfo;
ZeroMemory( &lpProfileInfo, sizeof( PROFILEINFO ) );
lpProfileInfo.dwSize = sizeof( PROFILEINFO );
lpProfileInfo.lpUserName = CurrentUser;
// get type of profile. roaming, mandatory or temporary
USER_INFO_4 *UserInfo = NULL;
int ret = GetTypeOfProfile();
if ( ret == 2 )
{
// if roaming profile get the path of it
if ( NetUserGetInfo( DC, CurrentUser, 4, (LPBYTE*)&UserInfo) != NERR_Success )
{
ShowErrorText( "NetUserGetInfo failed.", 0, true );
CloseHandle( hDuplicated );
CloseHandle( hToken );
return;
}
lpProfileInfo.lpProfilePath = UserInfo->usri3_profile;
}
if ( !LoadUserProfile( hDuplicated, &lpProfileInfo ) )
{
ShowErrorText( "LoadUserProfile failed.", GetLastError(), true );
if ( UserInfo )
NetApiBufferFree(UserInfo);
CloseHandle( hDuplicated );
CloseHandle( hToken );
return;
}
if ( UserInfo )
NetApiBufferFree(UserInfo);
ShowErrorText( "LoadUserProfile succeeded.", 0, true );
//do some stuff
if ( !UnloadUserProfile( hDuplicated, lpProfileInfo.hProfile ) )
{
ShowErrorText( "UnloadUserProfile failed.", GetLastError( ), true );
}
else
{
ShowErrorText( "UnloadUserProfile succeeded.", 0, true );
}
CloseHandle( hDuplicated );
CloseHandle( hToken );
Что касается реестра, то hProfile
ручка открыта HKEY
для пользователя HKEY_CURRENT_USER
дерево. Симпи набрать его из HANDLE
в HKEY
при передаче его в функции реестра API. Он уже открыт, поэтому вам не нужно звонить RegOpenKeyEx()
чтобы снова открыть тот же ключ, но вы можете использовать его в качестве корневого ключа при создании / открытии подразделов или чтении / записи значений в корневом ключе.
Других решений пока нет …