在没有管理员权限的情况下从应用程序启动Windows服务(C++)

4
我写了一个Windows服务并且它运行得很好。现在我有一个单独的应用程序,我想从中启动此服务,但似乎没有管理员权限是不可能的。
一个合适的解决方案是什么样子的,使得用户可以启动/停止服务(例如从托盘或应用程序)?
我的看法是,应用程序必须始终以管理员权限运行是不好的。
4个回答

11

你只需要更改服务对象的权限,最好在安装它的同时进行。

wchar_t sddl[] = L"D:"
  L"(A;;CCLCSWRPWPDTLOCRRC;;;SY)"           // default permissions for local system
  L"(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)"   // default permissions for administrators
  L"(A;;CCLCSWLOCRRC;;;AU)"                 // default permissions for authenticated users
  L"(A;;CCLCSWRPWPDTLOCRRC;;;PU)"           // default permissions for power users
  L"(A;;RP;;;IU)"                           // added permission: start service for interactive users
  ;

PSECURITY_DESCRIPTOR sd;

if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddl, SDDL_REVISION_1, &sd, NULL))
{
   fail();
}

if (!SetServiceObjectSecurity(service, DACL_SECURITY_INFORMATION, sd))
{
   fail();
}

我假设你已经打开了服务句柄。你需要WRITE_DAC权限。

如果你还想让非管理员用户能够停止服务,那么请添加WP权限,即:

L"(A;;RPWP;;;IU)"                           
  // added permissions: start service, stop service for interactive users

可以在Wayne Martin的博客文章“非管理员的服务控制管理器安全性”中找到用于服务权限的SDDL代码。


1

@Harry Johnston,除了回复之外。

这是C++ Builder的示例。

void __fastcall TService1::ServiceAfterInstall(TService *Sender)
{
 wchar_t lpBuffer[256];
 long errorCode;
 SC_HANDLE hSCManager,hService;

 hSCManager  = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
 if (hSCManager == NULL)
 {
   errorCode = GetLastError();
   FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpBuffer, 256, NULL);
   LogMessage("OpenSCManager Error "+AnsiString(lpBuffer), EVENTLOG_ERROR_TYPE);
   return;
 }


 hService = OpenService(hSCManager, this->Name.c_str(), READ_CONTROL | WRITE_DAC);
 if (hService == NULL)
 {
   errorCode = GetLastError();
   FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpBuffer, 256, NULL);
   LogMessage("OpenService Error "+AnsiString(lpBuffer), EVENTLOG_ERROR_TYPE);
   CloseServiceHandle(hSCManager);
 }

 wchar_t sddl[] = L"D:"
  L"(A;;CCLCSWRPWPDTLOCRRC;;;SY)"           // default permissions for local system
  L"(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)"   // default permissions for administrators
  L"(A;;CCLCSWLOCRRC;;;AU)"                 // default permissions for authenticated users
  L"(A;;CCLCSWRPWPDTLOCRRC;;;PU)"           // default permissions for power users
  L"(A;;RP;;;IU)"                           // added permission: start service for interactive users
  ;

  PSECURITY_DESCRIPTOR sd;
  if (!ConvertStringSecurityDescriptorToSecurityDescriptor(AnsiString(sddl).c_str(), SDDL_REVISION_1, &sd, NULL))
  {
    errorCode = GetLastError();
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpBuffer, 256, NULL);
    LogMessage("ConvertStringSecurityDescriptorToSecurityDescriptor Error "+AnsiString(lpBuffer), EVENTLOG_ERROR_TYPE);
  }

  if (!SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, sd))
  {
    errorCode = GetLastError();
    FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpBuffer, 256, NULL);
    LogMessage("SetServiceObjectSecurity Error "+AnsiString(lpBuffer), EVENTLOG_ERROR_TYPE);
  }

  CloseServiceHandle(hService);
  CloseServiceHandle(hSCManager);
}

构造函数 AnsiString(sddl).c_str() 是多余的:由于sddl只使用了一次,您可以将其定义更改为char并在必要时使用窄字符串。 - Harry Johnston

0

以编程方式启动服务是通过StartService函数完成的。在启动服务标题下还提供了一个全面的使用示例,其中还展示了如何执行以下操作:

  • 检测服务由于某种原因正在关闭
  • 等待服务处于稳定状态(已启动/已停止)
  • 以编程方式启动服务

至于管理员权限,这是必需的,因为如果几乎任何应用程序都可以关闭服务(或更重要的是,安装和启动新服务),那么就会存在非常真实且严重的安全问题。


0

@Harry Johnston的方法对我很有效,如果有人想用C#实现这个功能:

[DllImport("advapi32.dll", SetLastError = true)]
static extern bool SetServiceObjectSecurity(SafeHandle serviceHandle,
    UInt32 secInfos,
    IntPtr lpSecDesrBuf);

[DllImport("advapi32.dll", EntryPoint = "ConvertStringSecurityDescriptorToSecurityDescriptorW", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern Boolean ConvertStringSecurityDescriptorToSecurityDescriptor(
    [MarshalAs(UnmanagedType.LPWStr)] String strSecurityDescriptor,
    UInt32 sDRevision,
    ref IntPtr securityDescriptor,
    ref UInt32 securityDescriptorSize);

public static void SetServicePermissions(string service)
{
    System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController(service);
    bool ok;
    IntPtr pSD = IntPtr.Zero;
    uint securityDescriptorSize = 0;
    string secDesc = "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)(A;;RPWP;;;IU)";

    ok = ConvertStringSecurityDescriptorToSecurityDescriptor(secDesc, 1, ref pSD, ref securityDescriptorSize);
    if (!ok)
    {
        throw new ApplicationException("error calling ConvertStringSecurityDescriptorToSecurityDescriptor(): error code=" + Marshal.GetLastWin32Error());
    }

    ok = SetServiceObjectSecurity(sc.ServiceHandle, 4 , pSD);
    if (!ok)
    {
        throw new ApplicationException("error calling SetServiceObjectSecurity(); error code=" + Marshal.GetLastWin32Error());
    }
}

正如我所写的,这是与此答案等效的C#版本:https://dev59.com/ul3Va4cB1Zd3GeqPFPBr#8380009 - Tearsdontfalls

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接