如何以普通用户身份启动流程?

时间:2018-01-05 19:36:58

标签: c++ windows

有一个启动程序 - 一个C ++程序,您需要以管理员身份运行。启动程序也代表管理员启动另一个进程,因此,使用普通用户权限运行的第三方程序(AutoHotkey)无法访问它。第二个进程不需要管理员权限,因此我希望以普通用户的权限实现启动。怎么做?

目前我正在使用boost::process::system运行流程。

2 个答案:

答案 0 :(得分:3)

Raymond Chen有一篇关于这个主题的博客文章:

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

  

从一个不高尚的流程转变为一个升级的流程很容易。您可以通过将runas谓词传递给ShellExecute或ShellExecuteEx来运行具有提升的进程。

     

走另一条路是比较棘手的。首先,很难让你的令牌正确地去除高程性质。而另一方面,即使你能做到这一点,也不是正确的事情,因为没有提升的用户可能与提升的用户不同。

     

...

     

此处的解决方案是返回资源管理器并请求资源管理器为您启动该程序。由于资源管理器作为原始的非高级用户运行,因此程序...将作为[用户]运行。

他的文章提供了以下代码来启动一个非高效的流程,并详细解释了代码实际在做什么(下面提到的FindDesktopFolderView函数在他的Manipulating the positions of desktop icons博客文章中定义):< / p>

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

答案 1 :(得分:1)

如果我们以管理员身份运行(更具体的是,在令牌中启用了 S-1-5-32-544&#39;管理员&#39; 组),我们可以打开本地系统进程令牌(它授予“管理员”所需的所有权限。 所以我们可以做下一步:

  • 获取自我终端会话ID
  • 在令牌中启用调试权限,以便能够打开任何进程 PROCESS_QUERY_LIMITED_INFORMATION
  • 枚举系统中正在运行的进程
  • 使用PROCESS_QUERY_LIMITED_INFORMATION
  • 打开流程
  • 使用TOKEN_QUERY|TOKEN_DUPLICATE
  • 打开进程令牌
  • 查询令牌权限 - 它有 SE_ASSIGNPRIMARYTOKEN_PRIVILEGESE_INCREASE_QUOTA_PRIVILEGE - 它需要致电CreateProcessAsUserSE_TCB_PRIVILEGE 需要WTSQueryUserToken
  • 如果令牌具有所有这些权限(LocalSystem令牌具有) - 将此令牌复制到TokenImpersonation类型。
  • 如果需要在令牌
  • 中启用此3个权限中的某些权限
  • 使用此令牌冒充

现在我们可以

  • 使用自我会话ID调用WTSQueryUserToken - 因为没有提升 用户令牌
  • ,最后CreateProcessAsUser使用此令牌
  • 重置模仿

代码:

static volatile UCHAR guz;

ULONG RunNonElevated(_In_ ULONG SessionId,
                     _In_ HANDLE hToken, 
                     _In_opt_ LPCWSTR lpApplicationName,
                     _Inout_opt_ LPWSTR lpCommandLine,
                     _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
                     _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
                     _In_ BOOL bInheritHandles,
                     _In_ DWORD dwCreationFlags,
                     _In_opt_ LPVOID lpEnvironment,
                     _In_opt_ LPCWSTR lpCurrentDirectory,
                     _In_ LPSTARTUPINFOW lpStartupInfo,
                     _Out_ LPPROCESS_INFORMATION lpProcessInformation)
{
    ULONG err;

    PVOID stack = alloca(guz);

    ULONG cb = 0, rcb = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[SE_MAX_WELL_KNOWN_PRIVILEGE]);

    union {
        PVOID buf;
        ::PTOKEN_PRIVILEGES ptp;
    };

    do 
    {
        if (cb < rcb)
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (GetTokenInformation(hToken, ::TokenPrivileges, buf, cb, &rcb))
        {
            if (ULONG PrivilegeCount = ptp->PrivilegeCount)
            {
                int n = 3;
                BOOL fAdjust = FALSE;

                ::PLUID_AND_ATTRIBUTES Privileges = ptp->Privileges;
                do 
                {
                    switch (Privileges->Luid.LowPart)
                    {
                    case SE_ASSIGNPRIMARYTOKEN_PRIVILEGE:
                    case SE_INCREASE_QUOTA_PRIVILEGE:
                    case SE_TCB_PRIVILEGE:
                        if (!(Privileges->Attributes & SE_PRIVILEGE_ENABLED))
                        {
                            Privileges->Attributes |= SE_PRIVILEGE_ENABLED;
                            fAdjust = TRUE;
                        }

                        if (!--n)
                        {
                            err = NOERROR;

                            if (DuplicateTokenEx(hToken, 
                                TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE, 
                                0, ::SecurityImpersonation, ::TokenImpersonation, 
                                &hToken))
                            {
                                if (fAdjust)
                                {
                                    AdjustTokenPrivileges(hToken, FALSE, ptp, rcb, NULL, NULL);
                                    err = GetLastError();
                                }

                                if (err == NOERROR)
                                {
                                    if (SetThreadToken(0, hToken))
                                    {
                                        HANDLE hUserToken;
                                        if (WTSQueryUserToken(SessionId, &hUserToken))
                                        {
                                            if (!CreateProcessAsUserW(hUserToken, 
                                                lpApplicationName,
                                                lpCommandLine,
                                                lpProcessAttributes,
                                                lpThreadAttributes,
                                                bInheritHandles,
                                                dwCreationFlags,
                                                lpEnvironment,
                                                lpCurrentDirectory,
                                                lpStartupInfo,
                                                lpProcessInformation))
                                            {
                                                err = GetLastError();
                                            }

                                            CloseHandle(hUserToken);
                                        }
                                        else
                                        {
                                            err = GetLastError();
                                        }
                                        SetThreadToken(0, 0);
                                    }
                                    else
                                    {
                                        err = GetLastError();
                                    }
                                }

                                CloseHandle(hToken);
                            }
                            else
                            {
                                err = GetLastError();
                            }

                            return err;
                        }
                    }
                } while (Privileges++, --PrivilegeCount);
            }

            return ERROR_NOT_FOUND;
        }

    } while ((err = GetLastError()) == ERROR_INSUFFICIENT_BUFFER);

    return err;
}

ULONG RunNonElevated(_In_opt_ LPCWSTR lpApplicationName,
                     _Inout_opt_ LPWSTR lpCommandLine,
                     _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
                     _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
                     _In_ BOOL bInheritHandles,
                     _In_ DWORD dwCreationFlags,
                     _In_opt_ LPVOID lpEnvironment,
                     _In_opt_ LPCWSTR lpCurrentDirectory,
                     _In_ LPSTARTUPINFOW lpStartupInfo,
                     _Out_ LPPROCESS_INFORMATION lpProcessInformation
                     )
{

    ULONG SessionId;
    if (!ProcessIdToSessionId(GetCurrentProcessId(), &SessionId))
    {
        return GetLastError();
    }

    BOOLEAN b;
    RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, b);

    ULONG err = NOERROR;

    // much more effective of course use NtQuerySystemInformation(SystemProcessesAndThreadsInformation) here
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0), hToken;

    if (hSnapshot != INVALID_HANDLE_VALUE)
    {
        PROCESSENTRY32W pe = { sizeof(pe) };

        if (Process32FirstW(hSnapshot, &pe))
        {
            err = ERROR_NOT_FOUND;

            do 
            {
                if (pe.th32ProcessID && pe.th32ParentProcessID)
                {
                    if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID))
                    {
                        if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
                        {
                            err = RunNonElevated(
                                SessionId,
                                hToken, 
                                lpApplicationName,
                                lpCommandLine,
                                lpProcessAttributes,
                                lpThreadAttributes,
                                bInheritHandles,
                                dwCreationFlags,
                                lpEnvironment,
                                lpCurrentDirectory,
                                lpStartupInfo,
                                lpProcessInformation);

                            CloseHandle(hToken);
                        }
                        else
                        {
                            err = GetLastError();
                        }
                        CloseHandle(hProcess);
                    }
                    else
                    {
                        err = GetLastError();
                    }
                }
            } while (err && Process32NextW(hSnapshot, &pe));
        }
        else
        {
            err = GetLastError();
        }
        CloseHandle(hSnapshot);
    }

    return err;
}

并测试:

void test()
{
    STARTUPINFO si = { sizeof(si)};
    PROCESS_INFORMATION pi;
    WCHAR ApplicationName[MAX_PATH];
    if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
    {
        if (!RunNonElevated(ApplicationName, L"cmd /k whoami.exe /priv /groups",0,0,0,0,0,0,&si, &pi))
        {
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }
    }
}

有了这个,我们不仅可以运行不升级的进程,而且,如果需要,可以使用重复的句柄

启动它