如何正确地模拟用户的服务?

时间:2013-11-05 18:44:55

标签: c++ windows service impersonation

我正在使用一项服务,该服务应模拟已登录的用户。

到目前为止我的代码,基本错误处理:

 // 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函数。

如果我现在使用我的代码,那么它的工作原理。但是它有点奇怪,因为首先我必须冒充登录用户,然后结束模拟,加载用户配置文件。如果我没有结束模拟,那么LoadUserProfile将失败,错误5(访问被拒绝)。在LoadUserProfile成功之后,我应该再次冒充用户吗?

所以我的问题是,这意味着这样做,或者我做错了什么? 另一个问题是,如果LoadUserProfile成功,我可以使用hProfile作为登录用户注册表的句柄。问题是怎么样的?因为要使用RegOpenKeyEy和RegSetValueEx,我需要传递HKEY,而不是HANDLE。那么我该如何使用这个Handle?

感谢!

1 个答案:

答案 0 :(得分:7)

由于您将用户的令牌传递给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树的已打开HKEY_CURRENT_USER。在将它传递给Registry API函数时,Simpy将其从HANDLE类型转换为HKEY。它已经打开,因此您无需再次调用RegOpenKeyEx()来打开相同的密钥,但在创建/打开子密钥或在根密钥中读取/写入值时,可以将其用作根密钥。 / p>

相关问题