2016-06-21 3 views
2

Gibt es eine einfache Möglichkeit, einen normalen Administratorprozess (nicht erhöht) aus einem erhöhten Administratorprozess zu erstellen? Ich benutze Windows 10 Pro. Die Situation ist, dass ich versuche, eine Art von Bereitstellungstools zu machen. Das Tool wird mit einem erhöhten Administratorkontext ausgeführt, um Dateien in "Programme" zu schreiben (und auf andere privilegierte Ressourcen zuzugreifen). Aber einer der Schritte besteht darin, ein externes Programm aufzurufen. Dieses Programm scheint seltsame Probleme zu haben, wenn es mit erhöhten Administratorrechten erstellt wurde. Wir müssen es in einem nicht erhöhten Administratorkontext starten. Ich versuchte den Ansatz in einem MSDN-Blog, https://blogs.msdn.microsoft.com/winsdk/2010/05/31/dealing-with-administrator-and-standard-users-context es funktioniert überhaupt nicht.Wie startet man einen nicht erhöhten Administratorprozess aus einem erhöhten Administratorkontext unter Windows 10?

+0

Teilen Sie einige Codes und Schritte auf, wie haben Sie versucht, und wie Sie scheitern. –

+0

Ich würde, wenn ich sie finden könnte. Aber tatsächlich kopierte ich den Code von dem Link, den ich zur Verfügung gestellt habe. Ich verstehe, dass das Beispiel versucht, zu zeigen, dass es zwei verknüpfte Token bezüglich des erhöhten Administrators und des nicht erhöhten Administrators gibt. Wenn Sie die erhöhte Version erhalten, können Sie den verknüpften Teil finden. Mit dem verknüpften Teil können Sie das von CreateProcessWithToken benötigte Argument abrufen. Unter Windows 10 ist die Schlussfolgerung nicht richtig. Verknüpfte Token können abgerufen werden, aber diese scheint eine eingeschränkte Version oder eine Ansicht des Tokens zu sein. Es konnte nur nicht in CreateProcessWithToken verwendet werden. –

+0

Es gibt ein Open-Source-Projekt namens ProcessHacker, das nach meinem Verständnis ein Klon von ProcessExplorer von sysinternals ist. Ich habe den Code von ProcessHacker portiert. Ihr Ansatz besteht darin, das erhöhte Administrator-Token abzurufen und seine Sicherheitsbeschreibungen anzupassen. Es scheint für einfache Programme wie NotePad zu funktionieren, aber es ist uns nicht gelungen, komplexe Programme zu starten. Ich neige dazu zu glauben, dass ihr produzierter Token eine Annäherung ist, aber immer noch nicht der echte, der mit einem nicht-erhöhten Administrator verbunden ist. –

Antwort

0

Die Lösung ist ein bisschen komplex.Vielleicht können Sie nicht direkt von einem erhöhten Administrator zu einem nicht-erhöhten Administrator wechseln, indem Sie eine Art Zugriffstoken erhalten und es an createprocesswithtoken übergeben. Aber Sie können einen Schritt in eine andere Richtung gehen. Sie können von einem erhöhten Administratorkonto zu einem Systemkonto wechseln, das noch höhere Berechtigungen besitzt. Von der Systemkontoberechtigung aus sollten Sie in der Lage sein, einen Prozess in einem nicht erhöhten Administratorkontext zu starten. Die Suche mit dem Schlüsselwort "impersonate" kann Ihnen viele Beispiele geben. Dann, wie von erhöhten Administrator zu System gehen? Sie können nur einen Systemdienst schreiben und den Dienst unter einem erhöhten Administratorkontext erstellen/starten.

+0

Der Grund, warum ich diese komplexe Antwort als die "für mich arbeiten" wähle ist: Ich kann eine Prozess-ID für folgende Wartefunktionen zurückgeben. Im shellexecute-Ansatz fand ich keine einfache Möglichkeit, die Prozess-ID zu identifizieren, die von meinen Codes genau gestartet wurde. –

+0

Können Sie mehr Details zu diesem Identitätswechsel angeben? Benötigt man dazu das Passwort des Benutzers? – FlashTek

4

Raymond Chen adressierte genau diese Frage auf seinem "Old New Thing" Blog auf MSDN:

How can I launch an unelevated process from my elevated process and vice versa?

in der anderen Richtung ist schwieriger. Zum einen ist es wirklich schwierig, den Token so zu manipulieren, dass er die Elevation-Eigenschaft richtig entfernt. Und für eine andere Sache, selbst wenn Sie es tun könnten, ist es nicht das Richtige zu tun, weil sich der unerfahrene Benutzer von dem erhöhten Benutzer unterscheiden kann.

...

Die Lösung ist hier zu Explorer zurück zu gehen und fragen Explorer für Sie das Programm zu starten. Da der Explorer als ursprünglich unerheblicher Benutzer ausgeführt wird, wird das Programm (in diesem Fall der Webbrowser) als Bob ausgeführt. Dies ist auch wichtig für den Fall, dass der Handler für die Datei, die Sie öffnen möchten, als prozessinterne Erweiterung und nicht als separater Prozess ausgeführt wird. In diesem Fall wäre der Versuch der Aufhebung nicht sinnvoll, da kein neuer Prozess erstellt wurde den ersten Platz. (Und wenn der Handler für die Datei mit einer vorhandenen unerhöhten Kopie von sich selbst zu kommunizieren versucht, kann Dinge wegen UIPI scheitern.)

Raymond verwendet IShellFolderViewDual und IShellDispatch2 zu erreichen, dass :

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

// FindDesktopFolderView incorporated by reference 

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); 
} 

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)); 
} 

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; 
} 

Öffnen Sie eine Eingabeaufforderung mit erhöhten Rechten, und führen Sie dieses Programm auf verschiedene Arten aus.

scratch http://www.msn.com/
Öffnen Sie eine leere Webseite im Standardwebbrowser des Benutzers.

scratch cmd.exe "" C:\Users "" 3
Öffnen Sie eine nicht erhöhte Eingabeaufforderung bei C: \ Benutzer, maximiert.

scratch C:\Path\To\Image.bmp "" "" edit
Bearbeiten von Bitmaps in einem unerhöhten Bildeditor

: die Umsetzung von FindDesktopFolderView() ist in einem anderen Artikel auf Raymond Blog:

Manipulating the positions of desktop icons:

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); 
} 
+0

Danke! Ich habe versucht, den Ansatz selbst auszuführen und es funktioniert nicht für mich. Aber mein Code von shellexecute ist ziemlich einfach, verglichen mit denen, die Sie gepostet haben. Vielleicht ist der Unterschied hier ein Schlüssel. Ich werde es versuchen und melden. –

+0

@ H.Tao: Sie haben versucht, die tatsächliche 'ShellExecute()' -Funktion zu verwenden, während Raymond die 'IShellDispatch2.ShellExecute()' Schnittstellenmethode verwendet, um stattdessen an die unverwaltete Benutzer-laufende Kopie von Explorer zu delegieren. –

+0

Ich habe einen schnellen Test gemacht. Bis jetzt funktioniert es. Vielen Dank! BTW: Die Lösung von Juan. S funktioniert auch. Aber ich fühle, dass der hier präsentierte Weg eleganter ist. –

0

Hier ist meine Version Raymond Chen's code, die Fehlerbehandlung über C++ - Ausnahmen hinzugefügt.

Zuerst deklarieren wir einige Hilfsfunktionen zum Werfen std::system_error Ausnahme von HRESULT und GUID in Zeichenfolge konvertieren.

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

void ThrowIfFailed(HRESULT hr, const char* msg) 
{ 
    if(FAILED(hr)) 
     throw std::system_error{ hr, std::system_category(), msg }; 
} 
void ThrowIfFailed(HRESULT hr, const std::string& msg) 
{ 
    if(FAILED(hr)) 
     throw std::system_error{ hr, std::system_category(), msg }; 
} 

template< typename ResultT = std::string > 
ResultT to_string(REFIID riid) 
{ 
    LPOLESTR pstr = nullptr; 
    if(SUCCEEDED(::StringFromCLSID(riid, &pstr))) 
    { 
     // Use iterator arguments to cast from wchar_t to char if element type of ResultT is char. 
     ResultT result{ pstr, pstr + wcslen(pstr) }; 
     ::CoTaskMemFree(pstr); pstr = nullptr; 
     return result; 
    } 
    return {}; 
} 

Dies ist gefolgt von den Funktionen, die die eigentliche Arbeit erledigen. Dies ist im Grunde derselbe Code wie in Remy Lebeau's answer, aber mit zusätzlicher Fehlerbehandlung (und meinem persönlichen Formatierungsstil).

void FindDesktopFolderView(REFIID riid, void **ppv) 
{ 
    CComPtr<IShellWindows> spShellWindows; 
    ThrowIfFailed( 
     spShellWindows.CoCreateInstance(CLSID_ShellWindows), 
     "Could not create instance of IShellWindows"); 

    CComVariant vtLoc{ CSIDL_DESKTOP }; 
    CComVariant vtEmpty; 
    long lhwnd = 0; 
    CComPtr<IDispatch> spdisp; 
    ThrowIfFailed( 
     spShellWindows->FindWindowSW(
      &vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp), 
     "Could not find desktop shell window"); 

    CComQIPtr<IServiceProvider> spProv{ spdisp }; 
    if(! spProv) 
     ThrowIfFailed(E_NOINTERFACE, "Could not query interface IServiceProvider"); 

    CComPtr<IShellBrowser> spBrowser; 
    ThrowIfFailed( 
     spProv->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser)), 
     "Could not query service IShellBrowser"); 

    CComPtr<IShellView> spView; 
    ThrowIfFailed( 
     spBrowser->QueryActiveShellView(&spView), 
     "Could not query active IShellView"); 

    ThrowIfFailed( 
     spView->QueryInterface(riid, ppv), 
     "Could not query interface " + to_string(riid) + " from IShellView"); 
} 

void GetDesktopAutomationObject(REFIID riid, void **ppv) 
{ 
    CComPtr<IShellView> spsv; 
    FindDesktopFolderView(IID_PPV_ARGS(&spsv)); 

    CComPtr<IDispatch> spdispView; 
    ThrowIfFailed( 
     spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView)), 
     "Could not get item object SVGIO_BACKGROUND from IShellView"); 
    ThrowIfFailed( 
     spdispView->QueryInterface(riid, ppv), 
     "Could not query interface " + to_string(riid) + " from ShellFolderView"); 
} 

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; 
    ThrowIfFailed( 
     spFolderView->get_Application(&spdispShell), 
     "Could not get application object from IShellFolderViewDual"); 

    CComQIPtr<IShellDispatch2> spdispShell2{ spdispShell }; 
    if(!spdispShell2) 
     ThrowIfFailed(E_NOINTERFACE, "Could not query interface IShellDispatch2"); 

    ThrowIfFailed( 
     spdispShell2->ShellExecute(
      CComBSTR{ pszFile }, 
      CComVariant{ pszParameters ? pszParameters : L"" }, 
      CComVariant{ pszDirectory ? pszDirectory : L"" }, 
      CComVariant{ pszOperation ? pszOperation : L"" }, 
      CComVariant{ nShowCmd }), 
     "ShellExecute failed"); 
} 

Usage Beispiel, das zeigt, wie die Ausnahme behandeln:

int main() 
{ 
    CCoInitialize init; 

    try 
    { 
     ShellExecuteFromExplorer(L"http://www.stackoverflow.com"); 
    } 
    catch(std::system_error& e) 
    { 
     std::cout << "ERROR: " << e.what() << "\n" 
      << "Error code: " << e.code() << std::endl; 
    } 
}