即使应用程序以管理员身份运行,SetNamedSecurityInfo() 函数也会返回 ACCESS_IS_DENIED(Winerror.h 中的错误代码 5)

Posted

技术标签:

【中文标题】即使应用程序以管理员身份运行,SetNamedSecurityInfo() 函数也会返回 ACCESS_IS_DENIED(Winerror.h 中的错误代码 5)【英文标题】:SetNamedSecurityInfo() function returning ACCESS_IS_DENIED (error code 5 in Winerror.h) even when the application is run as admin 【发布时间】:2020-02-12 00:31:32 【问题描述】:

我有一个应用程序,最好将其安装的服务设置为用户不可编辑和中断(如下面已安装服务之一的屏幕截图所示),并且我已经实现了以下逻辑来完成此操作:

    读取不允许用户编辑的 Windows 服务的安全描述符并以任何方式终止它(如下面的屏幕截图所示,相关服务的服务属性甚至不允许用户终止它,确保它永久运行,并且仅在最终删除时终止)。 将先前读取的安全描述符设置为应用程序安装的服务以展示相同的功能。

真正的问题是:代码返回错误值 5 作为SetNamedSecurityInfo() 返回的值,它对应于ACCESS_IS_DENIED 根据Winerror.h,即使相关二进制文件以管理员身份运行也是如此。最后,我更迷失的部分是当我尝试使用其他已安装服务的服务名称或使用自定义字符串安全描述符(调用ConvertStringSecurityDescriptorToSecurityDescriptor() 函数时)时,我没有收到任何错误,因为代码运行为必需的。在进一步的调查中,我发现这个错误只能在使用实现所需行为的服务名称时重现:不允许最终用户编辑启动类型以及服务的服务状态。

用于执行上述任务的代码:

// Fetch the security descriptor for "WdNisSvc" (Service name that corresponds to the display name of "Windows Security Service")
LPCWSTR serviceNameToGetSecurityInformationFrom = L"WdNisSvc";
SC_HANDLE serviceManagerHandle = NULL, serviceHandle = NULL;
char securityDescriptorBuffer[1024];
DWORD lengthOfReturnedValue = 0;
LPWSTR stringBuffer = NULL;
unsigned long lengthOfString = 0;
PSECURITY_DESCRIPTOR securityDescriptor =  0 ;
LPSTR daclBuffer = nullptr, ownerSidBuffer = nullptr, absoluteSecurityDescriptionBuffer = nullptr;
DWORD daclBufferSize = NULL, ownerSidBufferSize = NULL, absoluteSecurityDescriptionBufferSize = NULL, saclBufferSize = NULL, primaryGroupSidBufferSize = NULL;
WCHAR serviceName[] = L"test_service";
DWORD securityInfoChangeOperationErrorValue = NULL;
BOOL securityDescriptorInAbsoluteFormatCreationErrorValue = NULL;
serviceManagerHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
if (serviceManagerHandle == nullptr) 
    cout << "Error opening Service Manager Handle (" << GetLastError() << ").\n";

else 
    serviceHandle = OpenService(serviceManagerHandle, serviceNameToGetSecurityInformationFrom, READ_CONTROL); //| WRITE_OWNER | WRITE_DAC
    if (serviceHandle == nullptr) 
        cout << "Error opening service (" << GetLastError() << ").\n";
    
    else 
        if (!QueryServiceObjectSecurity(serviceHandle, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, securityDescriptorBuffer, sizeof(securityDescriptorBuffer), &lengthOfReturnedValue)) 
            cout << "Error obtaining service's security information (" << GetLastError() << ").\n";
        
        else 
            if (!ConvertSecurityDescriptorToStringSecurityDescriptor(securityDescriptorBuffer, SDDL_REVISION_1, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, &stringBuffer, &lengthOfString)) 
                cout << "Error getting string value corresponding to the security information (" << GetLastError() << ")";
            
            else 

                if (!ConvertStringSecurityDescriptorToSecurityDescriptor(stringBuffer, SDDL_REVISION_1, &securityDescriptor, NULL)) 
                    cout << "Conversion of string descriptor to security descriptor failed with error " << GetLastError() << ".\n";
                 
                else 
                    absoluteSecurityDescriptionBuffer = new CHAR[0];
                    ownerSidBuffer = new CHAR[0];
                    daclBuffer = new CHAR[0];
                    securityDescriptorInAbsoluteFormatCreationErrorValue = MakeAbsoluteSD(securityDescriptor, absoluteSecurityDescriptionBuffer, &absoluteSecurityDescriptionBufferSize, (PACL)daclBuffer, &daclBufferSize, NULL, &saclBufferSize, ownerSidBuffer, &ownerSidBufferSize, NULL, &primaryGroupSidBufferSize);
                    if ((!securityDescriptorInAbsoluteFormatCreationErrorValue) && (ERROR_INSUFFICIENT_BUFFER) == GetLastError()) 
                        // Induced error
                        //cout << "ERROR: Inadequate size of the buffers implemented.\n";
                        delete[] absoluteSecurityDescriptionBuffer;
                        delete[] ownerSidBuffer;
                        delete[] daclBuffer;
                        absoluteSecurityDescriptionBuffer = new CHAR[absoluteSecurityDescriptionBufferSize];
                        ownerSidBuffer = new CHAR[ownerSidBufferSize];
                        daclBuffer = new CHAR[daclBufferSize];
                        securityDescriptorInAbsoluteFormatCreationErrorValue = MakeAbsoluteSD(securityDescriptor, absoluteSecurityDescriptionBuffer, &absoluteSecurityDescriptionBufferSize, (PACL)daclBuffer, &daclBufferSize, NULL, &saclBufferSize, ownerSidBuffer, &ownerSidBufferSize, NULL, &primaryGroupSidBufferSize);
                    
                    securityInfoChangeOperationErrorValue = SetNamedSecurityInfo(serviceName, SE_SERVICE, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, ownerSidBuffer, NULL, (ACL*)daclBuffer, NULL);
                    if (securityInfoChangeOperationErrorValue != ERROR_SUCCESS) 
                        cout << "Error setting security info (" << securityInfoChangeOperationErrorValue << ").\n";
                    
                    delete[] absoluteSecurityDescriptionBuffer;
                    delete[] ownerSidBuffer;
                    delete[] daclBuffer;
                
            
        
    

显示所需行为的已安装 Windows 服务之一的屏幕截图:

提前致谢。

【问题讨论】:

如果您只是尝试设置 DACL 而不是所有者,它是否有效?如SetNamedSecurityInfo 的文档中所述,设置所有者有许多条件,您可能无法满足所有条件。 起初是的,OWNER_SECURITY_INFORMATION 中的问题 - 您需要将 SeRestorePrivilege 设置为 not self SID 作为对象的所有者。比你的代码太复杂 - 为了什么这些操作?您需要从 QueryServiceObjectSecurity 获取 SD 并将其按原样传递给 SetServiceObjectSecurity @JonathanPotter 我已经删除了OWNER_SECURITY_INFORMATION 的所有实例并省略了ownerSidBuffer 的使用,但它仍然没有按预期运行;函数SetNamedSecurityInfo() 现在返回关于无效参数的错误。 @hecate - 你的代码毫无意义的复杂。 SD的所有这些转变是为了什么? @RbMm 请忽略我的最后评论,这是由于我在传递给SetServiceObjectSecurity()的参数上犯了一个愚蠢的错误! 【参考方案1】:

要设置OWNER_SECURITY_INFORMATION,调用进程必须具有WRITE_OWNER 访问权限,或者必须是对象的所有者或启用SE_TAKE_OWNERSHIP_NAME 权限或SE_RESTORE_NAME(当您将非自身SID 设置为对象的所有者时) .而且您不需要设置必要的对象所有者的 SID。 以下示例对我有用:

#include <windows.h>
#include <iostream>
#include <sddl.h>
#include <aclapi.h>
#include <WinError.h>
#pragma comment(lib, "Advapi32.lib")
using namespace std;
int main(int argc, char* argv[])

    // Fetch the security descriptor for "WdNisSvc" (Service name that corresponds to the display name of "Windows Security Service")

    LPCWSTR serviceNameToGetSecurityInformationFrom = L"SecurityHealthService";
    SC_HANDLE serviceManagerHandle = NULL, serviceHandle = NULL;
    char securityDescriptorBuffer[1024];
    DWORD lengthOfReturnedValue = 0;
    LPSTR daclBuffer = nullptr, absoluteSecurityDescriptionBuffer = nullptr;
    DWORD daclBufferSize = NULL, ownerSidBufferSize = NULL, absoluteSecurityDescriptionBufferSize = NULL, saclBufferSize = NULL, primaryGroupSidBufferSize = NULL;
    WCHAR serviceName[] = L"MySampleService1";
    DWORD securityInfoChangeOperationErrorValue = NULL;
    BOOL securityDescriptorInAbsoluteFormatCreationErrorValue = NULL;
    serviceManagerHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
    if (serviceManagerHandle == nullptr) 
        cout << "Error opening Service Manager Handle (" << GetLastError() << ").\n";
    
    else 
        serviceHandle = OpenService(serviceManagerHandle, serviceNameToGetSecurityInformationFrom, READ_CONTROL); //| WRITE_OWNER | WRITE_DAC


        if (serviceHandle == nullptr) 
            cout << "Error opening service (" << GetLastError() << ").\n";
        
        else 
            if (!QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, securityDescriptorBuffer, sizeof(securityDescriptorBuffer), &lengthOfReturnedValue)) 
                cout << "Error obtaining service's security information (" << GetLastError() << ").\n";
            
            else 
                BOOL bDaclPresent = false, bDaclDefaulted = false;
                PACL pDacl = NULL;
                BOOL ret = GetSecurityDescriptorDacl(securityDescriptorBuffer, &bDaclPresent, &pDacl, &bDaclDefaulted);
                if (bDaclPresent)
                
                    securityInfoChangeOperationErrorValue = SetNamedSecurityInfo(serviceName, SE_SERVICE, DACL_SECURITY_INFORMATION, NULL, NULL, pDacl, NULL);
                
                else
                
                    cout << "The security descriptor doesn't contain a DACL" << endl;
                
                //SC_HANDLE target_service = OpenService(serviceManagerHandle, serviceName, WRITE_DAC); //| WRITE_OWNER | WRITE_DAC
                //if (serviceHandle == nullptr) 
                //    cout << "Error opening service (" << GetLastError() << ").\n";
                //
                //else 
                //    BOOL ret = SetServiceObjectSecurity(target_service, DACL_SECURITY_INFORMATION, securityDescriptorBuffer);
                //    if (ret == 0) 
                //        cout << "Error SetServiceObjectSecurity (" << GetLastError() << ").\n";
                //    
                //    CloseServiceHandle(target_service);
                //

            
            CloseServiceHandle(serviceHandle);
        
        CloseServiceHandle(serviceManagerHandle);
    
    return 0;

或者直接使用SetServiceObjectSecurity:

#include <windows.h>
#include <iostream>
#include <sddl.h>
#include <aclapi.h>
#include <WinError.h>
#pragma comment(lib, "Advapi32.lib")
using namespace std;
int main(int argc, char* argv[])

    // Fetch the security descriptor for "WdNisSvc" (Service name that corresponds to the display name of "Windows Security Service")

    LPCWSTR serviceNameToGetSecurityInformationFrom = L"SecurityHealthService";
    SC_HANDLE serviceManagerHandle = NULL, serviceHandle = NULL;
    char securityDescriptorBuffer[1024];
    DWORD lengthOfReturnedValue = 0;
    LPSTR daclBuffer = nullptr, absoluteSecurityDescriptionBuffer = nullptr;
    DWORD daclBufferSize = NULL, ownerSidBufferSize = NULL, absoluteSecurityDescriptionBufferSize = NULL, saclBufferSize = NULL, primaryGroupSidBufferSize = NULL;
    WCHAR serviceName[] = L"MySampleService1";
    DWORD securityInfoChangeOperationErrorValue = NULL;
    BOOL securityDescriptorInAbsoluteFormatCreationErrorValue = NULL;
    serviceManagerHandle = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
    if (serviceManagerHandle == nullptr) 
        cout << "Error opening Service Manager Handle (" << GetLastError() << ").\n";
    
    else 
        serviceHandle = OpenService(serviceManagerHandle, serviceNameToGetSecurityInformationFrom, READ_CONTROL); //| WRITE_OWNER | WRITE_DAC


        if (serviceHandle == nullptr) 
            cout << "Error opening service (" << GetLastError() << ").\n";
        
        else 
            if (!QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, securityDescriptorBuffer, sizeof(securityDescriptorBuffer), &lengthOfReturnedValue)) 
                cout << "Error obtaining service's security information (" << GetLastError() << ").\n";
            
            else 
                //securityDescriptorInAbsoluteFormatCreationErrorValue = MakeAbsoluteSD(securityDescriptorBuffer, NULL, &absoluteSecurityDescriptionBufferSize, (PACL)daclBuffer, &daclBufferSize, NULL, &saclBufferSize, NULL, &ownerSidBufferSize, NULL, &primaryGroupSidBufferSize);
                //if ((!securityDescriptorInAbsoluteFormatCreationErrorValue) && (ERROR_INSUFFICIENT_BUFFER) == GetLastError()) 
                //    // Induced error
                //    //cout << "ERROR: Inadequate size of the buffers implemented.\n";
                //    absoluteSecurityDescriptionBuffer = new CHAR[absoluteSecurityDescriptionBufferSize];
                //    daclBuffer = new CHAR[daclBufferSize];
                //    securityDescriptorInAbsoluteFormatCreationErrorValue = MakeAbsoluteSD(securityDescriptorBuffer, absoluteSecurityDescriptionBuffer, &absoluteSecurityDescriptionBufferSize, (PACL)daclBuffer, &daclBufferSize, NULL, &saclBufferSize, NULL, &ownerSidBufferSize, NULL, &primaryGroupSidBufferSize);
                //
                //securityInfoChangeOperationErrorValue = SetNamedSecurityInfo(serviceName, SE_SERVICE, DACL_SECURITY_INFORMATION, NULL, NULL, (ACL*)daclBuffer, NULL);
                //if (securityInfoChangeOperationErrorValue != ERROR_SUCCESS) 
                //    cout << "Error setting security info (" << securityInfoChangeOperationErrorValue << ").\n";
                //
                //delete[] absoluteSecurityDescriptionBuffer;
                //delete[] daclBuffer;
                SC_HANDLE target_service = OpenService(serviceManagerHandle, serviceName, WRITE_DAC); //| WRITE_OWNER | WRITE_DAC
                if (serviceHandle == nullptr) 
                    cout << "Error opening service (" << GetLastError() << ").\n";
                
                else 
                    BOOL ret = SetServiceObjectSecurity(target_service, DACL_SECURITY_INFORMATION, securityDescriptorBuffer);
                    if (ret == 0) 
                        cout << "Error SetServiceObjectSecurity (" << GetLastError() << ").\n";
                    
                    CloseServiceHandle(target_service);
                

            
            CloseServiceHandle(serviceHandle);
        
        CloseServiceHandle(serviceManagerHandle);
    
    return 0;

注意:

[SetServiceObjectSecurity 可用于操作 要求部分中指定的系统。它可能会被更改或 在后续版本中不可用。相反,使用 SetNamedSecurityInfo函数。]

【讨论】:

设置OWNER_SECURITY_INFORMATION还需要SeRestorePrivilege当你设置不是自己的SID作为对象的所有者。 MakeAbsoluteSD - 为了什么?在返回的 SD 上使用 GetSecurityDescriptorDacl(仅在使用 SetNamedSecurityInfo 时才需要) 关于SetServiceObjectSecurity - 它曾经、存在并且将要存在。 SetNamedSecurityInfo 内部当然调用SetServiceObjectSecurity 和非常无效比较SetServiceObjectSecurity 并设置 D:(A;;GA;;;SY)(A;;GR;;;IU) 如果要禁用更改启动类型(写入)和启动/停止(执行)服务。如果设置 D:(A;;GA;;;SY) 它通过 services.msc 变得完全不可见

以上是关于即使应用程序以管理员身份运行,SetNamedSecurityInfo() 函数也会返回 ACCESS_IS_DENIED(Winerror.h 中的错误代码 5)的主要内容,如果未能解决你的问题,请参考以下文章

以管理员身份运行是啥意思?

默认情况下如何以管理员身份运行 Visual Studio?

如何以非管理员身份运行 dbms_output 的功能?

即使具有管理员权限,批处理脚本访问也被拒绝

如何让Delphi程序启动自动“以管理员身份运行

Win10 开启以管理员身份运行