2016-07-28 82 views
0

我有一段如下代码在我的系统级Windows服务中运行。如果我将CreateEnvironmentBlock()和CreateProcessAsUser()添加到代码中,那可行。但是我使用“LoadUserProfile()”,它会失败,错误5应该表示“访问被拒绝”。请帮助检查缺少的内容。我想要的是从该系统服务中检索用户级别的注册表值。代码中的注释是另一种方式,但也无法检索用户级别的注册表值。为什么LoadUserProfile()在系统服务中运行的代码中出现错误5“拒绝访问”失败?

void GetUserRegistry() 
{ 
#ifdef Q_OS_WIN 

    DWORD lastError = 0; 

    DWORD sessionId = WTSGetActiveConsoleSessionId(); 
    qInfo() << "Session ID = " << sessionId; 

    wchar_t* ppUserName[100]; 
    DWORD sizeOfUserName; 
    WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, ppUserName, &sizeOfUserName); 
    qInfo() << "Windows User Name = " << QString::fromWCharArray(*ppUserName); 

    std::wstring strValueOfBinDir = L"Unknown Value"; 
    // LONG regOpenResult = ERROR_SUCCESS; 

    HANDLE hUserToken = NULL; 
    HANDLE hFakeToken = NULL; 

    if (WTSQueryUserToken(sessionId, &hUserToken)) 
    { 
     if (DuplicateTokenEx(hUserToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &hFakeToken) == TRUE) 
     { 
      qInfo() << "Before ImpersonateLoggedOnUser()......"; 
      if (ImpersonateLoggedOnUser(hFakeToken)) 
      { 
       HKEY hKey; 

       // regOpenResult = RegOpenCurrentUser(KEY_READ, &hKey); 

       PROFILEINFO profileInfo; 
       ZeroMemory(&profileInfo, sizeof(PROFILEINFO)); 
       profileInfo.dwSize = sizeof(PROFILEINFO); 
       profileInfo.lpUserName = *ppUserName; 
       // wchar_t roamingPath[] = L"C:\\Users\\Finix"; // L"C:\\Users\\Finix\\AppData\\Roaming"; 
       // profileInfo.lpProfilePath = roamingPath; 

       if (LoadUserProfile(hFakeToken, &profileInfo)) 
       { 
        HANDLE hProfile = profileInfo.hProfile; 

        RegOpenKeyEx(HKEY_CURRENT_USER, 
           TEXT("Software\\Baidu\\BaiduYunGuanjia"), 
           0, 
           KEY_READ, 
           &hKey); 

        GetStringRegKey(hKey, TEXT("installDir"), strValueOfBinDir, TEXT("Unknown")); 

        UnloadUserProfile(hFakeToken, hProfile); 
       } 
       else 
       { 
        lastError = GetLastError(); 
       } 

       RevertToSelf(); 

      } 
      else 
      { 
       qCritical() << "Failed to ImpersonateLoggedOnUser..."; 
      } 

      CloseHandle(hFakeToken); 
     } 
     else 
     { 
      qCritical() << "Failed to call DuplicateTokenEx..."; 
     } 

     CloseHandle(hUserToken); 
    } 
    else 
    { 
     qCritical() << "Failed to get the user token of session " << sessionId; 
    } 

    if (lastError) 
    { 
     qCritical() << "Failed to LoadUserProfile(), The ERROR is " << lastError; 
    } 

// if (regOpenResult != ERROR_SUCCESS) 
// { 
//  qCritical() << "Failed to call RegOpenCurrentUser(), Error is " << regOpenResult; 
// } 

    qInfo() << "The value of Registry is " << QString::fromWCharArray(strValueOfBinDir.c_str()); 

#endif 
} 
+0

简单,调用的是:WTSGetActiveConsoleSessionId()+ WTSQueryUserToken()+ DuplicateTokenEx()+ ImpersonateLoggedOnUser()+ LoadUserProfile()。为什么LoadUserProfile()失败并出现错误5?在调用LoadUserProfile()之前,应该做什么?我已经做了一些上面显示的代码。我错过了什么? – Finix

+0

您正在声明一个指向字符数组“wchar_t * ppUserName [100]”的指针。您必须将其更改为'wchar_t ppUserName [100];'但可能存在其他错误。 –

+0

@BarmakShemirani上面的代码是正确的。 WTSQuerySessionInformation()的第四个参数是“LPWSTR *”,所以它是一个指向者的指向者。而且,正如我所提到的,如果将LoadUserProfile()更改为“CreateEnvironmentBlock()+ CreateProcessAsUser()”,此程序将起作用。这意味着所有其余的部分都是好的,但对LoadUserProfile()的调用失败。 – Finix

回答

3

的原因,你不能呼叫LoadUserProfile是你正在模拟的用户。 LoadUserProfile需要管理员权限,即您应该在您自己的环境中调用它,而不是在模拟的环境中。这不是用户加载自己的配置文件的功能,通常系统会以用户的名义调用它。

不应该的原因请致电LoadUserProfile是用户已经登录,所以配置文件已经被加载。我们知道有问题的用户已登录,因为WTSQueryUserToken不能用于获取不是用户的令牌。

(嗯,有可能是边缘的情况下,目前正在登录的用户打开或关闭,但调用登录期间LoadUserProfile功能/注销是在任何情况下可能是不明智的。)

如果你只是想打开登录用户的注册表配置单元,使用RegOpenCurrentUser。 (请注意,您要使用模拟使用此功能,如记录。)

+0

我注意到,你说你已经尝试过'RegOpenCurrentUser',它不起作用。我建议你发布一个关于该问题的单独问题,包括代码和关于它如何失败的详细描述。 –

+1

参见https://blogs.msdn.microsoft.com/oldnewthing/20141121-00/?p=43563/ –

+0

感谢您的建议@HarryJohnston。它调整了RegOpenKeyEx()的某些参数后工作。对于你附加的博客,我也做了一个测试。它看起来像博客所说的不正确。经过亵渎后,我可以获得活跃用户的一个特殊用户注册表配置单元;然后,在模仿结束后,我再次调用代码,它不能再获得相同的配置单元。 – Finix

相关问题