簡體   English   中英

Win32 - 作為普通用戶進程啟動一個最高可用的子進程

[英]Win32 - Launching a highestAvailable child process as a normal user process

假設您的 Windows 用戶帳戶在 Admin 組中,啟用了 UAC,並且您正在以普通用戶權限運行某個程序 A。 A從不要求提升,也從不接受。 現在假設 A 要啟動程序 B,它的清單中具有最高可用。

  • 如果 A 調用 CreateProcess(B),這將失敗並顯示錯誤 740(“需要提升”)

  • 如果 A 調用 ShellExecuteEx(B),Windows 將顯示一個 UAC 框,要求運行 B 提升。 用戶可以說是,在這種情況下 B 將運行提升,或者說否,在這種情況下啟動將失敗。

我的問題是:有沒有辦法實現第三種選擇,我們只需在沒有提升的情況下啟動 B?

原則上似乎這應該是可能的,因為“highestAvailable”意味着 B更喜歡在高程下運行,但完全能夠在普通用戶模式下運行。 但我想不出任何方法來實現它。 我已經嘗試了各種使用令牌和 CreateProcessAsUser() 的方法,但這一切似乎都歸結為:“highestAvailable”似乎不可改變地指的是用戶帳戶中固有的潛在特權,而不是任何明確表達的實際特權構造的令牌。

我希望實際上有某種方法可以使用 CreateProcessAsUser() 來做到這一點,而我只是錯過了正確構造令牌的技巧。

更新 - 已解決:下面的 __COMPAT_LAYER=RunAsInvoker 解決方案效果很好。 不過,有一個警告。 這會強制子進程無條件地“作為調用者”運行:即使被調用的 exe 在其清單中指定了“requireAdministrator”,它也適用。 我認為當 exe 指定“requireAdministrator”時,最初的“需要提升高度”錯誤通常更可取。 我希望對標有“highestAvailable”的程序使用 RunAsInvoker 行為的全部原因是,此類程序明確表示“我可以在任一模式下正常運行”-因此,當使用管理員模式不方便時,讓我們繼續以普通用戶模式運行。 但是“requireAdministrator”是另一回事:此類程序說“如果沒有提升的權限,我將無法正常運行”。 對於此類程序來說,預先失敗似乎比強制它們在未提升的情況下運行更好,這可能會使它們遇到權限/訪問錯誤,而它們沒有正確編程來處理。 所以我認為這里一個完整的通用解決方案需要檢查應用程序清單,並且只有在清單顯示“highestAvailable”時才應用 RunAsInvoker 強制。 一個更完整的解決方案是使用其他地方討論的技術之一,當出現“requireAdministrator”程序時,為調用者提供調用 UAC 的選項,並為用戶提供啟動它的機會。 我可以想象一個 CreateProcessEx() 帶有幾個新標志,用於“將進程特權視為最高可用特權”和“如果需要提升則調用 UAC”。 (下面描述的另一種方法,掛鈎 NTDLL!RtlQueryElevationFlags() 以告訴 CreateProcess() UAC 不可用,對於 requireAdministrator 程序具有完全相同的警告。)

(這可能說明 Windows 外殼甚至沒有提供執行此操作的方法...直接從外殼啟動 B 將為您提供 UAC 框,讓您可以使用 Admin privs 啟動或根本不啟動。如果有無論如何,UAC 框可能會提供第三個按鈕,無需特權即可啟動。但話又說回來,這可能只是 UX 決定,因為第三個選項對平民來說太混亂了。)

(請注意,在 StackOverflow 和 Microsoft 開發支持網站上有很多帖子詢問一個非常相似的場景,但不幸的是不適用於這里。這種場景是您有一個正在運行的父程序,並且它想要啟動一個非提升的子進程。典型的例子是一個安裝程序,像安裝程序一樣運行提升,它想在正常用戶級別啟動剛剛安裝的程序,就在它退出之前。有很多發布的代碼如何做到這一點,我已經基於其中一些技術進行了嘗試,但這確實是一個不同的場景,解決方案在我的情況下不起作用。最大的區別是他們試圖啟動的子程序在這種情況下沒有標有最高可用 - 孩子只是一個正常的程序,在正常情況下會在沒有任何 UAC 參與的情況下啟動。還有另一個區別,那就是在這些情況下,父母已經是運行提升,而在我的場景中,父級以普通用戶級別運行; 這稍微改變了一些事情,因為在另一個場景中的父進程可以訪問我無法使用的令牌上的一些特權操作,因為 A 本身沒有提升。 但據我所知,那些特權令牌操作無論如何也無濟於事; 事實上,孩子擁有最高可用標志,這是我的場景的關鍵元素。)

設置__COMPAT_LAYER環境變量RunAsInvoker在你的進程。 我認為這在任何地方都沒有正式記錄,但它一直工作到 Vista。

您還可以通過在注冊表中的AppCompatFlags\\Layers項下設置它來使其永久化。

可能的黑客解決方案從未提升的管理員用戶(受限管理員)調用CreateProcesshighestAvailable清單中的highestAvailable (或來自任何未提升用戶的requireAdministrator exe) - 這是鈎子RtlQueryElevationFlags調用並將返回的標志設置為 0。這是當前工作,但是當然沒有任何受贈者可以在下一個版本的 Windows 中工作,如果有什么改變的話。 然而照原樣。

對於鈎子單次 api 調用 - 我們可以將硬件斷點設置為函數地址和VEX 處理程序 演示工作代碼:

NTSTATUS NTAPI hookRtlQueryElevationFlags (DWORD* pFlags) 
{
    *pFlags = 0;
    return 0;
}

PVOID pvRtlQueryElevationFlags;

LONG NTAPI OnVex(::PEXCEPTION_POINTERS ExceptionInfo)
{
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP &&
        ExceptionInfo->ExceptionRecord->ExceptionAddress == pvRtlQueryElevationFlags)
    {
        ExceptionInfo->ContextRecord->
#if defined(_X86_)
        Eip
#elif defined (_AMD64_)
        Rip 
#else
#error not implemented
#endif
             = (ULONG_PTR)hookRtlQueryElevationFlags;

        return EXCEPTION_CONTINUE_EXECUTION;
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

ULONG exec(PCWSTR lpApplicationName)
{
    ULONG dwError = NOERROR;

    if (pvRtlQueryElevationFlags = GetProcAddress(GetModuleHandle(L"ntdll"), "RtlQueryElevationFlags"))
    {
        if (PVOID pv = AddVectoredExceptionHandler(TRUE, OnVex))
        {
            ::CONTEXT ctx = {};
            ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
            ctx.Dr7 = 0x404;
            ctx.Dr1 = (ULONG_PTR)pvRtlQueryElevationFlags;

            if (SetThreadContext(GetCurrentThread(), &ctx))
            {
                STARTUPINFO si = {sizeof(si)};
                PROCESS_INFORMATION pi;
                if (CreateProcessW(lpApplicationName, 0, 0, 0, 0, 0, 0, 0, &si,&pi))
                {
                    CloseHandle(pi.hThread);
                    CloseHandle(pi.hProcess);
                }
                else
                {
                    dwError = GetLastError();
                }

                ctx.Dr7 = 0x400;
                ctx.Dr1 = 0;
                SetThreadContext(GetCurrentThread(), &ctx);
            }
            else
            {
                dwError = GetLastError();
            }
            RemoveVectoredExceptionHandler(pv);
        }
        else
        {
            dwError = GetLastError();
        }
    }
    else
    {
        dwError = GetLastError();
    }

    return dwError;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM