模拟管理员以重新启动Windows服务

时间:2016-02-12 14:09:48

标签: delphi delphi-7

我有一个重启某些Windows服务的Delphi应用程序。

如果执行此应用程序的用户具有管理权限(Administrators组成员),则服务重启将成功。

对于普通用户,服务重启将失败。

如果普通用户使用“右键单击”以管理员身份运行,也会成功。 但是,普通用户必须输入管理员用户名和密码。

我想为普通用户解决这个问题。

我的想法是在代码中“冒充用户”,显然冒充本地管理员帐户。

但是,它仍然不起作用。

这是我的代码:

var servRequest = (from srDet in SRDB.ServRequestDetails
                               from srCustReqRef in srDet.ServRequest.ServReqCustReqRefs
                               from srOpSys in srDet.ServRequest.ServReqOpSys
                               from srOpSysDet in srOpSys.ServReqOpSysDetails
                               //from srPh in srDet.ServRequest.ServReqPhases
                               //from srTask in srPh.ServReqTasks


                               where srDet!=null && srCustReqRef.CustRequestID == 1 && srDet.IsActiveRow == true
                                && srCustReqRef.ServRequestID == 1 && srOpSysDet.IsActiveRow==true


                               select new ServReqDTO()
                               {
                                   ServRequestID = srDet.ServRequestID,
                                   Requestor = srDet.HRUser_Requestor.DisplayName,
                                   RequestorID = srDet.RequestorID,
                                   SubmittedDate = srDet.ServRequest.SubmitDateTime,
                                   ServRequestTypeID = srDet.ServRequestTypeID,
                                   ServRequestType = srDet.ServRequestType.ServRequestTypeVal,
                                   TargetDate = srDet.TargetCompletionDate,
                                   OS = srOpSysDet.OperatingSystem.OperatingSystemVal,
                                   OSID = srOpSysDet.OperatingSystem.OperatingSystemID,
                                   //TaskFollowupDates = srTask.ServReqTaskDetails.Where(i => i.IsActiveRow == true).Select(task => task.TaskFollowUpDate).ToList(),
                                   //TaskEndDates = srTask.ServReqTaskDetails.Where(i => i.IsActiveRow == true).Select(task => task.TaskEndDate).ToList(),
                                   //TaskStartDates = srTask.ServReqTaskDetails.Where(i => i.IsActiveRow == true).Select(task => task.TaskStartDate).ToList(),
                                   NextActionDate = null,
                                   ServReqStatus = srDet.ServReqStatusType.ServReqStatusTypeVal,
                                   ServReqStatusTypeID = srDet.ServRequestTypeID
                               }).ToList();

我称之为:

function GetCurrUserName: string;
var
  Size              : DWORD;
begin
  Size := MAX_COMPUTERNAME_LENGTH + 1;
  SetLength(Result, Size);
  if GetUserName(PChar(Result), Size) then
    SetLength(Result, Size)
  else
    Result := '';
end;

function ConnectAs(const lpszUsername, lpszPassword: string): Boolean;
var
  hToken       : THandle;
begin
  Result := LogonUser(PChar(lpszUsername), nil, PChar(lpszPassword), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken);
  if Result then
    Result := ImpersonateLoggedOnUser(hToken)
  else
    RaiseLastOSError;
end;

服务重启将失败。如果用户以管理员身份运行它(右键单击RunAs),它就可以运行。

模仿本身运作良好。我可以通过获取当前用户名来检查它。但是,为什么它仍然不起作用。看起来用户模拟没有管理员权限。

还有其他模仿吗?

1 个答案:

答案 0 :(得分:5)

这是可以预料的。在UAC下,管理员组中的用户会收到筛选的令牌,除非他们通过UAC提升过程。您正在模仿其他用户,但是您没有UAC提升,因此拥有过滤的,降低权限的令牌。

为了执行具有提升权限的代码,在UAC下,您必须通过UAC提升过程。如果你可以避免这样做,那么UAC将毫无意义。

据我所知,你有以下原则选择:

  1. 启动另一个进程来完成工作,使用runas shell动词强制提升,或者确实使用其他机制来安排提升。这将使您完成要避免的UAC对话框。
  2. 作为替代方案,请安装两项服务。第一个服务是您当前安装的服务,即您要重新启动的服务。第二项服务是一项小而简单的服务,其唯一任务是执行重新启动。因为第二个服务在没有UAC的会话0中运行,所以您可以轻松地安排它有足够的权限重新启动第一个服务。当您需要从桌面应用程序重新启动服务1时,请向服务2发送请求,以便服务2可以执行服务1的重新启动。
  3. 顺便说一句,我注意到您在调用ImpersonateLoggedOnUser时没有检查错误。