Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Launch an Exe at User Level from a Higher Level

Tags:

I would like a process to always run at the user level. Either when it is launched by the installer (custom, not msi), which runs as at the administrator level, or when a user logs on. Looking around, I'm not sure this is possible.

like image 293
Spilly Avatar asked Jan 12 '10 18:01

Spilly


1 Answers

The easiest way is to have 2 processes. One is normal user and it launches elevated/admin process. Then admin process can use IPC to ask normal user process to do things.

If you have no normal user process, then Raymond Chen documents:

Going from an unelevated process to an elevated process is easy. You can run a process with elevation by passing the runas verb to Shell­Execute or Shell­Execute­Ex.

Going the other way is trickier. For one thing, it's really hard to munge your token to remove the elevation nature properly. And for another thing, even if you could do it, it's not the right thing to do, because the unelevated user may be different from the elevated user.

The solution here is to go back to Explorer and ask Explorer to launch the program for you. Since Explorer is running as the original unelevated user, the program (in this case, the Web browser) will run as Bob. This is also important in the case that the handler for the file you want to open runs as an in-process extension rather than as a separate process, for in that case, the attempt to unelevate would be pointless since no new process was created in the first place. (And if the handler for the file tries to communicate with an existing unelevated copy of itself, things may fail because of UIPI.)

Okay, I know that Little Programs are not supposed to have motivation, but I couldn't help myself. Enough jabber. Let's write code. (Remember that Little Programs do little or no error checking, because that's the way they roll.)

#define STRICT
#include <windows.h>
#include <shldisp.h>
#include <shlobj.h>
#include <exdisp.h>
#include <atlbase.h>
#include <stdlib.h>

void FindDesktopFolderView(REFIID riid, void **ppv)
{
 CComPtr<IShellWindows> spShellWindows;
 spShellWindows.CoCreateInstance(CLSID_ShellWindows);

 CComVariant vtLoc(CSIDL_DESKTOP);
 CComVariant vtEmpty;
 long lhwnd;
 CComPtr<IDispatch> spdisp;
 spShellWindows->FindWindowSW(
     &vtLoc, &vtEmpty,
     SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);

 CComPtr<IShellBrowser> spBrowser;
 CComQIPtr<IServiceProvider>(spdisp)->
     QueryService(SID_STopLevelBrowser,
                  IID_PPV_ARGS(&spBrowser));

 CComPtr<IShellView> spView;
 spBrowser->QueryActiveShellView(&spView);

 spView->QueryInterface(riid, ppv);
}    

void GetDesktopAutomationObject(REFIID riid, void **ppv)
{
 CComPtr<IShellView> spsv;
 FindDesktopFolderView(IID_PPV_ARGS(&spsv));
 CComPtr<IDispatch> spdispView;
 spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
 spdispView->QueryInterface(riid, ppv);
}

The Get­Desktop­Automation­Object function locates the desktop folder view then asks for the dispatch object for the view. We then return that dispatch object in the form requested by the caller. This dispatch object is a Shell­Folder­View, and the C++ interface for that is IShell­Folder­View­Dual, so most callres are going to ask for that interface, but if you are a masochist, you can skip the dual interface and talk directly to IDispatch.

void ShellExecuteFromExplorer(
    PCWSTR pszFile,
    PCWSTR pszParameters = nullptr,
    PCWSTR pszDirectory  = nullptr,
    PCWSTR pszOperation  = nullptr,
    int nShowCmd         = SW_SHOWNORMAL)
{
 CComPtr<IShellFolderViewDual> spFolderView;
 GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
 CComPtr<IDispatch> spdispShell;
 spFolderView->get_Application(&spdispShell);

 CComQIPtr<IShellDispatch2>(spdispShell)
    ->ShellExecute(CComBSTR(pszFile),
                   CComVariant(pszParameters ? pszParameters : L""),
                   CComVariant(pszDirectory ? pszDirectory : L""),
                   CComVariant(pszOperation ? pszOperation : L""),
                   CComVariant(nShowCmd));
}

The Shell­Execute­From­Explorer function starts by getting the desktop folder automation object. We use the desktop not because it's particularly meaningful but because we know that it's always going to be there.

As with the desktop folder view, the Shell­Folder­View object is not interesting to us for itself. It's interesting to us because the object resides in the process that is hosting the desktop view (which is the main Explorer process). From the Shell­Folder­View, we ask for the Application property so that we can get to the main Shell.Application object, which has the IShell­Dispatch interface (and its extensions IShell­Dispatch2 through IShell­Dispatch6) as its C++ interfaces. And it is the IShell­Dispatch2::Shell­Execute method that is what we really want.

And we call IShell­Dispatch2::Shell­Execute with the appropriate parameters. Note that the parameters to IShell­Dispatch2::Shell­Execute are in a different order from the parameters to Shell­Execute!

Okay, let's put this inside a little program.

int __cdecl wmain(int argc, wchar_t **argv)
{
 if (argc < 2) return 0;

 CCoInitialize init;
 ShellExecuteFromExplorer(
    argv[1],
    argc >= 3 ? argv[2] : L"",
    argc >= 4 ? argv[3] : L"",
    argc >= 5 ? argv[4] : L"",
    argc >= 6 ? _wtoi(argv[5]) : SW_SHOWNORMAL);

 return 0;
}

The program takes a mandatory command line argument which is the thing to execute, be it a program or a document or a URL. Optional parameters are the parameters to the thing being executed, the current directory to use, the operation to perform, and how the window should be opened.

Open an elevated command prompt, and then run this program in various ways.

  • scratch http://www.msn.com/
    Open an unelevated Web page in the user's default Web browser.
  • scratch cmd.exe "" C:\Users "" 3
    Open an unelevated command prompt at C:\Users, maximized.
  • scratch C:\Path\To\Image.bmp "" "" edit
    Edit a bitmap in an unelevated image editor.
like image 90
Alex Avatar answered Oct 17 '22 12:10

Alex