【问题标题】:OpenProcessToken failing with Access Denied (5) on Windows XPOpenProcessToken 在 Windows XP 上因访问被拒绝 (5) 而失败
【发布时间】:2017-11-22 03:04:18
【问题描述】:

我的原型代码在我能够测试的所有 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);
}

【问题讨论】:

  • PROCESS_QUERY_LIMITED_INFORMATION 在 XP 上不存在,所以它甚至不应该走那么远。
  • 你说得对;更新了原型......但仍然得到相同的错误访问被拒绝
  • 您确定在 xp 上使用PROCESS_QUERY_INFORMATION 打开进程后仍然拒绝访问吗?不可能
  • 看看这是否有帮助groups.google.com/forum/#!topic/…
  • @TarunLalwani,我已经使用GetCurrentProcessId(); 进行了测试(根据链接中的示例),它会导致相同的错误。使用 drstrace 查看跟踪,我可以看到它在 NtOpenProcessToken 上失败,NTSTATUS 代码 0xC00000022(访问被拒绝)。

标签: windows winapi legacy


【解决方案1】:

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) 的任何访问权限。这样您就可以打开在 same 用户下运行的任何进程令牌。但是,如果您尝试在另一个用户(另一个 Sid)下运行的打开进程 toke,您将无法访问它并且访问被拒绝。您甚至无法阅读 dacl(您不是所有者,也没有 READ_CONTROL)。

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

但是,如果您拥有所有权权限 - 您可以先使用 WRITE_OWNER 打开令牌并将 self 设置为所有者,然后使用 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]

【讨论】:

  • 非常有帮助,我会确认的。
  • @wulfgarpro,请发布您使用的最终代码并将其添加到此答案中。以后参考会更方便
  • @TarunLalwani - 我在系统中添加了用于转储用户令牌的实用程序代码。它足够大并且使用了 ntapi,但是从 xp 到 win10 都可以正常工作
猜你喜欢
  • 2011-02-09
  • 2012-01-02
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
  • 2020-11-24
  • 2017-05-17
相关资源
最近更新 更多