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

问题描述

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

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

这是真正的问题:代码返回错误值 5 作为 的返回值SetNamedSecurityInfo(),对应于ACCESS_IS_DENIEDas per 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 服务之一的屏幕截图: 属性框截图

提前致谢。

标签: c++windowswinapivisual-c++service

解决方案


要设置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功能。]


推荐阅读