如何从服务中正确模拟用户?

我正在工作的服务,应该模仿login的用户。

我的代码到目前为止,基本的error handling:

// 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 stuff if ( !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:

当用户以交互方式login时,系统会自动加载用户的configuration文件。 如果服务或应用程序模拟用户,则系统不会加载用户的configuration文件。 因此,服务或应用程序应该用LoadUserProfile加载用户的configuration文件。

调用LoadUserProfile的服务和应用程序应检查用户是否有漫游configuration文件。 如果用户具有漫游configuration文件,请将其path指定为PROFILEINFO的lpProfilePath成员。 要检索用户的漫游configuration文件path,可以调用NetUserGetInfo函数,指定信息级别3或4。

成功返回后,PROFILEINFO的hProfile成员是一个向用户的configuration单元的根打开的registry项句柄。 它已被完全访问(KEY_ALL_ACCESS)打开。 如果模拟用户的服务需要读取或写入用户的registry文件,请使用此句柄而不是HKEY_CURRENT_USER。 不要closureshProfile句柄。 而是将其传递给UnloadUserProfile函数。

如果我现在使用我的代码,那么它的工作原理。 不过有点奇怪,因为首先我要模拟login的用户,然后结束模拟,加载用户configuration文件。 如果我不结束模拟,则LoadUserProfile将失败,并显示错误5(拒绝访问)。 LoadUserProfile成功后,我应该再次冒充用户?

所以我的问题是,这意味着要这样做,或者我做错了什么? 另一个问题是,如果LoadUserProfile成功,我可以使用hProfile作为处理login用户registry。 问题是如何? 因为要使用RegOpenKeyEy和RegSetValueEx我需要通过一个HKEY,而不是一个句柄。 那么我怎样才能使用这个句柄?

谢谢!

您不需要调用ImpersonateLoggedOnUser()因为您将用户令牌传递给LoadUserProfile() 。 仅当您需要调用不允许您将用户令牌传递给它们的API时,才能调用ImpersonateLoggedOnUser()

如果您阅读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_CURRENT_USER树的打开的HKEY 。 在将它传递给注册表API函数时,将其从HANDLE类型转换为HKEY类型。 它已经被打开了,所以你不需要调用RegOpenKeyEx()来再次打开同一个键,但是当你创建/打开子键,或者读取/写入根键的值时,你可以使用它作为根键。