PssCreateSnapshot - 访问被拒绝的奇怪情况

PssCreateSnapshot - access denied odd situation

我创建了一些代码,用于使用 PssCaptureSnapshot 执行进程克隆,然后执行克隆的小型转储。

但是,在某些进程中,当 运行ning PssCaptureSnapshot(运行ning 已提升)时,我的访问被拒绝。这根本不是问题,事实上,我无法执行克隆的进程也无法使用 ProcDump(来自 SysInternals 的工具)进行克隆。

然而,奇怪的是,如果我打开 PowerShell PSSession 到本地主机并从那里 运行 我的应用程序...创建克隆没有问题!

现在我的脑海里立刻浮现出……特权。所以我检查了 PSSession 内部的权限与外部的权限,然后......宾果游戏! PSSession 几乎拥有人类已知的所有特权,而在 PSSession 之外我只有少数特权。

没什么大不了的,我想...我会开始为自己分配权限,一次一个,中间调用 PssCaptureSnapshot。当我停止访问被拒绝时,我知道我需要哪种权限!

这个计划是万无一失的。也就是说...直到我 运行 没有权限分配并且我仍然被拒绝访问...

所以现在我真的很抓狂:为什么它在 PSSession 内部工作而不是外部,当所有特权(理论上)都相同时?我该如何进一步解决这个问题?

如有任何帮助,我们将不胜感激。

PS:如果您认为有帮助,我很乐意 post 编写代码。但请记住,我的访问被拒绝,事实上它在 PSSession 内部工作,但在 之外不工作,ProcDump 也不能为这些进程创建克隆。 .. 我认为代码不相关。

编辑

来自 whoami /all 的结果

本地会话:

USER INFORMATION
----------------

User Name          SID
================== ===========================================
xxxxxxxxxxxxxxxxxx S-1-5-21-1509752874-53682476-648048294-1107


GROUP INFORMATION
-----------------
(listing only groups that appear in this session, but don't appear in the other)
NT AUTHORITY\REMOTE INTERACTIVE LOGON       Well-known group S-1-5-14                                       Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\INTERACTIVE                    Well-known group S-1-5-4                                        Mandatory group, Enabled by default, Enabled group
LOCAL                                       Well-known group S-1-2-0


PRIVILEGES INFORMATION
----------------------

Privilege Name                  Description                               State
=============================== ========================================= ========
SeIncreaseQuotaPrivilege        Adjust memory quotas for a process        Disabled
SeSecurityPrivilege             Manage auditing and security log          Disabled
SeTakeOwnershipPrivilege        Take ownership of files or other objects  Disabled
SeLoadDriverPrivilege           Load and unload device drivers            Disabled
SeSystemProfilePrivilege        Profile system performance                Disabled
SeSystemtimePrivilege           Change the system time                    Disabled
SeProfileSingleProcessPrivilege Profile single process                    Disabled
SeIncreaseBasePriorityPrivilege Increase scheduling priority              Disabled
SeCreatePagefilePrivilege       Create a pagefile                         Disabled
SeBackupPrivilege               Back up files and directories             Disabled
SeRestorePrivilege              Restore files and directories             Disabled
SeShutdownPrivilege             Shut down the system                      Disabled
SeDebugPrivilege                Debug programs                            Enabled
SeSystemEnvironmentPrivilege    Modify firmware environment values        Disabled
SeChangeNotifyPrivilege         Bypass traverse checking                  Enabled
SeRemoteShutdownPrivilege       Force shutdown from a remote system       Disabled
SeUndockPrivilege               Remove computer from docking station      Disabled
SeManageVolumePrivilege         Perform volume maintenance tasks          Disabled
SeImpersonatePrivilege          Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege         Create global objects                     Enabled
SeIncreaseWorkingSetPrivilege   Increase a process working set            Disabled
SeTimeZonePrivilege             Change the time zone                      Disabled
SeCreateSymbolicLinkPrivilege   Create symbolic links                     Disabled

USER CLAIMS INFORMATION
-----------------------

User claims unknown.

Kerberos support for Dynamic Access Control on this device has been disabled.

内部 PS会话:

USER INFORMATION
----------------

User Name          SID
================== ===========================================
xxxxxxxxxxxxxxxxxx S-1-5-21-1509752874-53682476-648048294-1107


GROUP INFORMATION
-----------------
(listing only groups that appear in this session, but don't appear in the other)
NT AUTHORITY\NETWORK                        Well-known group S-1-5-2



PRIVILEGES INFORMATION
----------------------

Privilege Name                  Description                               State
=============================== ========================================= =======
SeIncreaseQuotaPrivilege        Adjust memory quotas for a process        Enabled
SeSecurityPrivilege             Manage auditing and security log          Enabled
SeTakeOwnershipPrivilege        Take ownership of files or other objects  Enabled
SeLoadDriverPrivilege           Load and unload device drivers            Enabled
SeSystemProfilePrivilege        Profile system performance                Enabled
SeSystemtimePrivilege           Change the system time                    Enabled
SeProfileSingleProcessPrivilege Profile single process                    Enabled
SeIncreaseBasePriorityPrivilege Increase scheduling priority              Enabled
SeCreatePagefilePrivilege       Create a pagefile                         Enabled
SeBackupPrivilege               Back up files and directories             Enabled
SeRestorePrivilege              Restore files and directories             Enabled
SeShutdownPrivilege             Shut down the system                      Enabled
SeDebugPrivilege                Debug programs                            Enabled
SeSystemEnvironmentPrivilege    Modify firmware environment values        Enabled
SeChangeNotifyPrivilege         Bypass traverse checking                  Enabled
SeRemoteShutdownPrivilege       Force shutdown from a remote system       Enabled
SeUndockPrivilege               Remove computer from docking station      Enabled
SeManageVolumePrivilege         Perform volume maintenance tasks          Enabled
SeImpersonatePrivilege          Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege         Create global objects                     Enabled
SeIncreaseWorkingSetPrivilege   Increase a process working set            Enabled
SeTimeZonePrivilege             Change the time zone                      Enabled
SeCreateSymbolicLinkPrivilege   Create symbolic links                     Enabled

USER CLAIMS INFORMATION
-----------------------

User claims unknown.

Kerberos support for Dynamic Access Control on this device has been disabled.

在对 cogumel0 进行了相对较长的研究后,我们找到了问题的确切根源。

首先我们发现了 PssCaptureSnapshot can failed when and only when it called with PSS_CAPTURE_VA_CLONE 标志 - 用于捕获进程中所有可克隆页面的快照。

尽管我们有进程句柄和所有必需的进程访问权限(比如 PROCESS_ALL_ACCESS)- 对于某些进程 PssCaptureSnapshot return 错误。为什么?

我注意到错误是 仅当进程在某些作业运行中。 (为了确定这一点,我们可以使用 IsProcessInJob)并且 PssCaptureSnapshot 在这种情况下可以 return 2 不同错误:

  • ERROR_NOT_ENOUGH_QUOTA - 我在 win8.1 和 win10 中查看 - 它 returned 说一些 chrome.exe 进程(不是全部)
  • ERROR_ACCESS_DENIED - 此错误仅在我们处理的过程中发生 想要 snap - 运行 在另一个 session (比较我们的过程)和这个 仅在 win8.1 - 在 win10 中没有这个错误,即使处理在 作业和另一个会话中。

为了理解为什么会发生这种情况 - 需要查看 PssCaptureSnapshot 内部如何实现 PSS_CAPTURE_VA_CLONE 语义。它通过分叉目标进程(是的,在 windows 下分叉)来完成。对于此任务,使用了未记录的 ZwCreateProcessEx api。当 SectionHandle == 0 (win32 CreateProcess 总是在此处传递在 exe 文件上创建的部分 (if say full tr​​ue now CreateProcess use another api, this was early)) ZwCreateProcessEx clone (fork) process (ParentProcess) 而不是创建新的一个基于 SectionHandle(基于某些 exe 文件)

但是如果 ParentProcess 在工作中 - 子进程(我们的分叉)也将被放置在这个工作中。这可能是问题。

第一个作业可以限制作业中的进程数。如果说作业有此限制 - 作业中不超过 1 个进程 - ZwCreateProcessEx 并因错误 STATUS_QUOTA_EXCEEDED 而失败 - 结果 PssCaptureSnapshot return 给我们 ERROR_NOT_ENOUGH_QUOTA。这是 chrome.exe 案例 - 出于安全原因,一些 chrome 进程 运行 在工作中(限制在 1 个进程中)(此进程也有 不受信任的强制级别 但这与问题无关)

但即使作业在 win8.1 中没有活动进程数限制(或者我们没有超过此限制)- 作业属于另一个会话- 调用失败 STATUS_ACCESS_DENIED。为什么这个 ?这已经是内部实现细节,现在(在 windows 10 中)它发生了变化——不再有这个错误——我们可以分叉进程,即使它在工作和另一个会话中。一些提示为什么我们可以在 AssignProcessToJobObject page:

All processes within a job must run within the same session as the job.

所以这里有一些 windows 与作业对象和跨会话相关的限制。

如何 reproduce/test windows 8.1 上的这个错误是否有趣?对于我来说最简单的方法 - 在会话 0 中使用一些 windows 服务 运行ning - 此服务必须对系统不重要(容易 start/stop 它)并且有自己的 exe。我看起来 "msdtc.exe" 完美的受害者。所以打开它,先尝试快照,然后放入作业中,然后再尝试快照。并查看不同的:

NTSTATUS status;
BOOLEAN b;
if (0 <= (status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &b)))
{
    // GetProcessIdByName custom function not shown here 
    if (ULONG dwProcessId = GetProcessIdByName(L"msdtc.exe"))
    {
        DbgPrint("found dwProcessId=%x\n", dwProcessId);

        if (HANDLE hProcess = OpenProcess(PROCESS_CREATE_PROCESS|PROCESS_QUERY_LIMITED_INFORMATION |
            PROCESS_SET_QUOTA|PROCESS_TERMINATE , FALSE, dwProcessId))
        {
            BOOL bInJob;

            if (IsProcessInJob(hProcess, 0, &bInJob))
            {
                if (!bInJob)
                {
                    HPSS SnapshotHandle;
                    ULONG err;
                    if (!(err = PssCaptureSnapshot(hProcess, PSS_CAPTURE_VA_CLONE, 0,&SnapshotHandle)))
                    {
                        PssFreeSnapshot(NtCurrentProcess(), SnapshotHandle);
                    }

                    DbgPrint("PssCaptureSnapshot=%u\n", err);

                    if (HANDLE hJob = CreateJobObject(0, 0))
                    {
                        bInJob = AssignProcessToJobObject(hJob, hProcess);
                        CloseHandle(hJob);

                        if (bInJob)
                        {
                            if (IsProcessInJob(hProcess, 0, &bInJob) && bInJob)
                            {
                                DbgPrint("process in job now!\n");

                                if (!(err = PssCaptureSnapshot(hProcess, PSS_CAPTURE_VA_CLONE, 0,&SnapshotHandle)))
                                {
                                    PssFreeSnapshot(NtCurrentProcess(), SnapshotHandle);
                                }

                                DbgPrint("PssCaptureSnapshot=%u\n", err);
                            }
                            else
                            {
                                DbgPrint("process not in job !?\n");
                            }
                        }
                        else
                        {
                            DbgPrint("AssignProcessToJobObject error=%u\n", GetLastError());
                        }
                    }
                    else
                    {
                        DbgPrint("CreateJobObject error=%u\n", GetLastError());
                    }
                }
                else
                {
                    DbgPrint("process already in job\n");
                }
            }
            else
            {
                DbgPrint("IsProcessInJob error=%u\n", GetLastError());
            }

            CloseHandle(hProcess);
        }
        else
        {
            DbgPrint("OpenProcess error=%u\n", GetLastError());
        }
    }
}
else
{
    DbgPrint("RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE)=%x\n", status);
}

在 windows 8.1 我得到下一个 dbgprint:

found dwProcessId=950
PssCaptureSnapshot=0
process in job now!
PssCaptureSnapshot=5

但在windows 10张图片中:

found dwProcessId=4d0
PssCaptureSnapshot=0
process in job now!
PssCaptureSnapshot=0

这是 windows 错误还是功能 - 很难说