0
votes

I have the following code (main part taken from MS SDK v7.1 Sample code, which demonstrates how to start an non-elevated process from an elevated one)- The elevated and non-elevated process will be started on an (unsupervised) server, so user interaction is a no-go (except for the configuring part, if needed, of course).

The problem is, that the non-elevated process started through IShellDispatch2->ShellExecute is still elevated (expected was to be non-elevated). This was confirmed by using IsUserAnAdmin() API. Any ideas why the process created through ShellExecute() does have still elevated rights?

Some more relevant details: on my machine UAC is disabled. The VS 2013 based application is manifested: ) (manifesting can be disabled with /MANIFEST:no linker flag, if it will help in solving this). I'm compiling it on Windows 7 x64 using VS 2013.

#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()
1

1 Answers

1
votes

On my machine UAC is disabled.

Right there is your problem. By disabling UAC you stop the system from creating processes as standard user. If the logged on user is an administrator, then processes run with a fully privileged token.

With UAC disabled, the explorer process that runs the shell is started using the full token of the interactive user, which it seems is the token of an admin user. And so when the shell starts a new process with IShellDispatch2->ShellExecute that new process runs under the same user token.

You'll need to enable UAC to allow the system to create processes with standard user tokens. When you enable UAC, the shell's explorer process will run as standard user and so IShellDispatch2->ShellExecute will create new processes running as standard user.