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

标签 windows winapi legacy

我的原型(prototype)代码在我测试过的所有 Windows 操作系统上都运行良好,Windows XP 除外。

当我在 Windows XP 上以管理员身份运行此程序时,在调用 OpenProcessToken 时,系统提示我访问被拒绝 (5)

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

#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 进程在 token 上有下一个 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) 访问权限

在任何其他帐户下运行的进程在 token 上有下一个 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'具体 用户的完全访问权限。但不授予对 Administrators 组的任何访问权限 (S-1-5-32-544)。因此您可以打开在相同 用户下运行的任何进程 token 。但是如果您尝试打开在另一个用户(另一个 Sid)下运行的进程 token ,您将无法访问它并且访问被拒绝。你甚至无法阅读它 dacl(你不是所有者,也没有 READ_CONTROL)。

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

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

实用代码:

#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]

关于windows - OpenProcessToken 在 Windows XP 上因访问被拒绝 (5) 而失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47426157/

相关文章:

c# - 如何检测 Windows 登录事件?

C++: gethostname() 失败

c - udp 套接字中接收缓冲区/队列的最大大小

windows - 如何将 Windows GUI 应用程序作为服务运行?

java - 如何使用 HttpURLConnection 在 Java 中等待 Expect 100-Continue 响应

C# ML.Net 图像分类 : Does GPU acceleration help improve the performance of predictions and how can I tell if it is?

windows - 在 Xampp 之后安装 Zend Framework

c - XC32 从 1.32 迁移到 1.34

java - 从 Java 中生成的文件路径在 Android 中创建的 File 对象上的 file.getName() 产生奇怪的结果

Windows 更新错误 0x80244022