9

ログオンしているユーザーを偽装する必要があるサービスを使用しています。

これまでの私のコード、基本的なエラー処理:

 // 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 メンバーとして指定します。ユーザーのローミング プロファイル パスを取得するには、情報レベル 3 または 4 を指定して NetUserGetInfo 関数を呼び出します。

正常に返されると、PROFILEINFO の hProfile メンバーは、ユーザーのハイブのルートに対して開かれたレジストリ キー ハンドルになります。フルアクセス (KEY_ALL_ACCESS) で開かれています。ユーザーを偽装しているサービスがユーザーのレジストリ ファイルの読み取りまたは書き込みを行う必要がある場合は、HKEY_CURRENT_USER の代わりにこのハンドルを使用します。hProfile ハンドルを閉じないでください。代わりに、UnloadUserProfile 関数に渡します。

現在のコードをそのまま使用すると、機能します。ただし、最初にログオンしているユーザーを偽装し、次に偽装を終了して、ユーザー プロファイルを読み込む必要があるため、少し奇妙です。偽装を終了しないと、LoadUserProfile はエラー 5 (アクセスが拒否されました) で失敗します。LoadUserProfile が成功した後、ユーザーを再度偽装する必要がありますか?

だから私の質問は、これはこのように行われることを意図したものですか、それとも私は何か間違ったことをしていますか? もう 1 つの質問は、LoadUserProfile が成功した場合、ログオンしているユーザーのレジストリへのハンドルとして hProfile を使用できるということです。質問はどうですか?RegOpenKeyEy と RegSetValueEx を使用するには、HANDLE ではなく HKEY を渡す必要があるためです。では、このハンドルをどのように使用できますか?

感謝!

4

1 に答える 1

9

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ツリーに対して開かれています。レジストリ API 関数に渡すときに、単純HANDLEに型キャストします。HKEYすでに開かれているので、再度同じキーを開くために呼び出す必要はありませんが、RegOpenKeyEx()サブキーを作成/開くとき、またはルート キーの値を読み書きするときにルート キーとして使用できます。

于 2013-11-05T19:07:38.693 に答える