Checking dependent services of a given service in Inno Setup

huangapple go评论125阅读模式
英文:

Checking dependent services of a given service in Inno Setup

问题

I'm currently working on an Inno Setup installer which checks for dependent Services of a given Service. According to the MSDN docs, I need the function EnumDependentServices().

However, when I pass parameters to the function, Inno Setup gives me status code 5, which stands for "Access Denied", although I execute my Setup as an Administrator.

I'm using the SC_MANAGER_ALL_ACCESS (0xF003F) right for starting the Service Handler, and it still denies my function call.

Both the SCManager and the opened Service (I tested it with "DnsCache") return non-zero values, and work with my other functions.

Here is the [Code] snippet from my .iss file:

#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

type
    SERVICE_STATUS = record
    dwServiceType: DWORD;
    dwCurrentState: DWORD;
    dwControlsAccepted: DWORD;
    dwWin32ExitCode: DWORD;
    dwServiceSpecificExitCode: DWORD;
    dwCheckPoint: DWORD;
    dwWaitHint: DWORD;
  end;
    HANDLE = THandle;
    PDWord = DWORD;

const
    SERVICE_QUERY_CONFIG        = $1;
    SC_MANAGER_CONNECT          = $0001;
    SERVICE_QUERY_STATUS        = $0004;
    SERVICE_CHANGE_CONFIG       = $2;
    SERVICE_CURRENT_STATUS      = $0003;
    SERVICE_START               = $10;
    SERVICE_STOP                = $20;
    SERVICE_ALL_ACCESS          = $f01ff;
    SC_MANAGER_ALL_ACCESS       = $F003F;
    SERVICE_WIN32_OWN_PROCESS   = $10;
    SERVICE_WIN32_SHARE_PROCESS = $20;
    SERVICE_WIN32               = $30;
    SERVICE_INTERACTIVE_PROCESS = $100;
    SERVICE_BOOT_START          = $0;
    SERVICE_SYSTEM_START        = $1;
    SERVICE_AUTO_START          = $2;
    SERVICE_DEMAND_START        = $3;
    SERVICE_DISABLED            = $4;
    SERVICE_DELETE              = $10000;
    SERVICE_CONTROL_STOP        = $1;
    SERVICE_CONTROL_PAUSE       = $2;
    SERVICE_CONTROL_CONTINUE    = $3;
    SERVICE_CONTROL_INTERROGATE = $4;
    SERVICE_STOPPED             = $1;
    SERVICE_START_PENDING       = $2;
    SERVICE_STOP_PENDING        = $3;
    SERVICE_RUNNING             = $4;
    SERVICE_CONTINUE_PENDING    = $5;
    SERVICE_PAUSE_PENDING       = $6;
    SERVICE_PAUSED              = $7;

// #######################################################################################
// nt based service utilities
// #######################################################################################
function OpenSCManager(lpMachineName, lpDatabaseName: string; dwDesiredAccess :cardinal): HANDLE;
external 'OpenSCManager{#AW}@advapi32.dll stdcall';

function OpenService(hSCManager :HANDLE;lpServiceName: string; dwDesiredAccess :cardinal): HANDLE;
external 'OpenService{#AW}@advapi32.dll stdcall';

function CloseServiceHandle(hSCObject :HANDLE): boolean;
external 'CloseServiceHandle@advapi32.dll stdcall';

function CreateService(hSCManager :HANDLE;lpServiceName, lpDisplayName: string;dwDesiredAccess,dwServiceType,dwStartType,dwErrorControl: cardinal;lpBinaryPathName,lpLoadOrderGroup: String; lpdwTagId : cardinal;lpDependencies,lpServiceStartName,lpPassword :string): cardinal;
external 'CreateService{#AW}@advapi32.dll stdcall';

function DeleteService(hService :HANDLE): boolean;
external 'DeleteService@advapi32.dll stdcall';

function StartNTService(hService :HANDLE;dwNumServiceArgs : cardinal;lpServiceArgVectors : cardinal) : boolean;
external 'StartService{#AW}@advapi32.dll stdcall';

function ControlService(hService :HANDLE; dwControl :cardinal;var ServiceStatus :SERVICE_STATUS) : boolean;
external 'ControlService@advapi32.dll stdcall';

function QueryServiceStatus(hService :HANDLE;var ServiceStatus :SERVICE_STATUS) : boolean;
external 'QueryServiceStatus@advapi32.dll stdcall';

function QueryServiceStatusEx(hService :HANDLE;ServiceStatus :SERVICE_STATUS) : boolean;
external 'QueryServiceStatus@advapi32.dll stdcall';

function GetLastError() : cardinal;
external 'GetLastError@kernel32.dll stdcall';

function EnumDependentServices(
  hService :HANDLE; 
  ServiceStatus : LongInt; 
  lpServices: DWORD; 
  cbBufSize: DWORD; 
  var pcbBytesNeeded: DWORD; 
  var lpServicesReturned : DWORD) : boolean;
external 'EnumDependentServices{#AW}@advapi32.dll stdcall';

function HasServiceDependencies(const ServiceName: string) : boolean;
var  
  hSCM: HANDLE;
  hService: HANDLE;
  pcbBytesNeeded : DWORD;
  lpServicesReturned : DWORD;
begin
    hSCM := OpenSCManager('', '', SC_MANAGER_ALL_ACCESS);
    Log(Format('%d', [hSCM]));
    if hSCM <> 0 then begin
        hService := OpenService(hSCM, ServiceName, SERVICE_QUERY_STATUS);
        Log(Format('%d', [hService]));
        if hService <> 0 then begin

            EnumDependentServices( hService, SERVICE_CURRENT_STATUS, 0, 0, pcbBytesNeeded, lpServicesReturned);
            
            MsgBox(SysErrorMessage(DLLGetLastError()), mbError, mb_Ok);

            CloseServiceHandle(hService)
            end;
        CloseServiceHandle(hSCM)
    end;
    Result := false;
end;

function InitializeSetup(): Boolean;
begin
  Log('InitializeSetup called');
  Result := HasServiceDependencies('Dnscache');
  if Result = False then
    MsgBox('InitializeSetup:'#13#13'Ok, bye bye.', mbInformation, MB_OK);
end;
英文:

I'm currently working on an Inno Setup installer which checks for dependent Services of a given Service. According to the MSDN docs, I need the function EnumDependentServices().

However, when I pass parameters to the function, Inno Setup gives me status code 5, which stands for "Access Denied", although I execute my Setup as an Administrator.

I'm using the SC_MANAGER_ALL_ACCESS (0xF003F) right for starting the Service Handler, and it still denies my function call.

Both the SCManager and the opened Service (I tested it with "DnsCache") return non-zero values, and work with my other functions.

Here is the [Code] snippet from my .iss file:

#ifdef UNICODE
  #define AW &quot;W&quot;
#else
  #define AW &quot;A&quot;
#endif

type
    SERVICE_STATUS = record
    dwServiceType: DWORD;
    dwCurrentState: DWORD;
    dwControlsAccepted: DWORD;
    dwWin32ExitCode: DWORD;
    dwServiceSpecificExitCode: DWORD;
    dwCheckPoint: DWORD;
    dwWaitHint: DWORD;
  end;
    HANDLE = THandle;
    PDWord = DWORD;

const
    SERVICE_QUERY_CONFIG        = $1;
    SC_MANAGER_CONNECT          = $0001;
    SERVICE_QUERY_STATUS        = $0004;
    SERVICE_CHANGE_CONFIG       = $2;
    SERVICE_CURRENT_STATUS      = $0003;
    SERVICE_START               = $10;
    SERVICE_STOP                = $20;
    SERVICE_ALL_ACCESS          = $f01ff;
    SC_MANAGER_ALL_ACCESS       = $F003F;
    SERVICE_WIN32_OWN_PROCESS   = $10;
    SERVICE_WIN32_SHARE_PROCESS = $20;
    SERVICE_WIN32               = $30;
    SERVICE_INTERACTIVE_PROCESS = $100;
    SERVICE_BOOT_START          = $0;
    SERVICE_SYSTEM_START        = $1;
    SERVICE_AUTO_START          = $2;
    SERVICE_DEMAND_START        = $3;
    SERVICE_DISABLED            = $4;
    SERVICE_DELETE              = $10000;
    SERVICE_CONTROL_STOP        = $1;
    SERVICE_CONTROL_PAUSE       = $2;
    SERVICE_CONTROL_CONTINUE    = $3;
    SERVICE_CONTROL_INTERROGATE = $4;
    SERVICE_STOPPED             = $1;
    SERVICE_START_PENDING       = $2;
    SERVICE_STOP_PENDING        = $3;
    SERVICE_RUNNING             = $4;
    SERVICE_CONTINUE_PENDING    = $5;
    SERVICE_PAUSE_PENDING       = $6;
    SERVICE_PAUSED              = $7;

// #######################################################################################
// nt based service utilities
// #######################################################################################
function OpenSCManager(lpMachineName, lpDatabaseName: string; dwDesiredAccess :cardinal): HANDLE;
external &#39;OpenSCManager{#AW}@advapi32.dll stdcall&#39;;

function OpenService(hSCManager :HANDLE;lpServiceName: string; dwDesiredAccess :cardinal): HANDLE;
external &#39;OpenService{#AW}@advapi32.dll stdcall&#39;;

function CloseServiceHandle(hSCObject :HANDLE): boolean;
external &#39;CloseServiceHandle@advapi32.dll stdcall&#39;;

function CreateService(hSCManager :HANDLE;lpServiceName, lpDisplayName: string;dwDesiredAccess,dwServiceType,dwStartType,dwErrorControl: cardinal;lpBinaryPathName,lpLoadOrderGroup: String; lpdwTagId : cardinal;lpDependencies,lpServiceStartName,lpPassword :string): cardinal;
external &#39;CreateService{#AW}@advapi32.dll stdcall&#39;;

function DeleteService(hService :HANDLE): boolean;
external &#39;DeleteService@advapi32.dll stdcall&#39;;

function StartNTService(hService :HANDLE;dwNumServiceArgs : cardinal;lpServiceArgVectors : cardinal) : boolean;
external &#39;StartService{#AW}@advapi32.dll stdcall&#39;;

function ControlService(hService :HANDLE; dwControl :cardinal;var ServiceStatus :SERVICE_STATUS) : boolean;
external &#39;ControlService@advapi32.dll stdcall&#39;;

function QueryServiceStatus(hService :HANDLE;var ServiceStatus :SERVICE_STATUS) : boolean;
external &#39;QueryServiceStatus@advapi32.dll stdcall&#39;;

function QueryServiceStatusEx(hService :HANDLE;ServiceStatus :SERVICE_STATUS) : boolean;
external &#39;QueryServiceStatus@advapi32.dll stdcall&#39;;

function GetLastError() : cardinal;
external &#39;GetLastError@kernel32.dll stdcall&#39;;

function EnumDependentServices(
  hService :HANDLE; 
  ServiceStatus : LongInt; 
  lpServices: DWORD; 
  cbBufSize: DWORD; 
  var pcbBytesNeeded: DWORD; 
  var lpServicesReturned : DWORD) : boolean;
external &#39;EnumDependentServices{#AW}@advapi32.dll stdcall&#39;;

function HasServiceDependencies(const ServiceName: string) : boolean;
var  
  hSCM: HANDLE;
  hService: HANDLE;
  pcbBytesNeeded : DWORD;
  lpServicesReturned : DWORD;
begin
    hSCM := OpenSCManager(&#39;&#39;, &#39;&#39;, SC_MANAGER_ALL_ACCESS);
    Log(Format(&#39;%d&#39;, [hSCM]));
    if hSCM &lt;&gt; 0 then begin
        hService := OpenService(hSCM, ServiceName, SERVICE_QUERY_STATUS);
        Log(Format(&#39;%d&#39;, [hService]));
        if hService &lt;&gt; 0 then begin

            EnumDependentServices( hService, SERVICE_CURRENT_STATUS, 0, 0, pcbBytesNeeded, lpServicesReturned);
            
            MsgBox(SysErrorMessage(DLLGetLastError()), mbError, mb_Ok);

            CloseServiceHandle(hService)
            end;
        CloseServiceHandle(hSCM)
    end;
    Result := false;
end;

function InitializeSetup(): Boolean;
begin
  Log(&#39;InitializeSetup called&#39;);
  Result := HasServiceDependencies(&#39;Dnscache&#39;);
  if Result = False then
    MsgBox(&#39;InitializeSetup:&#39; #13#13 &#39;Ok, bye bye.&#39;, mbInformation, MB_OK);
end;

I tried modifying the Status and Query Codes to other ones, but that didn't help, either.

答案1

得分: 2

根据EnumDependentServices()文档:

> [in] hService
>
> 服务句柄。此句柄由OpenServiceCreateService函数返回,必须具有SERVICE_ENUMERATE_DEPENDENTS访问权限。有关更多信息,请参阅服务安全性和访问权限
>
> ...
>
> 服务控制管理器可以设置以下错误代码。服务控制管理器调用的注册表函数可能设置其他错误代码。
>
> | 返回代码 | 描述 |
> | ----------- | ----------- |
> | ERROR_ACCESS_DENIED | 句柄没有SERVICE_ENUMERATE_DEPENDENTS访问权限。 |

当您打开hService句柄时,您未指定SERVICE_ENUMERATE_DEPENDENTS权限,只指定了SERVICE_QUERY_STATUS权限(仅由QueryServiceStatus/Ex()NotifyServiceStatusChange()使用,而不是EnumDependentServices())。

此外,SC_MANAGER_ALL_ACCESS权限太多,不要请求比实际需要的权限更多。在这种情况下,您只需要SC_MANAGER_CONNECT

此外,SERVICE_CURRENT_STATUS不是Win32 API定义的有效符号。您将其声明为$0003,这与SERVICE_STATE_ALL的值相同,而EnumDependentServices()实际定义了SERVICE_STATE_ALL

请尝试使用以下代码:

const
  ...
  SERVICE_STATE_ALL = $0003;
  SERVICE_ENUMERATE_DEPENDENTS = $0008;
  ERROR_MORE_DATA = 234;
  ...

procedure LogError(const FuncName: string);
var
  ErrCode: DWORD;
  ErrMsg: string;
begin
  ErrCode := GetLastError();
  ErrMsg := Format('%s failed: (%d) %s', [FuncName, ErrCode, SysErrorMessage(ErrCode)]);
  Log(ErrMsg);
  MsgBox(ErrMsg, mbError, mb_Ok);
end;

function HasServiceDependencies(const ServiceName: string): boolean;
var
  hSCM: HANDLE;
  hService: HANDLE;
  dwBytesNeeded: DWORD;
  dwServicesReturned: DWORD;
begin
  Result := False;
  hSCM := OpenSCManager('', '', SC_MANAGER_CONNECT);
  if hSCM = 0 then
  begin
    LogError('OpenSCManager');
  end
  else
  begin
    hService := OpenService(hSCM, ServiceName, SERVICE_ENUMERATE_DEPENDENTS);
    if hService = 0 then
    begin
      LogError('OpenService');
    end
    else
    begin
      // 指定空缓冲区,如果没有依赖项,将返回True,否则将返回False并设置LastError=ERROR_MORE_DATA
      if EnumDependentServices(hService, SERVICE_STATE_ALL, nil, 0, dwBytesNeeded, dwServicesReturned) then
      begin
        Log(Format('%s has no dependencies', [ServiceName]));
      end
      else if GetLastError() <> ERROR_MORE_DATA then
      begin
        LogError('EnumDependentServices');
      end
      else
      begin
        Log(Format('%s has dependencies', [ServiceName]));
      end;
      CloseServiceHandle(hService);
    end;
    CloseServiceHandle(hSCM);
  end;
end;
英文:

Per the EnumDependentServices() documentation:

> [in] hService
>
> A handle to the service. This handle is returned by the OpenService or CreateService function, and it must have the SERVICE_ENUMERATE_DEPENDENTS access right. For more information, see Service Security and Access Right
>
> ...
>
> The following error codes may be set by the service control manager. Other error codes may be set by the registry functions that are called by the service control manager.
>
> | Return code | Description |
> | ----------- | ----------- |
> | ERROR_ACCESS_DENIED | The handle does not have the SERVICE_ENUMERATE_DEPENDENTS access right. |

When you are opening your hService handle, you are not specifying the SERVICE_ENUMERATE_DEPENDENTS right, only the SERVICE_QUERY_STATUS right (which is used only by QueryServiceStatus/Ex() and NotifyServiceStatusChange(), not by EnumDependentServices()).

Also, SC_MANAGER_ALL_ACCESS is too many rights to ask for. Don't ask for more rights than you actually need. In this case, you only need SC_MANAGER_CONNECT.

Also, SERVICE_CURRENT_STATUS is not a valid symbol defined by the Win32 API. You are declaring it as $0003, which is the same value as SERVICE_STATE_ALL, which is the actual symbol that EnumDependentServices() defines.

Try this instead:

const
  ...
  SERVICE_STATE_ALL = $0003;
  SERVICE_ENUMERATE_DEPENDENTS = $0008;
  ERROR_MORE_DATA = 234;
  ...

procedure LogError(const FuncName: string);
var
  ErrCode : DWORD;
  ErrMsg : string; 
begin
  ErrCode := GetLastError();
  ErrMsg := Format(&#39;%s failed: (%d) %s&#39;, [FuncName, ErrCode, SysErrorMessage(ErrCode)]);
  Log(ErrMsg);
  MsgBox(ErrMsg, mbError, mb_Ok);
end;

function HasServiceDependencies(const ServiceName: string) : boolean;
var  
  hSCM: HANDLE;
  hService: HANDLE;
  dwBytesNeeded : DWORD;
  dwServicesReturned : DWORD;
begin
  Result := False;
  hSCM := OpenSCManager(&#39;&#39;, &#39;&#39;, SC_MANAGER_CONNECT);
  if hSCM = 0 then begin
    LogError(&#39;OpenSCManager&#39;);
  end
  else begin
    hService := OpenService(hSCM, ServiceName, SERVICE_ENUMERATE_DEPENDENTS);
    if hService = 0 then begin
      LogError(&#39;OpenService&#39;);
    end
    else begin
      // specifying a nil buffer, so will return True if there are no dependencies,
      // otherwise will return False and set LastError=ERROR_MORE_DATA if
      // dependency data is available...
      if EnumDependentServices(hService, SERVICE_STATE_ALL, 0, 0, dwBytesNeeded, dwServicesReturned) then begin
        Log(Format(&#39;%s has no dependencies&#39;, [ServiceName]));
      end
      else if GetLastError() &lt;&gt; ERROR_MORE_DATA then begin
        LogError(&#39;EnumDependentServices&#39;);
      end
      else begin
        Log(Format(&#39;%s has dependencies&#39;, [ServiceName]));
      end;
      CloseServiceHandle(hService);
    end;
    CloseServiceHandle(hSCM);
  end;
end;

huangapple
  • 本文由 发表于 2023年4月11日 01:08:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75979112.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定