概述

windows 服务中以管理员或普通权限启动进程

背景知识:UAC 与 Token 机制

在 Windows Vista 引入 UAC(User Account Control)之后,管理员账户登录时会得到两个令牌

令牌类型说明
Filtered Token(过滤令牌)默认分配给 explorer.exe 等用户进程,权限被削减
Linked Token(链接令牌)完整管理员权限令牌,仅在用户”以管理员身份运行”时激活

因此,从 explorer.exe 获取的令牌不一定是管理员令牌——UAC 开启时拿到的是 filtered token(普通用户权限)。

关键 API

API作用
GetTokenInformation(TokenElevation)检查令牌是否已提升(elevated)
GetTokenInformation(TokenLinkedToken)获取与 filtered token 关联的 linked token(完整管理员令牌)
DuplicateTokenEx复制令牌,生成可用于 CreateProcessAsUser 的主令牌

判断逻辑

graph TD
    A["explorer.exe 的令牌"] --> B{TokenElevation?}
    B -- "== TRUE<br/>UAC 关闭或已提权" --> C["已是管理员令牌"]
    C --> D["直接使用"]
    B -- "== FALSE<br/>UAC 已降权 (filtered token)" --> E["取 TokenLinkedToken"]
    E --> F["获得完整管理员令牌"]

管理员权限启动进程

C++cpp
// 管理员权限启动进程
#ifdef UNICODE
MMSYSSHARED_EXPORT bool CreateProcessWithAdmin(const std::wstring& exe, const std::wstring& param, bool show)
#else
MMSYSSHARED_EXPORT bool CreateProcessWithAdmin(const std::string& exe, const std::string& param, bool show)
#endif // UNICODE
{
    HANDLE hToken{ NULL };
    HANDLE hTokenDup{ NULL };
    LPVOID pEnvironment{ NULL };
    bool res{ false };
#ifdef UNICODE
    wchar_t* cmd = (wchar_t*)param.c_str();
#else
    char* cmd = (wchar_t*)param.c_str();
#endif
 
    do
    {
        if (exe.empty())
        {
            LOG_ERROR("exe is null!");
            break;
        }
        
        if (!GetTokenWithProcessName(L"explorer.exe", hToken))
        {
            LOG_ERROR("GetTokenWithProcessName Error: %u", GetLastError());
            break;
        }
 
        // ★ 检查令牌是否已提升(UAC 判断)
        // explorer.exe 在 UAC 开启时持有的是 filtered token(未提升)
        // 需要通过 TokenLinkedToken 获取完整管理员令牌
        TOKEN_ELEVATION elevation = { 0 };
        DWORD dwSize = 0;
        if (GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize))
        {
            if (!elevation.TokenIsElevated)
            {
                // 当前是 filtered token,获取 linked token(完整管理员令牌)
                TOKEN_LINKED_TOKEN linked = { 0 };
                DWORD dwLinkedSize = 0;
                if (GetTokenInformation(hToken, TokenLinkedToken, &linked, sizeof(linked), &dwLinkedSize))
                {
                    CloseHandle(hToken);
                    hToken = linked.LinkedToken;
                }
                else
                {
                    LOG_ERROR("GetTokenInformation(TokenLinkedToken) Error: %u", GetLastError());
                    // 无法获取 linked token,继续使用原令牌(降级为普通用户权限)
                }
            }
        }
 
        // 复制令牌,把调用方有效的所有访问权限给复制后的令牌.
        if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED/*TOKEN_ALL_ACCESS*/, NULL/*&sa*/, SecurityImpersonation, TokenPrimary, &hTokenDup))
        {
            LOG_ERROR("DuplicateTokenEx Error: %u", GetLastError());
            break;
        }
        STARTUPINFO si;
        ZeroMemory(&si, sizeof(STARTUPINFO));
        si.cb = sizeof(STARTUPINFO);
#ifdef UNICODE
        wchar_t desk[]{ TEXT("WinSta0\\Default") };
#else
        char desk[]{ TEXT("WinSta0\\Default") };
#endif
        si.lpDesktop = desk;
        if (show)
        {
            si.wShowWindow = SW_SHOW;
        }
        else
        {
            si.wShowWindow = SW_HIDE;
        }
        si.dwFlags = STARTF_USESHOWWINDOW;
        PROCESS_INFORMATION pi;
        // 检索指定用户的环境变量。然后,可以将此块传递给 CreateProcessAsUser 函数。
        if (!CreateEnvironmentBlock(&pEnvironment, hTokenDup, FALSE))
        {
            LOG_ERROR("CreateEnvironmentBlock Error: %u", GetLastError());
            break;
        }
        // 缺少环境变量时某些依赖环境变量的程序打不开,或者运行不正常。
        if (!CreateProcessAsUser(hTokenDup, exe.c_str(), cmd, NULL, NULL, FALSE
            , NORMAL_PRIORITY_CLASS 
            /*| CREATE_NEW_CONSOLE  */ 
            | CREATE_UNICODE_ENVIRONMENT
            , pEnvironment, NULL, &si, &pi))
        {
            LOG_ERROR("CreateProcessAsUser Error: %u", GetLastError());
            break;
        }
        res = true;
    } while (0);
    // 清理
    if (cmd)
    {
        delete[]cmd;
    }
    if (pEnvironment)
    {
        DestroyEnvironmentBlock(pEnvironment);
    }
    if(hToken)
        CloseHandle(hToken);
    if (hTokenDup)
        CloseHandle(hTokenDup);
    return res;
}

普通用户权限启动进程

C++cpp
// 普通用户权限启动进程
#ifdef UNICODE
bool CreateProcessWithUser(const std::wstring& exePath, const std::wstring& param, bool show)
#else
bool CreateProcessWithUser(const std::string& exePath, const std::string& param, bool show)
#endif // UNICODE
{
    HANDLE hToken = 0;
    HANDLE hNewToken = 0;
    LPVOID pEnvironment{ NULL };
    bool res{ false };
    int l = param.length();
#ifdef UNICODE
    wchar_t* cmd = new wchar_t[l + 1];
    memcpy(cmd, param.c_str(), l * sizeof(wchar_t));
    cmd[l] = 0;
#else
    char* cmd = new char[l + 1];
    memcpy(cmd, param.c_str(), l * sizeof(char));
    cmd[l] = 0;
#endif // UNICODE
 
    do 
    {
        if (!GetTokenWithProcessName(L"explorer.exe", hToken))
        {
            LOG_ERROR("GetTokenWithProcessName Error: %u", GetLastError());
            break;
        }
 
        // ★ 确保使用的是 filtered token(普通用户权限)
        // 如果 explorer.exe 持有的恰好是提升后的令牌(UAC 关闭场景),
        // 则无需处理;UAC 开启时 explorer.exe 持有的就是 filtered token
        TOKEN_ELEVATION elevation = { 0 };
        DWORD dwSize = 0;
        if (GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize))
        {
            if (elevation.TokenIsElevated)
            {
                // UAC 关闭场景:explorer.exe 持有完整管理员令牌
                // 此处不做降权处理,因为无法从完整令牌"降回" filtered token
                // 如需严格降权,应使用 CreateRestrictedToken 或以其他方式获取低权限令牌
                LOG_ERROR("Warning: explorer.exe token is elevated, process will run with admin rights");
            }
        }
 
        if (!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL/*&sa*/, SECURITY_MAX_IMPERSONATION_LEVEL, TokenPrimary, &hNewToken))
        {
            LOG_ERROR("DuplicateTokenEx Error: %u", GetLastError());
            break;
        }
        DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS /*| CREATE_NEW_CONSOLE*/ | CREATE_UNICODE_ENVIRONMENT;
        // 检索指定用户的环境变量。然后,可以将此块传递给 CreateProcessAsUser 函数。
        if (!CreateEnvironmentBlock(&pEnvironment, hNewToken, FALSE))
        {
            LOG_ERROR("CreateEnvironmentBlock Error: %u", GetLastError());
            break;
        }
        STARTUPINFO si;
        PROCESS_INFORMATION pi;
        ZeroMemory(&si, sizeof(STARTUPINFO));
        si.cb = sizeof(STARTUPINFO);
#ifdef UNICODE
        wchar_t desktop[] = L"winsta0\\default";
#else
        char desktop[] = "winsta0\\default";
#endif 
        si.lpDesktop = desktop;
        si.dwFlags = STARTF_USESHOWWINDOW;
        if (show)
        {
            si.wShowWindow = SW_SHOW;
        }
        else
        {
            si.wShowWindow = SW_HIDE;
        }
        if (!CreateProcessAsUser(hNewToken, exePath.c_str(), cmd, 0, 0, FALSE, dwCreationFlag, pEnvironment, 0, &si, &pi))
        {
#ifdef UNICODE
            std::string ansicmd = mm::Charset::UnicodeToANSI(cmd);
            std::string ansibat = mm::Charset::UnicodeToANSI(exePath.c_str());
            LOG_ERROR("CreateProcessAsUser error! LastError=%ld, %s, %s", GetLastError(), ansibat.c_str(), ansicmd.c_str());
#else
            LOG_ERROR("CreateProcessAsUser error! LastError=%ld, %s, %s", GetLastError(), exePath.c_str(), cmd.c_str());
#endif // UNICODE
            break;
        }
        res = true;
    } while (0);
    // 清理
    delete[] cmd;
    if (hToken)
    {
        CloseHandle(hToken);
    }
    if (hNewToken) 
    {
        CloseHandle(hNewToken);
    }
    if (pEnvironment)
    {
        DestroyEnvironmentBlock(pEnvironment);
    }
    return res;
}

根据进程名获取进程句柄

C++cpp
#ifdef UNICODE
    bool GetTokenWithProcessName(const wchar_t* szName, HANDLE& hToken)
#else
    bool GetTokenWithProcessName(const char* szName, HANDLE& hToken)
#endif // _DEBUG
    {
        // HANDLE hToken{ NULL };
        HANDLE hProcessSnap{ NULL };
        PROCESSENTRY32 pe32{ NULL }; 
        HANDLE hProcess{ NULL };
        bool res{ false };
        do 
        {
            // 多用户模式时任务管理器里可能出现多个explorer
            // 需要先获取当前会话ID,再通过枚举进程,通过比较sessionID进而得到token。
            //DWORD dwSessionId = WTSGetActiveConsoleSessionId();
            //PWTS_PROCESS_INFO ppi = NULL;
            //DWORD dwProcessCount = 0;
            //if (WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0, 1, &ppi, &dwProcessCount))
            //{
            //    for (int i = 0; i < dwProcessCount; i++)
            //    {      
            //        if (_wcsicmp(ppi[i].pProcessName, L"explorer.exe") == 0)
            //        {
            //            if (ppi[i].SessionId == dwSessionId)
            //            {
            //                break;
            //            }
            //        }
            //    }
            //    WTSFreeMemory(ppi);
            //}
            hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);       
            if (!hProcessSnap)
            {
                LOG_ERROR("CreateToolhelp32Snapshot error! %d", GetLastError());
                break;
            }
            pe32.dwSize = sizeof(PROCESSENTRY32);
            for (Process32First(hProcessSnap, &pe32); Process32Next(hProcessSnap, &pe32);)
            {
#ifdef UNICODE
                if (_wcsicmp((pe32.szExeFile), szName))
#else
                if (_stricmp((pe32.szExeFile), szName))
#endif // _DEBUG
                    continue;
                hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
                if (!hProcess)
                {
                    LOG_ERROR("OpenProcess error! %d", GetLastError());
                    break;
                }
                BOOL ret = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);
                if(!ret)
                {
                    LOG_ERROR("OpenProcess error! %d", GetLastError());
                    break;
                }
                res = true;
                break;
            }
        } while (0);
       
        if (hProcessSnap)
        {
            CloseHandle(hProcessSnap);
        }
        if (hProcess)
        {
            CloseHandle(hProcess);
        }
        return res;
    }

完整 Demo 源码

INFO

以下为自包含的控制台 Demo 源码,去除了 MMSYSSHARED_EXPORTLOG_ERRORmm::Charset 等项目依赖,可直接编译运行。 项目地址:Windows-API-Usage/WinAPI/ServiceCreateProcessDemo/

C++cpp
// ServiceCreateProcessDemo.cpp
// 演示在 Windows 服务中以管理员/普通用户权限启动进程
//
// 关键知识点:
//   1. UAC 开启时 explorer.exe 持有 filtered token(普通用户权限)
//      需通过 TokenLinkedToken 获取完整管理员令牌
//   2. UAC 关闭时 explorer.exe 持有完整管理员令牌
//      无法降级为 filtered token,CreateProcessWithUser 会发出警告
//   3. 两个函数的核心差异不在 DuplicateTokenEx 参数,
//      而在于是否通过 GetTokenInformation 做权限提升/验证
 
#include <windows.h>
#include <tlhelp32.h>
#include <userenv.h>
#include <stdio.h>
#include <tchar.h>
#include <string>
 
#pragma comment(lib, "userenv.lib")
 
using namespace std;
 
// ============================================================================
// 根据进程名获取进程令牌
// ============================================================================
bool GetTokenWithProcessName(const wchar_t* szName, HANDLE& hToken)
{
    HANDLE hProcessSnap = NULL;
    PROCESSENTRY32W pe32 = { 0 };
    HANDLE hProcess = NULL;
    bool res = false;
 
    do
    {
        hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (hProcessSnap == INVALID_HANDLE_VALUE)
        {
            wprintf(L"[-] CreateToolhelp32Snapshot failed: %lu\n", GetLastError());
            break;
        }
 
        pe32.dwSize = sizeof(PROCESSENTRY32W);
        if (!Process32FirstW(hProcessSnap, &pe32))
        {
            wprintf(L"[-] Process32FirstW failed: %lu\n", GetLastError());
            break;
        }
 
        do
        {
            if (_wcsicmp(pe32.szExeFile, szName) != 0)
                continue;
 
            hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
            if (!hProcess)
            {
                wprintf(L"[-] OpenProcess failed: %lu\n", GetLastError());
                break;
            }
 
            if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken))
            {
                wprintf(L"[-] OpenProcessToken failed: %lu\n", GetLastError());
                break;
            }
 
            res = true;
            break;
        } while (Process32NextW(hProcessSnap, &pe32));
    } while (false);
 
    if (hProcessSnap)
        CloseHandle(hProcessSnap);
    if (hProcess)
        CloseHandle(hProcess);
 
    return res;
}
 
// ============================================================================
// 以管理员权限启动进程
// 从 explorer.exe 获取令牌后,检查 UAC 状态:
//   - TokenElevation == FALSE → filtered token,需获取 TokenLinkedToken(完整管理员令牌)
//   - TokenElevation == TRUE  → 已是管理员令牌,直接使用
// ============================================================================
bool CreateProcessWithAdmin(const std::wstring& exe, const std::wstring& param, bool show)
{
    HANDLE hToken = NULL;
    HANDLE hTokenDup = NULL;
    LPVOID pEnvironment = NULL;
    bool res = false;
 
    do
    {
        if (exe.empty())
        {
            wprintf(L"[-] exe path is empty\n");
            break;
        }
 
        if (!GetTokenWithProcessName(L"explorer.exe", hToken))
        {
            wprintf(L"[-] GetTokenWithProcessName failed: %lu\n", GetLastError());
            break;
        }
 
        // ★ UAC 权限提升:检查 explorer.exe 令牌是否已提升
        TOKEN_ELEVATION elevation = { 0 };
        DWORD dwSize = 0;
        if (GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize))
        {
            if (!elevation.TokenIsElevated)
            {
                wprintf(L"[*] Token is not elevated (UAC filtered), obtaining linked token...\n");
                // filtered token → 获取 linked token(完整管理员令牌)
                TOKEN_LINKED_TOKEN linked = { 0 };
                DWORD dwLinkedSize = 0;
                if (GetTokenInformation(hToken, TokenLinkedToken, &linked, sizeof(linked), &dwLinkedSize))
                {
                    CloseHandle(hToken);
                    hToken = linked.LinkedToken;
                    wprintf(L"[+] Linked token obtained (full admin rights)\n");
                }
                else
                {
                    wprintf(L"[-] GetTokenInformation(TokenLinkedToken) failed: %lu\n", GetLastError());
                    wprintf(L"    Continuing with filtered token (non-elevated)\n");
                }
            }
            else
            {
                wprintf(L"[*] Token is already elevated (UAC disabled or already admin)\n");
            }
        }
 
        if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &hTokenDup))
        {
            wprintf(L"[-] DuplicateTokenEx failed: %lu\n", GetLastError());
            break;
        }
 
        STARTUPINFOW si = { 0 };
        si.cb = sizeof(STARTUPINFOW);
        si.lpDesktop = (LPWSTR)L"WinSta0\\Default";
        si.wShowWindow = show ? SW_SHOW : SW_HIDE;
        si.dwFlags = STARTF_USESHOWWINDOW;
 
        PROCESS_INFORMATION pi = { 0 };
 
        if (!CreateEnvironmentBlock(&pEnvironment, hTokenDup, FALSE))
        {
            wprintf(L"[-] CreateEnvironmentBlock failed: %lu\n", GetLastError());
            break;
        }
 
        // 构建命令行(可变参数需要可写缓冲区)
        std::wstring cmdLine = param;
 
        if (!CreateProcessAsUserW(hTokenDup, exe.c_str(),
            cmdLine.empty() ? NULL : &cmdLine[0],
            NULL, NULL, FALSE,
            NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
            pEnvironment, NULL, &si, &pi))
        {
            wprintf(L"[-] CreateProcessAsUserW failed: %lu\n", GetLastError());
            break;
        }
 
        wprintf(L"[+] Process created successfully (PID: %lu)\n", pi.dwProcessId);
 
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
        res = true;
    } while (false);
 
    if (pEnvironment)
        DestroyEnvironmentBlock(pEnvironment);
    if (hToken)
        CloseHandle(hToken);
    if (hTokenDup)
        CloseHandle(hTokenDup);
 
    return res;
}
 
// ============================================================================
// 以普通用户权限启动进程
// 从 explorer.exe 获取令牌后,检查 UAC 状态:
//   - TokenElevation == FALSE → filtered token,正是我们需要的(普通用户权限)
//   - TokenElevation == TRUE  → UAC 关闭,explorer.exe 持有完整管理员令牌
//     无法降级为 filtered token,发出警告(进程将以管理员权限运行)
// ============================================================================
bool CreateProcessWithUser(const std::wstring& exePath, const std::wstring& param, bool show)
{
    HANDLE hToken = NULL;
    HANDLE hNewToken = NULL;
    LPVOID pEnvironment = NULL;
    bool res = false;
 
    do
    {
        if (!GetTokenWithProcessName(L"explorer.exe", hToken))
        {
            wprintf(L"[-] GetTokenWithProcessName failed: %lu\n", GetLastError());
            break;
        }
 
        // ★ UAC 权限检查:确认使用的是 filtered token(普通用户权限)
        TOKEN_ELEVATION elevation = { 0 };
        DWORD dwSize = 0;
        if (GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize))
        {
            if (elevation.TokenIsElevated)
            {
                // UAC 关闭场景:explorer.exe 持有完整管理员令牌
                // 无法从完整令牌"降回" filtered token
                // 如需严格降权,应使用 CreateRestrictedToken 或以其他方式获取低权限令牌
                wprintf(L"[!] Warning: token is elevated (UAC disabled), process will run with admin rights\n");
            }
            else
            {
                wprintf(L"[*] Token is filtered (standard user rights)\n");
            }
        }
 
        if (!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL, SECURITY_MAX_IMPERSONATION_LEVEL, TokenPrimary, &hNewToken))
        {
            wprintf(L"[-] DuplicateTokenEx failed: %lu\n", GetLastError());
            break;
        }
 
        DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT;
 
        if (!CreateEnvironmentBlock(&pEnvironment, hNewToken, FALSE))
        {
            wprintf(L"[-] CreateEnvironmentBlock failed: %lu\n", GetLastError());
            break;
        }
 
        STARTUPINFOW si = { 0 };
        si.cb = sizeof(STARTUPINFOW);
        si.lpDesktop = (LPWSTR)L"winsta0\\default";
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = show ? SW_SHOW : SW_HIDE;
 
        PROCESS_INFORMATION pi = { 0 };
 
        std::wstring cmdLine = param;
 
        if (!CreateProcessAsUserW(hNewToken, exePath.c_str(),
            cmdLine.empty() ? NULL : &cmdLine[0],
            NULL, NULL, FALSE, dwCreationFlag,
            pEnvironment, NULL, &si, &pi))
        {
            wprintf(L"[-] CreateProcessAsUserW failed: %lu\n", GetLastError());
            break;
        }
 
        wprintf(L"[+] Process created successfully (PID: %lu)\n", pi.dwProcessId);
 
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
        res = true;
    } while (false);
 
    if (pEnvironment)
        DestroyEnvironmentBlock(pEnvironment);
    if (hToken)
        CloseHandle(hToken);
    if (hNewToken)
        CloseHandle(hNewToken);
 
    return res;
}
 
// ============================================================================
// 主函数 - 用法演示
// ============================================================================
int wmain(int argc, wchar_t* argv[])
{
    wprintf(L"=== Service Create Process Demo ===\n\n");
 
    if (argc < 3)
    {
        wprintf(L"Usage:\n");
        wprintf(L"  %s --admin  <exe_path> [params]  - Launch with admin token (elevated via UAC linked token)\n", argv[0]);
        wprintf(L"  %s --user   <exe_path> [params]  - Launch with user token (filtered, non-elevated)\n", argv[0]);
        wprintf(L"\nExample:\n");
        wprintf(L"  %s --admin  C:\\Windows\\System32\\notepad.exe\n", argv[0]);
        wprintf(L"  %s --user   C:\\Windows\\System32\\notepad.exe\n", argv[0]);
        return 1;
    }
 
    // 校验 mode 是否合法,避免后续对未知模式无意义的处理
    std::wstring mode = argv[1];
    if (mode != L"--admin" && mode != L"--user")
    {
        wprintf(L"[-] Unknown mode: %s\n", mode.c_str());
        wprintf(L"    Use --admin or --user\n");
        return 1;
    }
 
    std::wstring exePath = argv[2];
    std::wstring params;
    if (argc >= 4)
    {
        params = argv[3];
        for (int i = 4; i < argc; i++)
        {
            params += L" ";
            params += argv[i];
        }
    }
 
    bool show = true;
 
    if (mode == L"--admin")
    {
        wprintf(L"[*] Creating process with admin token...\n");
        if (CreateProcessWithAdmin(exePath, params, show))
            wprintf(L"[+] Success!\n");
        else
            wprintf(L"[-] Failed!\n");
    }
    else if (mode == L"--user")
    {
        wprintf(L"[*] Creating process with user token...\n");
        if (CreateProcessWithUser(exePath, params, show))
            wprintf(L"[+] Success!\n");
        else
            wprintf(L"[-] Failed!\n");
    }
    else
    {
        wprintf(L"[-] Unknown mode: %s\n", mode.c_str());
        wprintf(L"    Use --admin or --user\n");
        return 1;
    }
 
    return 0;
}

两个函数的对比总结

对比项CreateProcessWithAdminCreateProcessWithUser
目标权限管理员(elevated)普通用户(non-elevated)
UAC 开启通过 TokenLinkedToken 获取完整管理员令牌explorer.exe 本身就是 filtered token,直接使用
UAC 关闭explorer.exe 已持有完整令牌,直接使用⚠️ 无法降权,进程仍以管理员权限运行(发出警告)
关键 APIGetTokenInformation(TokenElevation) + GetTokenInformation(TokenLinkedToken)GetTokenInformation(TokenElevation) 仅做检测

注意事项

  1. TokenLinkedToken 需要 SE_TCB_PRIVILEGE 权限:服务运行在 SYSTEM 账户下时默认拥有此权限;普通进程可能无法获取 linked token
  2. UAC 关闭时的降权问题CreateProcessWithUser 在 UAC 关闭时无法真正降权。如需严格降权,可考虑:
    • CreateRestrictedToken — 限制令牌中的权限组
    • WTSQueryUserToken — 直接查询当前会话用户的令牌
    • SaferCreateLevel + CreateProcessAsUser 实现强制降权
  3. 多用户会话GetTokenWithProcessName 会取到第一个匹配的 explorer.exe 进程。在多用户环境(RDP 多会话)中,应结合 WTSGetActiveConsoleSessionId 筛选正确的会话