2016-06-21 94 views
2

是否有一種簡單的方法可以從高級管理員進程創建普通管理員進程(未提升)? 我正在使用Windows 10專業版。情況是我正在嘗試製作某種部署工具。該工具將以高級管理員上下文運行,以便將文件寫入「程序文件」(並訪問其他特權資源)。但其中一步是調用一個外部程序。使用提升的管理員權限創建該程序似乎有奇怪的問題。我們必須在非高級管理員環境中啓動它。 我在MSDN博客中嘗試過這種方法,https://blogs.msdn.microsoft.com/winsdk/2010/05/31/dealing-with-administrator-and-standard-users-context根本不起作用。如何在Windows 10上從高級管理員上下文啓動非高級管理員進程?

+0

請分享你如何你已經嘗試和失敗怎樣的一些代碼和步驟。 –

+0

我會,如果我能找到他們。但實際上我從我提供的鏈接複製了代碼。我知道這個例子試圖說明有兩個鏈接的關於高級管理員和非高級管理員的標記。通過獲得高架,你可以找到鏈接的部分。使用鏈接部分,您可以獲取CreateProcessWithToken所需的參數。在Windows 10上,結論是不正確的。鏈接的令牌可以被獲取,但是這似乎是縮小版本或令牌的視圖。它只能在CreateProcessWithToken中使用。 –

+0

有一個名爲ProcessHacker的開源項目,根據我的理解,它是來自sysinternals的ProcessExplorer的克隆。我從ProcessHacker移植了代碼。他們的方法是獲取提升的管理員令牌並調整其安全描述符。它似乎適用於像記事本這樣的簡單程序,但未能像我們的情況那樣啓動複雜的程序。我傾向於認爲他們製造的令牌是近似值,但仍然不是與非高級管理員相關的真實值。 –

回答

0

解決方案有點複雜。也許你不能通過獲得一種訪問令牌直接從高級管理員到非高級管理員,並將它傳遞給創建具有密碼的進程,但是你可以向另一個方向進一步。您可以從高級管理員轉到具有更高權限的系統帳戶。從系統帳戶權限,您應該能夠在非高級管理員環境中啓動進程。使用關鍵詞「impersonate」搜索可以給你很多例子。 那麼如何從高管提升到系統?您只能編寫系統服務並在提升的管理員環境下創建/啓動該服務。

+0

爲什麼我選擇這個複雜的答案作爲「爲我工作」的原因是:我可以獲得返回的進程ID以用於以下等待函數。在shellexecute方法中,我沒有找到一個簡單的方法來識別我的代碼剛剛啓動的進程ID。 –

+0

你能提供關於這種冒充的更多細節嗎?是否需要用戶的密碼才能執行此操作? – FlashTek

4

雷蒙德陳解決了這一問題詢問了他的「老新事物」博客上的MSDN:

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

走另一條路是棘手。一方面,要讓你的標記正確移除標高屬性真的很難。另一方面,即使你能做到,也不是正確的做法,因爲未升級的用戶可能與升級的用戶不同。

...

這裏的解決方案是去給Explorer,並要求瀏覽器啓動程序爲您服務。由於資源管理器以原始未升級的用戶身份運行,程序(在本例中爲Web瀏覽器)將以Bob的身份運行。這對於您要打開的文件的處理程序作爲進程內擴展而不是作爲單獨的進程運行也很重要,因爲在這種情況下,嘗試進行非篩選將毫無意義,因爲沒有創建新進程第一個地方。 (如果該文件的處理程序試圖與自身現有unelevated副本進行溝通,事情可能會失敗,因爲UIPI的。)

雷蒙德使用IShellFolderViewDualIShellDispatch2實現這一:

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

打開提升的命令提示符,然後以各種方式運行該程序。

scratch http://www.msn.com/
在用戶的默認Web瀏覽器中打開一個未啓動的網頁。

scratch cmd.exe "" C:\Users "" 3
在C:\ Users處打開未升級的命令提示符,最大化。

scratch C:\Path\To\Image.bmp "" "" edit
編輯在unelevated圖像編輯器位圖

:中FindDesktopFolderView()實現是在另一篇文章對雷蒙德的博客:

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

謝謝!我確實嘗試了自己的方法,但這對我並不適用。但是,與您發佈的相比,我的shellexecute代碼相當簡單。也許差異在這裏是一個關鍵。我會盡力回覆。 –

+0

@ H.Tao:您試圖使用實際的ShellExecute()函數,而Raymond使用'IShellDispatch2.ShellExecute()'接口方法代替未升級的用戶運行的Explorer副本。 –

+0

我做了一個快速測試。到目前爲止它的工作。謝謝!順便說一句:來自Juan的解決方案。 S也有效。但我覺得這裏呈現的方式更優雅。 –

0

這是我的Raymond Chen's code版本,它通過C++異常增加了錯誤處理。

首先我們聲明一些幫助函數,用於從HRESULT中拋出std::system_error異常,並將GUID轉換爲字符串。

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

接下來是執行實際工作的函數。這與Remy Lebeau's answer中的代碼基本相同,但增加了錯誤處理(以及我的個人格式化風格)。

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

用法示例,說明如何處理異常:

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; 
    } 
} 
相關問題