如何“取消提升”流程

Jab*_*cky 3 c++ winapi

我有一个提升的进程,在用户在 UAC 对话框中回答“是”后启动。

该过程开始顺利,一切都按预期进行。

现在我需要在某个时刻“取消提升”该进程,换句话说,该进程不应完全提升,就像用户正常启动它一样。

示例场景

  • 用户A已登录
  • 用户 A 启动进程 P1,该进程将通过 UAC 提升
  • 提升的进程 P1 启动进程 P2,并且 P2 不应提升,而应在用户 A 下再次运行。

有没有办法做到这一点?

RbM*_*bMm 6

提升的进程具有链接令牌 - 它指的是非提升的用户会话。我们可以通过两种方式使用这个链接令牌:

第一种方式

  1. 获取它(为此,我们 在查询此令牌时TokenPrimary需要有)SE_TCB_PRIVILEGE
  2. 使用此令牌调用 CreateProcessAsUser。为此我们还需要 SE_ASSIGNPRIMARYTOKEN_PRIVILEGESE_INCREASE_QUOTA_PRIVILEGE
  3. 为了获得所有这些特权 - 枚举进程,查询它的令牌,如果进程令牌具有所有这 3 个特权 - 在调用之前模拟它CreateProcessAsUser。因为提升的令牌有 SE_DEBUG_PRIVILEGE任务是可能的

第二种方式:

  1. 从链接令牌查询登录会话 ID ( AuthenticationId from TOKEN_STATISTICS)

  2. 找到具有相同AuthenticationId进程中令牌的进程。

  3. 通过帮助将此进程用作父进程 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS

方式1的实现:

static volatile UCHAR guz;

ULONG RunNonElevated(HANDLE hToken, HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
    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))
                                    {
                                        TOKEN_LINKED_TOKEN tlt;
                                        if (GetTokenInformation(hMyToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
                                        {
                                            STARTUPINFO si = { sizeof (si) };
                                            PROCESS_INFORMATION pi;

                                            if (!CreateProcessAsUserW(tlt.LinkedToken, lpApplicationName, lpCommandLine, 
                                                NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
                                            {
                                                err = GetLastError();
                                            }

                                            CloseHandle(tlt.LinkedToken);

                                            if (err == NOERROR)
                                            {
                                                CloseHandle(pi.hThread);
                                                CloseHandle(pi.hProcess);
                                            }
                                        }
                                        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(HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
    static TOKEN_PRIVILEGES tp = {
        1, { { { SE_DEBUG_PRIVILEGE } , SE_PRIVILEGE_ENABLED } }
    };

    AdjustTokenPrivileges(hMyToken, FALSE, &tp, sizeof(tp), NULL, NULL);

    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(hToken, hMyToken, lpApplicationName, lpCommandLine);
                            CloseHandle(hToken);
                        }
                        else
                        {
                            err = GetLastError();
                        }
                        CloseHandle(hProcess);
                    }
                    else
                    {
                        err = GetLastError();
                    }
                }
            } while (err && Process32NextW(hSnapshot, &pe));
        }
        else
        {
            err = GetLastError();
        }
        CloseHandle(hSnapshot);
    }

    return err;
}

ULONG RunNonElevated(PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
    HANDLE hToken;

    ULONG err = NOERROR;

    if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, &hToken))
    {
        TOKEN_ELEVATION_TYPE tet;

        ULONG rcb;

        if (GetTokenInformation(hToken, ::TokenElevationType, &tet, sizeof(tet), &rcb))
        {
            if (tet == TokenElevationTypeFull)
            {
                RunNonElevated(hToken, lpApplicationName, lpCommandLine);
            }
            else
            {
                err = ERROR_ALREADY_ASSIGNED;
            }
        }
        else
        {
            err = GetLastError();
        }

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

    return err;
}
Run Code Online (Sandbox Code Playgroud)

方式2的实现:

ULONG CreateProcessEx(HANDLE hProcess,
                      PCWSTR lpApplicationName,
                      PWSTR lpCommandLine)
{

    SIZE_T Size = 0;

    STARTUPINFOEX si = { sizeof(si) };
    PROCESS_INFORMATION pi;

    InitializeProcThreadAttributeList(0, 1, 0, &Size);

    ULONG err = GetLastError();

    if (err = ERROR_INSUFFICIENT_BUFFER)
    {
        si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)alloca(Size);

        if (InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &Size))
        {
            if (UpdateProcThreadAttribute(si.lpAttributeList, 0, 
                PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), 0, 0) &&
                CreateProcessW(lpApplicationName, lpCommandLine, 0, 0, 0, 
                EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi))
            {
                CloseHandle(pi.hThread);
                CloseHandle(pi.hProcess);
            }
            else
            {
                err = GetLastError();
            }

            DeleteProcThreadAttributeList(si.lpAttributeList);
        }
        else
        {
            err = GetLastError();
        }
    }
    else
    {
        err = GetLastError();
    }

    return err;
}

ULONG CreateProcessEx(LUID AuthenticationId,
                      PCWSTR lpApplicationName,
                      PWSTR lpCommandLine)
{
    ULONG err = ERROR_NOT_FOUND;

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

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

        ULONG rcb;

        if (Process32First(hSnapshot, &pe))
        {
            err = ERROR_NOT_FOUND;
            BOOL found = FALSE;

            do 
            {
                if (pe.th32ProcessID && pe.th32ParentProcessID)
                {
                    if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_CREATE_PROCESS, FALSE, pe.th32ProcessID))
                    {
                        HANDLE hToken;                  

                        if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
                        {
                            TOKEN_STATISTICS ts;

                            if (GetTokenInformation(hToken, TokenStatistics, &ts, sizeof(ts), &rcb))
                            {
                                if (ts.AuthenticationId.LowPart == AuthenticationId.LowPart && 
                                    ts.AuthenticationId.HighPart == AuthenticationId.HighPart)
                                {
                                    found = TRUE;

                                    err = CreateProcessEx(hProcess,
                                        lpApplicationName,
                                        lpCommandLine);
                                }
                            }
                            CloseHandle(hToken);
                        }

                        CloseHandle(hProcess);
                    }
                }

            } while (!found && Process32Next(hSnapshot, &pe));
        }
        else
        {
            err = GetLastError();
        }

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

    return err;
}

ULONG CreateProcessEx(PCWSTR lpApplicationName,
                      PWSTR lpCommandLine)
{
    HANDLE hToken;

    ULONG err = NOERROR;

    if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken))
    {
        union {
            TOKEN_ELEVATION_TYPE tet;
            TOKEN_LINKED_TOKEN tlt;
        };

        ULONG rcb;

        if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb))
        {
            if (tet == TokenElevationTypeFull)
            {
                if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
                {
                    TOKEN_STATISTICS ts;

                    BOOL fOk = GetTokenInformation(tlt.LinkedToken, TokenStatistics, &ts, sizeof(ts), &rcb);

                    CloseHandle(tlt.LinkedToken);

                    if (fOk)
                    {
                        err = CreateProcessEx(ts.AuthenticationId,
                            lpApplicationName,
                            lpCommandLine);
                    }
                    else
                    {
                        err = GetLastError();
                    }
                }
                else
                {
                    err = GetLastError();
                }
            }
            else
            {
                err = ERROR_ALREADY_ASSIGNED;
            }
        }
        else
        {
            err = GetLastError();
        }

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

    return err;
}
Run Code Online (Sandbox Code Playgroud)

并测试:

WCHAR ApplicationName[MAX_PATH];

if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
    WCHAR cmdline[] = L"cmd.exe /k whoami /priv /groups\r\n";
    CreateProcessEx(ApplicationName, cmdline);
    RunNonElevated(ApplicationName, cmdline);
}
Run Code Online (Sandbox Code Playgroud)

对于方法 #2 理论上,我们无法找到与AuthenticationId链接令牌中具有相同登录 ID () 的进程。但方法#1 总是必须有效。始终存在具有SeTcbPrivilege(用于获取链接令牌的主要形式)+ SeAssignPrimaryTokenPrivilege(用于CreateProcessAsUser)的系统进程(SeIncreaseQuotaPrivilege作为典型要求在msdn中监听,CreateProcessAsUser但在我的测试中,即使未启用此权限,这也能工作)。然而,所有系统进程(以 运行LocalSystem)在令牌中都具有这 3 个特权(从 开始smss.exe),并且某些系统进程始终在 system.h 中运行。

所以方法#1 永远不会失败并且是首选。我们也可以在这里使用例如从我们的进程继承的句柄,以便与子进程交互。这在方式#2 中是不可能的。它是为了图片的完整性而显示的


在开始时,我们检查TOKEN_ELEVATION_TYPE并执行工作,只有当它是时TokenElevationTypeFull。如果TokenElevationTypeLimited我们不提升进程 - 那么什么也不做。情况TokenElevationTypeDefault意味着或 UAC 如果关闭(LUA 禁用)或者我们作为内置管理员运行,并且 lua 不会过滤此帐户的令牌(因此所有进程都被“提升”或更确切地说,它不会通过 过滤令牌CreateRestrictedToken(..LUA_TOKEN..)) - 在这种情况下也没有感觉尝试在此用户下运行“未提升”进程