从用户会话进程打开在服务中创建的 JobObject

Posted

技术标签:

【中文标题】从用户会话进程打开在服务中创建的 JobObject【英文标题】:Open a JobObject created in a service from a user session process 【发布时间】:2017-02-01 16:42:20 【问题描述】:

我有一个 Windows 服务,它创建一个 JobObject,只要机器打开,我就需要保持活动状态 - 目标是管理一些可以随时使用此 JobObject 终止/启动的用户会话进程。我在服务中创建它以确保进程在启动时运行,并且它不能被普通用户杀死。

但是,我似乎无法从用户会话中打开此 JobObject 的句柄,我总是收到拒绝访问 (5) 错误,尽管使用 NULL DACL 创建它。

我在这里找到了一个有点相关的问题:Open an Event object created by my service from my application,但对我来说,即使使用 NULL DACL,在请求 JOB_OBJECT_ASSIGN_PROCESS 权限时,我也会被拒绝访问(例如请求 SYNCHRONIZE 工作)。

服务代码:

PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(psd, TRUE, NULL, FALSE);

SECURITY_ATTRIBUTES secAttr= 0;
secAttr.nLength = sizeof(secAttr);
secAttr.bInheritHandle = false;
secAttr.lpSecurityDescriptor = psd;

hJobObject = CreateJobObject(&secAttr, SCL_JOBOBJECTNAME);
LocalFree(psd);

用户会话代码:

hJobObject = OpenJobObject(JOB_OBJECT_ASSIGN_PROCESS, FALSE, SCL_JOBOBJECTNAME);
if (hJobObject == NULL)

    DWORD wError = GetLastError();
    printf("Error: %d\n", wError); // this always pops 5
    return 1;

有什么想法吗?作为测试,我尝试从服务中生成用户会话进程,并通过服务代码分配 JobObject,并且有效,.. 所以我相当确定它与我缺少的安全设置有关,尽管 NULL DACL .

【问题讨论】:

您为什么希望能够从不同的会话中打开作业对象?为什么你还需要这样做? 其他内核对象也可以像这样使用,我没有找到任何文档建议 JobObjects。也许有更好的方法来做到这一点,但我需要在同一个 JobObject 中保持在整个用户会话中独立启动的进程数天/数周,以确保它们永远不会超过一定的 RAM 使用量。如果我在用户会话本身中创建 JobObject,我无法确保它在第一个“托管”进程启动之前启动。服务似乎是实现此目的的工具。您对如何以不同的方式处理它有什么建议吗? 你是在全局命名空间中创建的吗?您不需要运行提升来访问全局命名空间中的对象吗? 我想你忘了在你的工作对象中设置标签,当你需要设置为SECURITY_MANDATORY_MEDIUM_RID或低时它默认有SECURITY_MANDATORY_SYSTEM_RID @DavidHeffernan 是的,#define SCL_JOBOBJECTNAME L"Global\\MyJobObject",否则我会得到预期的“未找到”(2) 错误。所以它似乎找到了它,但由于某种原因拒绝使用任何可能需要修改某些内容的访问权限进行访问。所以 JOB_OBJECT_SET_ATTRIBUTES 会失败(拒绝访问),但 JOB_OBJECT_QUERY 会给我一个句柄。 【参考方案1】:

如果您在服务中创建作业 - 默认情况下此对象将具有 WinSystemLabelSid 标签 SID: S-1-16-16384 - 系统强制级别。 (我只是检查一下)所以您不仅需要设置 Dacl,还需要设置 Sacl。例如:

    ULONG cb = MAX_SID_SIZE;
    PSID LowLabelSid = (PSID)alloca(MAX_SID_SIZE);
    if (CreateWellKnownSid(WinLowLabelSid, 0, LowLabelSid, &cb))
    
        PACL Sacl = (PACL)alloca(cb += sizeof(ACL) + sizeof(ACE_HEADER) + sizeof(ACCESS_MASK));
        InitializeAcl(Sacl, cb, ACL_REVISION);
        if (AddMandatoryAce(Sacl, ACL_REVISION, 0, 0, LowLabelSid))
        
            SECURITY_DESCRIPTOR sd;
            InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
            SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
            SetSecurityDescriptorSacl(&sd, TRUE, Sacl, FALSE);

            SECURITY_ATTRIBUTES sa=  sizeof(sa), &sd, FALSE ;

            if (HANDLE hJob = CreateJobObject(&sa, L"Global\\58BFC6DB-BE93-4cdb-919C-4C713ACB5A32"))
            
                CloseHandle(hJob);
            
        
    

【讨论】:

非常感谢,效果很好!没错,SACL 设置为 SYSTEM 级别。 @LaszloSebo - 有趣的是,这并不适用于所有对象。对于Job 对象,默认标签设置为System Mandatory Level,例如,如果您创建Event 对象-它将不是强制性标签-因此默认情况下-中等标签

以上是关于从用户会话进程打开在服务中创建的 JobObject的主要内容,如果未能解决你的问题,请参考以下文章

Perl - 如何使用在另一个 Perl 脚本的模块中创建的进程句柄

Delphi - 优雅地关闭服务中创建的进程。 (使用 tprocess / createProcess)

如何在Glassfishv4上的另一个Servlet中创建的Servlet中恢复会话?

如何从 Windows 服务启动进程到当前登录的用户会话

如何从Windows服务启动进程到当前登录的用户会话

从服务启动用户会话中的进程