简体   繁体   English

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

[英]How to impersonate a user from a service correctly?

I'm working a service, which should impersonate the logged on user. 我正在使用一项服务,该服务应模拟已登录的用户。

My code so far, with basic 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;
}

According to MSDN: 根据MSDN:

When a user logs on interactively, the system automatically loads the user's profile. 当用户以交互方式登录时,系统会自动加载用户的配置文件。 If a service or an application impersonates a user, the system does not load the user's profile. 如果服务或应用程序模拟用户,则系统不会加载用户的配置文件。 Therefore, the service or application should load the user's profile with LoadUserProfile. 因此,服务或应用程序应使用LoadUserProfile加载用户的配置文件。

Services and applications that call LoadUserProfile should check to see if the user has a roaming profile. 调用LoadUserProfile的服务和应用程序应检查用户是否具有漫游配置文件。 If the user has a roaming profile, specify its path as the lpProfilePath member of PROFILEINFO. 如果用户具有漫游配置文件,请将其路径指定为PROFILEINFO的lpProfilePath成员。 To retrieve the user's roaming profile path, you can call the NetUserGetInfo function, specifying information level 3 or 4. 要检索用户的漫游配置文件路径,可以调用NetUserGetInfo函数,指定信息级别3或4。

Upon successful return, the hProfile member of PROFILEINFO is a registry key handle opened to the root of the user's hive. 成功返回后,PROFILEINFO的hProfile成员是一个向用户配置单元的根目录打开的注册表键句柄。 It has been opened with full access (KEY_ALL_ACCESS). 它已以完全访问权限(KEY_ALL_ACCESS)打开。 If a service that is impersonating a user needs to read or write to the user's registry file, use this handle instead of HKEY_CURRENT_USER. 如果模拟用户的服务需要读取或写入用户的注册表文件,请使用此句柄而不是HKEY_CURRENT_USER。 Do not close the hProfile handle. 不要关闭hProfile句柄。 Instead, pass it to the UnloadUserProfile function. 而是将其传递给UnloadUserProfile函数。

If i use my code as it is now, then it works. 如果我现在使用我的代码,那么它的工作原理。 However is it a little strange, because first i have to impersonate the logged on user, and then end the impersonation, to Load the users profile. 但是它有点奇怪,因为首先我必须冒充登录用户,然后结束模拟,加载用户配置文件。 If i don't end the impersonation then LoadUserProfile will fail with error 5 ( Access denied ). 如果我没有结束模拟,那么LoadUserProfile将失败,错误5(访问被拒绝)。 And after LoadUserProfile succeeded i should impersonate the user again? 在LoadUserProfile成功后,我应该再次冒充用户?

So my question is, this meant to be done this way, or i am doing something wrong? 所以我的问题是,这意味着这样做,或者我做错了什么? Another question is, that if LoadUserProfile succeeded i could use hProfile as a Handle to the logged on users registry. 另一个问题是,如果LoadUserProfile成功,我可以使用hProfile作为登录用户注册表的句柄。 Question is how? 问题是怎么样的? Because to use RegOpenKeyEy and RegSetValueEx i need to pass a HKEY, not a HANDLE. 因为要使用RegOpenKeyEy和RegSetValueEx,我需要传递HKEY,而不是HANDLE。 So how can i use this Handle? 那么我该如何使用这个Handle?

Thank! 谢谢!

You don't need to call ImpersonateLoggedOnUser() since you are passing the user's token to LoadUserProfile() . 您不需要调用ImpersonateLoggedOnUser()因为您将用户的令牌传递给LoadUserProfile() Call ImpersonateLoggedOnUser() only if you need to call APIs that do not allow you to pass a user token to them. 仅当您需要调用不允许您向其传递用户令牌的API时,才调用ImpersonateLoggedOnUser()

If you read the rest of the LoadUserProfile() documentation, it says: 如果您阅读了LoadUserProfile()文档的其余部分,它会说:

The calling process must have the SE_RESTORE_NAME and SE_BACKUP_NAME privileges. 调用进程必须具有SE_RESTORE_NAME和SE_BACKUP_NAME权限。

By impersonating the user you are trying to load a profile for, you are likely losing those privileges. 通过冒充您尝试加载配置文件的用户,您可能会失去这些权限。 So don't impersonate the user. 所以不要冒充用户。

Update: Try something like this: 更新:尝试这样的事情:

// 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 );

As for the Registry, the hProfile handle is the opened HKEY for the user's HKEY_CURRENT_USER tree. 对于Registry, hProfile句柄是用户的HKEY_CURRENT_USER树的已打开HKEY Simpy type-cast it from HANDLE to HKEY when passing it to Registry API functions. 当将它传递给Registry API函数时,Simpy将它从HANDLE类型转换为HKEY It is already opened, so you do not need to call RegOpenKeyEx() to open that same key again, but you can use it as the root key when creating/opening subkeys, or reading/writing values in the root key. 它已经打开,因此您无需再次调用RegOpenKeyEx()来打开相同的密钥,但在创建/打开子密钥或在根密钥中读取/写入值时,可以将其用作根密钥。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用SYSTEM级别进程(Windows Service)中已登录的用户上下文模拟并运行任何方法: - Impersonate and run any method using a logged in user context from SYSTEM level process (Windows Service): 模仿标准用户 - Impersonate standard user 如何冒充其他域用户,因为我拥有此AD域的管理员帐户? - How to impersonate another domain user as i have the administrator account of this AD domain? 如何从服务获取用户桌面的路径? - How to get paths to user desktops from a service? 从管理员帐户模拟SYSTEM(或等效) - Impersonate SYSTEM (or equivalent) from Administrator Account 如何从服务获取用户帐户的Windows特殊路径 - How to obtain Windows special paths for a user account from a service 我如何从用户获取输入到列表中<int>正确然后打印出大小和元素? - how do i get input from user into list<int> correctly and then print out the size and elements? C++/Linux如何检查SDP服务记录是否注册正确 - How to check that SDP service record was registered correctly in C++/Linux 提示用户时文件中的数据无法正确显示 - Data from file not displaying correctly when user is prompted 如何正确地让计算机猜测7个问题中的用户号码 - How to correctly get the computer to guess a user's number in 7 questions
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM