{"id":2643,"date":"2013-11-18T07:00:00","date_gmt":"2013-11-18T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/11\/18\/how-can-i-launch-an-unelevated-process-from-my-elevated-process-and-vice-versa\/"},"modified":"2013-11-18T07:00:00","modified_gmt":"2013-11-18T07:00:00","slug":"how-can-i-launch-an-unelevated-process-from-my-elevated-process-and-vice-versa","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131118-00\/?p=2643","title":{"rendered":"How can I launch an unelevated process from my elevated process and vice versa?"},"content":{"rendered":"<p>\nGoing from an unelevated process to an elevated process is easy.\nYou can run a process with elevation by\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/vistacompatteam\/archive\/2006\/09\/25\/771232.aspx\">\npassing the <code>runas<\/code> verb<\/a>\nto <code>Shell&shy;Execute<\/code> or\n<code>Shell&shy;Execute&shy;Ex<\/code>.\n<\/p>\n<p>\nGoing the other way is trickier.\nFor one thing, it&#8217;s really hard to munge your token to remove the\nelevation nature properly.\nAnd for another thing, even if you could do it, it&#8217;s not the right\nthing to do, because the unelevated user may be different from the\nelevated user.\n<\/p>\n<p>\nLet me expand on that last bit.\n<\/p>\n<p>\nTake a user who is not an administrator.\nWhen that user tries to run a program with elevation,\nthe system will display a prompt that says,\n&#8220;Hey, like, since you&#8217;re not an administrator,\nI need you to type the userid and password of somebody\nwho <i>is<\/i> an administrator.&#8221;\nWhen that happens, the elevated program is running not as the\noriginal user but as the administrative user.\nEven if the elevated program tried to remove elevation from its token,\nall it managed to do is create an unelevated token\n<i>for the administrative user<\/i>, not the original user.\n<\/p>\n<p>\nSuppose we have Alice Administrator and Bob Banal.\nBob logs on,\nand then tries to run LitWare Dashboard,\nwhich requires elevation.\nThe prompt comes up, and Bob calls over Alice to grant\nadministrative privileges.\nAlice types her password, and boom, now LitWare Dashboard is running\nelevated <i>as Alice<\/i>.\n<\/p>\n<p>\nNow suppose LitWare Dashboard wants to launch the user&#8217;s Web browser\nto show some online content.\nSince there is no reason for the Web browser to run elevated,\nit tries to unelevate the browser in order to reduce the security\nattack surface.\nIf it simply neutered its token and used that to launch the browser,\nit would be running a copy of the browser unelevated <i>as Alice<\/i>.\nBut LitWare Dashboard presumably\nreally wanted to run the browser as Bob,\nsince it is Bob who is the unelevated user in this session.\n<\/p>\n<p>\nThe solution here is to go back to Explorer and ask Explorer to\nlaunch the program for you.\nSince Explorer is running as the original unelevated user,\nthe program (in this case, the Web browser) will run as Bob.\nThis is also important in the case that the handler for the file\nyou want to open runs as an in-process extension rather than as\na separate process,\nfor in that case,\nthe attempt to unelevate would be pointless since no new process\nwas created in the first place.\n(And if the handler for the file tries to communicate with\nan existing unelevated copy of itself, things may fail because of UIPI.)\n<\/p>\n<p>\nOkay, I know that Little Programs are not supposed to have motivation,\nbut I couldn&#8217;t help myself.\nEnough jabber.\nLet&#8217;s write code.\n(Remember that Little Programs do little or no error checking,\nbecause that&#8217;s the way they roll.)\n<\/p>\n<pre>\n#define STRICT\n#include &lt;windows.h&gt;\n#include &lt;shldisp.h&gt;\n#include &lt;shlobj.h&gt;\n#include &lt;exdisp.h&gt;\n#include &lt;atlbase.h&gt;\n#include &lt;stdlib.h&gt;\n\/\/ <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/03\/18\/10403054.aspx\">FindDesktopFolderView<\/a> incorporated by reference\nvoid GetDesktopAutomationObject(REFIID riid, void **ppv)\n{\n CComPtr&lt;IShellView&gt; spsv;\n FindDesktopFolderView(IID_PPV_ARGS(&amp;spsv));\n CComPtr&lt;IDispatch&gt; spdispView;\n spsv-&gt;GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&amp;spdispView));\n spdispView-&gt;QueryInterface(riid, ppv);\n}\n<\/pre>\n<p>\nThe\n<code>\nGet&shy;Desktop&shy;Automation&shy;Object<\/code>\nfunction\nlocates the desktop folder view\nthen asks for the dispatch object for the view.\nWe then return that dispatch object in the form requested by the caller.\nThis dispatch object is a <code>Shell&shy;Folder&shy;View<\/code>,\nand the C++ interface for that is\n<code>IShell&shy;Folder&shy;View&shy;Dual<\/code>,\nso most callers are going to ask for that interface,\nbut\nif you are a masochist, you can skip the dual interface and\ntalk directly to <code>IDispatch<\/code>.\n<\/p>\n<\/p>\n<pre>\nvoid ShellExecuteFromExplorer(\n    PCWSTR pszFile,\n    PCWSTR pszParameters = nullptr,\n    PCWSTR pszDirectory  = nullptr,\n    PCWSTR pszOperation  = nullptr,\n    int nShowCmd         = SW_SHOWNORMAL)\n{\n CComPtr&lt;IShellFolderViewDual&gt; spFolderView;\n GetDesktopAutomationObject(IID_PPV_ARGS(&amp;spFolderView));\n CComPtr&lt;IDispatch&gt; spdispShell;\n spFolderView-&gt;get_Application(&amp;spdispShell);\n CComQIPtr&lt;IShellDispatch2&gt;(spdispShell)\n    -&gt;ShellExecute(CComBSTR(pszFile),\n                   CComVariant(pszParameters ? pszParameters : L\"\"),\n                   CComVariant(pszDirectory ? pszDirectory : L\"\"),\n                   CComVariant(pszOperation ? pszOperation : L\"\"),\n                   CComVariant(nShowCmd));\n}\n<\/pre>\n<p>\nThe\n<code>Shell&shy;Execute&shy;From&shy;Explorer<\/code>\nfunction\nstarts by getting the desktop folder automation object.\nWe use the desktop not because it&#8217;s particularly meaningful\nbut because we know that it&#8217;s always going to be there.\n<\/p>\n<p>\nAs with the desktop folder view,\nthe <code>Shell&shy;Folder&shy;View<\/code> object is not interesting\nto us for itself.\nIt&#8217;s interesting to us because the object\nresides in the process that is hosting the desktop view\n(which is the main Explorer process).\nFrom the <code>Shell&shy;Folder&shy;View<\/code>, we ask for the\n<code>Application<\/code> property\nso that we can get to the main\n<code>Shell.Application<\/code> object,\nwhich has the <code>IShell&shy;Dispatch<\/code> interface\n(and its extensions\n<code>IShell&shy;Dispatch2<\/code> through\n<code>IShell&shy;Dispatch6<\/code>)\nas its C++ interfaces.\nAnd it is the\n<code>IShell&shy;Dispatch2::Shell&shy;Execute<\/code> method\nthat is what we really want.\n<\/p>\n<p>\n&#8220;You never loved me.\nYou only wanted me in order\nto get access to my family,&#8221; sobbed the shell folder view.\n<\/p>\n<p>\nAnd we call\n<code>IShell&shy;Dispatch2::Shell&shy;Execute<\/code> with\nthe appropriate parameters.\nNote that the parameters to\n<code>IShell&shy;Dispatch2::Shell&shy;Execute<\/code> are\n<i>in a different order<\/i> from the parameters to\n<code>Shell&shy;Execute<\/code>!\n<\/p>\n<p>\nOkay, let&#8217;s put this inside a little program.\n<\/p>\n<pre>\nint __cdecl wmain(int argc, wchar_t **argv)\n{\n if (argc &lt; 2) return 0;\n <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/05\/20\/135841.aspx\">CCoInitialize<\/a> init;\n ShellExecuteFromExplorer(\n    argv[1],\n    argc &gt;= 3 ? argv[2] : L\"\",\n    argc &gt;= 4 ? argv[3] : L\"\",\n    argc &gt;= 5 ? argv[4] : L\"\",\n    argc &gt;= 6 ? _wtoi(argv[5]) : SW_SHOWNORMAL);\n return 0;\n}\n<\/pre>\n<p>\nThe program takes a mandatory command line argument which is\nthe thing to execute, be it a program or a document or a URL.\nOptional parameters are the parameters to the thing being executed,\nthe current directory to use,\nthe operation to perform, and how the window should be opened.\n<\/p>\n<p>\nOpen an elevated command prompt, and then run this program\nin various ways.\n<\/p>\n<table STYLE=\"border-collapse: collapse\" BORDER=\"1\" CELLPADDING=\"3\">\n<tr>\n<td><code>scratch http:\/\/www.msn.com\/<\/code><\/td>\n<td>Open an unelevated Web page in the user&#8217;s default Web browser.<\/td>\n<\/tr>\n<tr>\n<td><code>scratch cmd.exe \"\" C:\\Users \"\" 3<\/code><\/td>\n<td>Open an unelevated command prompt at\n        <code>C:\\Users<\/code>, maximized.<\/td>\n<\/tr>\n<tr>\n<td><code>scratch C:\\Path\\To\\Image.bmp \"\" \"\" edit<\/code><\/td>\n<td>Edit a bitmap in an unelevated image editor.<\/td>\n<\/tr>\n<\/table>\n<p>\nThis program is basically the same as the\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/dd940355\">Execute in Explorer<\/a>\nsample.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&shy;Execute or Shell&shy;Execute&shy;Ex. Going the other way is trickier. For one thing, it&#8217;s really hard to munge your token to remove the elevation nature properly. And for another thing, even if [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-2643","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>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&shy;Execute or Shell&shy;Execute&shy;Ex. Going the other way is trickier. For one thing, it&#8217;s really hard to munge your token to remove the elevation nature properly. And for another thing, even if [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2643","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=2643"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2643\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=2643"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=2643"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=2643"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}