【问题标题】:Using IShellDispatch2->ShellExecute for launching a non-elevated process from an elevated process使用 IShellDispatch2->ShellExecute 从提升的进程启动非提升的进程
【发布时间】:2014-08-20 19:31:21
【问题描述】:

我有以下代码(主要部分取自 MS SDK v7.1 示例代码,它演示了如何从提升的进程启动非提升的进程)- 提升和非提升进程将在(无监督的)服务器上启动,因此用户交互是不可行的(当然,如果需要,配置部分除外)。

问题是,通过 IShellDispatch2->ShellExecute 启动的非提升进程仍然被提升(预期是非提升的)。 这已通过使用 IsUserAnAdmin() API 得到确认。 任何想法为什么通过 ShellExecute() 创建的进程确实仍然具有提升的权限?

一些更相关的细节:在我的机器上,UAC 被禁用。 基于 VS 2013 的应用程序表现在: ) (可以使用 /MANIFEST:no linker flag 禁用清单,如果它有助于解决这个问题)。 我正在使用 VS 2013 在 Windows 7 x64 上编译它。

#include <shlwapi.h>
#include <shlobj.h>
#include <comutil.h>
#pragma comment(lib, "shlwapi.lib")

// link with (at least) OleAut32.lib shlwapi.lib comsupp.lib shell32.lib uuid.lib
// sorry for the bad formatting 

int main(void)
{
std::wstring processName(L"myapp.exe"), processParams(L"-myAppParam");
LPWSTR processNamePtr = const_cast<LPWSTR>(processName.c_str());
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
  IShellView *psv;
  HRESULT hr = GetShellViewForDesktop(IID_PPV_ARGS(&psv));
  if (SUCCEEDED(hr))
  {
    IShellDispatch2 *psd;
    hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd));
    if (SUCCEEDED(hr))
    {
      BSTR bstrProcessName = SysAllocString(processNamePtr);
      hr = bstrProcessName ? S_OK : E_OUTOFMEMORY;
      if (SUCCEEDED(hr))
      {
        VARIANT vtEmpty = {}; // VT_EMPTY

        LPWSTR processParamsPtr = const_cast<LPWSTR>(processParams.c_str());
        _bstr_t bstrProcessParams(processParamsPtr);
        VARIANT varParams;
        varParams.vt = VT_BSTR;
        varParams.bstrVal = bstrProcessParams;

        char processDir[MAX_PATH + 1];
        ::GetCurrentDirectory(MAX_PATH, processDir);
        _bstr_t bstrProcessDir(processDir);
        VARIANT varProcessDir;
        varProcessDir.vt = VT_BSTR;
        varProcessDir.bstrVal = bstrProcessDir;

        _bstr_t bstrOperation("open");
        VARIANT varOperation;
        varOperation.vt = VT_BSTR;
        varOperation.bstrVal = bstrOperation;

        hr = psd->ShellExecute(bstrProcessName, 
          varParams, // reinterpret_cast<_variant_t&>(bstrProcessParams), 
          varProcessDir,
          varOperation,
          vtEmpty);
        SysFreeString(bstrProcessName);
        SysFreeString(bstrProcessParams);
      }
      psd->Release();
    }
    psv->Release();
  }

  CoUninitialize();
}

} // main()

// use the shell view for the desktop using the shell windows automation to find the
// desktop web browser and then grabs its view
//
// returns:
//      IShellView, IFolderView and related interfaces

HRESULT GetShellViewForDesktop(REFIID riid, void **ppv)
{
  *ppv = NULL;

  IShellWindows *psw;
  HRESULT hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
  if (SUCCEEDED(hr))
  {
    HWND hwnd;
    IDispatch* pdisp;
    VARIANT vEmpty = {}; // VT_EMPTY
    if (S_OK == psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd,    SWFO_NEEDDISPATCH, &pdisp))
    {
      IShellBrowser *psb;
      hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
      if (SUCCEEDED(hr))
      {
        IShellView *psv;
        hr = psb->QueryActiveShellView(&psv);
    if (SUCCEEDED(hr))
    {
      hr = psv->QueryInterface(riid, ppv);
      psv->Release();
    }
    psb->Release();
  }
  pdisp->Release();
}
else
{
  hr = E_FAIL;
}
psw->Release();
}
return hr;
} // GetShellViewForDesktop()

// From a shell view object gets its automation interface and from that gets the shell
// application object that implements IShellDispatch2 and related interfaces.

HRESULT GetShellDispatchFromView(IShellView *psv, REFIID riid, void **ppv)
{
  *ppv = NULL;

 IDispatch *pdispBackground;
 HRESULT hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
 if (SUCCEEDED(hr))
 {
   IShellFolderViewDual *psfvd;
   hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
   if (SUCCEEDED(hr))
   {
     IDispatch *pdisp;
     hr = psfvd->get_Application(&pdisp);
     if (SUCCEEDED(hr))
     {
       hr = pdisp->QueryInterface(riid, ppv);
       pdisp->Release();
     }
     psfvd->Release();
   }
   pdispBackground->Release();
}
return hr;
} // GetShellDispatchFromView()

【问题讨论】:

    标签: windows winapi uac


    【解决方案1】:

    在我的机器上,UAC 被禁用。

    这就是你的问题。通过禁用 UAC,您可以阻止系统以标准用户身份创建进程。如果登录用户是管理员,则进程使用完全特权令牌运行。

    在禁用 UAC 的情况下,运行 shell 的资源管理器进程使用交互式用户的完整令牌启动,这似乎是管理员用户的令牌。因此,当 shell 使用 IShellDispatch2-&gt;ShellExecute 启动一个新进程时,该新进程在相同的用户令牌下运行。

    您需要启用 UAC 以允许系统使用标准用户令牌创建进程。启用 UAC 后,shell 的资源管理器进程将以标准用户身份运行,因此IShellDispatch2-&gt;ShellExecute 将创建以标准用户身份运行的新进程。

    【讨论】:

    • 感谢大卫,我现在启用了 UAC。现在的问题是其他问题,这意味着主进程(将调用 ShellExecute())不再被提升。我怎么能总是启动提升的主进程(并且不必弹出 UAC 确认,因为这个应用程序将在服务器上运行,如前所述)。
    • 这确实是一个不同的问题,与这个问题并不真正相关。但是,您似乎陷入了困境。您希望获得 UAC 的好处,但又不想看到 UAC 对话框。那是不可能的。由于您在服务器上运行,并且您不想要交互,因此您应该制作服务而不是桌面应用程序。然后你可以通过在不同的用户帐户下运行不同的服务来控制用户权限。
    • 谢谢大卫。现在在我看来,使用服务是我可以同时拥有两者(UAC 和没有 UI 交互)的唯一方法。
    • 在服务器上,您真的希望一切都作为服务而毁掉。这样您就可以很好地控制运行服务的用户,并且可以确保没有 UI。
    猜你喜欢
    • 2011-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-30
    • 2015-07-20
    • 1970-01-01
    • 2013-07-19
    • 2011-01-31
    相关资源
    最近更新 更多