如何正确模拟服务中的用户
How to impersonate a user from a service correctly?
我正在开发一项服务,该服务应该模拟登录用户。
到目前为止,我的代码具有基本的错误处理:
// 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:
当用户以交互方式登录时,系统会自动加载用户的配置文件。如果服务或应用程序模拟用户,则系统不会加载用户的配置文件。因此,服务或应用程序应使用 LoadUserProfile 加载用户的配置文件。
调用 LoadUserProfile 的服务和应用程序应检查用户是否具有漫游配置文件。如果用户具有漫游配置文件,请将其路径指定为 PROFILEINFO 的 lpProfilePath 成员。若要检索用户的漫游配置文件路径,可以调用 NetUserGetInfo 函数,指定信息级别 3 或 4。
成功返回后,PROFILEINFO 的 hProfile 成员是打开到用户配置单元根目录的注册表项句柄。它已以完全访问权限 (KEY_ALL_ACCESS) 打开。如果模拟用户的服务需要读取或写入用户的注册表文件,请使用此句柄而不是HKEY_CURRENT_USER。不要关闭 hProfile 手柄。相反,将其传递给 UnloadUserProfile 函数。
如果我像现在这样使用我的代码,那么它可以工作。但是这有点奇怪,因为首先我必须模拟登录用户,然后结束模拟,以加载用户配置文件。如果我不结束模拟,则加载用户配置文件将失败并显示错误 5(拒绝访问)。在加载用户配置文件成功后,我应该再次模拟用户吗?
所以我的问题是,这意味着这样做,还是我做错了什么?另一个问题是,如果LoadUserProfile成功,我可以使用hProfile作为登录用户注册表的句柄。问题是怎么做?因为要使用RegOpenKeyEy和RegSetValueEx,我需要传递一个HKEY,而不是HANDLE。那么如何使用这个句柄呢?
谢谢!
您不需要调用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()
即可再次打开同一密钥,但在创建/打开子项或在根密钥中读取/写入值时,可以将其用作根密钥。
- Windows 后台服务(系统配置文件)无法使用 C++ 访问 win 10 上的用户 appData 文件夹?
- Win7 C++ - 从以用户身份登录的服务启动可执行文件的问题
- 如何从 Windows 上的服务在未登录用户的桌面上启动应用程序
- 从Windows上的系统工作的服务中获取用户文档文件夹.C++
- 如何在C 中使用我的服务来获取用户的SID和用户名
- 如何从服务获取用户桌面的路径
- 从 mfc 应用程序点击网络服务,并希望从网络服务登录方法读取用户代码返回
- 服务和用户模式进程之间的共享全局事件不起作用
- 如何正确模拟服务中的用户
- 服务中的 DDE 客户端与用户会话中的服务器通信
- 如何从作为服务运行的c++程序访问当前登录的用户信息
- 是否可以创建一个服务或应用程序,以编程方式在windows中登录用户
- Windows 7中的用户交互服务
- 如何授予用户对Web服务的权限
- 向多个自定义c++ BLE GATT服务添加特征用户描述
- 向自定义c++ BLE GATT服务添加特征用户描述
- 在终端服务服务器会话中存储用户的应用程序首选项。托管C++
- c++服务-模拟用户登录
- 用于启动和重新启动用户进程的Windows服务(带有GUI)
- 在自动化服务部署时设置用户服务运行的最佳实践是什么?