2016-03-07 8 views
0

У меня есть программа установки, которая пытается (повторно) запустить мое приложение в текущем пользовательском контексте после завершения установки. Установщик работает в контексте SYSTEM, и перед запуском приложения он пытается (и теоретически успешно) выдавать себя за текущего пользователя. Однако, когда я смотрю в диспетчере задач, я вижу, что мое приложение работает в контексте SYSTEM.ImpersonateLoggedOnUser успешно, но вторичный процесс все еще выполняется в начальном контексте

Это (отрывок из) мой код:

TCHAR szUsername[128] = _T(""); 
    DWORD dwUsernameSize = 128; 
    GetUserName(szUsername, &dwUsernameSize); 

    // Lets the calling process impersonate the security context of a logged-on user. 
    if (!ImpersonateLoggedOnUser(hToken)) 
    { 
     throw Win32Exception(GetLastError(), _T("Failed to impersonate current user")); 
    } 

    TCHAR szUsername2[128] = _T(""); 
    DWORD dwUsernameSize2 = 128; 
    GetUserName(szUsername2, &dwUsernameSize2); 

    MLOGD(_T("ProcessUtils::StartProcessInCurrentUserContext: Successfully impersonated %s"), szUsername2); 

    ProcessUtils::StartProcess(sExeName, lstParams, sWorkingDir, bWaitToFinish, errCode); 

ProcessUtils :: StartProcess является оберткой CreateProcess.

szUsername содержит SYSTEM, а szUsername2 содержит текущего пользователя. Таким образом ImpersonateLoggedOnUser успешно. Однако, как упоминалось выше, процесс запускается в контексте SYSTEM, а не в текущем пользовательском.

Я не уверен, насколько это полезно, но мой установщик написан в NSIS, и он вызывает функцию, которая содержит код сверху через плагин, написанный на C/C++.

Кто-нибудь знает, почему мое приложение не запускается в текущем контексте пользователя?

ответ

3

Win32 CreateProcess создает процесс в том же контекст безопасности, что и вызывающий, который является SYSTEM (даже при том, что вы олицетворяете себя).

Думайте, что вам нужно позвонить CreateProcessAsUser.

2

У меня была очень похожая проблема пару лет назад, когда я также был , работая над приложением установщика. После LOT разочарования вызвало неудачными попытками запустить приложение в контексте текущего пользователя с использованием CreateProcessAsUser, я, наконец, сдался. После тщательного поиска в Интернете я нашел реалистичную реализацию, которая использует интерфейс IShellDispatch2. Вот пример:

#include <Windows.h> 
#include <exdisp.h> 
#include <Shobjidl.h> 
#include <Shlwapi.h> 
#include <comutil.h> 
#include <SHLGUID.h> 
#include <cstdlib> 
#include <iostream> 

#pragma comment(lib, "Shlwapi.lib") 
#pragma comment(lib, "comsuppw.lib") 

bool ShellExecuteAsCurrentUser(const TCHAR *pcOperation, const TCHAR *pcFileName, const TCHAR *pcParameters, 
    const TCHAR *pcsDirectory, const DWORD dwShow) 
{ 
    bool bSuccess = false; 

    IShellWindows *psw = NULL; 
    HRESULT hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw)); 

    if(SUCCEEDED(hr)) 
    { 
     HWND hwnd = 0; 
     IDispatch* pdisp = NULL; 
     _variant_t vEmpty; 
     if(S_OK == psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, reinterpret_cast<long*>(&hwnd), SWFO_NEEDDISPATCH, &pdisp)) 
     { 
      if((hwnd != NULL) && (hwnd != INVALID_HANDLE_VALUE)) 
      { 
       IShellBrowser *psb; 
       hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb)); 
       if(SUCCEEDED(hr)) 
       { 
        IShellView *psv = NULL; 
        hr = psb->QueryActiveShellView(&psv); 
        if(SUCCEEDED(hr)) 
        { 
         IDispatch *pdispBackground = NULL; 
         HRESULT hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground)); 
         if(SUCCEEDED(hr)) 
         { 
          IShellFolderViewDual *psfvd = NULL; 
          hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd)); 
          if(SUCCEEDED(hr)) 
          { 
           IDispatch *pdisp = NULL; 
           hr = psfvd->get_Application(&pdisp); 
           if(SUCCEEDED(hr)) 
           { 
            IShellDispatch2 *psd; 
            hr = pdisp->QueryInterface(IID_PPV_ARGS(&psd)); 
            if(SUCCEEDED(hr)) 
            { 
             _variant_t verb(pcOperation); 
             _variant_t file(pcFileName); 
             _variant_t para(pcParameters); 
             _variant_t dir(pcsDirectory); 
             _variant_t show(dwShow); 
             if(SUCCEEDED(psd->ShellExecute(file.bstrVal, para, vEmpty, verb, show))) 
              bSuccess = true; 

             psd->Release(); 
             psd = NULL; 
            } 
            pdisp->Release(); 
            pdisp = NULL; 
           } 
          } 
          pdispBackground->Release(); 
          pdispBackground = NULL; 
         } 
         psv->Release(); 
         psv = NULL; 
        } 
        psb->Release(); 
        psb = NULL; 
       } 
      } 
      pdisp->Release(); 
      pdisp = NULL; 
     } 
     psw->Release(); 
     psw = NULL; 
    } 

    return bSuccess; 
} 

int main(int argc, char *argv[]) 
{ 
    CoInitialize(NULL); 

    if(ShellExecuteAsCurrentUser(L"open", L"notepad", nullptr, nullptr, SW_SHOWNORMAL)) 
     std::cout << "SUCCESS" << std::endl; 

    CoUninitialize(); 

    return 0; 
} 

Это просто быстрый демо, реализация ShellExecuteAsCurrentUser может быть улучшена за счет использования смарт-указатели для COM-интерфейсов и некоторые рефакторинга. Этот метод работал для меня на версии WinXP SP3 - Win 8.1, не уверен, если он работает на Windows, 10. Для получения более подробной информации , проверьте авторам GitHub страницы:

https://github.com/lordmulder/stdutils/tree/master/Contrib/StdUtils

+0

Wow! Это что-то. К счастью, CreateProcessAsUser работал для меня, но ваше решение хорошо знать, если что-то пойдет не так с подходом CreateProcessAsuser. – conectionist

+0

Будьте осторожны, чтобы не называть этот код слишком быстро при запуске. FindWindowSW вернет S_FALSE и вернет нулевой указатель, если он был вызван до того, как рабочий стол был запущен. Мы видим это много, когда новый пользователь входит в систему Windows 10. Для запуска рабочего стола требуется несколько минут. – tdemay

1

Если вы читали the documentation for CreateProcess, вам нашел бы ответ на ваш вопрос в первых трех предложениях:

Создает новый процесс и его основной поток. Новый процесс выполняется в контексте безопасности вызывающего процесса.

Если вызывающий процесс олицетворяет другого пользователя, новый процесс использует токен для вызывающего процесса, а не токен олицетворения.

На самом деле еще нечего сказать; описанное вами поведение является документированным.Если вы хотите создать процесс как другой пользователь, вы должны использовать CreateProcessAsUser или одну из связанных функций.