Windows XP 上的 OpenProcessToken 因访问被拒绝 (5) 而失败

OpenProcessToken failing with Access Denied (5) on Windows XP

我的原型代码在我能够测试的所有 Windows OS 上都运行良好,Windows XP 除外。

当我 运行 作为管理员在 Windows XP 上执行此操作时,我在调用 时得到 访问被拒绝 (5) OpenProcessToken

有什么我不知道的区别吗?

#include "stdafx.h"
#include <Windows.h>
#include <userenv.h>

#pragma comment(lib, "userenv")

void DisplayError(LPWSTR pszAPI)
{
    LPVOID lpvMessageBuffer;

    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPWSTR)&lpvMessageBuffer, 0, NULL);

    wprintf(L"ERROR: API        = %s.\n", pszAPI);
    wprintf(L"       error code = %d.\n", GetLastError());
    wprintf(L"       message    = %s.\n", (LPWSTR)lpvMessageBuffer);

    LocalFree(lpvMessageBuffer);

    ExitProcess(GetLastError());
}

void SetDebugPrivileges()
{
    void* tokenHandle;

    TOKEN_PRIVILEGES privilegeToken;
    LookupPrivilegeValue(0, SE_DEBUG_NAME, &privilegeToken.Privileges[0].Luid);
    privilegeToken.PrivilegeCount = 1;
    privilegeToken.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle);
    AdjustTokenPrivileges(tokenHandle, 0, &privilegeToken, sizeof(TOKEN_PRIVILEGES), 0, 0);
    CloseHandle(tokenHandle);
}

void wmain(int argc, WCHAR *argv[])
{
    DWORD     dwSize;
    HANDLE    hToken;
    LPVOID    lpvEnv;
    PROCESS_INFORMATION pi = { 0 };
    STARTUPINFO         si = { 0 };
    WCHAR               szUserProfile[256] = L"";

    si.cb = sizeof(STARTUPINFO);

    if (argc != 4)
    {
        wprintf(L"Usage: %s [user@domain] [password] [cmd]", argv[0]);
        wprintf(L"\n\n");
        return;
    }

    if (!LogonUser(argv[1], NULL, argv[2], LOGON32_LOGON_INTERACTIVE,
        LOGON32_PROVIDER_DEFAULT, &hToken))
        DisplayError(L"LogonUser");

    if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
        DisplayError(L"CreateEnvironmentBlock");

    dwSize = sizeof(szUserProfile) / sizeof(WCHAR);

    if (!GetUserProfileDirectory(hToken, szUserProfile, &dwSize))
        DisplayError(L"GetUserProfileDirectory");

    if (!CreateProcessWithLogonW(argv[1], NULL, argv[2],
        LOGON_WITH_PROFILE, NULL, argv[3],
        CREATE_UNICODE_ENVIRONMENT, lpvEnv, szUserProfile,
        &si, &pi))
        DisplayError(L"CreateProcessWithLogonW");

    if (!DestroyEnvironmentBlock(lpvEnv))
        DisplayError(L"DestroyEnvironmentBlock");

    //Sleep(5000);

    SetDebugPrivileges();

    HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pi.dwProcessId);
    if(process == NULL)
        DisplayError(L"OpenProcess");

    // Not working on Windows XP
    HANDLE token;
    if(!OpenProcessToken(process, TOKEN_QUERY, &token))
        DisplayError(L"OpenProcessToken");

    CloseHandle(token);
    CloseHandle(process);
    CloseHandle(hToken);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}

LocalSystem(S-1-5-18) 下 运行 的 XP 进程上有下一个 DACL 在令牌上:

A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 00020008 S-1-5-32-544 'Administrators'
Owner: S-1-5-32-544 'Administrators' @ 'BUILTIN' [Alias]

这意味着 SYSTEM 具有完全访问权限 TOKEN_ALL_ACCESS (000F01FF) 并且管理员组 (S-1-5-32-544) 的用户具有 READ_DACL|TOKEN_QUERY(00020008) 访问

任何其他帐户下 运行 的进程在令牌上有下一个 Dacl

A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 000F01FF <UserSid>
Owner: <UserSid>

其中 UserSid 一些唯一的(不是组!)sid。说服务 运行 不在 LocalSystem 下它看起来像 S-1-5-80-..,对于用户 - S-1-5-21-.. 这意味着此 dacl 授予 'SYSTEM'concrete 用户的完全访问权限。但不授予对管理员组的任何访问权限 (S-1-5-32-544)。因此您可以打开 相同 用户下 运行 的任何进程令牌。但是如果你尝试打开进程令牌,在另一个用户(另一个 Sid)下 运行 你没有任何访问权限并且访问被拒绝。你甚至无法阅读它 dacl(你不是所有者,也没有 READ_CONTROL)。

在代码中,您尝试从另一个用户进程(LogonUser 使用)打开令牌。在此令牌 dacl 中 - 没有您的用户 sid 或管理员 sid。结果访问被拒绝

但是,如果您拥有所有权权限 - 您可以先使用 WRITE_OWNER 打开令牌并将自己设置为所有者,然后使用 WRITE_DAC 打开它(所有者具有此访问权限)并更改 dacl。或者,如果您有调试权限,您可以使用 NtImpersonateThread 模拟系统线程并拥有对令牌的完全访问权限。

使用代码:

#ifdef __cplusplus
extern "C" {
#endif

NTSYSCALLAPI
NTSTATUS 
NTAPI 
NtOpenThread(
             _Out_ PHANDLE ThreadHandle, 
             _In_ ULONG DesiredAccess, 
             _In_ POBJECT_ATTRIBUTES ObjectAttributes, 
             _In_ PCLIENT_ID ClientId
             );

extern "C"
NTSYSCALLAPI
NTSTATUS
NTAPI
NtImpersonateThread(
                    _In_ HANDLE ServerThreadHandle,
                    _In_ HANDLE ClientThreadHandle,
                    _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos
                    );

#ifdef __cplusplus
}
#endif

ULONG gOsVersion;

volatile UCHAR guz;
OBJECT_ATTRIBUTES zoa = { sizeof(zoa) };

void GetVersionEx()
{
    RTL_OSVERSIONINFOW VersionInformation;
    RtlGetVersion(&VersionInformation);
    gOsVersion = (VersionInformation.dwMajorVersion << 8) + VersionInformation.dwMinorVersion;
}

PCSTR GetSidNameUseName(::SID_NAME_USE snu)
{
    switch (snu)
    {
    case SidTypeUser: return "User";
    case SidTypeGroup: return "Group";
    case SidTypeDomain: return "Domain";
    case SidTypeAlias: return "Alias";
    case SidTypeWellKnownGroup: return "WellKnownGroup";
    case SidTypeDeletedAccount: return "DeletedAccount";
    case SidTypeInvalid: return "Invalid";
    case SidTypeUnknown: return "Unknown";
    case SidTypeComputer: return "Computer";
    case SidTypeLabel: return "Label";
    case SidTypeLogonSession: return "LogonSession";
    }
    return "?";
}

#define MAX_DOMAIN_NAME_LEN 128 

void DumpAcl(PACL acl, PCSTR caption)
{
    DbgPrint(caption);

    if (!acl)
    {
        DbgPrint("NULL\n");
        return;
    }

    USHORT AceCount = acl->AceCount;

    if (!AceCount)
    {
        DbgPrint("empty\n");
        return;
    }

    DbgPrint("T FL AcessMsK Sid\n");

    union {
        PVOID pv;
        PBYTE pb;
        PACE_HEADER pah;
        PACCESS_ALLOWED_ACE paaa;
    };

    pv = acl + 1;

    char sz[16], sz2[16];

    do
    {
        switch (pah->AceType)
        {
        case ACCESS_ALLOWED_ACE_TYPE:
        case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
        case ACCESS_DENIED_ACE_TYPE:
        case ACCESS_DENIED_CALLBACK_ACE_TYPE:
        case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
            break;
        default:
            DbgPrint("AceType=%u\n", pah->AceType);
            continue;
        }

        UNICODE_STRING us;
        if (0 <= RtlConvertSidToUnicodeString(&us, (PSID)&paaa->SidStart, TRUE))
        {
            WCHAR name[256], DomainName[MAX_DOMAIN_NAME_LEN];
            ULONG cch = RTL_NUMBER_OF(name);
            ::SID_NAME_USE snu;
            DWORD cchReferencedDomainName = MAX_DOMAIN_NAME_LEN;

            if (!LookupAccountSidW(0, (PSID)&paaa->SidStart, name, &cch, DomainName, &cchReferencedDomainName, &snu))
            {
                name[0]=0;
            }

            ACCESS_MASK Mask = paaa->Mask;
            sprintf(sz2, "%08X", Mask);

            switch (pah->AceType)
            {
            case ACCESS_ALLOWED_ACE_TYPE:
            case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
                sz[0] = 'A', sz[1] = 0;
                break;
            case ACCESS_DENIED_ACE_TYPE:
            case ACCESS_DENIED_CALLBACK_ACE_TYPE:
                sz[0] = 'D', sz[1] = 0;
                break;
            case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
                sz[0] = 'L', sz[1] = 0;
                sz2[0] = Mask & SYSTEM_MANDATORY_LABEL_NO_READ_UP ? 'R' : ' ';
                sz2[1] = Mask & SYSTEM_MANDATORY_LABEL_NO_WRITE_UP ? 'W' : ' ';
                sz2[2] = Mask & SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP ? 'E' : ' ';
                sz2[3] = 0;
                break;
            default: __assume(false);
            }
            DbgPrint("%s %02X %s %wZ '%S'\n", sz, paaa->Header.AceFlags, sz2, &us, name);
            RtlFreeUnicodeString(&us);
        }

    } while (pb += pah->AceSize, --AceCount);
}

void Dump(HANDLE hToken)
{
    ULONG cb = 0, rcb = 128;

    PVOID stack = alloca(guz);

    union {
        PVOID buf;
        PSECURITY_DESCRIPTOR psd;
        PTOKEN_USER ptu;
    };

    UNICODE_STRING us;
    ::SID_NAME_USE snu;
    WCHAR name[256], DomainName[MAX_DOMAIN_NAME_LEN];
    ULONG cch, cchReferencedDomainName;

    NTSTATUS status;

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

        if (0 <= (status = NtQueryInformationToken(hToken, TokenUser, buf, cb, &rcb)))
        {
            if (0 <= RtlConvertSidToUnicodeString(&us, ptu->User.Sid, TRUE))
            {
                cch = RTL_NUMBER_OF(name);
                cchReferencedDomainName = RTL_NUMBER_OF(DomainName);

                if (!LookupAccountSidW(NULL, ptu->User.Sid, name, &cch, DomainName, &cchReferencedDomainName, &snu))
                {
                    *name = 0;
                    *DomainName = 0;
                }
                DbgPrint("User: %wZ '%S' @ '%S' [%s]\r\n", &us, name, DomainName, GetSidNameUseName(snu));
                RtlFreeUnicodeString(&us);
            }

            break;
        }

    } while (status == STATUS_BUFFER_TOO_SMALL);

    if (0 > status)
    {
        DbgPrint("TokenUser=%x\n", status);
    }

    SECURITY_INFORMATION SecurityInformation = gOsVersion < _WIN32_WINNT_VISTA 
        ? OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION 
        : OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION|LABEL_SECURITY_INFORMATION;

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

        if (0 <= (status = NtQuerySecurityObject(hToken, SecurityInformation, psd, cb, &rcb)))
        {
            PACL Acl;
            BOOLEAN bPresent, bDefault;

            if (0 <= RtlGetDaclSecurityDescriptor(psd, &bPresent, &Acl, &bDefault))
            {
                DumpAcl(bPresent ? Acl : 0, "DACL:\n");
            }

            if (0 <= RtlGetSaclSecurityDescriptor(psd, &bPresent, &Acl, &bDefault))
            {
                DumpAcl(bPresent ? Acl : 0, "LABEL:\n");
            }

            PSID Owner;
            if (0 <= RtlGetOwnerSecurityDescriptor(psd, &Owner, &bDefault) && Owner)
            {
                if (0 <= RtlConvertSidToUnicodeString(&us, Owner, TRUE))
                {
                    cch = RTL_NUMBER_OF(name);
                    cchReferencedDomainName = RTL_NUMBER_OF(DomainName);

                    if (!LookupAccountSidW(NULL, Owner, name, &cch, DomainName, &cchReferencedDomainName, &snu))
                    {
                        *name = 0;
                        *DomainName = 0;
                    }
                    DbgPrint("Owner: %wZ '%S' @ '%S' [%s]\r\n", &us, name, DomainName, GetSidNameUseName(snu));
                    RtlFreeUnicodeString(&us);
                }
            }
        }

    } while (status == STATUS_BUFFER_TOO_SMALL);

    if (0 > status)
    {
        DbgPrint("QuerySecurityObject=%x\n", status);
    }
}

void Dump(ACCESS_MASK DesiredAccess, PSYSTEM_PROCESS_INFORMATION pspi)
{
    HANDLE hProcess, hToken;

    CLIENT_ID cid = { pspi->UniqueProcessId };

    DbgPrint("==============\n%p %wZ\n", cid.UniqueProcess, &pspi->ImageName);

    NTSTATUS status = NtOpenProcess(&hProcess, DesiredAccess, &zoa, &cid);

    if (0 > status)
    {
        DbgPrint("OpenProcess=%x\n", status);
        return;
    }

    status = NtOpenProcessToken(hProcess, READ_CONTROL|TOKEN_QUERY, &hToken);

    NtClose(hProcess);

    if (0 > status)
    {
        DbgPrint("OpenProcessToken=%x\n", status);
    }
    else
    {
        Dump(hToken);
        NtClose(hToken);
    }
}

void DumpProcessAndTokens(PVOID buf)
{
    union {
        PVOID pv;
        PBYTE pb;
        PSYSTEM_PROCESS_INFORMATION pspi;
    };

    pv = buf;

    ULONG NextEntryOffset = 0;

    ACCESS_MASK DesiredAccess = gOsVersion < _WIN32_WINNT_VISTA 
        ? PROCESS_QUERY_INFORMATION : PROCESS_QUERY_LIMITED_INFORMATION;

    do 
    {
        pb += NextEntryOffset;

        if (pspi->UniqueProcessId)
        {           
            Dump(DesiredAccess, pspi);
        }

    } while (NextEntryOffset = pspi->NextEntryOffset);
}

NTSTATUS GetSystemToken(PCLIENT_ID ClientId)
{
    HANDLE hThread;

    NTSTATUS status = NtOpenThread(&hThread, THREAD_DIRECT_IMPERSONATION, &zoa, ClientId);

    if (0 <= status)
    {
        static SECURITY_QUALITY_OF_SERVICE sqos = {
            sizeof sqos, SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE
        };

        if (0 <= (status = NtImpersonateThread(NtCurrentThread(), hThread, &sqos)))
        {
            HANDLE hToken;
            if (0 <= (status = NtOpenThreadTokenEx(NtCurrentThread(), TOKEN_QUERY,FALSE, 0, &hToken)))
            {

                ULONG cb = 0, rcb = 32;
                PVOID stack = alloca(guz);

                union {
                    PVOID buf;
                    PTOKEN_USER ptu;

                };

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

                    if (0 <= (status = NtQueryInformationToken(hToken, TokenUser, buf, cb, &rcb)))
                    {
                        static _SID LocalSystem = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_LOCAL_SYSTEM_RID } };

                        if (!RtlEqualSid(&LocalSystem, ptu->User.Sid))
                        {
                            RevertToSelf();
                            status = STATUS_SERVER_SID_MISMATCH;
                        }

                        break;
                    }

                } while (status == STATUS_BUFFER_TOO_SMALL);

                NtClose(hToken);
            }
        }

        NtClose(hThread);
    }

    return status;
}

NTSTATUS ImpersonateLocalSystem(PVOID buf)
{
    union {
        PVOID pv;
        PBYTE pb;
        PSYSTEM_PROCESS_INFORMATION pspi;
    };

    pv = buf;

    ULONG NextEntryOffset = 0;

    do 
    {
        pb += NextEntryOffset;

        if (pspi->UniqueProcessId && pspi->NumberOfThreads)
        {           
            if (0 <= GetSystemToken(&pspi->TH->ClientId))
            {
                return STATUS_SUCCESS;
            }
        }

    } while (NextEntryOffset = pspi->NextEntryOffset);

    return STATUS_UNSUCCESSFUL;
}

void DumpProcessTokens()
{
    BOOLEAN b;
    NTSTATUS status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &b);

    if (0 > status)
    {
        return ;
    }

    ULONG cb = 0x8000;

    do 
    {
        status = STATUS_INSUFFICIENT_RESOURCES;

        if (PVOID buf = new UCHAR[cb])
        {
            if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
            {
                if (0 <= ImpersonateLocalSystem(buf))
                {
                    DumpProcessAndTokens(buf);
                    RevertToSelf();
                }
            }

            delete [] buf;
        }

    } while (status == STATUS_INFO_LENGTH_MISMATCH);
}

    GetVersionEx();
    DumpProcessTokens();

一些来自 xp 的结果:

00000004 System
User: S-1-5-18 'SYSTEM' @ 'NT AUTHORITY' [User]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
LABEL:
NULL
Owner: S-1-5-32-544 'Administrators' @ 'BUILTIN' [Alias]

==============
0000021C smss.exe
User: S-1-5-18 'SYSTEM' @ 'NT AUTHORITY' [User]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 00020008 S-1-5-32-544 'Administrators'
LABEL:
NULL
Owner: S-1-5-32-544 'Administrators' @ 'BUILTIN' [Alias]
==============
000003B0 svchost.exe
User: S-1-5-20 'NETWORK SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 000F01FF S-1-5-80-979556362-403687129-3954533659-2335141334-1547273080 ''
LABEL:
NULL
Owner: S-1-5-80-979556362-403687129-3954533659-2335141334-1547273080 '' @ '' [WellKnownGroup]

==============
0000047C svchost.exe
User: S-1-5-20 'NETWORK SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 000F01FF S-1-5-20 'NETWORK SERVICE'
LABEL:
NULL
Owner: S-1-5-20 'NETWORK SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]

==============
000004C4 svchost.exe
User: S-1-5-19 'LOCAL SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 000F01FF S-1-5-19 'LOCAL SERVICE'
LABEL:
NULL
Owner: S-1-5-19 'LOCAL SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]

==============
000005EC explorer.exe
User: S-1-5-21-839522115-2025429265-725345543-500 'Administrator' @ '*' [User]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-21-839522115-2025429265-725345543-500 'Administrator'
A 00 000F01FF S-1-5-18 'SYSTEM'
LABEL:
NULL
Owner: S-1-5-21-839522115-2025429265-725345543-500 'Administrator' @ '*' [User]