CreateProcessWithTokenW的cmdline参数的最大长度是否有变通方法?

问题描述 投票:1回答:2

我正在尝试从Windows 7-10上的升级过程创建一个非高效的过程。

我使用以下作为参考:

FAQ: How do I start a program as the desktop user from an elevated app?

现在,这种方法非常有效,但是由于可能的遗留检查,似乎CreateProcessWithTokenW()只允许cmdline参数小于或等于1024个字符。

我需要通过的cmdline远不止于此,导致E_INVALIDARG错误。

有没有人遇到和我一样的问题?如果是这样,你是如何解决这个绝对荒谬的1024字符限制?

c++ winapi
2个回答
0
投票

也许尝试使用CreateProcessAsUserW()代替?我知道它需要特殊权限,但如果您的进程以管理员身份运行,则有一种方法可以克服它。

我们的想法是在调用API之前模拟强大的令牌。强大的令牌被定义为拥有所有所需权限的令牌。

首先,通过像NtQuerySystemInformation()这样的API枚举进程ID。对于每个进程ID,打开进程令牌并使用GetTokenInformation(hToken, TokenPrivileges, ...)获取令牌拥有的特权的信息。如果令牌拥有我们想要的所有权限,我们可以通过DuplicateTokenEx(..., TokenImpersonation, &hTokenImp)从其中复制模拟令牌(并且当然要停止枚举)。

现在我们拥有了强大的令牌,我们将检查它所需的权限,看看它们是否都已启用。使用PrivilegeCheck()检查状态并使用AdjustTokenPrivileges()启用它。最后,让当前线程通过SetThreadToken(NULL, hTokenImp)模拟令牌,然后调用你喜欢的CreateProcessAsUserW(),并停止SetThreadToken(NULL, NULL)的模仿。

至于CreateProcessAsUserW()的令牌,我建议你使用WTSQueryUserToken()使用当前用户会话的主令牌,因为即使你做了OTS elevation它也会按预期工作。上次我尝试使用链接令牌,如here所述,并发现它不适用于OTS提升。

这是ANSI C中的测试代码。以管理员身份运行时,它将自动运行另一个自身实例并显示命令行长度。是的,CreateProcessAsUserW()支持超过1024的命令行。:)

#include <stdio.h>
#include <Windows.h>
#include <objbase.h>
#include "EnumProcessesId.h"

#define MY_LuidEqual(a, b)  \
    ( ((a)->HighPart == (b)->HighPart) && ((a)->LowPart == (b)->LowPart) )

static LPVOID MyAllocZero(SIZE_T cb)
{
    LPVOID ptr = CoTaskMemAlloc(cb);
    if (ptr) { ZeroMemory(ptr, cb); }
    return ptr;
}
static void MyFree(LPVOID ptr)
{
    CoTaskMemFree(ptr);
}

static DWORD MyWTSGetActiveConsoleSessionId(void)
{
    typedef DWORD(WINAPI *fn_t)(void);
    fn_t fn = (fn_t)GetProcAddress(LoadLibraryA("kernel32"),
        "WTSGetActiveConsoleSessionId");
    if (fn) { return fn(); }
    return 0;
}

static BOOL MyWTSQueryUserToken(DWORD sessId, HANDLE *phToken)
{
    typedef BOOL(WINAPI *fn_t)(DWORD, HANDLE*);
    fn_t fn = (fn_t)GetProcAddress(LoadLibraryA("wtsapi32"),
        "WTSQueryUserToken");
    if (fn) {
        return fn(sessId, phToken);
    }
    return FALSE;
}

static BOOL MyPrivIsEnabled(HANDLE hToken, LUID const *pPrivLuid)
{
    BOOL isEnabled = FALSE;
    PRIVILEGE_SET ps = { 0 };
    ps.PrivilegeCount = 1;
    ps.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT;
    PrivilegeCheck(hToken, &ps, &isEnabled);
    if (!isEnabled) {
        ps.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
        PrivilegeCheck(hToken, &ps, &isEnabled);
    }
    return isEnabled;
}

static HRESULT MyEnablePriv(HANDLE hToken, LUID const *pPrivLuid, BOOL enable)
{
    BOOL ok = FALSE;
    TOKEN_PRIVILEGES tp = { 0 };
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
    tp.Privileges[0].Luid = *pPrivLuid;
    ok = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
    return ok ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}

typedef struct {
    /* in : */
    LUID const *pLuidAPT;
    LUID const *pLuidIQN;
    LUID const *pLuidTCB;
    /* out : */
    HANDLE hptPowerful;
} MyEnumPowerfulTokenData_t;

static BOOL CALLBACK MyEnumPowerfulTokenProc(DWORD pid, void * user)
{
    DWORD const ProcessQueryLimitedInfo = 0x1000;
    MyEnumPowerfulTokenData_t *pData = user;
    BOOL ok = FALSE, wantContinue = TRUE;
    HANDLE hProc = NULL;
    HANDLE hProcToken = NULL;
    DWORD i = 0, cbTP = 0;
    TOKEN_PRIVILEGES *pTP = NULL;
    BOOL gotAPT = FALSE, gotIQN = FALSE, gotTCB = FALSE;

    /* Get process token */
    hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (!hProc) {
        hProc = OpenProcess(ProcessQueryLimitedInfo, FALSE, pid);
    }
    if (!hProc) goto eof;
    ok = OpenProcessToken(hProc, TOKEN_QUERY | TOKEN_DUPLICATE, &hProcToken);
    if (!ok) goto eof;

    /* Check if token possesses desired privileges */
    GetTokenInformation(hProcToken, TokenPrivileges, NULL, 0, &cbTP);
    if (!cbTP) goto eof;
    pTP = MyAllocZero(cbTP);
    if (!pTP) goto eof;
    ok = GetTokenInformation(hProcToken, TokenPrivileges, pTP, cbTP, &cbTP);
    if (!ok) goto eof;
    for (i = 0; i < pTP->PrivilegeCount; ++i)
    {
        LUID const *pThat = &(pTP->Privileges[i].Luid);
        if (gotAPT && gotIQN && gotTCB) {
            wantContinue = FALSE;
            pData->hptPowerful = hProcToken;
            hProcToken = NULL; /* to avoid eof CloseHandle() */
            break;
        }
        else if (!gotAPT && MY_LuidEqual(pThat, pData->pLuidAPT)) {
            gotAPT = TRUE;
        }
        else if (!gotIQN && MY_LuidEqual(pThat, pData->pLuidIQN)) {
            gotIQN = TRUE;
        }
        else if (!gotTCB && MY_LuidEqual(pThat, pData->pLuidTCB)) {
            gotTCB = TRUE;
        }
    }
eof:
    if (pTP) { MyFree(pTP); }
    if (hProcToken) { CloseHandle(hProcToken); }
    if (hProc) { CloseHandle(hProc); }
    return wantContinue;
}

static HRESULT MyCreateProcess(LPWSTR szCmdLine)
{
    HRESULT hr = 0;
    BOOL ok = FALSE;
    LUID luidAPT = { 0 }; /* SE_ASSIGNPRIMARYTOKEN_NAME */
    LUID luidIQN = { 0 }; /* SE_INCREASE_QUOTA_NAME */
    LUID luidTCB = { 0 }; /* SE_TCB_NAME */
    MyEnumPowerfulTokenData_t enumData = { 0 };
    HANDLE hptPowerful = NULL; /* primary/process token */
    HANDLE hitPowerful = NULL; /* impersonation token */
    HANDLE hptCurrSessUser = NULL;
    DWORD dwCurrSessId = 0;
    STARTUPINFOW si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };

    ok = LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME, &luidAPT)
        && LookupPrivilegeValue(NULL, SE_INCREASE_QUOTA_NAME, &luidIQN)
        && LookupPrivilegeValue(NULL, SE_TCB_NAME, &luidTCB);
    if (!ok) {
        hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
    }
    enumData.pLuidAPT = &luidAPT;
    enumData.pLuidIQN = &luidIQN;
    enumData.pLuidTCB = &luidTCB;
    hr = EnumProcessesId_WinNT(MyEnumPowerfulTokenProc, &enumData);
    if (FAILED(hr)) goto eof;
    hptPowerful = enumData.hptPowerful;
    if (!hptPowerful) {
        hr = E_UNEXPECTED; goto eof;
    }

    ok = DuplicateTokenEx(hptPowerful, TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE,
        NULL, SecurityImpersonation, TokenImpersonation, &hitPowerful);
    if (!ok) {
        hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
    }
    if (!MyPrivIsEnabled(hitPowerful, &luidAPT))
    {
        hr = MyEnablePriv(hitPowerful, &luidAPT, TRUE);
        if (FAILED(hr)) goto eof;
    }
    if (!MyPrivIsEnabled(hitPowerful, &luidIQN))
    {
        hr = MyEnablePriv(hitPowerful, &luidIQN, TRUE);
        if (FAILED(hr)) goto eof;
    }
    if (!MyPrivIsEnabled(hitPowerful, &luidTCB))
    {
        hr = MyEnablePriv(hitPowerful, &luidTCB, TRUE);
        if (FAILED(hr)) goto eof;
    }
    ok = SetThreadToken(NULL, hitPowerful);
    if (!ok) {
        hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
    }

    dwCurrSessId = MyWTSGetActiveConsoleSessionId();
    ok = MyWTSQueryUserToken(dwCurrSessId, &hptCurrSessUser);
    if (!ok) {
        hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
    }
    ok = CreateProcessAsUserW(hptCurrSessUser, NULL, szCmdLine, 0, 0, 0, 0, 0, 0, &si, &pi);
    if (!ok) {
        hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
    }

eof:
    SetThreadToken(NULL, NULL);
    if (hptCurrSessUser) { CloseHandle(hptCurrSessUser); }
    if (hitPowerful) { CloseHandle(hitPowerful); }
    if (hptPowerful) { CloseHandle(hptPowerful); }
    if (FAILED(hr)) {
        printf("HRESULT = 0x%.8X \n", hr);
    }
    return 0;
}

int main(int argc, char **argv)
{
    if (argc > 1)
    {
        WCHAR szMsg[999] = {0};
        wsprintfW(szMsg,
            L"lstrlenW(GetCommandLineW()) = %i \n",
            lstrlenW(GetCommandLineW()));
        MessageBoxW(NULL, szMsg, L"Test", MB_ICONINFORMATION);
    }
    else
    {
        WCHAR szMyExePath[MAX_PATH] = {0};
        WCHAR szCmdLine[9999] = {0}, *p;
        GetModuleFileNameW(NULL, szMyExePath, MAX_PATH);
        wsprintfW(szCmdLine, L"\"%s\" ", szMyExePath);
        for (p = szCmdLine; *p; ++p);
        while (p < (szCmdLine + 9999 - 1))
        {
            *p++ = 'a';
        }
        MyCreateProcess(szCmdLine);
    }
    return 0;
}

我将把EnumProcessesId_WinNT()的实施留给你。该函数的原型是:

/* return TRUE to continue */
typedef BOOL(CALLBACK *EnumProcessesId_Callback_t)
    (DWORD pid, void * user);

EXTERN_C
HRESULT __stdcall
EnumProcessesId_WinNT(
    EnumProcessesId_Callback_t fnCallback,
    void *user
);

1
投票

对于exec而言,我们提升的进程(在同一个会话中)不会提升,我们需要做下一步:

  • 使用LUA_TOKEN通过CreateRestrictedToken从我们的提升令牌创建受限制的令牌
  • 在新令牌中设置中等完整性级别
  • 打电话给CreateProcessAsUser - 请注意

如果hToken是调用方主要令牌的受限版本,则不需要SE_ASSIGNPRIMARYTOKEN_NAME权限。

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

    ULONG cb = GetSidLengthRequired(1);

    TOKEN_MANDATORY_LABEL tml = { { (PSID)alloca(cb) } };

    ULONG dwError = NOERROR;

    if (CreateWellKnownSid(WinMediumLabelSid, 0, tml.Label.Sid, &cb) &&
        OpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY | 
        TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY, &hToken))
    {
        BOOL fOk = CreateRestrictedToken(hToken, LUA_TOKEN, 0, 0, 0, 0, 0, 0, &hLowToken);

        if (!fOk)
        {
            dwError = GetLastError();
        }

        CloseHandle(hToken);

        if (fOk)
        {
            if (SetTokenInformation(hLowToken, ::TokenIntegrityLevel, &tml, sizeof(tml)))
            {
                STARTUPINFOW si = { sizeof(si)};
                PROCESS_INFORMATION pi;

                if (CreateProcessAsUser(hLowToken, lpApplicationName, lpCommandLine, 0, 0, TRUE, 0, 0, 0, &si, &pi))
                {
                    CloseHandle(pi.hThread);
                    CloseHandle(pi.hProcess);
                }
                else
                {
                    dwError = GetLastError();
                }
            }
            else
            {
                dwError = GetLastError();
            }

            CloseHandle(hLowToken);
        }
    }
    else
    {
        dwError = GetLastError();
    }

    return dwError;
}
© www.soinside.com 2019 - 2024. All rights reserved.